android: 2.14.0 - minSdk 23 + adaptive navigation (Stage 3)
- minSdkVersion 21 -> 23 (device is Android 17, no other users); jni Application.mk: APP_PLATFORM android-23 + -DHAVE_SIGWAITINFO (the NDK sysroot declares sigwaitinfo since API 23, conflicting with strongSwan's fallback prototype; the system implementation is used now) - replace the bottom NavigationBar with NavigationSuiteScaffold (bottom bar on phones, navigation rail in landscape/on tablets); item colors keep the brand palette via NavigationSuiteItemColors(bar/rail/drawer) - HomeScreen applies statusBarsPadding itself (the adaptive scaffold no longer pads the content area) - predictive back was already enabled (android:enableOnBackInvokedCallback)
This commit is contained in:
1 parent
285d14e996
commit
2c69cbe775
4 files changed
+82
-44
No files matched your search
@@ -34,11 +34,11 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "one.shx.strongswan.ext"
|
||||
compileSdk = 36
|
||||
minSdkVersion 21
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 36
|
||||
|
||||
versionCode 152
|
||||
versionName "2.13.15"
|
||||
versionCode 153
|
||||
versionName "2.14.0"
|
||||
|
||||
/* Base URL of the update storage, trailing slash included */
|
||||
buildConfigField "String", "UPDATE_BASE_URL", "\"${updateBaseUrl}\""
|
||||
@@ -146,15 +146,15 @@ dependencies {
|
||||
implementation 'androidx.work:work-runtime-ktx:2.10.1'
|
||||
implementation 'com.google.android.material:material:1.13.0'
|
||||
|
||||
/* BOM 2025.12.01 (compose 1.10) требует minSdk 23 — приложение поддерживает API 21.
|
||||
2025.09.01 = первый BOM с material3 1.4.0 (Expressive) на compose 1.9 (minSdk 21). */
|
||||
/* BOM 2025.12.01 (compose 1.10, Expressive-тема) — отдельная задача после
|
||||
стабилизации; 2025.09.01 = material3 1.4.0 на compose 1.9. */
|
||||
def composeBom = platform('androidx.compose:compose-bom:2025.09.01')
|
||||
implementation composeBom
|
||||
implementation 'androidx.compose.ui:ui'
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
implementation 'androidx.compose.material3:material3-window-size-class'
|
||||
/* adaptive-navigation-suite требует minSdk 23 (adaptive:1.2.0) — подключить на этапе 3
|
||||
вместе с решением о minSdk; сейчас приложение поддерживает API 21 */
|
||||
/* adaptive-navigation-suite (NavigationSuiteScaffold): BOM 1.4.0, требует minSdk 23 */
|
||||
implementation 'androidx.compose.material3:material3-adaptive-navigation-suite'
|
||||
implementation 'androidx.compose.material:material-icons-extended'
|
||||
implementation 'androidx.activity:activity-compose:1.11.0'
|
||||
implementation 'androidx.navigation:navigation-compose:2.9.4'
|
||||
|
||||
+4
-1
@@ -44,6 +44,7 @@ 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.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
@@ -191,7 +192,9 @@ fun HomeScreen(
|
||||
StatusGlowBackground(color = statusColor, active = connected || connecting)
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
/* the adaptive navigation scaffold no longer pads the content area,
|
||||
* so the status headline offsets itself below the status bar */
|
||||
modifier = Modifier.fillMaxSize().statusBarsPadding(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
)
|
||||
{
|
||||
|
||||
+67
-35
@@ -24,10 +24,8 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -43,13 +41,15 @@ import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.NavigationBarItemDefaults
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.NavigationDrawerItemDefaults
|
||||
import androidx.compose.material3.NavigationRailItemDefaults
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteItemColors
|
||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScope
|
||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -57,6 +57,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -107,17 +108,18 @@ fun ShxApp(viewModel: HomeViewModel = viewModel())
|
||||
/* the home list keeps its scroll state across tab switches */
|
||||
val homeListState = rememberLazyListState()
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
ShxBottomBar(selected = state.tab, onSelect = viewModel::setTab)
|
||||
/* adaptive navigation: bottom bar on phones, navigation rail in landscape
|
||||
* and on tablets (Stage 3 of ui-ux-plan). Item colors are built here in a
|
||||
* composable context - the items lambda itself does not compose. */
|
||||
val navItemColors = shxNavigationItemColors()
|
||||
NavigationSuiteScaffold(
|
||||
navigationSuiteItems = {
|
||||
ShxNavigationSuiteItems(selected = state.tab, onSelect = viewModel::setTab, colors = navItemColors)
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.background,
|
||||
) { padding ->
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
AnimatedContent(
|
||||
@@ -206,32 +208,62 @@ private fun ShareProfileDialog(profile: VpnProfile, onDismiss: () -> Unit, onCon
|
||||
)
|
||||
}
|
||||
|
||||
/** Item colors for the adaptive navigation suite, keeping the brand palette. */
|
||||
@Composable
|
||||
private fun ShxBottomBar(selected: ShxTab, onSelect: (ShxTab) -> Unit)
|
||||
private fun shxNavigationItemColors(): NavigationSuiteItemColors
|
||||
{
|
||||
val bg = MaterialTheme.colorScheme.surfaceContainer
|
||||
val indicator = MaterialTheme.colorScheme.primary.copy(alpha = 0.32f).compositeOver(bg)
|
||||
NavigationBar(containerColor = bg, tonalElevation = 0.dp) {
|
||||
ShxTab.entries.forEach { tab ->
|
||||
NavigationBarItem(
|
||||
selected = tab == selected,
|
||||
onClick = { onSelect(tab) },
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = tab.icon(selected = tab == selected),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
label = { Text(stringResource(tab.label())) },
|
||||
colors = NavigationBarItemDefaults.colors(
|
||||
selectedIconColor = MaterialTheme.colorScheme.primary,
|
||||
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
selectedTextColor = MaterialTheme.colorScheme.primary,
|
||||
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
indicatorColor = indicator,
|
||||
),
|
||||
)
|
||||
}
|
||||
return NavigationSuiteItemColors(
|
||||
navigationBarItemColors = NavigationBarItemDefaults.colors(
|
||||
selectedIconColor = MaterialTheme.colorScheme.primary,
|
||||
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
selectedTextColor = MaterialTheme.colorScheme.primary,
|
||||
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
indicatorColor = indicator,
|
||||
),
|
||||
navigationRailItemColors = NavigationRailItemDefaults.colors(
|
||||
selectedIconColor = MaterialTheme.colorScheme.primary,
|
||||
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
selectedTextColor = MaterialTheme.colorScheme.primary,
|
||||
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
indicatorColor = indicator,
|
||||
),
|
||||
navigationDrawerItemColors = NavigationDrawerItemDefaults.colors(
|
||||
selectedContainerColor = indicator,
|
||||
unselectedContainerColor = Color.Transparent,
|
||||
selectedIconColor = MaterialTheme.colorScheme.primary,
|
||||
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
selectedTextColor = MaterialTheme.colorScheme.primary,
|
||||
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the app tabs as navigation suite items. The scope's item() is a
|
||||
* plain registration function - only the icon/label lambdas compose - so
|
||||
* colors are built outside (in a composable context) and passed in.
|
||||
*/
|
||||
private fun NavigationSuiteScope.ShxNavigationSuiteItems(
|
||||
selected: ShxTab,
|
||||
onSelect: (ShxTab) -> Unit,
|
||||
colors: NavigationSuiteItemColors,
|
||||
)
|
||||
{
|
||||
ShxTab.entries.forEach { tab ->
|
||||
item(
|
||||
selected = tab == selected,
|
||||
onClick = { onSelect(tab) },
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = tab.icon(selected = tab == selected),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
label = { Text(stringResource(tab.label())) },
|
||||
colors = colors,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
APP_PLATFORM := android-21
|
||||
APP_PLATFORM := android-23
|
||||
APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true
|
||||
APP_ABI := armeabi-v7a arm64-v8a x86_64
|
||||
# minSdk 23: the NDK sysroot declares sigwaitinfo() (introduced in API 23),
|
||||
# so use the system implementation instead of strongSwan's fallback prototype
|
||||
APP_CFLAGS += -DHAVE_SIGWAITINFO
|
||||
Reference in new issue
Block a user