feat: add manual 'Check Update' button in Settings > About section

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 22:30:25 +08:00
parent 029abaa60c
commit 92ae0b5e46
2 changed files with 50 additions and 7 deletions

View File

@@ -23,6 +23,7 @@ import com.tiangong.aiagent.data.local.TokenDataStore
import com.tiangong.aiagent.data.remote.ApiService import com.tiangong.aiagent.data.remote.ApiService
import com.tiangong.aiagent.data.remote.DynamicUrlInterceptor import com.tiangong.aiagent.data.remote.DynamicUrlInterceptor
import com.tiangong.aiagent.data.repository.AuthRepository import com.tiangong.aiagent.data.repository.AuthRepository
import com.tiangong.aiagent.util.AppUpdateManager
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@@ -37,12 +38,16 @@ class SettingsViewModel @Inject constructor(
private val authRepository: AuthRepository, private val authRepository: AuthRepository,
private val tokenDataStore: TokenDataStore, private val tokenDataStore: TokenDataStore,
private val dynamicUrlInterceptor: DynamicUrlInterceptor, private val dynamicUrlInterceptor: DynamicUrlInterceptor,
private val apiService: ApiService private val apiService: ApiService,
private val appUpdateManager: AppUpdateManager
) : ViewModel() { ) : ViewModel() {
private val _username = MutableStateFlow("admin") private val _username = MutableStateFlow("admin")
val username: StateFlow<String> = _username.asStateFlow() val username: StateFlow<String> = _username.asStateFlow()
private val _updateMessage = MutableStateFlow<String?>(null)
val updateMessage: StateFlow<String?> = _updateMessage.asStateFlow()
val ttsEnabled: StateFlow<Boolean> = tokenDataStore.ttsEnabled val ttsEnabled: StateFlow<Boolean> = tokenDataStore.ttsEnabled
.stateIn(viewModelScope, SharingStarted.Eagerly, false) .stateIn(viewModelScope, SharingStarted.Eagerly, false)
@@ -80,6 +85,23 @@ class SettingsViewModel @Inject constructor(
val notificationQuietEnd: StateFlow<String> = tokenDataStore.notificationQuietEnd val notificationQuietEnd: StateFlow<String> = tokenDataStore.notificationQuietEnd
.stateIn(viewModelScope, SharingStarted.Eagerly, "07:00") .stateIn(viewModelScope, SharingStarted.Eagerly, "07:00")
fun checkUpdate() {
_updateMessage.value = "正在检查..."
appUpdateManager.checkForUpdate { response ->
_updateMessage.value = if (response.hasUpdate) {
"发现新版本 ${response.latestVersion}"
} else if (response.releaseNotes.isNotBlank()) {
response.releaseNotes // error message
} else {
"已是最新版本 (${response.currentVersion})"
}
}
}
fun clearUpdateMessage() {
_updateMessage.value = null
}
fun logout() { fun logout() {
viewModelScope.launch { viewModelScope.launch {
authRepository.logout() authRepository.logout()
@@ -380,7 +402,7 @@ fun SettingsScreen(
ListItem( ListItem(
headlineContent = { Text("天工智能体") }, headlineContent = { Text("天工智能体") },
supportingContent = { Text("版本 1.0.0") }, supportingContent = { Text("版本 ${BuildConfig.VERSION_NAME}") },
leadingContent = { leadingContent = {
Icon(Icons.Default.Info, contentDescription = null) Icon(Icons.Default.Info, contentDescription = null)
}, },
@@ -389,6 +411,18 @@ fun SettingsScreen(
} else Modifier } else Modifier
) )
HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp))
val updateMessage by viewModel.updateMessage.collectAsState()
ListItem(
headlineContent = { Text("检查更新") },
supportingContent = { Text(updateMessage ?: "点击检查新版本") },
leadingContent = {
Icon(Icons.Default.Update, contentDescription = null)
},
modifier = Modifier.clickable { viewModel.checkUpdate() }
)
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(32.dp))
// Logout button // Logout button

View File

@@ -23,20 +23,29 @@ class AppUpdateManager @Inject constructor(
* Check for app update asynchronously. If update is found, shows a dialog. * Check for app update asynchronously. If update is found, shows a dialog.
* Called from MainActivity.onCreate(). * Called from MainActivity.onCreate().
*/ */
fun checkForUpdate() { fun checkForUpdate(onResult: ((AppVersionResponse) -> Unit)? = null) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
val response = apiService.checkUpdate( val response = apiService.checkUpdate(
platform = "android", platform = "android",
version = BuildConfig.VERSION_NAME version = BuildConfig.VERSION_NAME
) )
if (response.hasUpdate) { withContext(Dispatchers.Main) {
withContext(Dispatchers.Main) { if (response.hasUpdate) {
showUpdateDialog(response) showUpdateDialog(response)
} }
onResult?.invoke(response)
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
onResult?.invoke(
AppVersionResponse(
latestVersion = "", currentVersion = BuildConfig.VERSION_NAME,
hasUpdate = false, forceUpdate = false,
releaseNotes = "检查失败: ${e.message}"
)
)
} }
} catch (_: Exception) {
// Silently ignore — update check is non-critical
} }
} }
} }