diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/icon/OxygenIcons.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/icon/OxygenIcons.kt index 8744fe8..4e2bef7 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/icon/OxygenIcons.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/icon/OxygenIcons.kt @@ -13,6 +13,7 @@ import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.Inbox import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.Reorder +import androidx.compose.material.icons.filled.Upgrade import androidx.compose.material.icons.outlined.Home import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.StarBorder @@ -55,6 +56,7 @@ object OxygenIcons { val Success = Icons.Rounded.CheckCircle val Time = Icons.Default.AccessTime val Tool = Icons.Default.Build + val Upgrade = Icons.Default.Upgrade @OptIn(ExperimentalEncodingApi::class) fun fromSvgBase64(base64String: String): ImageBitmap { diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/tool/ToolEntity.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/tool/ToolEntity.kt index 84a292d..e6f6f02 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/tool/ToolEntity.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/model/tool/ToolEntity.kt @@ -54,6 +54,25 @@ data class ToolEntity( @ColumnInfo(defaultValue = "NULL") val upgrade: String? = null ) { + constructor(toolId: String, authorUsername: String, ver: String, upgrade: String? = null) : + this( + id = -1, + name = "Unknown", + toolId = toolId, + icon = "", + platform = Platform.Android, + authorUsername = authorUsername, + authorNickname = "Unknown", + authorAvatar = "", + ver = ver, + keywords = emptyList(), + categories = emptyList(), + entryPoint = "", + createTime = LocalDateTime(1970, 1, 1, 0, 0), + updateTime = LocalDateTime(1970, 1, 1, 0, 0), + upgrade = upgrade + ) + enum class Platform { Web, diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/component/ToolCard.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/component/ToolCard.kt index fad7295..304c63a 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/component/ToolCard.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/component/ToolCard.kt @@ -39,6 +39,7 @@ import top.fatweb.oxygen.toolbox.model.tool.ToolEntity fun ToolCard( modifier: Modifier = Modifier, tool: ToolEntity, + specifyVer: String? = null, actionIcon: ImageVector? = null, actionIconContentDescription: String = "", onAction: () -> Unit = {}, @@ -59,7 +60,7 @@ fun ToolCard( modifier = Modifier.padding(16.dp) ) { ToolHeader( - ver = tool.ver, + ver = specifyVer ?: tool.ver, actionIcon = actionIcon, actionIconContentDescription = actionIconContentDescription, onAction = onAction diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolStoreScreen.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolStoreScreen.kt index f9638f2..3059fcc 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolStoreScreen.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolStoreScreen.kt @@ -73,7 +73,10 @@ internal fun ToolStoreRoute( modifier = modifier, onNavigateToToolView = onNavigateToToolView, toolStorePagingItems = toolStorePagingItems, - onChangeInstallStatus = viewModel::changeInstallStatus, + onChangeInstallStatus = viewModel::changeInstallInfo, + onChangeInstallType = { + viewModel.changeInstallInfo(type = it) + }, onInstallTool = viewModel::installTool, installInfo = installInfo ) @@ -84,8 +87,9 @@ internal fun ToolStoreScreen( modifier: Modifier = Modifier, onNavigateToToolView: (username: String, toolId: String) -> Unit, toolStorePagingItems: LazyPagingItems, - onChangeInstallStatus: (installStatus: ToolStoreUiState.Status) -> Unit, - onInstallTool: (username: String, toolId: String) -> Unit, + onChangeInstallStatus: (status: ToolStoreUiState.InstallInfo.Status) -> Unit, + onChangeInstallType: (type: ToolStoreUiState.InstallInfo.Type) -> Unit, + onInstallTool: (installTool: ToolEntity) -> Unit, installInfo: ToolStoreUiState.InstallInfo ) { val isToolLoading = @@ -101,8 +105,7 @@ internal fun ToolStoreScreen( val infiniteTransition = rememberInfiniteTransition(label = "infiniteTransition") - var installToolUsername by remember { mutableStateOf("Unknown") } - var installToolId by remember { mutableStateOf("Unknown") } + var installTool by remember { mutableStateOf(ToolEntity("Unknown", "Unknown", "Unknown")) } Box( modifier.fillMaxSize() @@ -116,12 +119,14 @@ internal fun ToolStoreScreen( ) { toolsPanel( toolStorePagingItems = toolStorePagingItems, - onAction = { username, toolId -> - installToolUsername = username - installToolId = toolId - onChangeInstallStatus(ToolStoreUiState.Status.Pending) + onAction = { tool, installType -> + installTool = tool + onChangeInstallStatus(ToolStoreUiState.InstallInfo.Status.Pending) + onChangeInstallType(installType) }, - onClick = onNavigateToToolView + onClick = { + onNavigateToToolView(it.authorUsername, it.toolId) + } ) item(span = StaggeredGridItemSpan.FullLine) { @@ -165,18 +170,17 @@ internal fun ToolStoreScreen( } InstallAlertDialog( - status = installInfo.status, + installTool = installTool, + installInfo = installInfo, onChangeInstallStatus = onChangeInstallStatus, - onInstallTool = { onInstallTool(installToolUsername, installToolId) }, - username = installToolUsername, - toolId = installToolId + onInstallTool = { onInstallTool(installTool) } ) } private fun LazyStaggeredGridScope.toolsPanel( toolStorePagingItems: LazyPagingItems, - onAction: (username: String, toolId: String) -> Unit, - onClick: (username: String, toolId: String) -> Unit + onAction: (installTool: ToolEntity, installType: ToolStoreUiState.InstallInfo.Type) -> Unit, + onClick: (ToolEntity) -> Unit ) { items( items = toolStorePagingItems.itemSnapshotList, @@ -184,28 +188,34 @@ private fun LazyStaggeredGridScope.toolsPanel( ) { ToolCard( tool = it!!, - actionIcon = if (!it.isInstalled) OxygenIcons.Download else null, + specifyVer = it.upgrade, + actionIcon = if (it.upgrade != null) OxygenIcons.Upgrade else if (!it.isInstalled) OxygenIcons.Download else null, actionIconContentDescription = stringResource(R.string.core_install), - onAction = { onAction(it.authorUsername, it.toolId) }, - onClick = { onClick(it.authorUsername, it.toolId) }, - onLongClick = { onClick(it.authorUsername, it.toolId) }, + onAction = { + onAction( + it, + if (it.upgrade != null) ToolStoreUiState.InstallInfo.Type.Upgrade else ToolStoreUiState.InstallInfo.Type.Install + ) + }, + onClick = { onClick(it) } ) } } @Composable private fun InstallAlertDialog( - status: ToolStoreUiState.Status, - onChangeInstallStatus: (ToolStoreUiState.Status) -> Unit, - onInstallTool: () -> Unit, - username: String, - toolId: String + installTool: ToolEntity, + installInfo: ToolStoreUiState.InstallInfo, + onChangeInstallStatus: (ToolStoreUiState.InstallInfo.Status) -> Unit, + onInstallTool: () -> Unit ) { - if (status != ToolStoreUiState.Status.None) { + val (status, type) = installInfo + + if (status != ToolStoreUiState.InstallInfo.Status.None) { AlertDialog( onDismissRequest = { - if (status == ToolStoreUiState.Status.Pending) { - onChangeInstallStatus(ToolStoreUiState.Status.None) + if (status == ToolStoreUiState.InstallInfo.Status.Pending) { + onChangeInstallStatus(ToolStoreUiState.InstallInfo.Status.None) } }, title = { @@ -214,20 +224,34 @@ private fun InstallAlertDialog( ) { Icon( imageVector = when (status) { - ToolStoreUiState.Status.Success -> OxygenIcons.Success - ToolStoreUiState.Status.Fail -> OxygenIcons.Error + ToolStoreUiState.InstallInfo.Status.Success -> OxygenIcons.Success + ToolStoreUiState.InstallInfo.Status.Fail -> OxygenIcons.Error else -> OxygenIcons.Info }, - contentDescription = stringResource(R.string.core_install) + contentDescription = stringResource( + when (type) { + ToolStoreUiState.InstallInfo.Type.Install -> R.string.core_install + ToolStoreUiState.InstallInfo.Type.Upgrade -> R.string.core_upgrade + } + ) ) Spacer(modifier = Modifier.width(4.dp)) Text( text = stringResource( - when (status) { - ToolStoreUiState.Status.Success -> R.string.feature_store_install_success - ToolStoreUiState.Status.Fail -> R.string.feature_store_install_fail - else -> R.string.feature_store_install_tool + when (type) { + ToolStoreUiState.InstallInfo.Type.Install -> when (status) { + ToolStoreUiState.InstallInfo.Status.Success -> R.string.feature_store_install_success + ToolStoreUiState.InstallInfo.Status.Fail -> R.string.feature_store_install_fail + else -> R.string.feature_store_install_tool + } + + ToolStoreUiState.InstallInfo.Type.Upgrade -> when (status) { + ToolStoreUiState.InstallInfo.Status.Success -> R.string.feature_store_upgrade_success + ToolStoreUiState.InstallInfo.Status.Fail -> R.string.feature_store_upgrade_fail + else -> R.string.feature_store_upgrade_tool + } } + ) ) } @@ -239,39 +263,71 @@ private fun InstallAlertDialog( .padding(vertical = 16.dp) ) { when (status) { - ToolStoreUiState.Status.Pending -> + ToolStoreUiState.InstallInfo.Status.Pending -> Text( - text = stringResource( - R.string.feature_store_ask_install, - username, - toolId - ) + text = when (type) { + ToolStoreUiState.InstallInfo.Type.Install -> stringResource( + R.string.feature_store_ask_install, + installTool.authorUsername, + installTool.toolId + ) + + ToolStoreUiState.InstallInfo.Type.Upgrade -> stringResource( + R.string.feature_store_ask_upgrade, + installTool.authorUsername, + installTool.toolId, + installTool.ver, + installTool.upgrade ?: installTool.ver + ) + } + ) - ToolStoreUiState.Status.Installing -> + ToolStoreUiState.InstallInfo.Status.Processing -> Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { CircularProgressIndicator() Spacer(modifier = Modifier.height(16.dp)) - Text(text = stringResource(R.string.core_installing)) + Text( + text = stringResource( + when (type) { + ToolStoreUiState.InstallInfo.Type.Install -> R.string.core_installing + ToolStoreUiState.InstallInfo.Type.Upgrade -> R.string.core_upgrading + } + ) + ) } - ToolStoreUiState.Status.Success -> - Text(text = stringResource(R.string.feature_store_install_success_info)) + ToolStoreUiState.InstallInfo.Status.Success -> + Text( + text = stringResource( + when (type) { + ToolStoreUiState.InstallInfo.Type.Install -> R.string.feature_store_install_success_info + ToolStoreUiState.InstallInfo.Type.Upgrade -> R.string.feature_store_upgrade_success_info + } + ) + ) - ToolStoreUiState.Status.Fail -> - Text(text = stringResource(R.string.feature_store_install_fail_info)) + ToolStoreUiState.InstallInfo.Status.Fail -> + Text( + text = stringResource( + when (type) { + ToolStoreUiState.InstallInfo.Type.Install -> R.string.feature_store_install_fail_info + ToolStoreUiState.InstallInfo.Type.Upgrade -> R.string.feature_store_upgrade_fail_info + } + ) + ) else -> Unit } } }, dismissButton = { - if (status == ToolStoreUiState.Status.Pending) { + if (status == ToolStoreUiState.InstallInfo.Status.Pending) { TextButton(onClick = { - onChangeInstallStatus(ToolStoreUiState.Status.None) + onChangeInstallStatus(ToolStoreUiState.InstallInfo.Status.None) }) { Text(text = stringResource(R.string.core_cancel)) } @@ -279,19 +335,23 @@ private fun InstallAlertDialog( }, confirmButton = { when (status) { - ToolStoreUiState.Status.Pending -> + ToolStoreUiState.InstallInfo.Status.Pending -> TextButton(onClick = onInstallTool) { - Text(text = stringResource(R.string.core_install)) + Text(text = stringResource( + when (type) { + ToolStoreUiState.InstallInfo.Type.Install -> R.string.core_install + ToolStoreUiState.InstallInfo.Type.Upgrade -> R.string.core_upgrade + })) } - ToolStoreUiState.Status.Success, - ToolStoreUiState.Status.Fail -> + ToolStoreUiState.InstallInfo.Status.Success, + ToolStoreUiState.InstallInfo.Status.Fail -> TextButton(onClick = { - onChangeInstallStatus(ToolStoreUiState.Status.None) + onChangeInstallStatus(ToolStoreUiState.InstallInfo.Status.None) }) { Text( text = stringResource( - if (status == ToolStoreUiState.Status.Success) R.string.core_ok + if (status == ToolStoreUiState.InstallInfo.Status.Success) R.string.core_ok else R.string.core_close ) ) diff --git a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolStoreViewModel.kt b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolStoreViewModel.kt index 94a67d9..1a19c50 100644 --- a/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolStoreViewModel.kt +++ b/app/src/main/kotlin/top/fatweb/oxygen/toolbox/ui/tool/ToolStoreViewModel.kt @@ -38,24 +38,36 @@ class ToolStoreViewModel @Inject constructor( ) } - fun changeInstallStatus(installStatus: ToolStoreUiState.Status) { - savedStateHandle[INSTALL_INFO] = ToolStoreUiState.InstallInfo(installStatus) + fun changeInstallInfo( + status: ToolStoreUiState.InstallInfo.Status = installInfo.value.status, + type: ToolStoreUiState.InstallInfo.Type = installInfo.value.type + ) { + savedStateHandle[INSTALL_INFO] = ToolStoreUiState.InstallInfo(status, type) } - fun installTool(username: String, toolId: String) { + fun installTool( + toolEntity: ToolEntity + ) { viewModelScope.launch { - storeRepository.detail(username, toolId).collect { - when (it) { - Result.Loading -> savedStateHandle[INSTALL_INFO] = - ToolStoreUiState.InstallInfo(ToolStoreUiState.Status.Installing) + storeRepository.detail(toolEntity.authorUsername, toolEntity.toolId).collect { result -> + when (result) { + Result.Loading -> changeInstallInfo(status = ToolStoreUiState.InstallInfo.Status.Processing) - is Result.Error, is Result.Fail -> savedStateHandle[INSTALL_INFO] = - ToolStoreUiState.InstallInfo(ToolStoreUiState.Status.Fail) + is Result.Error, is Result.Fail -> changeInstallInfo(status = ToolStoreUiState.InstallInfo.Status.Fail) is Result.Success -> { - toolRepository.saveTool(it.data) - savedStateHandle[INSTALL_INFO] = - ToolStoreUiState.InstallInfo(ToolStoreUiState.Status.Success) + when (installInfo.value.type) { + ToolStoreUiState.InstallInfo.Type.Install -> toolRepository.saveTool( + result.data + ) + + ToolStoreUiState.InstallInfo.Type.Upgrade -> { + toolRepository.removeTool(toolEntity) + toolRepository.saveTool(result.data) + } + } + + changeInstallInfo(status = ToolStoreUiState.InstallInfo.Status.Success) } } } @@ -69,11 +81,16 @@ data class ToolStoreUiState( ) : Parcelable { @Parcelize data class InstallInfo( - var status: Status = Status.None - ) : Parcelable + var status: Status = Status.None, + var type: Type = Type.Install + ) : Parcelable { + enum class Status { + None, Pending, Processing, Success, Fail + } - enum class Status { - None, Pending, Installing, Success, Fail + enum class Type { + Install, Upgrade + } } } diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 2f1c3e0..c3cba93 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -14,6 +14,8 @@ ⚠️ 无法连接至互联网 安装 安装中…… + 更新 + 更新中…… 卸载 卸载成功 取消 @@ -21,11 +23,17 @@ 商店 安装工具 - 确定安装由用户 %1$s 提供的工具 %2$s 吗? + 确定安装由用户 %1$s 提供的工具 %2$s 吗? 安装成功 恭喜!工具安装成功。 安装失败 安装失败!请稍后重试…… + 更新工具 + 确定要将用户 %2$s 提供的工具 %1$s 从版本 %3$s 更新到版本 %4$s 吗? + 更新成功 + 恭喜!工具更新成功。 + 更新失败 + 更新失败!请稍后重试…… 工具 简介 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 81f668f..0e11509 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,8 @@ ⚠️ Unable to connect to the internet Install Installing… + Upgrade + Upgrading… Uninstall Uninstalled successfully Cancel @@ -20,11 +22,17 @@ Store Install Tool - Are you sure to install tool %1$s provided by user %2$s? + Are you sure you want to install tool %1$s provided by user %2$s? Install Success Congratulations, the tool installation is successful. Install Failed Installation failed, please try again later… + Upgrade Tool + Are you sure you want to upgrade tool %1$s provided by user %2$s from version %3$s to version %4$s? + Upgrade Success + Congratulations, the tool upgrade was successful. + Upgrade Failed + Upgrade failed, please try again later… Tools Desc