From 80a2eb8325b740f49f9d46fe77171738441c095e Mon Sep 17 00:00:00 2001 From: KremeCN <3265494849@qq.com> Date: Fri, 28 Aug 2026 09:45:39 +0800 Subject: [PATCH] feat(app): add about screen and mark 1.0 release --- android/app/build.gradle.kts | 2 +- .../abox/manager/ui/ABoxManagerApp.kt | 15 ++ .../abox/manager/ui/about/AboutScreen.kt | 183 ++++++++++++++++++ .../manager/ui/shell/AccountProviderDialog.kt | 9 + .../src/main/res/values-zh-rCN/strings.xml | 19 ++ android/app/src/main/res/values/strings.xml | 19 ++ 6 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 android/app/src/main/kotlin/com/flagship/abox/manager/ui/about/AboutScreen.kt diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index a985b5d..2799f3d 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -20,7 +20,7 @@ android { minSdk = 26 targetSdk = 36 versionCode = 1 - versionName = "0.1.0" + versionName = "1.0.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt index 5925e6e..210eb3a 100644 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/ABoxManagerApp.kt @@ -25,6 +25,7 @@ import com.flagship.abox.manager.domain.Username import com.flagship.abox.manager.ui.login.LoginRoute import com.flagship.abox.manager.ui.login.LoginViewModel import com.flagship.abox.manager.ui.navigation.AuthenticatedNavigation +import com.flagship.abox.manager.ui.about.AboutScreen import com.flagship.abox.manager.ui.preferences.SettingsRoute import com.flagship.abox.manager.ui.preferences.SettingsExperienceState import com.flagship.abox.manager.ui.preferences.SettingsViewModel @@ -49,6 +50,7 @@ fun ABoxManagerApp( val screenKey = sessionState.screenKey() var showAccountDialog by rememberSaveable(screenKey) { mutableStateOf(false) } var showSettings by rememberSaveable(screenKey) { mutableStateOf(false) } + var showAbout by rememberSaveable(screenKey) { mutableStateOf(false) } val settingsExperienceState = rememberSaveable(screenKey, saver = SettingsExperienceState.Saver) { SettingsExperienceState() } @@ -87,9 +89,11 @@ fun ABoxManagerApp( onSessionAction = sessionActionsViewModel::dispatch, showAccountDialog = showAccountDialog, showSettings = showSettings, + showAbout = showAbout, settingsExperienceState = settingsExperienceState, onShowAccountDialogChange = { showAccountDialog = it }, onShowSettingsChange = { showSettings = it }, + onShowAboutChange = { showAbout = it }, ) } } @@ -104,9 +108,11 @@ private fun AuthenticatedNavHost( onSessionAction: (SessionUiAction) -> Unit, showAccountDialog: Boolean, showSettings: Boolean, + showAbout: Boolean, settingsExperienceState: SettingsExperienceState, onShowAccountDialogChange: (Boolean) -> Unit, onShowSettingsChange: (Boolean) -> Unit, + onShowAboutChange: (Boolean) -> Unit, ) { Box(Modifier.fillMaxSize()) { AuthenticatedNavigation( @@ -127,6 +133,11 @@ private fun AuthenticatedNavHost( ) } } + if (showAbout) { + ABoxBackLayer(onBack = { onShowAboutChange(false) }) { + AboutScreen(onBack = { onShowAboutChange(false) }) + } + } } if (showAccountDialog) { @@ -139,6 +150,10 @@ private fun AuthenticatedNavHost( settingsExperienceState.showTheme(returnToHome = false) onShowSettingsChange(true) }, + onOpenAbout = { + onShowAccountDialogChange(false) + onShowAboutChange(true) + }, onSwitchProvider = { onShowAccountDialogChange(false) onSessionAction(SessionUiAction.SwitchProvider) diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/about/AboutScreen.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/about/AboutScreen.kt new file mode 100644 index 0000000..90e6d80 --- /dev/null +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/about/AboutScreen.kt @@ -0,0 +1,183 @@ +package com.flagship.abox.manager.ui.about + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.flagship.abox.manager.R +import com.flagship.abox.manager.designsystem.ABoxIconButton +import com.flagship.abox.manager.designsystem.icon.ABoxIconKey + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AboutScreen( + onBack: () -> Unit, + modifier: Modifier = Modifier, +) { + val context = LocalContext.current + val packageInfo = remember(context.packageName) { + runCatching { + context.packageManager.getPackageInfo(context.packageName, 0) + }.getOrNull() + } + val versionName = packageInfo?.versionName ?: stringResource(R.string.about_unknown) + val versionCode = packageInfo?.let { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) it.longVersionCode else { + @Suppress("DEPRECATION") + it.versionCode.toLong() + } + } + + Scaffold( + modifier = modifier.fillMaxSize(), + topBar = { + TopAppBar( + title = { Text(stringResource(R.string.about_title)) }, + navigationIcon = { + ABoxIconButton( + key = ABoxIconKey.BACK, + contentDescription = stringResource(R.string.action_back), + onClick = onBack, + ) + }, + ) + }, + ) { padding -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(padding) + .verticalScroll(rememberScrollState()) + .padding(horizontal = 20.dp, vertical = 24.dp), + verticalArrangement = Arrangement.spacedBy(24.dp), + ) { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + Image( + painter = painterResource(R.drawable.ic_abox_logo_master), + contentDescription = stringResource(R.string.app_name), + modifier = Modifier.size(88.dp), + ) + Text( + stringResource(R.string.app_name), + style = MaterialTheme.typography.headlineMedium, + fontWeight = FontWeight.Bold, + ) + Text( + stringResource(R.string.about_tagline), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + + AboutSection(stringResource(R.string.about_app_info)) { + AboutValueRow(stringResource(R.string.about_version), "$versionName (${versionCode ?: stringResource(R.string.about_unknown)})") + AboutValueRow(stringResource(R.string.about_package), context.packageName) + AboutValueRow(stringResource(R.string.about_platform), stringResource(R.string.about_platform_value)) + } + + AboutSection(stringResource(R.string.about_product)) { + Text( + stringResource(R.string.about_product_desc), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + + AboutSection(stringResource(R.string.about_team)) { + TeamMember("KremeCN", stringResource(R.string.about_role_foundation)) + TeamMember("alZerNest", stringResource(R.string.about_role_devices)) + TeamMember("Aurum", stringResource(R.string.about_role_environment)) + TeamMember("Moler", stringResource(R.string.about_role_infrared)) + TeamMember("tzh", stringResource(R.string.about_role_scenes)) + } + + Text( + stringResource(R.string.about_copyright), + modifier = Modifier.fillMaxWidth(), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Spacer(Modifier.height(12.dp)) + } + } +} + +@Composable +private fun AboutSection( + title: String, + content: @Composable () -> Unit, +) { + Column(verticalArrangement = Arrangement.spacedBy(10.dp)) { + Text(title, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold) + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(8.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), + ) { + Column( + modifier = Modifier.fillMaxWidth().padding(16.dp), + verticalArrangement = Arrangement.spacedBy(14.dp), + ) { + content() + } + } + } +} + +@Composable +private fun AboutValueRow(label: String, value: String) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text(label, color = MaterialTheme.colorScheme.onSurfaceVariant) + Text(value, fontWeight = FontWeight.Medium, modifier = Modifier.padding(start = 16.dp)) + } +} + +@Composable +private fun TeamMember(name: String, role: String) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text(name, fontWeight = FontWeight.SemiBold) + Text( + role, + modifier = Modifier.padding(start = 16.dp), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } +} diff --git a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/shell/AccountProviderDialog.kt b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/shell/AccountProviderDialog.kt index d792c1f..74e7ae7 100644 --- a/android/app/src/main/kotlin/com/flagship/abox/manager/ui/shell/AccountProviderDialog.kt +++ b/android/app/src/main/kotlin/com/flagship/abox/manager/ui/shell/AccountProviderDialog.kt @@ -15,6 +15,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.Logout import androidx.compose.material.icons.outlined.CloudDone import androidx.compose.material.icons.outlined.Palette +import androidx.compose.material.icons.outlined.Info import androidx.compose.material3.AlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -46,6 +47,7 @@ fun AccountProviderDialog( providerName: String, onDismissRequest: () -> Unit, onOpenSettings: () -> Unit, + onOpenAbout: () -> Unit, onSwitchProvider: () -> Unit, onEndSession: () -> Unit, onForgetCredentials: () -> Unit, @@ -113,6 +115,13 @@ fun AccountProviderDialog( tag = "account_dialog_open_settings", onClick = onOpenSettings, ) + AccountSheetAction( + icon = Icons.Outlined.Info, + label = stringResource(R.string.account_about), + description = stringResource(R.string.account_about_desc), + tag = "account_dialog_open_about", + onClick = onOpenAbout, + ) AccountSheetAction( icon = Icons.AutoMirrored.Outlined.Logout, label = stringResource(R.string.account_logout), diff --git a/android/app/src/main/res/values-zh-rCN/strings.xml b/android/app/src/main/res/values-zh-rCN/strings.xml index 5e117e2..8bc922e 100644 --- a/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/android/app/src/main/res/values-zh-rCN/strings.xml @@ -348,6 +348,25 @@ 退出当前会话 结束当前连接并返回登录流程 确定退出当前会话吗? + 关于 + App 信息、版本与开发团队 + 关于 + 面向 Android 的 ABox 智能家居管理应用 + App 信息 + 版本 + 应用包名 + 平台 + Android 手机与平板 + 产品介绍 + ABox 家庭通过 ABSDK 连接 ABox 网关,提供设备控制、红外遥控器、情景、自动化与活动记录。本地体验提供独立的模拟环境,不使用 ABox 凭据。 + 开发团队 + 项目架构与全模块开发 + 设备目录与导航 + 插座与环境传感器 + 红外遥控器 + 情景与应用集成 + 由 Flagship 开发团队共同构建。 + 未知 新建指令 选择指令的启动方式 手动点击运行,适合常用的一键设备组合。 diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 502a8a2..5bbcec3 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -348,6 +348,25 @@ Sign out End the current connection and return to sign-in Sign out of the current session? + About + App information, version, and development team + About + ABox smart home management for Android + App information + Version + Package + Platform + Android phones and tablets + Product + ABox Home connects to an ABox gateway through ABSDK and provides device control, infrared remotes, scenes, automation, and activity history. Local Experience offers an isolated simulation environment without using ABox credentials. + Development team + Project architecture and development across all modules + Device catalog and navigation + Socket and environment sensors + Infrared remotes + Scenes and integration + Developed by Flagship. + Unknown New command Choose how the command starts Run manually for frequently used device combinations. -- 2.54.0