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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user