feat(app): add authenticated app shell with top bar and bottom navigation (#6)
### 概述 / Summary 实现认证后的主应用壳层(`MainAppShell`),包含胶囊顶栏(`ABoxTopPillBar`)与 Material 3 底部导航栏(`ABoxNavigationBar`),并为团队其他成员预留标准 Composable 插槽。 ### 核心变更 / Changes 1. **设计系统 (`:android:core:designsystem`)**: - 新增 `ABoxTopPillBar`:展示提供方/空间信息、快捷 `+` 操作与用户头像徽标。 - 新增 `ABoxUserAvatar`:支持用户名首字符与主题强调轮廓。 - 新增 `ABoxNavigationBar` & `ABoxNavigationBarItem`:M3 胶囊指示器底部导航栏封装。 2. **应用壳层 (`:android:app`)**: - 新增 `MainTab`(`DEVICES`, `SCENES`, `ACTIVITY`),严格对齐需求与分工术语。 - 新增 `MainAppShell`:承载顶栏、底栏、Tab 平滑切换,并开放 `devicesSlot`、`scenesSlot`、`activitySlot`。 - 新增 `AccountProviderBottomSheet`:点击头像展示当前会话/提供方信息、支持切换提供方与退出登录。 3. **多语言与测试**: - 补充 `values` 与 `values-zh-rCN` 双语资源。 - 新增 `MainTabTest` 单元测试并通过全量 `check` 与 `test`。 ### 分工保护 / Boundaries - 仅提供顶栏、底栏与主页面容器骨架; - 设备列表、情景编排与设备控制详情的具体 UI 保持为插槽,供 alZerNest、tzh 和 Aurum 后续无缝填充。 Reviewed-on: #6 Co-authored-by: KremeCN <3265494849@qq.com>
This commit was merged in pull request #6.
This commit is contained in:
+3
@@ -21,6 +21,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -117,6 +118,8 @@ class AppSessionViewModelTest {
|
||||
|
||||
val state = viewModel.uiState.value
|
||||
assertTrue(state is AppSessionUiState.Authenticated)
|
||||
val authenticated = state as AppSessionUiState.Authenticated
|
||||
assertEquals(credentials.username, authenticated.username)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.flagship.abox.manager.ui.shell
|
||||
|
||||
import com.flagship.abox.manager.R
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Test
|
||||
|
||||
class MainTabTest {
|
||||
|
||||
@Test
|
||||
fun `all main tabs have distinct non-empty routes and valid label resources`() {
|
||||
val tabs = MainTab.values()
|
||||
assertEquals(3, tabs.size)
|
||||
|
||||
val routes = tabs.map { it.route }
|
||||
assertEquals(routes.distinct().size, tabs.size)
|
||||
|
||||
tabs.forEach { tab ->
|
||||
assertNotNull(tab.selectedIcon)
|
||||
assertNotNull(tab.unselectedIcon)
|
||||
assert(tab.route.isNotBlank())
|
||||
}
|
||||
|
||||
assertEquals(R.string.tab_devices, MainTab.DEVICES.labelRes)
|
||||
assertEquals(R.string.tab_scenes, MainTab.SCENES.labelRes)
|
||||
assertEquals(R.string.tab_activity, MainTab.ACTIVITY.labelRes)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user