diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1982f76..6a94983 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -24,7 +24,7 @@ android { minSdk = 29 targetSdk = 34 versionCode = (System.currentTimeMillis() / 1000).toInt() - versionName = "0.20.3-beta" + versionName = "0.21.0-beta" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -150,6 +150,7 @@ dependencies { implementation(libs.ktx) debugImplementation(libs.compose.tooling) implementation(libs.compose.tooling.preview) + implementation(libs.compose.material3) // room implementation(libs.room.runtime) diff --git a/app/src/main/java/f/cking/software/TheApp.kt b/app/src/main/java/f/cking/software/TheApp.kt index 97469b6..3fb8b40 100644 --- a/app/src/main/java/f/cking/software/TheApp.kt +++ b/app/src/main/java/f/cking/software/TheApp.kt @@ -1,6 +1,7 @@ package f.cking.software import android.app.Application +import com.google.android.material.color.DynamicColors import f.cking.software.data.DataModule import f.cking.software.domain.interactor.InteractorsModule import f.cking.software.domain.interactor.SaveFirstAppLaunchTimeInteractor @@ -16,11 +17,16 @@ class TheApp : Application() { override fun onCreate() { super.onCreate() instance = this + applyDynamicColors() initDi() initTimber() saveFirstLaunchTime() } + private fun applyDynamicColors() { + DynamicColors.applyToActivitiesIfAvailable(this) + } + private fun saveFirstLaunchTime() { getKoin().get().execute() } diff --git a/app/src/main/java/f/cking/software/ui/MainActivity.kt b/app/src/main/java/f/cking/software/ui/MainActivity.kt index 845d3c8..52c4a60 100644 --- a/app/src/main/java/f/cking/software/ui/MainActivity.kt +++ b/app/src/main/java/f/cking/software/ui/MainActivity.kt @@ -1,25 +1,25 @@ package f.cking.software.ui +import android.annotation.SuppressLint import android.content.Intent import android.content.SharedPreferences import android.graphics.Color +import android.os.Build import android.os.Bundle import androidx.activity.SystemBarStyle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.statusBars -import androidx.compose.foundation.layout.windowInsetsTopHeight -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Typography -import androidx.compose.ui.Modifier +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Typography +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.colorResource import androidx.lifecycle.ViewModel @@ -67,20 +67,13 @@ class MainActivity : AppCompatActivity() { setContent { val focusManager = LocalFocusManager.current + val colors = themeColorScheme() MaterialTheme( - colors = MaterialTheme.colors.copy( - primary = colorResource(id = R.color.primary), - primaryVariant = colorResource(id = R.color.primary_variant), - onPrimary = colorResource(id = R.color.on_primary), - secondary = colorResource(id = R.color.secondary), - secondaryVariant = colorResource(id = R.color.secondary_variant), - onSecondary = colorResource(id = R.color.on_secondary), - surface = colorResource(id = R.color.surface_color), - onSurface = colorResource(id = R.color.on_surface), - ), + colorScheme = colors, typography = Typography( - body1 = MaterialTheme.typography.body1.copy(color = colorResource(id = R.color.on_surface)), - body2 = MaterialTheme.typography.body2.copy(color = colorResource(id = R.color.on_surface)), + bodyMedium = MaterialTheme.typography.bodyMedium.copy(color = colors.onSurface), + bodyLarge = MaterialTheme.typography.bodyLarge.copy(color = colors.onSurface), + bodySmall = MaterialTheme.typography.bodySmall.copy(color = colors.onSurface), ) ) { val stack = viewModel.navigator.stack @@ -88,22 +81,8 @@ class MainActivity : AppCompatActivity() { finish() } else { focusManager.clearFocus(true) - Column { - Box( - modifier = Modifier - .windowInsetsTopHeight(WindowInsets.statusBars) - .fillMaxWidth() - .background(MaterialTheme.colors.primary) - ) - Box( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - ) { - stack.forEach { screen -> - screen() - } - } + stack.forEach { screen -> + screen() } } } @@ -121,6 +100,7 @@ class MainActivity : AppCompatActivity() { intentHelper.handleActivityResult(requestCode, resultCode, data) } + @SuppressLint("MissingSuperCall") @Deprecated("Deprecated in Java") override fun onBackPressed() { router.navigate(BackCommand) @@ -135,4 +115,84 @@ class MainActivity : AppCompatActivity() { private class MainActivityViewModel : ViewModel() { val navigator: Navigator = Navigator(root = ScreenNavigationCommands.OpenMainScreen) } + + @Composable + private fun themeColorScheme(): ColorScheme { + val dynamicColorsAreSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S + val darkMode = isSystemInDarkTheme() + val colors = when { + dynamicColorsAreSupported && darkMode -> dynamicDarkColorScheme(this) + dynamicColorsAreSupported && !darkMode -> dynamicLightColorScheme(this) + darkMode -> darkColorScheme( + primary = colorResource(id = R.color.md_theme_dark_primary), + onPrimary = colorResource(id = R.color.md_theme_dark_onPrimary), + primaryContainer = colorResource(id = R.color.md_theme_dark_primaryContainer), + onPrimaryContainer = colorResource(id = R.color.md_theme_dark_onPrimaryContainer), + secondary = colorResource(id = R.color.md_theme_dark_secondary), + onSecondary = colorResource(id = R.color.md_theme_dark_onSecondary), + secondaryContainer = colorResource(id = R.color.md_theme_dark_secondaryContainer), + onSecondaryContainer = colorResource(id = R.color.md_theme_dark_onSecondaryContainer), + tertiary = colorResource(id = R.color.md_theme_dark_tertiary), + onTertiary = colorResource(id = R.color.md_theme_dark_onTertiary), + tertiaryContainer = colorResource(id = R.color.md_theme_dark_tertiaryContainer), + onTertiaryContainer = colorResource(id = R.color.md_theme_dark_onTertiaryContainer), + error = colorResource(id = R.color.md_theme_dark_error), + errorContainer = colorResource(id = R.color.md_theme_dark_errorContainer), + onError = colorResource(id = R.color.md_theme_dark_onError), + onErrorContainer = colorResource(id = R.color.md_theme_dark_onErrorContainer), + background = colorResource(id = R.color.md_theme_dark_background), + onBackground = colorResource(id = R.color.md_theme_dark_onBackground), + surface = colorResource(id = R.color.md_theme_dark_surface), + surfaceContainer = colorResource(id = R.color.md_theme_dark_surfaceContainer), + surfaceContainerHigh = colorResource(id = R.color.md_theme_dark_surfaceContainerHigh), + surfaceContainerHighest = colorResource(id = R.color.md_theme_dark_surfaceContainerHighest), + onSurface = colorResource(id = R.color.md_theme_dark_onSurface), + surfaceVariant = colorResource(id = R.color.md_theme_dark_surfaceVariant), + onSurfaceVariant = colorResource(id = R.color.md_theme_dark_onSurfaceVariant), + outline = colorResource(id = R.color.md_theme_dark_outline), + inverseOnSurface = colorResource(id = R.color.md_theme_dark_inverseOnSurface), + inverseSurface = colorResource(id = R.color.md_theme_dark_inverseSurface), + inversePrimary = colorResource(id = R.color.md_theme_dark_inversePrimary), + surfaceTint = colorResource(id = R.color.md_theme_dark_surfaceTint), + outlineVariant = colorResource(id = R.color.md_theme_dark_outlineVariant), + scrim = colorResource(id = R.color.md_theme_dark_scrim), + ) + !darkMode -> lightColorScheme( + primary = colorResource(id = R.color.md_theme_light_primary), + onPrimary = colorResource(id = R.color.md_theme_light_onPrimary), + primaryContainer = colorResource(id = R.color.md_theme_light_primaryContainer), + onPrimaryContainer = colorResource(id = R.color.md_theme_light_onPrimaryContainer), + secondary = colorResource(id = R.color.md_theme_light_secondary), + onSecondary = colorResource(id = R.color.md_theme_light_onSecondary), + secondaryContainer = colorResource(id = R.color.md_theme_light_secondaryContainer), + onSecondaryContainer = colorResource(id = R.color.md_theme_light_onSecondaryContainer), + tertiary = colorResource(id = R.color.md_theme_light_tertiary), + onTertiary = colorResource(id = R.color.md_theme_light_onTertiary), + tertiaryContainer = colorResource(id = R.color.md_theme_light_tertiaryContainer), + onTertiaryContainer = colorResource(id = R.color.md_theme_light_onTertiaryContainer), + error = colorResource(id = R.color.md_theme_light_error), + errorContainer = colorResource(id = R.color.md_theme_light_errorContainer), + onError = colorResource(id = R.color.md_theme_light_onError), + onErrorContainer = colorResource(id = R.color.md_theme_light_onErrorContainer), + background = colorResource(id = R.color.md_theme_light_background), + onBackground = colorResource(id = R.color.md_theme_light_onBackground), + surface = colorResource(id = R.color.md_theme_light_surface), + surfaceContainer = colorResource(id = R.color.md_theme_light_surfaceContainer), + surfaceContainerHigh = colorResource(id = R.color.md_theme_light_surfaceContainerHigh), + surfaceContainerHighest = colorResource(id = R.color.md_theme_light_surfaceContainerHighest), + onSurface = colorResource(id = R.color.md_theme_light_onSurface), + surfaceVariant = colorResource(id = R.color.md_theme_light_surfaceVariant), + onSurfaceVariant = colorResource(id = R.color.md_theme_light_onSurfaceVariant), + outline = colorResource(id = R.color.md_theme_light_outline), + inverseOnSurface = colorResource(id = R.color.md_theme_light_inverseOnSurface), + inverseSurface = colorResource(id = R.color.md_theme_light_inverseSurface), + inversePrimary = colorResource(id = R.color.md_theme_light_inversePrimary), + surfaceTint = colorResource(id = R.color.md_theme_light_surfaceTint), + outlineVariant = colorResource(id = R.color.md_theme_light_outlineVariant), + scrim = colorResource(id = R.color.md_theme_light_scrim), + ) + else -> throw IllegalStateException("This state is unreachable") + } + return colors + } } diff --git a/app/src/main/java/f/cking/software/ui/devicedetails/DeviceDetailsScreen.kt b/app/src/main/java/f/cking/software/ui/devicedetails/DeviceDetailsScreen.kt index 519ecde..fbc6787 100644 --- a/app/src/main/java/f/cking/software/ui/devicedetails/DeviceDetailsScreen.kt +++ b/app/src/main/java/f/cking/software/ui/devicedetails/DeviceDetailsScreen.kt @@ -8,30 +8,31 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize 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.width -import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Button -import androidx.compose.material.Chip -import androidx.compose.material.ChipDefaults -import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.LinearProgressIndicator -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold -import androidx.compose.material.Text -import androidx.compose.material.TopAppBar +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SuggestionChip +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -59,7 +60,6 @@ import f.cking.software.dpToPx import f.cking.software.frameRate import f.cking.software.ui.AsyncBatchProcessor import f.cking.software.ui.tagdialog.TagDialog -import f.cking.software.utils.graphic.GlassSystemNavbar import f.cking.software.utils.graphic.MapView import f.cking.software.utils.graphic.RoundedBox import f.cking.software.utils.graphic.SystemNavbarSpacer @@ -73,6 +73,7 @@ import org.osmdroid.views.MapView import org.osmdroid.views.overlay.Marker import timber.log.Timber +@OptIn(ExperimentalMaterial3Api::class) object DeviceDetailsScreen { @Composable @@ -81,21 +82,18 @@ object DeviceDetailsScreen { Scaffold( modifier = Modifier - .background(MaterialTheme.colors.surface) - .fillMaxWidth() - .fillMaxHeight(), + .background(MaterialTheme.colorScheme.surface) + .fillMaxSize(), topBar = { AppBar(viewModel = viewModel) }, content = { - GlassSystemNavbar { - Content( - modifier = Modifier - .background(MaterialTheme.colors.surface) - .padding(it), - viewModel = viewModel, - ) - } + Content( + modifier = Modifier + .background(MaterialTheme.colorScheme.surface) + .padding(it), + viewModel = viewModel, + ) } ) } @@ -116,7 +114,7 @@ object DeviceDetailsScreen { Icon( imageVector = ImageVector.vectorResource(id = iconId), contentDescription = text, - tint = MaterialTheme.colors.onPrimary, + tint = MaterialTheme.colorScheme.onSurface, ) } } @@ -124,9 +122,9 @@ object DeviceDetailsScreen { navigationIcon = { IconButton(onClick = { viewModel.back() }) { Icon( - imageVector = Icons.Filled.ArrowBack, + imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colors.onPrimary + tint = MaterialTheme.colorScheme.onSurface ) } } @@ -149,9 +147,7 @@ object DeviceDetailsScreen { @Composable private fun Progress(modifier: Modifier = Modifier) { Box( - modifier = modifier - .fillMaxWidth() - .fillMaxHeight(), + modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center, ) { CircularProgressIndicator() @@ -166,21 +162,22 @@ object DeviceDetailsScreen { ) { Column( modifier = modifier - .fillMaxHeight() - .fillMaxWidth() + .padding(horizontal = 16.dp) + .fillMaxSize() ) { LocationHistory( modifier = Modifier .weight(1f) - .fillMaxWidth(), viewModel = viewModel + .fillMaxWidth(), + deviceData = deviceData, + viewModel = viewModel ) - LazyColumn( - modifier = Modifier - .fillMaxWidth() - .weight(1.5f) - ) { - item { DeviceContent(deviceData = deviceData, viewModel = viewModel) } - } + Spacer(modifier = Modifier.height(16.dp)) + Tags(deviceData = deviceData, viewModel = viewModel) + Spacer(modifier = Modifier.height(16.dp)) + DeviceContent(modifier = Modifier.weight(1f), deviceData = deviceData) + Spacer(modifier = Modifier.height(16.dp)) + SystemNavbarSpacer() } } @@ -188,62 +185,61 @@ object DeviceDetailsScreen { private fun DeviceContent( modifier: Modifier = Modifier, deviceData: DeviceData, - viewModel: DeviceDetailsViewModel, ) { - Column( + RoundedBox( modifier = modifier - .fillMaxWidth() - .padding(16.dp), + .fillMaxWidth(), + internalPaddings = 0.dp, ) { + Column( + Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + .verticalScroll(rememberScrollState()) + ) { + Spacer(modifier = Modifier.height(16.dp)) + Text( + text = deviceData.buildDisplayName(), + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + ) + Spacer(modifier = Modifier.height(8.dp)) - HistoryPeriod(deviceData = deviceData, viewModel = viewModel) - Spacer(modifier = Modifier.height(8.dp)) - - Tags(deviceData = deviceData, viewModel = viewModel) - Spacer(modifier = Modifier.height(8.dp)) + Text(text = stringResource(R.string.device_details_name), fontWeight = FontWeight.Bold) + Text(text = deviceData.name ?: stringResource(R.string.not_applicable)) + Spacer(modifier = Modifier.height(8.dp)) - Text( - text = deviceData.buildDisplayName(), - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - ) - Spacer(modifier = Modifier.height(8.dp)) + Text(text = stringResource(R.string.device_details_address), fontWeight = FontWeight.Bold) + Text(text = deviceData.address) + Spacer(modifier = Modifier.height(8.dp)) - Text(text = stringResource(R.string.device_details_name), fontWeight = FontWeight.Bold) - Text(text = deviceData.name ?: stringResource(R.string.not_applicable)) - Spacer(modifier = Modifier.height(8.dp)) + Text(text = stringResource(R.string.device_details_manufacturer), fontWeight = FontWeight.Bold) + Text(text = deviceData.manufacturerInfo?.name ?: stringResource(R.string.not_applicable)) + Spacer(modifier = Modifier.height(8.dp)) - Text(text = stringResource(R.string.device_details_address), fontWeight = FontWeight.Bold) - Text(text = deviceData.address) - Spacer(modifier = Modifier.height(8.dp)) + Row { + Text( + text = stringResource(R.string.device_details_detect_count), + fontWeight = FontWeight.Bold, + ) + Spacer(Modifier.width(4.dp)) + Text(text = deviceData.detectCount.toString()) + } + Spacer(modifier = Modifier.height(8.dp)) - Text(text = stringResource(R.string.device_details_manufacturer), fontWeight = FontWeight.Bold) - Text(text = deviceData.manufacturerInfo?.name ?: stringResource(R.string.not_applicable)) - Spacer(modifier = Modifier.height(8.dp)) + Text(text = stringResource(R.string.device_details_first_detection), fontWeight = FontWeight.Bold) + Text( + text = stringResource(R.string.time_ago, deviceData.firstDetectionPeriod(LocalContext.current)) + ) + Spacer(modifier = Modifier.height(8.dp)) - Row { + Text(text = stringResource(R.string.device_details_last_detection), fontWeight = FontWeight.Bold) Text( - text = stringResource(R.string.device_details_detect_count), - fontWeight = FontWeight.Bold, + text = stringResource(R.string.time_ago, deviceData.lastDetectionPeriod(LocalContext.current)) ) - Spacer(Modifier.width(4.dp)) - Text(text = deviceData.detectCount.toString()) + Spacer(modifier = Modifier.height(16.dp)) } - Spacer(modifier = Modifier.height(8.dp)) - - Text(text = stringResource(R.string.device_details_first_detection), fontWeight = FontWeight.Bold) - Text( - text = stringResource(R.string.time_ago, deviceData.firstDetectionPeriod(LocalContext.current)) - ) - Spacer(modifier = Modifier.height(8.dp)) - - Text(text = stringResource(R.string.device_details_last_detection), fontWeight = FontWeight.Bold) - Text( - text = stringResource(R.string.time_ago, deviceData.lastDetectionPeriod(LocalContext.current)) - ) - Spacer(modifier = Modifier.height(8.dp)) - SystemNavbarSpacer() } } @@ -282,9 +278,9 @@ object DeviceDetailsScreen { buttons = { negativeButton( text = stringResource(R.string.cancel), - textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant) + textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer) ) { dialogState.hide() } - positiveButton(text = stringResource(R.string.confirm), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { + positiveButton(text = stringResource(R.string.confirm), textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) { dialogState.hide() viewModel.onRemoveTagClick(deviceData, name) } @@ -299,7 +295,6 @@ object DeviceDetailsScreen { TagChip(tagName = name, tagIcon = Icons.Filled.Delete) { dialogState.show() } } - @OptIn(ExperimentalMaterialApi::class) @Composable fun AddTag( deviceData: DeviceData, @@ -308,16 +303,13 @@ object DeviceDetailsScreen { val addTagDialog = TagDialog.rememberDialog { viewModel.onNewTagSelected(deviceData, it) } - Chip( - colors = ChipDefaults.chipColors( - backgroundColor = MaterialTheme.colors.secondary, - contentColor = MaterialTheme.colors.onSecondary, - ), + SuggestionChip( onClick = { addTagDialog.show() }, - leadingIcon = { - Icon(imageVector = Icons.Default.Add, contentDescription = null, tint = MaterialTheme.colors.onSurface) - } - ) { Text(text = stringResource(R.string.add_tag)) } + icon = { + Icon(imageVector = Icons.Default.Add, contentDescription = null, tint = MaterialTheme.colorScheme.onSurface) + }, + label = { Text(text = stringResource(R.string.add_tag)) } + ) } @Composable @@ -331,7 +323,7 @@ object DeviceDetailsScreen { buttons = { negativeButton( stringResource(R.string.cancel), - textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant) + textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer) ) { dialog.hide() } }, ) { @@ -361,53 +353,53 @@ object DeviceDetailsScreen { } } } - - RoundedBox( - modifier = Modifier.fillMaxWidth(), - internalPaddings = 0.dp + Box( + modifier = Modifier + .fillMaxWidth() + .clickable { dialog.show() }, ) { - Box( + Row( modifier = Modifier .fillMaxWidth() - .clickable { dialog.show() }, + .padding(horizontal = 16.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically, ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 8.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Column(modifier = Modifier.weight(1f)) { - Row { - Text(text = stringResource(R.string.device_details_history_period), fontSize = 18.sp) - Text(text = stringResource(viewModel.historyPeriod.displayNameRes), fontWeight = FontWeight.Bold, fontSize = 18.sp) - } - Spacer(modifier = Modifier.height(4.dp)) - Text( - text = stringResource(R.string.device_details_history_period_subtitle), - fontWeight = FontWeight.Light, - ) + Column(modifier = Modifier.weight(1f)) { + Row { + Text(text = stringResource(R.string.device_details_history_period), fontSize = 18.sp) + Text(text = stringResource(viewModel.historyPeriod.displayNameRes), fontWeight = FontWeight.Bold, fontSize = 18.sp) } - Spacer(modifier = Modifier.width(8.dp)) - Image( - modifier = Modifier.size(24.dp), - imageVector = Icons.Default.Edit, - colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface), - contentDescription = stringResource(R.string.change) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = stringResource(R.string.device_details_history_period_subtitle), + fontWeight = FontWeight.Light, ) } + Spacer(modifier = Modifier.width(8.dp)) + Image( + modifier = Modifier.size(24.dp), + imageVector = Icons.Default.Edit, + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface), + contentDescription = stringResource(R.string.change) + ) } } } @Composable - private fun LocationHistory(modifier: Modifier = Modifier, viewModel: DeviceDetailsViewModel) { - Box(modifier = modifier) { - Map( - viewModel = viewModel, - isLoading = { viewModel.markersInLoadingState = it } - ) - MapOverlay(viewModel = viewModel) + private fun LocationHistory(modifier: Modifier = Modifier, deviceData: DeviceData, viewModel: DeviceDetailsViewModel) { + RoundedBox(modifier = modifier, internalPaddings = 0.dp) { + Box(modifier = Modifier + .fillMaxWidth() + .weight(1f)) { + Map( + Modifier.fillMaxSize(), + viewModel = viewModel, + isLoading = { viewModel.markersInLoadingState = it } + ) + MapOverlay(viewModel = viewModel) + } + HistoryPeriod(deviceData = deviceData, viewModel = viewModel) } } @@ -442,7 +434,7 @@ object DeviceDetailsScreen { modifier = Modifier .fillMaxWidth() .height(5.dp), - color = Color.Black + color = MaterialTheme.colorScheme.onSurface ) } } @@ -450,6 +442,7 @@ object DeviceDetailsScreen { @Composable private fun Map( + modifier: Modifier, viewModel: DeviceDetailsViewModel, isLoading: (isLoading: Boolean) -> Unit, ) { @@ -490,9 +483,7 @@ object DeviceDetailsScreen { } MapView( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), + modifier = modifier, onLoad = { map -> initMapState(map) }, onUpdate = { map -> refreshMap(map, viewModel, batchProcessor) } ) diff --git a/app/src/main/java/f/cking/software/ui/devicelist/DeviceListScreen.kt b/app/src/main/java/f/cking/software/ui/devicelist/DeviceListScreen.kt index 9ed7bb4..9953c8f 100644 --- a/app/src/main/java/f/cking/software/ui/devicelist/DeviceListScreen.kt +++ b/app/src/main/java/f/cking/software/ui/devicelist/DeviceListScreen.kt @@ -7,7 +7,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -15,21 +15,20 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.material.Button -import androidx.compose.material.Chip -import androidx.compose.material.ChipDefaults -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.LinearProgressIndicator -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.material.TextField import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.Button +import androidx.compose.material3.FilterChip +import androidx.compose.material3.Icon +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SuggestionChip +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember @@ -42,7 +41,6 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -50,19 +48,20 @@ import com.vanpra.composematerialdialogs.rememberMaterialDialogState import f.cking.software.R import f.cking.software.ui.ScreenNavigationCommands import f.cking.software.ui.filter.SelectFilterTypeScreen -import f.cking.software.utils.graphic.BottomSpacer import f.cking.software.utils.graphic.ContentPlaceholder import f.cking.software.utils.graphic.DeviceListItem import f.cking.software.utils.graphic.Divider +import f.cking.software.utils.graphic.FABSpacer import f.cking.software.utils.graphic.RoundedBox import org.koin.androidx.compose.koinViewModel +@OptIn(ExperimentalFoundationApi::class) object DeviceListScreen { - @OptIn(ExperimentalFoundationApi::class) @Composable fun Screen() { - val modifier = Modifier.background(MaterialTheme.colors.surface) + val modifier = Modifier.background(MaterialTheme.colorScheme.surface) + .fillMaxSize() val viewModel: DeviceListViewModel = koinViewModel() val focusManager = LocalFocusManager.current val nestedScroll = remember { @@ -83,15 +82,12 @@ object DeviceListScreen { modifier = Modifier .fillMaxWidth() .height(4.dp), - color = MaterialTheme.colors.onPrimary + color = MaterialTheme.colorScheme.onSurfaceVariant ) } } else { LazyColumn( - modifier = modifier - .fillMaxWidth() - .fillMaxHeight() - .nestedScroll(nestedScroll), + modifier = modifier.nestedScroll(nestedScroll), ) { stickyHeader { Box() { @@ -101,7 +97,7 @@ object DeviceListScreen { modifier = Modifier .fillMaxWidth() .height(4.dp), - color = MaterialTheme.colors.onPrimary + color = MaterialTheme.colorScheme.onSurfaceVariant ) } } @@ -129,7 +125,7 @@ object DeviceListScreen { } item { - BottomSpacer() + FABSpacer() } } } @@ -192,13 +188,12 @@ object DeviceListScreen { } } - @OptIn(ExperimentalMaterialApi::class) @Composable private fun Filters(viewModel: DeviceListViewModel) { - Surface(elevation = 4.dp) { + Surface(shadowElevation = 4.dp) { Column( modifier = Modifier - .background(colorResource(id = R.color.primary_surface)) + .background(MaterialTheme.colorScheme.surfaceContainerHighest) .fillMaxWidth() ) { LazyRow( @@ -206,23 +201,18 @@ object DeviceListScreen { ) { val allFilters = (viewModel.quickFilters + viewModel.appliedFilter).toSet() - item { Spacer(modifier = Modifier.width(8.dp)) } + item { Spacer(modifier = Modifier.width(16.dp)) } + + item { + SearchChip(viewModel) + Spacer(modifier = Modifier.width(8.dp)) + } allFilters.forEach { item { val isSelected = viewModel.appliedFilter.contains(it) - val colors = if (isSelected) { - ChipDefaults.chipColors( - backgroundColor = MaterialTheme.colors.primaryVariant, - contentColor = MaterialTheme.colors.onPrimary, - leadingIconContentColor = MaterialTheme.colors.onPrimary, - ) - } else { - ChipDefaults.chipColors() - } - Chip( - colors = colors, + FilterChip( onClick = { viewModel.onFilterClick(it) }, leadingIcon = { if (isSelected) { @@ -232,22 +222,19 @@ object DeviceListScreen { modifier = Modifier.size(24.dp) ) } + }, + selected = isSelected, + label = { + Text(text = it.displayName) } - ) { - Text(text = it.displayName) - } + ) Spacer(modifier = Modifier.width(8.dp)) } } - item { - SearchChip(viewModel) - Spacer(modifier = Modifier.width(8.dp)) - } - item { AddFilterChip(viewModel) - Spacer(modifier = Modifier.width(8.dp)) + Spacer(modifier = Modifier.width(16.dp)) } } @@ -258,7 +245,6 @@ object DeviceListScreen { } } - @OptIn(ExperimentalMaterialApi::class) @Composable private fun AddFilterChip(viewModel: DeviceListViewModel) { @@ -277,39 +263,30 @@ object DeviceListScreen { }) } - Chip( - leadingIcon = { + SuggestionChip( + icon = { Icon(Icons.Filled.Add, contentDescription = stringResource(R.string.delete), modifier = Modifier.size(24.dp)) }, onClick = { selectFilterDialog.show() }, - ) { - Text(text = stringResource(R.string.add_filter)) - } + label = { + Text(text = stringResource(R.string.add_filter)) + } + ) } - @OptIn(ExperimentalMaterialApi::class) @Composable private fun SearchChip(viewModel: DeviceListViewModel) { - val colors = if (viewModel.isSearchMode) { - ChipDefaults.chipColors( - backgroundColor = MaterialTheme.colors.primaryVariant, - contentColor = MaterialTheme.colors.onPrimary, - leadingIconContentColor = MaterialTheme.colors.onPrimary, - ) - } else { - ChipDefaults.chipColors() - } - - Chip( - colors = colors, + FilterChip( leadingIcon = { val icon = if (viewModel.isSearchMode) Icons.Filled.Delete else Icons.Filled.Search Icon(icon, contentDescription = stringResource(R.string.delete), modifier = Modifier.size(24.dp)) }, onClick = { viewModel.onOpenSearchClick() }, - ) { - Text(text = viewModel.searchQuery?.takeIf { it.isNotBlank() } ?: stringResource(R.string.search)) - } + selected = viewModel.isSearchMode, + label = { + Text(text = viewModel.searchQuery?.takeIf { it.isNotBlank() } ?: stringResource(R.string.search)) + } + ) } @Composable diff --git a/app/src/main/java/f/cking/software/ui/filter/FilterScreen.kt b/app/src/main/java/f/cking/software/ui/filter/FilterScreen.kt index 5e4ff18..3482fba 100644 --- a/app/src/main/java/f/cking/software/ui/filter/FilterScreen.kt +++ b/app/src/main/java/f/cking/software/ui/filter/FilterScreen.kt @@ -14,20 +14,18 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Checkbox -import androidx.compose.material.Chip -import androidx.compose.material.ChipDefaults -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.TextField import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.List +import androidx.compose.material3.Checkbox +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SuggestionChip +import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -168,7 +166,6 @@ object FilterScreen { } } - @OptIn(ExperimentalMaterialApi::class) @Composable private fun FilterTag( filter: FilterUiState.Tag, @@ -186,16 +183,13 @@ object FilterScreen { val tag = filter.tag if (tag == null) { - Chip( - colors = ChipDefaults.chipColors( - backgroundColor = MaterialTheme.colors.primary, - contentColor = Color.White, - ), + SuggestionChip( onClick = { addTagDialog.show() }, - leadingIcon = { + icon = { Icon(imageVector = Icons.Default.Add, contentDescription = null) - } - ) { Text(text = stringResource(R.string.select_tag)) } + }, + label = { Text(text = stringResource(R.string.select_tag)) } + ) } else { TagChip(tagName = tag, tagIcon = Icons.Filled.Delete) { filter.tag = null } } @@ -586,8 +580,8 @@ object FilterScreen { Checkbox( checked = filter.defaultValueIfNoLocation, onCheckedChange = { - filter.defaultValueIfNoLocation = it - }) + filter.defaultValueIfNoLocation = it + }) Spacer(modifier = Modifier.width(16.dp)) } } @@ -619,7 +613,6 @@ object FilterScreen { } } - @OptIn(ExperimentalMaterialApi::class) @Composable private fun FilterNot( filter: FilterUiState.Not, @@ -639,19 +632,13 @@ object FilterScreen { if (filter.filter != null) { Filter(filter.filter!!, router = router, onDeleteClick = filter::delete) } else { - Chip( + SuggestionChip( onClick = { selectFilterDialog.show() }, - colors = ChipDefaults.chipColors( - backgroundColor = colorResource(R.color.filter_not), - contentColor = Color.Black, - leadingIconContentColor = Color.Black - ), - leadingIcon = { + icon = { Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.add_filter)) - } - ) { - Text(text = stringResource(R.string.select)) - } + }, + label = { Text(text = stringResource(R.string.select)) }, + ) } } } @@ -673,13 +660,13 @@ object FilterScreen { ) { Box(Modifier.clickable { minimize.value = !minimize.value }) { Row(Modifier.padding(8.dp), verticalAlignment = Alignment.CenterVertically) { - Text(text = title, fontSize = 20.sp, fontWeight = FontWeight.Bold, color = Color.Black) + Text(text = title, fontSize = 20.sp, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurface) val icon = if (!minimize.value) painterResource(R.drawable.ic_drop_down) else painterResource(R.drawable.ic_drop_up) Icon( painter = icon, contentDescription = stringResource(R.string.delete), modifier = Modifier.size(24.dp), - tint = Color.Black, + tint = MaterialTheme.colorScheme.onSurface, ) Spacer(Modifier.weight(1f)) IconButton(onClick = onDeleteButtonClick) { @@ -687,7 +674,7 @@ object FilterScreen { imageVector = Icons.Filled.Delete, contentDescription = stringResource(R.string.delete), modifier = Modifier.size(24.dp), - tint = Color.Black, + tint = MaterialTheme.colorScheme.onSurface, ) } } @@ -705,7 +692,6 @@ object FilterScreen { } } - @OptIn(ExperimentalMaterialApi::class) @Composable private fun FilterGroup( title: String, @@ -719,19 +705,13 @@ object FilterScreen { Column { content.invoke() Spacer(modifier = Modifier.height(4.dp)) - Chip( + SuggestionChip( onClick = addClick, - colors = ChipDefaults.chipColors( - backgroundColor = color, - contentColor = Color.Black, - leadingIconContentColor = Color.Black - ), - leadingIcon = { + icon = { Icon(imageVector = Icons.Default.Add, contentDescription = addText) - } - ) { - Text(text = addText) - } + }, + label = { Text(text = addText) }, + ) } } } diff --git a/app/src/main/java/f/cking/software/ui/filter/SelectFilterScreen.kt b/app/src/main/java/f/cking/software/ui/filter/SelectFilterScreen.kt index dbdca4a..7b967e7 100644 --- a/app/src/main/java/f/cking/software/ui/filter/SelectFilterScreen.kt +++ b/app/src/main/java/f/cking/software/ui/filter/SelectFilterScreen.kt @@ -4,31 +4,38 @@ import android.widget.Toast import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.Button -import androidx.compose.material.ButtonDefaults -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.material.TopAppBar +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import f.cking.software.R import f.cking.software.domain.model.RadarProfile +import f.cking.software.utils.graphic.BottomNavigationSpacer +import f.cking.software.utils.graphic.GlassBottomSpace import f.cking.software.utils.graphic.SystemNavbarSpacer import f.cking.software.utils.navigation.BackCommand import f.cking.software.utils.navigation.Router +@OptIn(ExperimentalMaterial3Api::class) object SelectFilterScreen { @Composable @@ -37,77 +44,80 @@ object SelectFilterScreen { router: Router, onConfirm: (filterState: RadarProfile.Filter) -> Unit ) { + + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() Scaffold( - topBar = { AppBar { router.navigate(BackCommand) } }, + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + AppBar(scrollBehavior) { router.navigate(BackCommand) } + }, content = { paddings -> - Column(modifier = Modifier - .background(MaterialTheme.colors.surface) - .padding(paddings)) { - LazyColumn( - Modifier - .fillMaxWidth() - .weight(1f) - ) { - item { - Box( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - ) { - FilterScreen.Filter( - filterState = initialFilterState, - router = router, - onDeleteClick = { router.navigate(BackCommand) } - ) + GlassBottomSpace( + modifier = Modifier.fillMaxSize(), + bottomContent = { + val context = LocalContext.current + Button( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + onClick = { + val filter = initialFilterState + .takeIf { it.isCorrect() } + ?.let { FilterUiMapper.mapToDomain(it) } + + if (filter != null) { + router.navigate(BackCommand) + onConfirm.invoke(filter) + } else { + Toast.makeText(context, context.getString(R.string.filter_is_not_valid), Toast.LENGTH_SHORT).show() + } } + ) { + Text(text = stringResource(R.string.confirm), color = MaterialTheme.colorScheme.onPrimary) } - } - Surface(elevation = 12.dp) { + SystemNavbarSpacer() + }, + globalContent = { Column( modifier = Modifier - .background(MaterialTheme.colors.primary) - .fillMaxWidth(), + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .background(MaterialTheme.colorScheme.surface) + .padding(top = paddings.calculateTopPadding()) ) { - val context = LocalContext.current - Button( + Box( modifier = Modifier - .fillMaxWidth() .padding(16.dp), - colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.primaryVariant), - onClick = { - val filter = initialFilterState - .takeIf { it.isCorrect() } - ?.let { FilterUiMapper.mapToDomain(it) } - - if (filter != null) { - router.navigate(BackCommand) - onConfirm.invoke(filter) - } else { - Toast.makeText(context, context.getString(R.string.filter_is_not_valid), Toast.LENGTH_SHORT).show() - } - } ) { - Text(text = stringResource(R.string.confirm), color = MaterialTheme.colors.onPrimary) + FilterScreen.Filter( + filterState = initialFilterState, + router = router, + onDeleteClick = { router.navigate(BackCommand) } + ) } - - SystemNavbarSpacer() + BottomNavigationSpacer() } } - } + ) } ) } + @OptIn(ExperimentalMaterial3Api::class) @Composable - private fun AppBar(onBackClick: () -> Unit) { + private fun AppBar(scrollBehavior: TopAppBarScrollBehavior, onBackClick: () -> Unit) { TopAppBar( + scrollBehavior = scrollBehavior, + colors = TopAppBarDefaults.topAppBarColors( + scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainerHighest, + ), title = { Text(text = stringResource(R.string.create_filter)) }, navigationIcon = { IconButton(onClick = onBackClick) { - Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = stringResource(R.string.back)) + Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back)) } } ) diff --git a/app/src/main/java/f/cking/software/ui/filter/SelectFilterTypeScreen.kt b/app/src/main/java/f/cking/software/ui/filter/SelectFilterTypeScreen.kt index a1c98a0..8ae42da 100644 --- a/app/src/main/java/f/cking/software/ui/filter/SelectFilterTypeScreen.kt +++ b/app/src/main/java/f/cking/software/ui/filter/SelectFilterTypeScreen.kt @@ -5,8 +5,8 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.MaterialTheme import androidx.compose.material.Text +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -28,7 +28,7 @@ object SelectFilterTypeScreen { MaterialDialog( dialogState = dialogState, buttons = { - negativeButton(stringResource(R.string.cancel), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { dialogState.hide() } + negativeButton(stringResource(R.string.cancel), textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) { dialogState.hide() } } ) { LazyColumn { diff --git a/app/src/main/java/f/cking/software/ui/journal/JournalScreen.kt b/app/src/main/java/f/cking/software/ui/journal/JournalScreen.kt index 1c48bbe..a297acc 100644 --- a/app/src/main/java/f/cking/software/ui/journal/JournalScreen.kt +++ b/app/src/main/java/f/cking/software/ui/journal/JournalScreen.kt @@ -6,32 +6,31 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.Chip -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SuggestionChip +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.google.accompanist.flowlayout.FlowRow import f.cking.software.R -import f.cking.software.utils.graphic.BottomSpacer import f.cking.software.utils.graphic.ContentPlaceholder import f.cking.software.utils.graphic.Divider +import f.cking.software.utils.graphic.FABSpacer import org.koin.androidx.compose.koinViewModel object JournalScreen { @@ -40,31 +39,43 @@ object JournalScreen { fun Screen() { val viewModel: JournalViewModel = koinViewModel() val journal = viewModel.journal - val modifier = Modifier.background(MaterialTheme.colors.surface) - if (journal.isEmpty()) { - ContentPlaceholder(text = stringResource(R.string.journal_placeholder), modifier = modifier) - } else { - LazyColumn( - modifier = modifier - .fillMaxWidth() - .fillMaxHeight() - ) { - journal.map { - item { JournalEntry(uiModel = it, viewModel) } - item { Divider() } + val modifier = Modifier + .background(MaterialTheme.colorScheme.surface) + .fillMaxSize() + Box(modifier = modifier) { + if (journal.isEmpty()) { + ContentPlaceholder( + modifier = Modifier.fillMaxSize(), + text = stringResource(R.string.journal_placeholder) + ) + } else { + LazyColumn( + modifier = Modifier.fillMaxSize(), + ) { + journal.map { + item { JournalEntry(uiModel = it, viewModel) } + item { Divider() } + } + item { FABSpacer() } } - item { BottomSpacer() } + } + if (viewModel.loading) { + LinearProgressIndicator( + modifier = Modifier + .fillMaxWidth() + .height(4.dp), + color = MaterialTheme.colorScheme.onSurface + ) } } } - @OptIn(ExperimentalMaterialApi::class) @Composable fun JournalEntry(uiModel: JournalViewModel.JournalEntryUiModel, viewModel: JournalViewModel) { Box( modifier = Modifier .fillMaxWidth() - .background(colorResource(id = uiModel.color)) + .background(uiModel.color()) .clickable { viewModel.onEntryClick(uiModel.journalEntry) } ) { Column( @@ -79,9 +90,10 @@ object JournalScreen { fontWeight = FontWeight.Bold, maxLines = 4, overflow = TextOverflow.Ellipsis, + color = uiModel.colorForeground() ) Spacer(modifier = Modifier.width(4.dp)) - Text(text = uiModel.dateTime, fontWeight = FontWeight.Thin) + Text(text = uiModel.dateTime, fontWeight = FontWeight.Thin, color = uiModel.colorForeground()) } Spacer(modifier = Modifier.height(4.dp)) var isExpanded by remember { mutableStateOf(false) } @@ -91,10 +103,11 @@ object JournalScreen { modifier = Modifier.clickable { isExpanded = !isExpanded }, - text = uiModel.subtitle, + text = if (isExpanded) uiModel.subtitle else uiModel.subtitleCollapsed.orEmpty(), fontWeight = FontWeight.Normal, maxLines = if (isExpanded) Int.MAX_VALUE else 5, overflow = TextOverflow.Ellipsis, + color = uiModel.colorForeground() ) } @@ -103,11 +116,10 @@ object JournalScreen { mainAxisSpacing = 8.dp, ) { items.forEach { item -> - Chip( + SuggestionChip( onClick = { viewModel.onJournalListItemClick(item.payload) }, - ) { - Text(text = item.displayName) - } + label = { Text(text = item.displayName) } + ) } } } diff --git a/app/src/main/java/f/cking/software/ui/journal/JournalViewModel.kt b/app/src/main/java/f/cking/software/ui/journal/JournalViewModel.kt index 7f1db2f..efa829d 100644 --- a/app/src/main/java/f/cking/software/ui/journal/JournalViewModel.kt +++ b/app/src/main/java/f/cking/software/ui/journal/JournalViewModel.kt @@ -2,9 +2,12 @@ package f.cking.software.ui.journal import android.app.Application import android.widget.Toast +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.Color import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import f.cking.software.R @@ -15,7 +18,9 @@ import f.cking.software.dateTimeStringFormat import f.cking.software.domain.model.JournalEntry import f.cking.software.ui.ScreenNavigationCommands import f.cking.software.utils.navigation.Router +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch +import kotlin.math.min class JournalViewModel( private val journalRepository: JournalRepository, @@ -26,6 +31,7 @@ class JournalViewModel( ) : ViewModel() { var journal: List by mutableStateOf(emptyList()) + var loading by mutableStateOf(true) init { observeJournal() @@ -46,8 +52,11 @@ class JournalViewModel( private fun observeJournal() { viewModelScope.launch { journalRepository.observe() + .onStart { loading = true } .collect { update -> + loading = true journal = update.sortedBy { it.timestamp }.reversed().map { map(it) } + loading = false } } } @@ -68,16 +77,14 @@ class JournalViewModel( } else { report.title } - val description = if (report.stackTrace.length > MAX_ERROR_DESCRIPTION_LENGTH) { - report.stackTrace.substring(0 until MAX_ERROR_DESCRIPTION_LENGTH) - } else { - report.stackTrace - } + val description = report.stackTrace return JournalEntryUiModel( dateTime = journalEntry.timestamp.formattedDate(), - color = R.color.error_background, + color = { MaterialTheme.colorScheme.error }, + colorForeground = { MaterialTheme.colorScheme.onError }, title = title, subtitle = description, + subtitleCollapsed = description.substring(0 until min(MAX_ERROR_DESCRIPTION_COLLAPSED_LENGTH, description.length)), journalEntry = journalEntry, items = null, ) @@ -89,9 +96,11 @@ class JournalViewModel( ): JournalEntryUiModel { return JournalEntryUiModel( dateTime = journalEntry.timestamp.formattedDate(), - color = R.color.profile_report_background, + color = { MaterialTheme.colorScheme.surface }, + colorForeground = { MaterialTheme.colorScheme.onSurface }, title = context.getString(R.string.journal_profile_detected, getProfileName(report.profileId)), subtitle = null, + subtitleCollapsed = null, journalEntry = journalEntry, items = mapListItems(report.deviceAddresses), ) @@ -116,9 +125,11 @@ class JournalViewModel( data class JournalEntryUiModel( val dateTime: String, - val color: Int, + val color: @Composable () -> Color, + val colorForeground: @Composable () -> Color, val title: String, val subtitle: String?, + val subtitleCollapsed: String?, val items: List?, val journalEntry: JournalEntry, ) { @@ -130,6 +141,6 @@ class JournalViewModel( companion object { private const val MAX_ERROR_TITLE_LENGTH = 256 - private const val MAX_ERROR_DESCRIPTION_LENGTH = 10000 + private const val MAX_ERROR_DESCRIPTION_COLLAPSED_LENGTH = 500 } } \ No newline at end of file diff --git a/app/src/main/java/f/cking/software/ui/main/MainScreen.kt b/app/src/main/java/f/cking/software/ui/main/MainScreen.kt index 28484bf..891975f 100644 --- a/app/src/main/java/f/cking/software/ui/main/MainScreen.kt +++ b/app/src/main/java/f/cking/software/ui/main/MainScreen.kt @@ -8,9 +8,19 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExtendedFloatingActionButton +import androidx.compose.material3.FabPosition +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -30,11 +40,11 @@ import com.vanpra.composematerialdialogs.MaterialDialogState import com.vanpra.composematerialdialogs.rememberMaterialDialogState import f.cking.software.R import f.cking.software.ui.GlobalUiState -import f.cking.software.utils.graphic.GlassNavigationbar +import f.cking.software.utils.graphic.GlassBottomNavBar import f.cking.software.utils.graphic.SystemNavbarSpacer -import f.cking.software.utils.graphic.pxToDp import org.koin.androidx.compose.koinViewModel +@OptIn(ExperimentalMaterial3Api::class) object MainScreen { @SuppressLint("NewApi") @@ -47,21 +57,19 @@ object MainScreen { TopBar(viewModel) }, content = { innerPaddings -> - GlassNavigationbar( - modifier = Modifier - .padding(innerPaddings) - .fillMaxWidth() - .fillMaxHeight(), - overlayColor = MaterialTheme.colors.primaryVariant.copy(alpha = 0.3f), - navBarContent = { - BottomNavigationBar(Modifier, viewModel) - }, + GlassBottomNavBar( + modifier = Modifier.fillMaxSize(), content = { - viewModel.tabs.firstOrNull { it.selected }?.screen?.invoke() + Box(Modifier.padding(top = innerPaddings.calculateTopPadding())) { + viewModel.tabs.firstOrNull { it.selected }?.screen?.invoke() + } }, ) }, floatingActionButtonPosition = FabPosition.Center, + bottomBar = { + BottomNavigationBar(Modifier, viewModel) + }, floatingActionButton = { ScanFab(viewModel) }, @@ -75,8 +83,8 @@ object MainScreen { MaterialDialog( dialogState = viewModel.showLocationDisabledDialog, buttons = { - negativeButton(stringResource(R.string.cancel), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) - positiveButton(stringResource(R.string.turn_on), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { + negativeButton(stringResource(R.string.cancel), textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) + positiveButton(stringResource(R.string.turn_on), textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) { viewModel.onTurnOnLocationClick() } }, @@ -94,8 +102,8 @@ object MainScreen { MaterialDialog( dialogState = viewModel.showBluetoothDisabledDialog, buttons = { - negativeButton(stringResource(R.string.cancel), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) - positiveButton(stringResource(R.string.turn_on), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { + negativeButton(stringResource(R.string.cancel), textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) + positiveButton(stringResource(R.string.turn_on), textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) { viewModel.onTurnOnBluetoothClick() } }, @@ -148,11 +156,11 @@ object MainScreen { Image( painter = painterResource(id = icon), contentDescription = targetTab.text, - colorFilter = ColorFilter.tint(MaterialTheme.colors.onPrimary.copy(alpha = 0.7f)), + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant), modifier = Modifier.size(32.dp), ) Spacer(modifier = Modifier.height(2.dp)) - Text(text = targetTab.text, fontSize = 12.sp, fontWeight = font, color = MaterialTheme.colors.onPrimary.copy(alpha = 0.7f)) + Text(text = targetTab.text, fontSize = 12.sp, fontWeight = font, color = MaterialTheme.colorScheme.onSurfaceVariant) } } @@ -181,10 +189,8 @@ object MainScreen { ) ExtendedFloatingActionButton( - modifier = Modifier - .padding(bottom = pxToDp(px = GlobalUiState.navbarOffsetPx.value).dp) - .onGloballyPositioned { GlobalUiState.setBottomOffset(fabOffset = it.size.height.toFloat()) }, - text = { Text(text = text, fontWeight = FontWeight.Bold, color = MaterialTheme.colors.onSecondary) }, + modifier = Modifier.onGloballyPositioned { GlobalUiState.setBottomOffset(fabOffset = it.size.height.toFloat()) }, + text = { Text(text = text, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onPrimaryContainer) }, onClick = { if (viewModel.needToShowPermissionsIntro()) { permissionsIntro.show() @@ -196,7 +202,7 @@ object MainScreen { Image( painter = painterResource(id = icon), contentDescription = text, - colorFilter = ColorFilter.tint(color = MaterialTheme.colors.onSecondary) + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onPrimaryContainer) ) } ) @@ -211,11 +217,11 @@ object MainScreen { MaterialDialog( dialogState = state, buttons = { - positiveButton(stringResource(id = R.string.confirm), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { + positiveButton(stringResource(id = R.string.confirm), textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) { state.hide() onPassed.invoke() } - negativeButton(stringResource(id = R.string.decline), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { + negativeButton(stringResource(id = R.string.decline), textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) { state.hide() onDeclined.invoke() } @@ -295,13 +301,12 @@ object MainScreen { @Composable private fun TopBar(viewModel: MainViewModel) { TopAppBar( - title = { - Text(text = stringResource(R.string.app_name), color = MaterialTheme.colors.onPrimary) - }, + colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.surfaceContainerHighest), + title = { Text(text = stringResource(R.string.app_name), color = MaterialTheme.colorScheme.onSurface) }, actions = { if (viewModel.scanStarted && viewModel.bgServiceIsActive) { CircularProgressIndicator( - color = MaterialTheme.colors.onPrimary, + color = MaterialTheme.colorScheme.onSurface, modifier = Modifier .size(24.dp) ) @@ -314,7 +319,7 @@ object MainScreen { .size(24.dp), imageVector = Icons.Filled.Refresh, contentDescription = stringResource(R.string.refresh), - colorFilter = ColorFilter.tint(MaterialTheme.colors.onPrimary) + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface) ) } } diff --git a/app/src/main/java/f/cking/software/ui/profiledetails/ProfileDetailsScreen.kt b/app/src/main/java/f/cking/software/ui/profiledetails/ProfileDetailsScreen.kt index 27d0fbf..f61dc17 100644 --- a/app/src/main/java/f/cking/software/ui/profiledetails/ProfileDetailsScreen.kt +++ b/app/src/main/java/f/cking/software/ui/profiledetails/ProfileDetailsScreen.kt @@ -6,29 +6,33 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.Button -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold -import androidx.compose.material.Switch -import androidx.compose.material.Text -import androidx.compose.material.TextField -import androidx.compose.material.TopAppBar +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Done +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -46,24 +50,29 @@ import f.cking.software.utils.graphic.SystemNavbarSpacer import org.koin.androidx.compose.koinViewModel import org.koin.core.parameter.parametersOf +@OptIn(ExperimentalMaterial3Api::class) object ProfileDetailsScreen { @Composable fun Screen(profileId: Int?, template: FilterUiState?, key: String) { val viewModel: ProfileDetailsViewModel = koinViewModel(key = key) { parametersOf(profileId, template) } + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() + Scaffold( modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), + .nestedScroll(scrollBehavior.nestedScrollConnection) + .fillMaxSize(), topBar = { - AppBar(viewModel) + AppBar(viewModel, scrollBehavior) }, content = { - GlassSystemNavbar { - Box(modifier = Modifier - .background(MaterialTheme.colors.surface) - .fillMaxSize() - .padding(it)) { + GlassSystemNavbar(Modifier.padding(top = it.calculateTopPadding())) { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .background(MaterialTheme.colorScheme.surface) + ) { Content(viewModel) } } @@ -71,8 +80,9 @@ object ProfileDetailsScreen { ) } + @OptIn(ExperimentalMaterial3Api::class) @Composable - private fun AppBar(viewModel: ProfileDetailsViewModel) { + private fun AppBar(viewModel: ProfileDetailsViewModel, scrollBehavior: TopAppBarScrollBehavior) { val discardChangesDialog = rememberMaterialDialogState() MaterialDialog( @@ -80,9 +90,12 @@ object ProfileDetailsScreen { buttons = { negativeButton( stringResource(R.string.stay), - textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant) + textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer) ) { discardChangesDialog.hide() } - positiveButton(stringResource(R.string.discard_changes), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { + positiveButton( + stringResource(R.string.discard_changes), + textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer) + ) { discardChangesDialog.hide() viewModel.back() } @@ -110,6 +123,10 @@ object ProfileDetailsScreen { } TopAppBar( + scrollBehavior = scrollBehavior, + colors = TopAppBarDefaults.topAppBarColors( + scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainerHighest, + ), title = { Text(text = stringResource(R.string.radar_profile_title)) }, @@ -119,13 +136,17 @@ object ProfileDetailsScreen { Icon( imageVector = Icons.Filled.Delete, contentDescription = stringResource(R.string.delete), - tint = MaterialTheme.colors.onPrimary + tint = MaterialTheme.colorScheme.onSurface ) } Spacer(modifier = Modifier.width(8.dp)) } IconButton(onClick = { viewModel.onSaveClick() }) { - Icon(imageVector = Icons.Filled.Done, contentDescription = stringResource(R.string.save), tint = MaterialTheme.colors.onPrimary) + Icon( + imageVector = Icons.Filled.Done, + contentDescription = stringResource(R.string.save), + tint = MaterialTheme.colorScheme.onSurface + ) } }, navigationIcon = { @@ -137,9 +158,9 @@ object ProfileDetailsScreen { } }) { Icon( - imageVector = Icons.Filled.ArrowBack, + imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colors.onPrimary + tint = MaterialTheme.colorScheme.onSurface ) } } @@ -148,22 +169,18 @@ object ProfileDetailsScreen { @Composable private fun Content(viewModel: ProfileDetailsViewModel) { - LazyColumn(Modifier.fillMaxWidth()) { - item { Header(viewModel) } - - val filter = viewModel.filter - if (filter != null) { - item { - Box(modifier = Modifier.padding(8.dp)) { - FilterScreen.Filter(filterState = filter, router = viewModel.router, onDeleteClick = { viewModel.filter = null }) - } - } - } else { - item { CreateFilter(viewModel = viewModel) } - } + Header(viewModel) - item { SystemNavbarSpacer() } + val filter = viewModel.filter + if (filter != null) { + Box(modifier = Modifier.padding(16.dp)) { + FilterScreen.Filter(filterState = filter, router = viewModel.router, onDeleteClick = { viewModel.filter = null }) + } + } else { + CreateFilter(viewModel = viewModel) } + + SystemNavbarSpacer() } @Composable @@ -228,7 +245,7 @@ object ProfileDetailsScreen { selectFilterDialog.show() }, content = { - Text(text = stringResource(R.string.add_filter)) + Text(text = stringResource(R.string.add_filter), color = MaterialTheme.colorScheme.onPrimary) } ) } diff --git a/app/src/main/java/f/cking/software/ui/profileslist/ProfilesListScreen.kt b/app/src/main/java/f/cking/software/ui/profileslist/ProfilesListScreen.kt index 3861cbe..7228588 100644 --- a/app/src/main/java/f/cking/software/ui/profileslist/ProfilesListScreen.kt +++ b/app/src/main/java/f/cking/software/ui/profileslist/ProfilesListScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -14,15 +15,14 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.material.Chip -import androidx.compose.material.ChipDefaults -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SuggestionChip +import androidx.compose.material3.SuggestionChipDefaults +import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -35,8 +35,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import f.cking.software.R import f.cking.software.domain.model.RadarProfile -import f.cking.software.utils.graphic.BottomSpacer import f.cking.software.utils.graphic.ContentPlaceholder +import f.cking.software.utils.graphic.FABSpacer import org.koin.androidx.compose.koinViewModel object ProfilesListScreen { @@ -46,9 +46,8 @@ object ProfilesListScreen { val viewModel: ProfilesListViewModel = koinViewModel() Column( Modifier - .background(MaterialTheme.colors.surface) - .fillMaxWidth() - .fillMaxHeight() + .background(MaterialTheme.colorScheme.surface) + .fillMaxSize() ) { Header(viewModel = viewModel) val profiles = viewModel.profiles @@ -60,40 +59,41 @@ object ProfilesListScreen { ) { profiles.map { item { ListItem(profile = it, viewModel = viewModel) } } item { - BottomSpacer() + FABSpacer() } } } else { - ContentPlaceholder(stringResource(R.string.radar_profile_placeholder)) + ContentPlaceholder(stringResource(R.string.radar_profile_placeholder), Modifier.fillMaxSize()) } } } - @OptIn(ExperimentalMaterialApi::class) @Composable private fun Header(viewModel: ProfilesListViewModel) { Surface( modifier = Modifier .fillMaxWidth(), - elevation = 4.dp, + shadowElevation = 4.dp, ) { LazyRow( modifier = Modifier - .background(colorResource(id = R.color.primary_surface)) + .background(MaterialTheme.colorScheme.surfaceContainerHighest) .padding(vertical = 8.dp), ) { + item { Spacer(modifier = Modifier.width(16.dp)) } item { - Spacer(modifier = Modifier.width(8.dp)) - Chip( - colors = ChipDefaults.chipColors( - backgroundColor = MaterialTheme.colors.primaryVariant, - contentColor = MaterialTheme.colors.onPrimary, - ), + SuggestionChip( onClick = { viewModel.createNewClick() }, - leadingIcon = { + colors = SuggestionChipDefaults.suggestionChipColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + iconContentColor = MaterialTheme.colorScheme.onPrimaryContainer, + labelColor = MaterialTheme.colorScheme.onPrimaryContainer, + ), + icon = { Icon(imageVector = Icons.Default.Add, contentDescription = null) - } - ) { Text(text = stringResource(R.string.create_new)) } + }, + label = { Text(text = stringResource(R.string.create_new)) } + ) Spacer(modifier = Modifier.width(8.dp)) } @@ -103,16 +103,16 @@ object ProfilesListScreen { Spacer(modifier = Modifier.width(8.dp)) } } + item { Spacer(modifier = Modifier.width(8.dp)) } } } } - @OptIn(ExperimentalMaterialApi::class) @Composable private fun DefaultFilterChip(filter: ProfilesListViewModel.FilterTemplate, viewModel: ProfilesListViewModel) { - Chip(onClick = { viewModel.selectFilterTemplate(filter) }) { + SuggestionChip(onClick = { viewModel.selectFilterTemplate(filter) }, label = { Text(text = stringResource(filter.displayNameRes)) - } + }) } @Composable @@ -131,7 +131,7 @@ object ProfilesListScreen { Spacer(modifier = Modifier.height(4.dp)) val activeText = if (profile.isActive) stringResource(R.string.profile_is_active) else stringResource(R.string.profile_is_not_active) - val color = if (profile.isActive) colorResource(id = R.color.green_600) else MaterialTheme.colors.onSurface + val color = if (profile.isActive) colorResource(id = R.color.green_600) else MaterialTheme.colorScheme.onBackground Row(verticalAlignment = Alignment.CenterVertically) { Icon( modifier = Modifier.size(12.dp), diff --git a/app/src/main/java/f/cking/software/ui/selectdevice/SelectDeviceScreen.kt b/app/src/main/java/f/cking/software/ui/selectdevice/SelectDeviceScreen.kt index c420fc5..60342e0 100644 --- a/app/src/main/java/f/cking/software/ui/selectdevice/SelectDeviceScreen.kt +++ b/app/src/main/java/f/cking/software/ui/selectdevice/SelectDeviceScreen.kt @@ -2,22 +2,29 @@ package f.cking.software.ui.selectdevice import androidx.compose.foundation.background import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.Divider -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold -import androidx.compose.material.Text -import androidx.compose.material.TextField -import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import f.cking.software.R import f.cking.software.domain.model.DeviceData import f.cking.software.utils.graphic.DeviceListItem @@ -25,26 +32,18 @@ import f.cking.software.utils.graphic.GlassSystemNavbar import f.cking.software.utils.graphic.SystemNavbarSpacer import org.koin.androidx.compose.koinViewModel +@OptIn(ExperimentalMaterial3Api::class) object SelectDeviceScreen { - private val generalComparator = Comparator { second, first -> - when { - first.lastDetectTimeMs != second.lastDetectTimeMs -> first.lastDetectTimeMs.compareTo(second.lastDetectTimeMs) - first.name != second.name -> first.name?.compareTo(second.name ?: return@Comparator 1) ?: -1 - first.manufacturerInfo?.name != second.manufacturerInfo?.name -> - first.manufacturerInfo?.name?.compareTo(second.manufacturerInfo?.name ?: return@Comparator 1) ?: -1 - - else -> first.address.compareTo(second.address) - } - } - @Composable fun Screen( onSelected: (deviceData: DeviceData) -> Unit ) { val viewModel: SelectDeviceViewModel = koinViewModel() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() Scaffold( - topBar = { AppBar(viewModel) }, + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { AppBar(viewModel, scrollBehavior) }, content = { paddings -> GlassSystemNavbar { Content(Modifier.padding(paddings), viewModel, onSelected) @@ -57,26 +56,27 @@ object SelectDeviceScreen { private fun Content( modifier: Modifier, viewModel: SelectDeviceViewModel, - onSelected: (deviceData: DeviceData) -> Unit + onSelected: (deviceData: DeviceData) -> Unit, ) { LazyColumn( - modifier = Modifier - .background(MaterialTheme.colors.surface) + modifier = modifier + .background(MaterialTheme.colorScheme.surface) .fillMaxSize() ) { - val list = viewModel.devices.asSequence() - .filter { device -> - viewModel.searchStr.takeIf { it.isNotBlank() }?.let { searchStr -> - (device.name?.contains(searchStr, true) ?: false) - || (device.customName?.contains(searchStr, true) ?: false) - || (device.manufacturerInfo?.name?.contains(searchStr, true) ?: false) - || device.address.contains(searchStr, true) - } ?: true + if (viewModel.loading) { + item { + LinearProgressIndicator( + modifier = Modifier + .fillMaxWidth() + .height(2.dp), + color = MaterialTheme.colorScheme.onSurface + ) } - .sortedWith(generalComparator) - .toList() + } + val list = viewModel.devices list.forEachIndexed { index, device -> + item { DeviceListItem(device = device) { onSelected.invoke(device) @@ -93,18 +93,23 @@ object SelectDeviceScreen { } @Composable - private fun AppBar(viewModel: SelectDeviceViewModel) { + private fun AppBar(viewModel: SelectDeviceViewModel, scrollBehavior: TopAppBarScrollBehavior) { TopAppBar( + scrollBehavior = scrollBehavior, + colors = TopAppBarDefaults.topAppBarColors( + scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainerHighest, + ), title = { TextField( value = viewModel.searchStr, - onValueChange = { viewModel.searchStr = it }, - placeholder = { Text(text = stringResource(R.string.search), color = Color.White) } + maxLines = 1, + onValueChange = { viewModel.searchRequest(it) }, + placeholder = { Text(text = stringResource(R.string.search)) } ) }, navigationIcon = { IconButton(onClick = { viewModel.back() }) { - Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = stringResource(R.string.back)) + Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back)) } } ) diff --git a/app/src/main/java/f/cking/software/ui/selectdevice/SelectDeviceViewModel.kt b/app/src/main/java/f/cking/software/ui/selectdevice/SelectDeviceViewModel.kt index 2c84f61..cc40057 100644 --- a/app/src/main/java/f/cking/software/ui/selectdevice/SelectDeviceViewModel.kt +++ b/app/src/main/java/f/cking/software/ui/selectdevice/SelectDeviceViewModel.kt @@ -17,14 +17,47 @@ class SelectDeviceViewModel( ) : ViewModel() { var devices: List by mutableStateOf(emptyList()) + var loading by mutableStateOf(false) var searchStr: String by mutableStateOf("") + private val generalComparator = Comparator { second, first -> + when { + first.lastDetectTimeMs != second.lastDetectTimeMs -> first.lastDetectTimeMs.compareTo(second.lastDetectTimeMs) + first.name != second.name -> first.name?.compareTo(second.name ?: return@Comparator 1) ?: -1 + first.manufacturerInfo?.name != second.manufacturerInfo?.name -> + first.manufacturerInfo?.name?.compareTo(second.manufacturerInfo?.name ?: return@Comparator 1) ?: -1 + + else -> first.address.compareTo(second.address) + } + } + init { + refreshDevices() + } + + private fun refreshDevices() { viewModelScope.launch { - devices = devicesRepository.getDevices() + loading = true + devices = devicesRepository.getDevices().asSequence() + .filter { device -> + searchStr.takeIf { it.isNotBlank() }?.let { searchStr -> + (device.name?.contains(searchStr, true) ?: false) + || (device.customName?.contains(searchStr, true) ?: false) + || (device.manufacturerInfo?.name?.contains(searchStr, true) ?: false) + || device.address.contains(searchStr, true) + } ?: true + } + .sortedWith(generalComparator) + .toList() + loading = false } } + fun searchRequest(str: String) { + searchStr = str + refreshDevices() + } + fun back() { router.navigate(BackCommand) } diff --git a/app/src/main/java/f/cking/software/ui/selectlocation/SelectLocationScreen.kt b/app/src/main/java/f/cking/software/ui/selectlocation/SelectLocationScreen.kt index a366f7e..b288ec6 100644 --- a/app/src/main/java/f/cking/software/ui/selectlocation/SelectLocationScreen.kt +++ b/app/src/main/java/f/cking/software/ui/selectlocation/SelectLocationScreen.kt @@ -5,26 +5,24 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize 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.width import androidx.compose.foundation.shape.AbsoluteCutCornerShape -import androidx.compose.material.Button -import androidx.compose.material.ButtonDefaults -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold -import androidx.compose.material.Slider -import androidx.compose.material.SliderDefaults -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Slider +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -46,6 +44,7 @@ import f.cking.software.data.helpers.LocationProvider import f.cking.software.domain.model.LocationModel import f.cking.software.ui.devicedetails.MapConfig import f.cking.software.utils.graphic.MapView +import f.cking.software.utils.graphic.RoundedBox import f.cking.software.utils.graphic.SystemNavbarSpacer import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.take @@ -54,6 +53,7 @@ import org.koin.androidx.compose.getKoin import org.osmdroid.util.GeoPoint import org.osmdroid.views.MapView +@OptIn(ExperimentalMaterial3Api::class) object SelectLocationScreen { @Composable @@ -65,15 +65,14 @@ object SelectLocationScreen { ) { Scaffold( modifier = Modifier - .background(MaterialTheme.colors.surface) - .fillMaxWidth() - .fillMaxHeight(), + .background(MaterialTheme.colorScheme.surface) + .fillMaxSize(), topBar = { AppBar(onCloseClick) }, content = { paddings -> Content( modifier = Modifier - .background(MaterialTheme.colors.surface) - .padding(paddings), + .background(MaterialTheme.colorScheme.surface) + .padding(top = paddings.calculateTopPadding()), onSelected = onSelected, initialLocationModel = initialLocationModel, initialRadius = initialRadius @@ -107,49 +106,49 @@ object SelectLocationScreen { Column( modifier = modifier - .fillMaxHeight() - .fillMaxWidth() + .padding(horizontal = 16.dp) + .fillMaxSize() ) { - Box( + RoundedBox( modifier = Modifier .fillMaxWidth() .weight(1f), - contentAlignment = Alignment.Center, + internalPaddings = 0.dp, ) { - Map( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(), - initialLocationModel = initialLocationModel, - onMapReady = { map.value = it } - ) - Box( - modifier = Modifier - .size(width = 20.dp, height = 10.dp) - .blur(2.dp), - contentAlignment = Alignment.Center, - ) { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Map( + modifier = Modifier.fillMaxSize(), + initialLocationModel = initialLocationModel, + onMapReady = { map.value = it } + ) Box( modifier = Modifier - .size(width = 10.dp, height = 5.dp) - .background(color = Color.DarkGray, shape = AbsoluteCutCornerShape(10.dp)) - ) - } - Column( - modifier = Modifier - .height(120.dp) - .width(60.dp) - ) { - val painter = painterResource(R.drawable.ic_location) - Image( + .size(width = 20.dp, height = 10.dp) + .blur(2.dp), + contentAlignment = Alignment.Center, + ) { + Box( + modifier = Modifier + .size(width = 10.dp, height = 5.dp) + .background(color = Color.DarkGray, shape = AbsoluteCutCornerShape(10.dp)) + ) + } + Column( modifier = Modifier - .fillMaxWidth() - .height(60.dp), - contentScale = ContentScale.FillWidth, - painter = painter, - contentDescription = null, - colorFilter = ColorFilter.tint(MaterialTheme.colors.secondary) - ) + .height(120.dp) + .width(60.dp) + ) { + val painter = painterResource(R.drawable.ic_location) + Image( + modifier = Modifier + .fillMaxWidth() + .height(60.dp), + contentScale = ContentScale.FillWidth, + painter = painter, + contentDescription = null, + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary) + ) + } } } BottomPanel(map.value, initialRadius, onSelected) @@ -164,46 +163,41 @@ object SelectLocationScreen { ) { val radiusMeters = remember { mutableStateOf(initialRadius ?: TheAppConfig.DEFAULT_LOCATION_FILTER_RADIUS) } - Surface(elevation = 12.dp) { - Column( + Column( + modifier = Modifier + .fillMaxWidth(), + ) { + Spacer(modifier = Modifier.height(16.dp)) + Text( + modifier = Modifier.padding(horizontal = 16.dp), + text = stringResource(R.string.select_location_radius, radiusMeters.value), + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp + ) + Slider( modifier = Modifier - .background(MaterialTheme.colors.primary) .fillMaxWidth(), - ) { - Spacer(modifier = Modifier.height(16.dp)) - Text(modifier = Modifier.padding(horizontal = 16.dp), text = stringResource(R.string.select_location_radius, radiusMeters.value), fontWeight = FontWeight.SemiBold, fontSize = 16.sp) - Slider( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - value = radiusMeters.value, - onValueChange = { value -> radiusMeters.value = value }, - valueRange = 5f..1000f, - colors = SliderDefaults.colors( - thumbColor = MaterialTheme.colors.secondary, - activeTrackColor = MaterialTheme.colors.secondary, + value = radiusMeters.value, + onValueChange = { value -> radiusMeters.value = value }, + valueRange = 5f..1000f, + ) + Spacer(modifier = Modifier.height(16.dp)) + Button( + modifier = Modifier + .fillMaxWidth(), + enabled = map != null, + onClick = { + val cameraCenter = map!!.mapCenter + onSelected.invoke( + LocationModel(cameraCenter.latitude, cameraCenter.longitude, System.currentTimeMillis()), + radiusMeters.value ) - ) - Spacer(modifier = Modifier.height(16.dp)) - Button( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - colors = ButtonDefaults.buttonColors(backgroundColor = MaterialTheme.colors.primaryVariant), - enabled = map != null, - onClick = { - val cameraCenter = map!!.mapCenter - onSelected.invoke( - LocationModel(cameraCenter.latitude, cameraCenter.longitude, System.currentTimeMillis()), - radiusMeters.value - ) - } - ) { - Text(text = stringResource(R.string.confirm), color = MaterialTheme.colors.onPrimary) } - Spacer(modifier = Modifier.height(16.dp)) - SystemNavbarSpacer() + ) { + Text(text = stringResource(R.string.confirm), color = MaterialTheme.colorScheme.onPrimary) } + Spacer(modifier = Modifier.height(16.dp)) + SystemNavbarSpacer() } } diff --git a/app/src/main/java/f/cking/software/ui/selectmanufacturer/SelectManufacturerScreen.kt b/app/src/main/java/f/cking/software/ui/selectmanufacturer/SelectManufacturerScreen.kt index f64a1f6..31b9bc4 100644 --- a/app/src/main/java/f/cking/software/ui/selectmanufacturer/SelectManufacturerScreen.kt +++ b/app/src/main/java/f/cking/software/ui/selectmanufacturer/SelectManufacturerScreen.kt @@ -3,19 +3,24 @@ package f.cking.software.ui.selectmanufacturer import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Scaffold -import androidx.compose.material.Text -import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -26,6 +31,7 @@ import f.cking.software.utils.graphic.GlassSystemNavbar import f.cking.software.utils.graphic.SystemNavbarSpacer import org.koin.androidx.compose.koinViewModel +@OptIn(ExperimentalMaterial3Api::class) object SelectManufacturerScreen { @Composable @@ -33,16 +39,17 @@ object SelectManufacturerScreen { onSelected: (type: ManufacturerInfo) -> Unit ) { val viewModel: SelectManufacturerViewModel = koinViewModel() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() Scaffold( modifier = Modifier - .background(MaterialTheme.colors.surface) - .fillMaxWidth() - .fillMaxWidth(), - topBar = { AppBar(viewModel) }, + .nestedScroll(scrollBehavior.nestedScrollConnection) + .background(MaterialTheme.colorScheme.surface) + .fillMaxSize(), + topBar = { AppBar(viewModel, scrollBehavior) }, content = { paddings -> GlassSystemNavbar { LazyColumn(modifier = Modifier - .background(MaterialTheme.colors.surface) + .background(MaterialTheme.colorScheme.surface) .padding(paddings)) { viewModel.manufacturers.forEach { type -> item { @@ -60,14 +67,18 @@ object SelectManufacturerScreen { } @Composable - private fun AppBar(viewModel: SelectManufacturerViewModel) { + private fun AppBar(viewModel: SelectManufacturerViewModel, scrollBehavior: TopAppBarScrollBehavior) { TopAppBar( + scrollBehavior = scrollBehavior, + colors = TopAppBarDefaults.topAppBarColors( + scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainerHighest, + ), title = { Text(text = stringResource(R.string.select_manufacturer)) }, navigationIcon = { IconButton(onClick = { viewModel.back() }) { - Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = stringResource(R.string.back)) + Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back)) } } ) diff --git a/app/src/main/java/f/cking/software/ui/settings/SettingsScreen.kt b/app/src/main/java/f/cking/software/ui/settings/SettingsScreen.kt index 1395d84..6455f84 100644 --- a/app/src/main/java/f/cking/software/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/f/cking/software/ui/settings/SettingsScreen.kt @@ -7,16 +7,17 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.Button -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Switch -import androidx.compose.material.Text +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Switch +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -34,7 +35,8 @@ import com.vanpra.composematerialdialogs.rememberMaterialDialogState import f.cking.software.BuildConfig import f.cking.software.R import f.cking.software.dateTimeStringFormat -import f.cking.software.utils.graphic.BottomSpacer +import f.cking.software.utils.graphic.BottomNavigationSpacer +import f.cking.software.utils.graphic.FABSpacer import f.cking.software.utils.graphic.RoundedBox import org.koin.androidx.compose.koinViewModel @@ -43,35 +45,28 @@ object SettingsScreen { @Composable fun Screen() { val viewModel: SettingsViewModel = koinViewModel() - LazyColumn( + Column( modifier = Modifier - .background(MaterialTheme.colors.surface) - .fillMaxWidth() - .fillMaxHeight(), + .background(MaterialTheme.colorScheme.surface) + .fillMaxSize() + .verticalScroll(rememberScrollState()) ) { - item { - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Spacer(modifier = Modifier.height(16.dp)) - ProjectGithub(viewModel = viewModel) - Spacer(modifier = Modifier.height(8.dp)) - ReportIssue(viewModel = viewModel) - Spacer(modifier = Modifier.height(8.dp)) - ClearDatabaseBlock(viewModel = viewModel) - Spacer(modifier = Modifier.height(8.dp)) - BackupDatabaseBlock(viewModel = viewModel) - Spacer(modifier = Modifier.height(8.dp)) - RunOnStartup(viewModel = viewModel) - Spacer(modifier = Modifier.height(8.dp)) - LocationBlock(viewModel = viewModel) - Spacer(modifier = Modifier.height(8.dp)) - AppInfo() - SecretCatPhoto() - BottomSpacer() - } - } + Spacer(modifier = Modifier.height(16.dp)) + ProjectGithub(viewModel = viewModel) + Spacer(modifier = Modifier.height(8.dp)) + ReportIssue(viewModel = viewModel) + Spacer(modifier = Modifier.height(8.dp)) + ClearDatabaseBlock(viewModel = viewModel) + Spacer(modifier = Modifier.height(8.dp)) + BackupDatabaseBlock(viewModel = viewModel) + Spacer(modifier = Modifier.height(8.dp)) + RunOnStartup(viewModel = viewModel) + Spacer(modifier = Modifier.height(8.dp)) + LocationBlock(viewModel = viewModel) + Spacer(modifier = Modifier.height(8.dp)) + AppInfo() + SecretCatPhoto() + FABSpacer() } } @@ -129,7 +124,7 @@ object SettingsScreen { onClick = { viewModel.onRemoveGarbageClick() }, enabled = !viewModel.garbageRemovingInProgress ) { - Text(text = stringResource(R.string.clear_garbage)) + Text(text = stringResource(R.string.clear_garbage), color = MaterialTheme.colorScheme.onPrimary) } } @@ -142,9 +137,9 @@ object SettingsScreen { buttons = { negativeButton( text = stringResource(R.string.cancel), - textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant) + textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer) ) { dialogState.hide() } - positiveButton(text = stringResource(R.string.confirm), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { + positiveButton(text = stringResource(R.string.confirm), textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) { dialogState.hide() viewModel.onRestoreDBClick() } @@ -161,7 +156,7 @@ object SettingsScreen { onClick = { dialogState.show() }, enabled = !viewModel.backupDbInProgress ) { - Text(text = stringResource(R.string.settings_restore_database)) + Text(text = stringResource(R.string.settings_restore_database), color = MaterialTheme.colorScheme.onPrimary) } } @@ -174,9 +169,9 @@ object SettingsScreen { buttons = { negativeButton( text = stringResource(R.string.cancel), - textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant) + textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer) ) { dialogState.hide() } - positiveButton(text = stringResource(R.string.confirm), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { + positiveButton(text = stringResource(R.string.confirm), textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) { dialogState.hide() viewModel.onBackupDBClick() } @@ -193,34 +188,36 @@ object SettingsScreen { onClick = { dialogState.show() }, enabled = !viewModel.backupDbInProgress ) { - Text(text = stringResource(R.string.settings_backup_database)) + Text(text = stringResource(R.string.settings_backup_database), color = MaterialTheme.colorScheme.onPrimary) } } @Composable private fun SecretCatPhoto() { - Column { - Spacer(modifier = Modifier.height(16.dp)) + Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { + BottomNavigationSpacer() repeat(50) { Image( modifier = Modifier .align(Alignment.CenterHorizontally) .alpha(0.3f) .width(30.dp), - colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface), + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface), painter = painterResource(id = R.drawable.cat_footprint), contentDescription = null ) Spacer(modifier = Modifier.height(4.dp)) } Spacer(modifier = Modifier.height(16.dp)) - Image( - painter = painterResource(id = R.drawable.appa), - contentDescription = stringResource(id = R.string.secret_cat), - contentScale = ContentScale.FillWidth - ) - Spacer(modifier = Modifier.height(4.dp)) - Text(text = stringResource(id = R.string.secret_cat), fontWeight = FontWeight.Light) + Column { + Image( + painter = painterResource(id = R.drawable.appa), + contentDescription = stringResource(id = R.string.secret_cat), + contentScale = ContentScale.FillWidth + ) + Spacer(modifier = Modifier.height(4.dp)) + Text(text = stringResource(id = R.string.secret_cat), fontWeight = FontWeight.Light) + } } } @@ -234,9 +231,9 @@ object SettingsScreen { buttons = { negativeButton( text = stringResource(R.string.cancel), - textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant) + textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer) ) { dialogState.hide() } - positiveButton(text = stringResource(R.string.confirm), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { + positiveButton(text = stringResource(R.string.confirm), textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) { dialogState.hide() viewModel.onClearLocationsClick() } @@ -252,7 +249,7 @@ object SettingsScreen { onClick = { dialogState.show() }, enabled = !viewModel.locationRemovingInProgress ) { - Text(text = stringResource(R.string.settings_clear_all_location_history)) + Text(text = stringResource(R.string.settings_clear_all_location_history), color = MaterialTheme.colorScheme.onPrimary) } } @@ -284,7 +281,7 @@ object SettingsScreen { Text(text = stringResource(R.string.report_issue_title), fontWeight = FontWeight.SemiBold) Spacer(modifier = Modifier.height(4.dp)) Button(modifier = Modifier.fillMaxWidth(), onClick = { viewModel.opReportIssueClick() }) { - Text(text = stringResource(R.string.report)) + Text(text = stringResource(R.string.report), color = MaterialTheme.colorScheme.onPrimary) } } } @@ -308,7 +305,7 @@ object SettingsScreen { Text(text = stringResource(R.string.project_github_title, stringResource(id = R.string.app_name)), fontWeight = FontWeight.SemiBold) Spacer(modifier = Modifier.height(4.dp)) Button(modifier = Modifier.fillMaxWidth(), onClick = { viewModel.onGithubClick() }) { - Text(text = stringResource(R.string.open_github)) + Text(text = stringResource(R.string.open_github), color = MaterialTheme.colorScheme.onPrimary) } } } diff --git a/app/src/main/java/f/cking/software/ui/tagdialog/TagDialog.kt b/app/src/main/java/f/cking/software/ui/tagdialog/TagDialog.kt index 87ffbf6..971b2dd 100644 --- a/app/src/main/java/f/cking/software/ui/tagdialog/TagDialog.kt +++ b/app/src/main/java/f/cking/software/ui/tagdialog/TagDialog.kt @@ -7,10 +7,10 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.Button -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.TextField +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf @@ -41,7 +41,7 @@ object TagDialog { MaterialDialog( dialogState = dialog, buttons = { - negativeButton("Cancel", textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { dialog.hide() } + negativeButton("Cancel", textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer)) { dialog.hide() } }, onCloseRequest = { dialog.hide() }, ) { diff --git a/app/src/main/java/f/cking/software/utils/graphic/ComposeFunctions.kt b/app/src/main/java/f/cking/software/utils/graphic/ComposeFunctions.kt index e8c55e8..19bd1f0 100644 --- a/app/src/main/java/f/cking/software/utils/graphic/ComposeFunctions.kt +++ b/app/src/main/java/f/cking/software/utils/graphic/ComposeFunctions.kt @@ -2,13 +2,13 @@ package f.cking.software.utils.graphic import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBars @@ -18,15 +18,16 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.windowInsetsBottomHeight import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Chip -import androidx.compose.material.ChipDefaults -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.TextField import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Star +import androidx.compose.material3.AssistChip +import androidx.compose.material3.AssistChipDefaults +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.mutableStateOf @@ -79,10 +80,13 @@ fun rememberDateDialog( MaterialDialog( dialogState = dialogState, buttons = { - positiveButton(stringResource(R.string.ok), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { dialogState.hide() } + positiveButton( + stringResource(R.string.ok), + textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer) + ) { dialogState.hide() } negativeButton( stringResource(R.string.cancel), - textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant) + textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer) ) { dialogState.hide() } }, ) { @@ -102,10 +106,13 @@ fun rememberTimeDialog( MaterialDialog( dialogState = dialogState, buttons = { - positiveButton(stringResource(R.string.ok), textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant)) { dialogState.hide() } + positiveButton( + stringResource(R.string.ok), + textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer) + ) { dialogState.hide() } negativeButton( stringResource(R.string.cancel), - textStyle = TextStyle(color = MaterialTheme.colors.secondaryVariant) + textStyle = TextStyle(color = MaterialTheme.colorScheme.secondaryContainer) ) { dialogState.hide() } }, ) { @@ -170,7 +177,7 @@ fun DeviceListItem( Icon( imageVector = Icons.Filled.Star, contentDescription = stringResource(R.string.is_favorite), - tint = MaterialTheme.colors.onSurface + tint = MaterialTheme.colorScheme.onSurface ) Spacer(modifier = Modifier.width(8.dp)) } @@ -258,6 +265,7 @@ fun MapView( Text( text = stringResource(R.string.osm_copyright), modifier = Modifier + .padding(start = 4.dp) .align(Alignment.BottomStart) .alpha(0.9f) .clickable { context.openUrl("https://www.openstreetmap.org/copyright") }, @@ -275,7 +283,7 @@ fun Divider() { modifier = Modifier .height(1.dp) .fillMaxWidth() - .background(Color.LightGray) + .background(MaterialTheme.colorScheme.onSurface.copy(alpha = 0.2f)) ) } } @@ -287,9 +295,7 @@ fun ContentPlaceholder( icon: Painter = painterResource(R.drawable.ic_ghost), ) { Box( - modifier = modifier - .fillMaxWidth() - .fillMaxHeight(), + modifier = modifier, contentAlignment = Alignment.Center ) { Column(Modifier.padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally) { @@ -318,14 +324,14 @@ fun RoundedBox( Column( Modifier .fillMaxWidth() - .background(color = MaterialTheme.colors.primaryVariant, shape = shape) + .background(color = MaterialTheme.colorScheme.surfaceContainer, shape = shape) .clip(shape = shape) .padding(internalPaddings) ) { boxContent(this) } } } -private val colors = listOf( +private val colorsLight = listOf( Color(0xFFE57373), Color(0xFFF06292), Color(0xFFBA68C8), @@ -345,28 +351,51 @@ private val colors = listOf( Color(0xFF90A4AE), ) +private val colorsDark = listOf( + Color(0xFF813535), + Color(0xFF742A43), + Color(0xFF5E2F66), + Color(0xFF443066), + Color(0xFF363E69), + Color(0xFF2F5574), + Color(0xFF275A70), + Color(0xFF2D6A72), + Color(0xFF235E58), + Color(0xFF457047), + Color(0xFF546D37), + Color(0xFF885241), + Color(0xFF6A7030), + Color(0xFF776426), + Color(0xFF7C643F), + Color(0xFF7A5446), + Color(0xFF3D545F), +) + +@Composable fun colorByHash(hash: Int): Color { + val colors = if (isSystemInDarkTheme()) colorsDark else colorsLight return colors[abs(hash % colors.size)] } -@OptIn(ExperimentalMaterialApi::class) @Composable fun TagChip( tagName: String, tagIcon: ImageVector? = null, onClick: () -> Unit = {}, ) { - Chip( - colors = ChipDefaults.chipColors( - backgroundColor = colorByHash(tagName.hashCode()), - contentColor = Color.Black, + AssistChip( + colors = AssistChipDefaults.assistChipColors( + containerColor = colorByHash(tagName.hashCode()), + labelColor = Color.Black, leadingIconContentColor = Color.Black, ), + border = null, onClick = onClick, - leadingIcon = { tagIcon?.let { Icon(imageVector = it, contentDescription = null, tint = MaterialTheme.colors.onSurface) } }, - ) { - Text(text = tagName) - } + leadingIcon = { tagIcon?.let { Icon(imageVector = it, contentDescription = null, tint = MaterialTheme.colorScheme.onSurface) } }, + label = { + Text(text = tagName, color = MaterialTheme.colorScheme.onSurface) + } + ) } @Composable @@ -380,7 +409,15 @@ fun pxToDp(px: Float): Float { } @Composable -fun BottomSpacer() { +fun BottomNavigationSpacer() { + val bottomOffset = remember { GlobalUiState.navbarOffsetPx } + Column { + Spacer(modifier = Modifier.height(pxToDp(bottomOffset.value).dp)) + } +} + +@Composable +fun FABSpacer() { val bottomOffset = remember { GlobalUiState.totalOffset } Column { Spacer(modifier = Modifier.height(pxToDp(bottomOffset.value).dp)) @@ -391,4 +428,8 @@ fun BottomSpacer() { @Composable fun SystemNavbarSpacer() { Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) +} + +fun ColorScheme.surfaceEvaluated(evaluation: Dp = 3.dp): Color { + return this.surfaceColorAtElevation(evaluation) } \ No newline at end of file diff --git a/app/src/main/java/f/cking/software/utils/graphic/GlassNavigationbar.kt b/app/src/main/java/f/cking/software/utils/graphic/GlassBottomSpace.kt similarity index 81% rename from app/src/main/java/f/cking/software/utils/graphic/GlassNavigationbar.kt rename to app/src/main/java/f/cking/software/utils/graphic/GlassBottomSpace.kt index 0e64742..440a697 100644 --- a/app/src/main/java/f/cking/software/utils/graphic/GlassNavigationbar.kt +++ b/app/src/main/java/f/cking/software/utils/graphic/GlassBottomSpace.kt @@ -10,7 +10,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.material.MaterialTheme +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -29,35 +29,59 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import f.cking.software.dpToPx +@Composable +fun GlassBottomNavBar( + modifier: Modifier = Modifier, + blur: Float = 3f, + glassCurveSizeDp: Float = 3f, + fallbackColor: Color = MaterialTheme.colorScheme.surfaceContainerHighest, + overlayColor: Color = MaterialTheme.colorScheme.surfaceContainerHighest.copy(alpha = 0.3f), + content: @Composable () -> Unit, +) { + GlassBottomSpace( + modifier = modifier, + blur = blur, + glassCurveSizeDp = glassCurveSizeDp, + fallbackColor = fallbackColor, + overlayColor = overlayColor, + bottomContent = { BottomNavigationSpacer() } + ) { + content() + } +} + @Composable fun GlassSystemNavbar( modifier: Modifier = Modifier, blur: Float = 3f, glassCurveSizeDp: Float = 3f, + fallbackColor: Color = MaterialTheme.colorScheme.surfaceContainerHighest, + overlayColor: Color = Color.Transparent, content: @Composable () -> Unit, ) { - GlassNavigationbar( + GlassBottomSpace( modifier = modifier, blur = blur, glassCurveSizeDp = glassCurveSizeDp, - fallbackColor = Color.Transparent, - navBarContent = { SystemNavbarSpacer() } + fallbackColor = fallbackColor, + overlayColor = overlayColor, + bottomContent = { SystemNavbarSpacer() } ) { content() } } @Composable -fun GlassNavigationbar( +fun GlassBottomSpace( modifier: Modifier = Modifier, height: Dp? = null, blur: Float = 3f, glassCurveSizeDp: Float = 3f, zIndex: Float = 1f, - fallbackColor: Color = MaterialTheme.colors.primary, - overlayColor: Color = MaterialTheme.colors.primary.copy(alpha = 0.3f), - navBarContent: @Composable () -> Unit, - content: @Composable () -> Unit, + fallbackColor: Color = MaterialTheme.colorScheme.surfaceContainerHighest, + overlayColor: Color = MaterialTheme.colorScheme.surfaceContainerHighest.copy(alpha = 0.3f), + bottomContent: @Composable () -> Unit, + globalContent: @Composable () -> Unit, ) { Box(modifier = modifier) { val context = LocalContext.current @@ -72,7 +96,7 @@ fun GlassNavigationbar( } else it } ) { - content() + globalContent() } Box( modifier = Modifier @@ -96,7 +120,7 @@ fun GlassNavigationbar( } .align(Alignment.BottomCenter) ) { - navBarContent() + bottomContent() Box( Modifier diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 4aebdb1..8d79a03 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -1,35 +1,21 @@ - - #464237 - @color/white_90 - - #6B5B39 - #685F45 - #7F7150 - @color/white_90 - - #A95C0E - @color/secondary - @color/white_90 - - #80DEEA - #A5D6A7 - #B39DDB - #FFE082 - #FFAB91 - #F48FB1 - #80CBC4 - #EF9A9A - #90CAF9 - #FFCC80 - #E6EE9C - #CE93D8 - #FFFFFF - #FFAB91 - #E6EE9C - #80CBC4 + #006064 + #004D40 + #371165 + #CD5C04 + #815244 + #8F576A + #56958F + #C17D7D + #4E718E + #C19D68 + #7E8541 + #7D4E85 + #616161 + #9F5E5E + #875647 + #575C27 + #376561 #0000 - - #9B5C5C \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index feb32b5..ce054e5 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,20 +1,41 @@ - \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index fcc1981..489d2f4 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -15,17 +15,74 @@ #E64A19 #D84315 - #FDF7E9 - @color/black_90 + #ff9b4b + #944B00 + #FFFFFF + #FFDCC5 + #301400 + #8D4F00 + #FFFFFF + #FFDCC0 + #2D1600 + #5D6300 + #FFFFFF + #E2EA74 + #1B1D00 + #BA1A1A + #FFDAD6 + #FFFFFF + #410002 + #FFFBFF + #201A17 + #FFFFF3E7 + #F6E3D0 + #F3D4B5 + #EFCFAF + #201A17 + #E4D0BC + #52443B + #84746A + #FBEEE8 + #362F2B + #FFB783 + #000000 + #944B00 + #D6C3B7 + #000000 - #FFF1CF - #F8EED7 - #F1DEAD - @color/black_90 - - #F57C00 - @color/secondary - @color/white + #FFB783 + #4F2500 + #703700 + #FFDCC5 + #FFB876 + #4B2700 + #6B3B00 + #FFDCC0 + #C5CE5C + #303300 + #464A00 + #E2EA74 + #FFB4AB + #93000A + #690005 + #FFDAD6 + #201A17 + #ECE0DA + #201D17 + #50422C + #554833 + #635640 + #ECE0DA + #52443B + #D6C3B7 + #9F8D83 + #201A17 + #ECE0DA + #944B00 + #000000 + #FFB783 + #52443B + #000000 #80DEEA #A5D6A7 diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index c978eed..ae843b8 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,20 +1,41 @@ - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9d1faa7..10bd365 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,6 +15,7 @@ compose-compiler = "1.5.6" compose-accompanist = "0.28.0" compose-constraint = "1.0.1" compose-navigation = "2.5.2" +compose-material3 = "1.2.0-beta01" horologist = "0.1.13" serialization-json = "1.4.0" wear = "1.2.0" @@ -103,6 +104,7 @@ datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" compose-activity = { module = "androidx.activity:activity-compose", version.ref = "ktx-activity" } material = { module = "com.google.android.material:material", version.ref = "material" } compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" } +compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "compose-material3" } compose-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } compose-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" } compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" }