Skip to content

Commit 2766628

Browse files
committed
feat: Compose UI enhancements and refactoring for system navigation bar
This commit introduces several improvements and refactorings to the Compose UI, focusing on adding new features, enhancing existing components, and improving the overall user experience. - feat: Add overlay controller - feat: Add AppDefaultProperties - feat: Add ComposeActivity - refactor: Implement overlay for MainPager - refactor: Add padding for end of list - refactor: Implement system window inset for ComposeActivity - refactor: Remove immersion bar - refactor: Refactor FloatingAddConfigButtonGroup - refactor: Refactor SoftKeyboardToolbar - refactor: Refactor code editor - refactor: ReplaceRuleEditScreen - refactor: PluginManagerScreen - refactor: LogScreen - refactor: ReplaceRuleManagerScreen - refactor: MainSettingsScreen - refactor: ListManagerScreen - refactor: remove SetupSystemBars() - refactor: Fix system ui behavior
1 parent 27d6b79 commit 2766628

File tree

20 files changed

+445
-339
lines changed

20 files changed

+445
-339
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
android:requestLegacyExternalStorage="true"
3434
android:roundIcon="@mipmap/ic_app_launcher_round"
3535
android:supportsRtl="true"
36-
android:theme="@style/Theme.TtsServer"
36+
android:theme="@style/Theme.TtsServer.NoActionBar"
3737
android:usesCleartextTraffic="true"
3838
tools:ignore="UnusedAttribute">
3939
<activity
@@ -46,8 +46,7 @@
4646
<activity
4747
android:name=".compose.systts.replace.ReplaceManagerActivity"
4848
android:exported="true"
49-
android:label="@string/replace_rule_manager"
50-
android:windowSoftInputMode="adjustResize" />
49+
android:label="@string/replace_rule_manager" />
5150
<activity
5251
android:name=".compose.systts.directlink.LinkUploadRuleActivity"
5352
android:exported="true"
@@ -101,8 +100,7 @@
101100
<activity
102101
android:name=".compose.systts.plugin.PluginManagerActivity"
103102
android:exported="true"
104-
android:label="@string/plugin_manager"
105-
android:taskAffinity="com.github.jing332.tts_server_android.plugin" />
103+
android:label="@string/plugin_manager" />
106104
<activity
107105
android:name=".ui.ImportConfigActivity"
108106
android:exported="true"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.github.jing332.tts_server_android.compose
2+
3+
import androidx.compose.ui.unit.dp
4+
5+
object AppDefaultProperties {
6+
val LIST_END_PADDING = 100.dp
7+
8+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.github.jing332.tts_server_android.compose
2+
3+
import android.os.Bundle
4+
import androidx.appcompat.app.AppCompatActivity
5+
import androidx.core.view.WindowCompat
6+
7+
abstract class ComposeActivity : AppCompatActivity() {
8+
9+
@Suppress("DEPRECATION")
10+
override fun onCreate(savedInstanceState: Bundle?) {
11+
super.onCreate(savedInstanceState)
12+
WindowCompat.setDecorFitsSystemWindows(window, false)
13+
}
14+
}

app/src/main/java/com/github/jing332/tts_server_android/compose/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ fun Context.asActivity(): Activity {
6666
}
6767

6868

69-
class MainActivity : AppCompatActivity() {
69+
class MainActivity : ComposeActivity() {
7070
companion object {
7171
private const val TAG = "MainActivity"
7272
private val logger = KotlinLogging.logger(TAG)
Lines changed: 92 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
package com.github.jing332.tts_server_android.compose
22

3+
import androidx.compose.animation.AnimatedContentScope
4+
import androidx.compose.animation.animateColorAsState
5+
import androidx.compose.animation.core.LinearEasing
6+
import androidx.compose.animation.core.tween
37
import androidx.compose.foundation.ExperimentalFoundationApi
8+
import androidx.compose.foundation.background
9+
import androidx.compose.foundation.clickable
10+
import androidx.compose.foundation.layout.Box
411
import androidx.compose.foundation.layout.ExperimentalLayoutApi
512
import androidx.compose.foundation.layout.fillMaxSize
613
import androidx.compose.foundation.layout.fillMaxWidth
@@ -11,18 +18,21 @@ import androidx.compose.material3.BottomAppBar
1118
import androidx.compose.material3.BottomAppBarDefaults
1219
import androidx.compose.material3.BottomAppBarScrollBehavior
1320
import androidx.compose.material3.ExperimentalMaterial3Api
21+
import androidx.compose.material3.MaterialTheme
1422
import androidx.compose.material3.NavigationBarDefaults
1523
import androidx.compose.material3.NavigationBarItem
1624
import androidx.compose.material3.Scaffold
1725
import androidx.compose.material3.Text
1826
import androidx.compose.runtime.Composable
1927
import androidx.compose.runtime.CompositionLocalProvider
2028
import androidx.compose.runtime.compositionLocalOf
29+
import androidx.compose.runtime.getValue
2130
import androidx.compose.runtime.rememberCoroutineScope
2231
import androidx.compose.ui.Modifier
32+
import androidx.compose.ui.graphics.Color
2333
import androidx.compose.ui.input.nestedscroll.nestedScroll
2434
import androidx.compose.ui.res.stringResource
25-
import com.github.jing332.compose.widgets.InitSystemNavigation
35+
import androidx.compose.ui.zIndex
2636
import com.github.jing332.compose.widgets.rememberA11TouchEnabled
2737
import com.github.jing332.tts_server_android.compose.forwarder.systts.SystemTtsForwarderScreen
2838
import com.github.jing332.tts_server_android.compose.settings.SettingsScreen
@@ -35,70 +45,105 @@ import kotlinx.coroutines.launch
3545
@OptIn(ExperimentalMaterial3Api::class)
3646
val LocalBottomBarBehavior =
3747
compositionLocalOf<BottomAppBarScrollBehavior>() { error("LocalBottomBarBehavior not initialized") }
48+
val LocalOverlayController =
49+
compositionLocalOf<OverlayController> { error("LocalOverlayController not initialized") }
3850

3951
@OptIn(
4052
ExperimentalFoundationApi::class, ExperimentalLayoutApi::class,
4153
ExperimentalMaterial3Api::class
4254
)
4355
@Composable
44-
fun MainPager(sharedVM: SharedViewModel) {
56+
fun AnimatedContentScope.MainPager(sharedVM: SharedViewModel) {
4557
val pagerState = rememberPagerState { PagerDestination.routes.size }
4658
val scope = rememberCoroutineScope()
4759
MigrationTips()
4860

4961
val a11yTouchEnabled = rememberA11TouchEnabled()
50-
5162
val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior(canScroll = {
5263
!a11yTouchEnabled
5364
})
5465

55-
Scaffold(
56-
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
57-
bottomBar = {
58-
val containerColor = NavigationBarDefaults.containerColor
59-
InitSystemNavigation(containerColor)
60-
BottomAppBar(
61-
modifier = Modifier
62-
.fillMaxWidth(),
63-
containerColor = containerColor,
64-
scrollBehavior = scrollBehavior,
65-
actions = {
66-
for (destination in PagerDestination.routes) {
67-
val isSelected = pagerState.currentPage == destination.index
68-
NavigationBarItem(
69-
selected = isSelected,
70-
alwaysShowLabel = a11yTouchEnabled,
71-
onClick = {
72-
scope.launch {
73-
pagerState.animateScrollToPage(destination.index)
74-
}
75-
},
76-
icon = destination.icon,
77-
label = {
78-
Text(stringResource(id = destination.strId))
79-
}
80-
)
81-
}
82-
}
66+
val overlayController = rememberOverlayController()
67+
68+
CompositionLocalProvider(
69+
LocalBottomBarBehavior provides scrollBehavior,
70+
LocalOverlayController provides overlayController,
71+
) {
72+
Box {
73+
val backgroundColor by animateColorAsState(
74+
targetValue = if (overlayController.visible) {
75+
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.2f)
76+
} else {
77+
Color.Transparent
78+
},
79+
animationSpec = tween(durationMillis = 600, easing = LinearEasing),
80+
label = "background color animation"
8381
)
8482

85-
}
86-
) { paddingValues ->
87-
HorizontalPager(
88-
modifier = Modifier
89-
.padding(bottom = paddingValues.calculateBottomPadding())
90-
.fillMaxSize(),
91-
state = pagerState,
92-
userScrollEnabled = true
93-
) { index ->
94-
CompositionLocalProvider(LocalBottomBarBehavior provides scrollBehavior) {
95-
when (index) {
96-
PagerDestination.SystemTts.index -> ListManagerScreen(sharedVM)
97-
PagerDestination.SystemTtsLog.index -> TtsLogScreen()
98-
PagerDestination.Settings.index -> SettingsScreen()
99-
PagerDestination.SystemTtsForwarder.index -> SystemTtsForwarderScreen()
83+
Box(
84+
Modifier
85+
.fillMaxSize()
86+
.background(backgroundColor)
87+
.zIndex(0.1f)
88+
.then(
89+
if (overlayController.visible) {
90+
Modifier.clickable(
91+
interactionSource = null,
92+
indication = null,
93+
onClick = { overlayController.hide() }
94+
)
95+
} else {
96+
Modifier
97+
}
98+
)
99+
) {}
100+
101+
Scaffold(
102+
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
103+
bottomBar = {
104+
val containerColor = NavigationBarDefaults.containerColor
105+
BottomAppBar(
106+
modifier = Modifier
107+
.fillMaxWidth(),
108+
containerColor = containerColor,
109+
scrollBehavior = scrollBehavior,
110+
actions = {
111+
for (destination in PagerDestination.routes) {
112+
val isSelected = pagerState.currentPage == destination.index
113+
NavigationBarItem(
114+
selected = isSelected,
115+
alwaysShowLabel = a11yTouchEnabled,
116+
onClick = {
117+
scope.launch {
118+
pagerState.animateScrollToPage(destination.index)
119+
}
120+
},
121+
icon = destination.icon,
122+
label = {
123+
Text(stringResource(id = destination.strId))
124+
}
125+
)
126+
}
127+
}
128+
)
129+
130+
}
131+
) { paddingValues ->
132+
HorizontalPager(
133+
modifier = Modifier
134+
.padding(bottom = paddingValues.calculateBottomPadding())
135+
.fillMaxSize(),
136+
state = pagerState,
137+
userScrollEnabled = true
138+
) { index ->
139+
when (index) {
140+
PagerDestination.SystemTts.index -> ListManagerScreen(sharedVM)
141+
PagerDestination.SystemTtsLog.index -> TtsLogScreen()
142+
PagerDestination.Settings.index -> SettingsScreen()
143+
PagerDestination.SystemTtsForwarder.index -> SystemTtsForwarderScreen()
144+
}
100145
}
101146
}
102147
}
103148
}
104-
}
149+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.github.jing332.tts_server_android.compose
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.MutableState
5+
import androidx.compose.runtime.getValue
6+
import androidx.compose.runtime.mutableStateOf
7+
import androidx.compose.runtime.saveable.Saver
8+
import androidx.compose.runtime.saveable.rememberSaveable
9+
import androidx.compose.runtime.setValue
10+
11+
class OverlayController() {
12+
private var mVisible: MutableState<Boolean> = mutableStateOf(false)
13+
14+
var visible: Boolean by mVisible
15+
16+
fun show() {
17+
mVisible.value = true
18+
}
19+
20+
fun hide() {
21+
mVisible.value = false
22+
}
23+
}
24+
25+
@Composable
26+
fun rememberOverlayController(): OverlayController {
27+
return rememberSaveable(saver = OverlayControllerSaver) {
28+
OverlayController()
29+
}
30+
}
31+
32+
// 自定义 Saver
33+
private val OverlayControllerSaver = Saver<OverlayController, Boolean>(
34+
save = { it.visible },
35+
restore = { savedVisibility ->
36+
OverlayController().apply {
37+
visible = savedVisibility
38+
}
39+
}
40+
)

app/src/main/java/com/github/jing332/tts_server_android/compose/codeeditor/CodeEditorScreen.kt

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@ import androidx.compose.foundation.background
55
import androidx.compose.foundation.layout.Box
66
import androidx.compose.foundation.layout.Column
77
import androidx.compose.foundation.layout.ColumnScope
8+
import androidx.compose.foundation.layout.ExperimentalLayoutApi
89
import androidx.compose.foundation.layout.Row
10+
import androidx.compose.foundation.layout.WindowInsets
911
import androidx.compose.foundation.layout.fillMaxSize
1012
import androidx.compose.foundation.layout.fillMaxWidth
13+
import androidx.compose.foundation.layout.imePadding
14+
import androidx.compose.foundation.layout.isImeVisible
15+
import androidx.compose.foundation.layout.navigationBarsPadding
1116
import androidx.compose.foundation.layout.padding
1217
import androidx.compose.foundation.lazy.LazyRow
1318
import androidx.compose.foundation.lazy.items
@@ -66,7 +71,7 @@ import kotlinx.coroutines.launch
6671

6772
fun CodeEditor.string(): String = this.text.toString()
6873

69-
@OptIn(ExperimentalMaterial3Api::class)
74+
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
7075
@Composable
7176
fun CodeEditorScreen(
7277
title: @Composable () -> Unit,
@@ -133,6 +138,7 @@ fun CodeEditorScreen(
133138
}
134139

135140
Scaffold(
141+
contentWindowInsets = WindowInsets(0,0,0,0),
136142
topBar = {
137143
TopAppBar(title = title, navigationIcon = {
138144
IconButton(onClick = onBack) {
@@ -279,19 +285,19 @@ fun CodeEditorScreen(
279285
startColumn: Int,
280286
endLine: Int,
281287
endColumn: Int,
282-
insertedContent: CharSequence
288+
insertedContent: CharSequence,
283289
) {
284-
}
290+
}
285291

286292
override fun afterDelete(
287293
content: Content,
288294
startLine: Int,
289295
startColumn: Int,
290296
endLine: Int,
291297
endColumn: Int,
292-
deletedContent: CharSequence
298+
deletedContent: CharSequence,
293299
) {
294-
}
300+
}
295301
})
296302
codeEditor = it
297303

@@ -322,7 +328,14 @@ fun CodeEditorScreen(
322328
}
323329

324330
HorizontalDivider(thickness = 1.dp)
325-
LazyRow(Modifier.background(MaterialTheme.colorScheme.background)) {
331+
LazyRow(
332+
Modifier
333+
.background(MaterialTheme.colorScheme.background)
334+
.imePadding()
335+
.then(
336+
if (WindowInsets.isImeVisible) Modifier else Modifier.navigationBarsPadding()
337+
)
338+
) {
326339
items(symbolMap.toList()) {
327340
Box(
328341
Modifier

app/src/main/java/com/github/jing332/tts_server_android/compose/settings/MainSettingsScreen.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package com.github.jing332.tts_server_android.compose.settings
33
import android.content.Intent
44
import androidx.compose.foundation.layout.Column
55
import androidx.compose.foundation.layout.Spacer
6-
import androidx.compose.foundation.layout.height
76
import androidx.compose.foundation.layout.padding
87
import androidx.compose.foundation.rememberScrollState
98
import androidx.compose.foundation.verticalScroll
@@ -32,10 +31,10 @@ import androidx.compose.runtime.setValue
3231
import androidx.compose.ui.Modifier
3332
import androidx.compose.ui.platform.LocalContext
3433
import androidx.compose.ui.res.stringResource
35-
import androidx.compose.ui.unit.dp
3634
import com.github.jing332.tts_server_android.AppLocale
3735
import com.github.jing332.tts_server_android.R
3836
import com.github.jing332.tts_server_android.app
37+
import com.github.jing332.tts_server_android.compose.AppDefaultProperties
3938
import com.github.jing332.tts_server_android.compose.backup.BackupRestoreActivity
4039
import com.github.jing332.tts_server_android.compose.nav.NavTopAppBar
4140
import com.github.jing332.tts_server_android.compose.systts.directlink.LinkUploadRuleActivity
@@ -243,7 +242,7 @@ fun SettingsScreen() {
243242

244243
OtherSettingsScreen()
245244

246-
Spacer(Modifier.height(100.dp))
245+
Spacer(Modifier.padding(bottom = AppDefaultProperties.LIST_END_PADDING))
247246
}
248247
}
249248
}

0 commit comments

Comments
 (0)