From bba58df9d31c0b87c09a36028926b79e7d000379 Mon Sep 17 00:00:00 2001 From: Tommy-Geenexus Date: Wed, 23 Oct 2024 18:49:19 +0200 Subject: [PATCH] fix(control): adapt layout for Android 15 --- app/config/detekt/baseline.xml | 3 +- .../4.json | 142 ++++++++++++++++++ .../UsbDongleControlActivity.kt | 2 +- .../control/ui/ControlBottomAppBar.kt | 3 +- .../control/ui/ControlScreen.kt | 51 ++++--- .../control/ui/ControlTopAppBar.kt | 6 +- 6 files changed, 183 insertions(+), 24 deletions(-) create mode 100644 app/schemas/io.github.tommygeenexus.usbdonglecontrol.core.db.ProfileDatabase/4.json diff --git a/app/config/detekt/baseline.xml b/app/config/detekt/baseline.xml index afd1dbb..11e4674 100644 --- a/app/config/detekt/baseline.xml +++ b/app/config/detekt/baseline.xml @@ -2,6 +2,7 @@ + CyclomaticComplexMethod:ControlScreen.kt$@Composable fun ControlScreen( modifier: Modifier = Modifier, windowSizeClass: WindowSizeClass = WindowSizeClass.calculateFromSize(DpSize.Zero), scrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(), profileListState: LazyStaggeredGridState = rememberLazyStaggeredGridState(), snackBarHostState: SnackbarHostState = remember { SnackbarHostState() }, profiles: LazyPagingItems<Profile> = UnsupportedUsbDongle.profileFlow().collectAsLazyPagingItems(), usbDongle: UsbDongle = UnsupportedUsbDongle, isLoading: Boolean = false, onRefresh: () -> Unit = {}, onReset: () -> Unit = {}, onProfileShortcutAdd: (Profile) -> Unit = {}, onProfileShortcutRemove: (Profile) -> Unit = {}, onProfileDelete: (Profile) -> Unit = { _ -> }, onProfileApply: (Profile) -> Unit = { _ -> }, onProfileExport: (String) -> Unit = { _ -> }, onChannelBalanceSelected: (Int) -> Unit = { _ -> }, onDacModeSelected: (Byte) -> Unit = { _ -> }, onDisplayBrightnessSelected: (Int) -> Unit = { _ -> }, onDisplayTimeoutSelected: (Int) -> Unit = { _ -> }, onDisplayInvertChange: (Boolean) -> Unit = { _ -> }, onFilterSelected: (Byte) -> Unit = { _ -> }, onGainSelected: (Byte) -> Unit = { _ -> }, onHardwareMuteEnabledSelected: (Boolean) -> Unit = { _ -> }, onHidModeSelected: (Byte) -> Unit = { _ -> }, onIndicatorStateSelected: (Byte) -> Unit = { _ -> }, onSpdifOutEnabledSelected: (Boolean) -> Unit = { _ -> }, onVolumeLevelSelected: (Int) -> Unit = { _ -> }, onVolumeModeSelected: (Byte) -> Unit = { _ -> } ) CyclomaticComplexMethod:ControlScreen.kt$@Composable fun ControlScreen( windowSizeClass: WindowSizeClass, viewModel: ControlViewModel, onNavigateUp: () -> Unit ) EmptyFunctionBlock:ItemAudio.kt$<no name provided>${ } EmptyFunctionBlock:ItemDisplay.kt$<no name provided>${ } @@ -21,7 +22,7 @@ LongMethod:SetupScreen.kt$@Composable fun SetupScreen(viewModel: SetupViewModel, onNavigateToControl: () -> Unit) LongMethod:UsbServiceNotification.kt$UsbServiceNotification$fun <D> build( context: Context, usbDongle: D, volumeStepSize: Int ): Notification LongParameterList:ControlDropDownMenu.kt$( windowSizeClass: WindowSizeClass, onShouldShowMore: () -> Boolean, onDismissRequest: () -> Unit, onRefresh: () -> Unit, onReset: () -> Unit, onProfileExport: (String) -> Unit, modifier: Modifier = Modifier ) - LongParameterList:ControlTopAppBar.kt$( windowSizeClass: WindowSizeClass, scrollBehavior: TopAppBarScrollBehavior, productName: String, onRefresh: () -> Unit, onReset: () -> Unit, onProfileExport: (String) -> Unit, shouldShowActions: () -> Boolean, modifier: Modifier = Modifier ) + LongParameterList:ControlTopAppBar.kt$( windowSizeClass: WindowSizeClass, scrollBehavior: TopAppBarScrollBehavior, productName: String, onRefresh: () -> Unit, onReset: () -> Unit, onProfileExport: (String) -> Unit, shouldShowActions: () -> Boolean, windowInsets: WindowInsets, modifier: Modifier = Modifier ) LongParameterList:ControlViewModel.kt$ControlViewModel$( savedStateHandle: SavedStateHandle, pagingConfig: PagingConfig, private val usbRepository: UsbRepository, private val profileRepository: ProfileRepository, private val setProfileUseCase: SetProfileUseCase, private val getCurrentStateUseCase: GetCurrentStateUseCase, private val getVolumeLevelUseCase: GetVolumeLevelUseCase, private val setChannelBalanceUseCase: SetChannelBalanceUseCase, private val setDacFilterUseCase: SetDacFilterUseCase, private val setDacModeUseCase: SetDacModeUseCase, private val setDisplayBrightnessUseCase: SetDisplayBrightnessUseCase, private val setDisplayInvertEnabledUseCase: SetDisplayInvertEnabledUseCase, private val setDisplayTimeoutUseCase: SetDisplayTimeoutUseCase, private val setGainUseCase: SetGainUseCase, private val setHardwareMuteEnabledUseCase: SetHardwareMuteEnabledUseCase, private val setHidModeUseCase: SetHidModeUseCase, private val setIndicatorStateUseCase: SetIndicatorStateUseCase, private val setSpdifOutEnabledUseCase: SetSpdifOutEnabledUseCase, private val setVolumeLevelUseCase: SetVolumeLevelUseCase, private val setVolumeModeUseCase: SetVolumeModeUseCase ) LongParameterList:FiioKa5UsbRepository.kt$FiioKa5UsbRepository$( fiioKa5: FiioKa5, channelBalance: ChannelBalance, dacMode: DacMode, displayBrightness: DisplayBrightness, displayInvert: DisplayInvert, displayTimeout: DisplayTimeout, filter: Filter, gain: Gain, hardwareMute: HardwareMute, hidMode: HidMode, spdifOut: SpdifOut, volumeLevel: VolumeLevel, volumeMode: VolumeMode ) MagicNumber:ControlScreen.kt$0.01f diff --git a/app/schemas/io.github.tommygeenexus.usbdonglecontrol.core.db.ProfileDatabase/4.json b/app/schemas/io.github.tommygeenexus.usbdonglecontrol.core.db.ProfileDatabase/4.json new file mode 100644 index 0000000..4acc407 --- /dev/null +++ b/app/schemas/io.github.tommygeenexus.usbdonglecontrol.core.db.ProfileDatabase/4.json @@ -0,0 +1,142 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "b2a1ecff1ab9ca1e9067e09cab4d7df9", + "entities": [ + { + "tableName": "Profile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `vendorId` INTEGER NOT NULL, `productId` INTEGER NOT NULL, `channelBalance` INTEGER NOT NULL, `dacModeId` INTEGER NOT NULL, `displayBrightness` INTEGER NOT NULL, `displayTimeout` INTEGER NOT NULL, `filterId` INTEGER NOT NULL, `firmwareVersion` TEXT NOT NULL, `gainId` INTEGER NOT NULL, `hidModeId` INTEGER NOT NULL, `indicatorStateId` INTEGER NOT NULL, `isDisplayInvertEnabled` INTEGER NOT NULL, `isHardwareMuteEnabled` INTEGER NOT NULL, `isSpdifOutEnabled` INTEGER NOT NULL, `sampleRate` TEXT NOT NULL, `volumeLevel` INTEGER NOT NULL, `volumeModeId` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "vendorId", + "columnName": "vendorId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "channelBalance", + "columnName": "channelBalance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dacModeId", + "columnName": "dacModeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "displayBrightness", + "columnName": "displayBrightness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "displayTimeout", + "columnName": "displayTimeout", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filterId", + "columnName": "filterId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "firmwareVersion", + "columnName": "firmwareVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gainId", + "columnName": "gainId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hidModeId", + "columnName": "hidModeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "indicatorStateId", + "columnName": "indicatorStateId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDisplayInvertEnabled", + "columnName": "isDisplayInvertEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isHardwareMuteEnabled", + "columnName": "isHardwareMuteEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isSpdifOutEnabled", + "columnName": "isSpdifOutEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sampleRate", + "columnName": "sampleRate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "volumeLevel", + "columnName": "volumeLevel", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "volumeModeId", + "columnName": "volumeModeId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b2a1ecff1ab9ca1e9067e09cab4d7df9')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/UsbDongleControlActivity.kt b/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/UsbDongleControlActivity.kt index 8dcbe04..ba515ff 100644 --- a/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/UsbDongleControlActivity.kt +++ b/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/UsbDongleControlActivity.kt @@ -38,8 +38,8 @@ class UsbDongleControlActivity : ComponentActivity() { } override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) enableEdgeToEdge() + super.onCreate(savedInstanceState) setContent { UsbDongleControlTheme { NavGraph( diff --git a/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlBottomAppBar.kt b/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlBottomAppBar.kt index 5be5d45..6e1e954 100644 --- a/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlBottomAppBar.kt +++ b/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlBottomAppBar.kt @@ -20,7 +20,6 @@ package io.github.tommygeenexus.usbdonglecontrol.control.ui -import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material.icons.outlined.Refresh @@ -66,7 +65,7 @@ fun ControlBottomAppBar( ) } }, - modifier = modifier.navigationBarsPadding(), + modifier = modifier, floatingActionButton = { FloatingActionButton( onClick = onRefresh, diff --git a/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlScreen.kt b/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlScreen.kt index d03eaa9..5047b9e 100644 --- a/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlScreen.kt +++ b/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlScreen.kt @@ -23,21 +23,26 @@ package io.github.tommygeenexus.usbdonglecontrol.control.ui import android.app.Activity import android.content.Intent import android.content.IntentFilter +import android.content.res.Configuration import android.hardware.usb.UsbManager +import android.os.Build import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.FastOutLinearInEasing import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding +import androidx.compose.foundation.layout.displayCutout import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState -import androidx.compose.material3.BottomAppBarDefaults import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold @@ -46,7 +51,6 @@ import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior -import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.material3.windowsizeclass.WindowSizeClass import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable @@ -63,6 +67,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.lerp import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource @@ -275,13 +280,13 @@ fun ControlScreen( context.unregisterReceiver(usbServiceVolumeLevelChangedReceiver) } } - val activity = LocalContext.current as Activity - val bottomAppBarColor = MaterialTheme - .colorScheme - .surfaceColorAtElevation(BottomAppBarDefaults.ContainerElevation) - .toArgb() - SideEffect { - activity.window?.navigationBarColor = bottomAppBarColor + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + val activity = LocalContext.current as Activity + val bottomAppBarColor = MaterialTheme.colorScheme.surfaceContainer.toArgb() + SideEffect { + @Suppress("DEPRECATION") + activity.window?.navigationBarColor = bottomAppBarColor + } } val state by viewModel.collectAsState() ControlScreen( @@ -385,7 +390,15 @@ fun ControlScreen( onVolumeModeSelected: (Byte) -> Unit = { _ -> } ) { Scaffold( - modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + modifier = if (LocalConfiguration.current.orientation == + Configuration.ORIENTATION_LANDSCAPE + ) { + modifier + .windowInsetsPadding(WindowInsets.displayCutout) + .nestedScroll(scrollBehavior.nestedScrollConnection) + } else { + modifier.nestedScroll(scrollBehavior.nestedScrollConnection) + }, topBar = { ControlTopAppBar( windowSizeClass = windowSizeClass, @@ -396,7 +409,8 @@ fun ControlScreen( onProfileExport = onProfileExport, shouldShowActions = { windowSizeClass.widthSizeClass == WindowWidthSizeClass.Expanded - } + }, + windowInsets = WindowInsets.statusBars ) }, bottomBar = { @@ -427,9 +441,7 @@ fun ControlScreen( LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) } val surfaceColor = MaterialTheme.colorScheme.surface - val bottomAppBarColor = MaterialTheme.colorScheme.surfaceColorAtElevation( - BottomAppBarDefaults.ContainerElevation - ) + val bottomAppBarColor = MaterialTheme.colorScheme.surfaceContainer val overlappedFraction = if (scrollBehavior.state.overlappedFraction > 0.01f) 1f else 0f val animatedColor by animateColorAsState( targetValue = lerp( @@ -440,10 +452,13 @@ fun ControlScreen( animationSpec = spring(stiffness = Spring.StiffnessMediumLow), label = "StatusBarColorAnimation" ) - val activity = LocalContext.current as? Activity - if (activity != null) { - LaunchedEffect(animatedColor) { - activity.window?.statusBarColor = animatedColor.toArgb() + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + val activity = LocalContext.current as? Activity + if (activity != null) { + LaunchedEffect(animatedColor) { + @Suppress("DEPRECATION") + activity.window?.statusBarColor = animatedColor.toArgb() + } } } var selectedTabIndex by rememberSaveable { mutableIntStateOf(ControlTabs.State.index) } diff --git a/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlTopAppBar.kt b/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlTopAppBar.kt index 7d9fc6b..2a2b0df 100644 --- a/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlTopAppBar.kt +++ b/app/src/main/kotlin/io/github/tommygeenexus/usbdonglecontrol/control/ui/ControlTopAppBar.kt @@ -20,7 +20,7 @@ package io.github.tommygeenexus.usbdonglecontrol.control.ui -import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material3.CenterAlignedTopAppBar @@ -48,6 +48,7 @@ fun ControlTopAppBar( onReset: () -> Unit, onProfileExport: (String) -> Unit, shouldShowActions: () -> Boolean, + windowInsets: WindowInsets, modifier: Modifier = Modifier ) { CenterAlignedTopAppBar( @@ -58,7 +59,7 @@ fun ControlTopAppBar( overflow = TextOverflow.Ellipsis ) }, - modifier = modifier.statusBarsPadding(), + modifier = modifier, actions = { if (shouldShowActions()) { var showMore by rememberSaveable { mutableStateOf(false) } @@ -78,6 +79,7 @@ fun ControlTopAppBar( } } }, + windowInsets = windowInsets, scrollBehavior = scrollBehavior ) }