11package com.muort.upworker
22
3+ import android.content.res.Configuration
4+ import android.graphics.PorterDuff
5+ import android.graphics.PorterDuffColorFilter
6+ import android.text.SpannableString
7+ import android.text.Spanned
8+ import android.text.style.RelativeSizeSpan
9+ import android.text.style.StyleSpan
310import android.os.Bundle
11+ import android.util.TypedValue
412import android.view.LayoutInflater
513import androidx.activity.viewModels
614import androidx.appcompat.app.AppCompatActivity
15+ import androidx.core.view.WindowCompat
716import androidx.lifecycle.Lifecycle
817import androidx.lifecycle.lifecycleScope
918import androidx.lifecycle.repeatOnLifecycle
@@ -13,13 +22,15 @@ import androidx.navigation.ui.setupActionBarWithNavController
1322import androidx.recyclerview.widget.LinearLayoutManager
1423import com.google.android.material.dialog.MaterialAlertDialogBuilder
1524import com.muort.upworker.core.model.Account
25+ import com.muort.upworker.core.model.Zone
1626import com.muort.upworker.core.util.DataMigrationHelper
1727import com.muort.upworker.core.util.MigrationResult
1828import com.muort.upworker.core.util.showToast
1929import com.muort.upworker.databinding.ActivityMainBinding
2030import com.muort.upworker.databinding.DialogAccountSelectionBinding
2131import com.muort.upworker.feature.account.AccountViewModel
2232import dagger.hilt.android.AndroidEntryPoint
33+ import kotlinx.coroutines.flow.combine
2334import kotlinx.coroutines.launch
2435import timber.log.Timber
2536import javax.inject.Inject
@@ -39,11 +50,92 @@ class MainActivity : AppCompatActivity() {
3950 setContentView(binding.root)
4051
4152 setupNavigation()
53+ configureSystemBars()
4254 setupAccountSelector()
4355 observeViewModel()
4456 performMigrationIfNeeded()
4557 }
4658
59+ private fun configureSystemBars () {
60+ // 1. 获取 MD3 颜色定义
61+ val typedValueContainer = TypedValue ()
62+ // Surface Container (用于 Toolbar 背景)
63+ val hasContainer = theme.resolveAttribute(com.google.android.material.R .attr.colorSurfaceContainer, typedValueContainer, true )
64+ val colorSurfaceContainer = if (hasContainer) typedValueContainer.data else {
65+ val typedValueSurface = TypedValue ()
66+ theme.resolveAttribute(com.google.android.material.R .attr.colorSurface, typedValueSurface, true )
67+ typedValueSurface.data
68+ }
69+
70+ // Secondary Container (用于账号选择器背景 - 胶囊样式)
71+ val typedValueSecContainer = TypedValue ()
72+ theme.resolveAttribute(com.google.android.material.R .attr.colorSecondaryContainer, typedValueSecContainer, true )
73+ val colorSecondaryContainer = typedValueSecContainer.data
74+
75+ // On Secondary Container (用于账号选择器前景)
76+ val typedValueOnSecContainer = TypedValue ()
77+ theme.resolveAttribute(com.google.android.material.R .attr.colorOnSecondaryContainer, typedValueOnSecContainer, true )
78+ val colorOnSecondaryContainer = typedValueOnSecContainer.data
79+
80+ // On Surface (用于 Toolbar 上的通用图标)
81+ val typedValueOnSurface = TypedValue ()
82+ theme.resolveAttribute(com.google.android.material.R .attr.colorOnSurface, typedValueOnSurface, true )
83+ val colorOnSurface = typedValueOnSurface.data
84+
85+ // 2. 设置状态栏和 Toolbar 背景
86+ window.statusBarColor = colorSurfaceContainer
87+ binding.toolbar.setBackgroundColor(colorSurfaceContainer)
88+ binding.toolbar.setTitleTextColor(colorOnSurface)
89+
90+ // 3. Toolbar 样式调整:去阴影、居中
91+ (binding.toolbar as ? com.google.android.material.appbar.MaterialToolbar )?.isTitleCentered = true
92+ binding.toolbar.elevation = 0f
93+
94+ // 4. 打造 "胶囊" (Chip) 样式的账号选择器
95+ // 创建圆角背景
96+ val chipBackground = android.graphics.drawable.GradientDrawable ().apply {
97+ shape = android.graphics.drawable.GradientDrawable .RECTANGLE
98+ setColor(colorSecondaryContainer)
99+ cornerRadius = 100f // 大圆角
100+ }
101+ binding.selectAccountButton.background = chipBackground
102+
103+ // 增加内边距 (8dp vertical, 16dp horizontal)
104+ val density = resources.displayMetrics.density
105+ val paddingH = (16 * density).toInt()
106+ val paddingV = (6 * density).toInt()
107+ binding.selectAccountButton.setPadding(paddingH, paddingV, paddingH, paddingV)
108+
109+ // 5. 设置选择器内容颜色 (OnSecondaryContainer)
110+ val contentColorFilter = PorterDuffColorFilter (colorOnSecondaryContainer, PorterDuff .Mode .SRC_IN )
111+
112+ binding.currentAccountText.typeface = android.graphics.Typeface .DEFAULT_BOLD
113+ binding.currentAccountText.setTextColor(colorOnSecondaryContainer)
114+ binding.currentAccountText.isSingleLine = false
115+ binding.currentAccountText.maxLines = 2
116+ binding.currentAccountText.ellipsize = android.text.TextUtils .TruncateAt .END
117+ binding.currentAccountText.compoundDrawables.forEach { it?.mutate()?.colorFilter = contentColorFilter }
118+ binding.currentAccountText.compoundDrawablesRelative.forEach { it?.mutate()?.colorFilter = contentColorFilter }
119+
120+ (binding.selectAccountButton as ? android.widget.TextView )?.let { textView ->
121+ textView.text = " "
122+ val icon = getDrawable(android.R .drawable.ic_menu_more)
123+ textView.setCompoundDrawablesRelativeWithIntrinsicBounds(null , null , icon, null )
124+ textView.setTextColor(colorOnSecondaryContainer)
125+ textView.compoundDrawables.forEach { it?.mutate()?.colorFilter = contentColorFilter }
126+ textView.compoundDrawablesRelative.forEach { it?.mutate()?.colorFilter = contentColorFilter }
127+ }
128+
129+ // 6. 设置 Toolbar 导航图标颜色 (OnSurface)
130+ val navColorFilter = PorterDuffColorFilter (colorOnSurface, PorterDuff .Mode .SRC_IN )
131+ binding.toolbar.navigationIcon?.mutate()?.colorFilter = navColorFilter
132+ binding.toolbar.overflowIcon?.mutate()?.colorFilter = navColorFilter
133+
134+ val isNightMode = (resources.configuration.uiMode and Configuration .UI_MODE_NIGHT_MASK ) ==
135+ Configuration .UI_MODE_NIGHT_YES
136+ WindowCompat .getInsetsController(window, window.decorView).isAppearanceLightStatusBars = ! isNightMode
137+ }
138+
47139 private fun setupAccountSelector () {
48140 binding.selectAccountButton.setOnClickListener {
49141 showAccountSelectionDialog()
@@ -110,6 +202,14 @@ class MainActivity : AppCompatActivity() {
110202 )
111203
112204 setupActionBarWithNavController(navController, appBarConfiguration)
205+
206+ // 监听导航变化,确保返回按钮/菜单图标颜色在页面切换后依然正确
207+ navController.addOnDestinationChangedListener { _, _, _ ->
208+ val typedValue = TypedValue ()
209+ theme.resolveAttribute(com.google.android.material.R .attr.colorOnSurface, typedValue, true )
210+ val colorOnSurface = typedValue.data
211+ binding.toolbar.navigationIcon?.mutate()?.colorFilter = PorterDuffColorFilter (colorOnSurface, PorterDuff .Mode .SRC_IN )
212+ }
113213 }
114214
115215 override fun onSupportNavigateUp (): Boolean {
@@ -122,12 +222,21 @@ class MainActivity : AppCompatActivity() {
122222 private fun observeViewModel () {
123223 lifecycleScope.launch {
124224 repeatOnLifecycle(Lifecycle .State .STARTED ) {
225+ launch {
226+ combine(
227+ accountViewModel.defaultAccount,
228+ accountViewModel.selectedZone
229+ ) { account, zone ->
230+ Pair (account, zone)
231+ }.collect { (account, zone) ->
232+ updateTitleBar(account, zone)
233+ }
234+ }
235+
125236 launch {
126237 accountViewModel.defaultAccount.collect { account ->
127238 account?.let {
128- binding.currentAccountText.text = it.name
129- } ? : run {
130- binding.currentAccountText.text = " 未选择账号"
239+ accountViewModel.loadZonesForAccount(it.id)
131240 }
132241 }
133242 }
@@ -141,6 +250,32 @@ class MainActivity : AppCompatActivity() {
141250 }
142251 }
143252
253+ private fun updateTitleBar (account : Account ? , zone : Zone ? ) {
254+ if (account == null ) {
255+ binding.currentAccountText.text = " 未选择账号"
256+ return
257+ }
258+
259+ val accountName = account.name
260+ val zoneName = zone?.name
261+
262+ if (zoneName.isNullOrEmpty()) {
263+ binding.currentAccountText.text = accountName
264+ } else {
265+ val text = " $accountName \n $zoneName "
266+ val spannable = SpannableString (text)
267+
268+ // 设置域名部分为正常字体(非粗体)和小字号 (75%)
269+ val start = accountName.length + 1
270+ val end = text.length
271+
272+ spannable.setSpan(StyleSpan (android.graphics.Typeface .NORMAL ), start, end, Spanned .SPAN_EXCLUSIVE_EXCLUSIVE )
273+ spannable.setSpan(RelativeSizeSpan (0.75f ), start, end, Spanned .SPAN_EXCLUSIVE_EXCLUSIVE )
274+
275+ binding.currentAccountText.text = spannable
276+ }
277+ }
278+
144279 private fun showZoneSelectionDialog (account : Account ) {
145280 val zones = accountViewModel.zones.value
146281 val selectedZone = accountViewModel.selectedZone.value
0 commit comments