-
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package com.lorenzovainigli.foodexpirationdates.view | ||
|
||
import android.os.Build | ||
import android.os.Bundle | ||
import androidx.activity.ComponentActivity | ||
import androidx.activity.compose.setContent | ||
import androidx.activity.viewModels | ||
import androidx.annotation.RequiresApi | ||
import androidx.compose.foundation.isSystemInDarkTheme | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.Surface | ||
import androidx.compose.runtime.collectAsState | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.ui.Modifier | ||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen | ||
import androidx.core.view.WindowCompat | ||
import androidx.lifecycle.viewmodel.compose.viewModel | ||
import androidx.navigation.compose.rememberNavController | ||
import com.lorenzovainigli.foodexpirationdates.di.AppModule | ||
import com.lorenzovainigli.foodexpirationdates.di.DaggerAppComponent | ||
import com.lorenzovainigli.foodexpirationdates.model.NotificationManager | ||
import com.lorenzovainigli.foodexpirationdates.model.repository.PreferencesRepository | ||
import com.lorenzovainigli.foodexpirationdates.ui.theme.FoodExpirationDatesTheme | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.MyScaffold | ||
import com.lorenzovainigli.foodexpirationdates.viewmodel.ExpirationDatesViewModel | ||
import com.lorenzovainigli.foodexpirationdates.viewmodel.PreferencesViewModel | ||
import dagger.hilt.android.AndroidEntryPoint | ||
|
||
@AndroidEntryPoint | ||
class MainActivity : ComponentActivity() { | ||
|
||
val viewModel: ExpirationDatesViewModel by viewModels() | ||
val preferencesViewModel: PreferencesViewModel by viewModels() | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
WindowCompat.setDecorFitsSystemWindows(window, false) | ||
|
||
val splashScreen = installSplashScreen() | ||
splashScreen.setKeepOnScreenCondition { viewModel.isSplashScreenLoading.value } | ||
|
||
DaggerAppComponent.builder() | ||
.appModule(AppModule()) | ||
.build() | ||
NotificationManager.setupNotificationChannel(this) | ||
} | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
override fun onResume() { | ||
super.onResume() | ||
val context = this | ||
setContent { | ||
val viewModel: ExpirationDatesViewModel = viewModel() | ||
val prefsViewModel: PreferencesViewModel = viewModel() | ||
val darkThemeState = prefsViewModel.getThemeMode(context).collectAsState().value | ||
val dynamicColorsState = prefsViewModel.getDynamicColors(context).collectAsState().value | ||
val isInDarkTheme = when (darkThemeState) { | ||
PreferencesRepository.Companion.ThemeMode.LIGHT.ordinal -> false | ||
PreferencesRepository.Companion.ThemeMode.DARK.ordinal -> true | ||
else -> isSystemInDarkTheme() | ||
} | ||
FoodExpirationDatesTheme( | ||
darkTheme = isInDarkTheme, | ||
dynamicColor = dynamicColorsState | ||
) { | ||
Surface( | ||
modifier = Modifier | ||
.fillMaxSize(), | ||
color = MaterialTheme.colorScheme.background | ||
) { | ||
val navController = rememberNavController() | ||
val showSnackbar = remember { | ||
mutableStateOf(false) | ||
} | ||
MyScaffold( | ||
activity = this, | ||
navController = navController, | ||
showSnackbar = showSnackbar | ||
) { | ||
Navigation( | ||
activity = this, | ||
navController = navController, | ||
showSnackbar = showSnackbar | ||
) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package com.lorenzovainigli.foodexpirationdates.view | ||
|
||
import android.os.Build | ||
import androidx.annotation.RequiresApi | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.MutableState | ||
import androidx.compose.ui.Modifier | ||
import androidx.navigation.NavHostController | ||
import androidx.navigation.NavType | ||
import androidx.navigation.compose.NavHost | ||
import androidx.navigation.compose.composable | ||
import androidx.navigation.navArgument | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.screen.InfoScreen | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.screen.InsertScreen | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.screen.MainScreen | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.screen.Screen | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.screen.SettingsScreen | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
@Composable | ||
fun Navigation( | ||
activity: MainActivity? = null, | ||
navController: NavHostController, | ||
showSnackbar: MutableState<Boolean> | ||
) { | ||
NavHost( | ||
modifier = Modifier.fillMaxSize(), | ||
navController = navController, | ||
startDestination = Screen.MainScreen.route | ||
) { | ||
composable(route = Screen.MainScreen.route) { | ||
MainScreen( | ||
activity = activity, | ||
navController = navController, | ||
showSnackbar = showSnackbar | ||
) | ||
} | ||
composable( | ||
route = Screen.InsertScreen.route + "?itemId={itemId}", | ||
arguments = listOf( | ||
navArgument("itemId"){ | ||
type = NavType.StringType | ||
nullable = true | ||
} | ||
) | ||
){ entry -> | ||
InsertScreen( | ||
activity = activity, | ||
navController = navController, | ||
itemId = entry.arguments?.getString("itemId") | ||
) | ||
} | ||
composable(route = Screen.AboutScreen.route){ | ||
InfoScreen() | ||
} | ||
composable(route = Screen.SettingsScreen.route){ | ||
SettingsScreen(activity = activity) | ||
} | ||
} | ||
} | ||
|
||
//@RequiresApi(Build.VERSION_CODES.O) | ||
//@Preview | ||
//@Composable | ||
//fun NavigationPreview(){ | ||
// FoodExpirationDatesTheme { | ||
// Surface(modifier = Modifier.fillMaxSize()) { | ||
// Navigation( | ||
// navController = rememberNavController(), | ||
// coroutineScope = rememberCoroutineScope(), | ||
// snackbarHostState = SnackbarHostState() | ||
// ) | ||
// } | ||
// } | ||
//} |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package com.lorenzovainigli.foodexpirationdates.view.composable | ||
|
||
import androidx.compose.material.icons.Icons | ||
import androidx.compose.material.icons.filled.Info | ||
import androidx.compose.material.icons.filled.List | ||
import androidx.compose.material.icons.filled.Settings | ||
import androidx.compose.material.icons.outlined.Info | ||
import androidx.compose.material.icons.outlined.List | ||
import androidx.compose.material.icons.outlined.Settings | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.NavigationBar | ||
import androidx.compose.material3.NavigationBarItem | ||
import androidx.compose.material3.Surface | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.text.style.TextOverflow | ||
import androidx.compose.ui.tooling.preview.PreviewLightDark | ||
import androidx.navigation.NavBackStackEntry | ||
import androidx.navigation.NavHostController | ||
import androidx.navigation.compose.rememberNavController | ||
import com.lorenzovainigli.foodexpirationdates.R | ||
import com.lorenzovainigli.foodexpirationdates.ui.theme.FoodExpirationDatesTheme | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.screen.Screen | ||
import com.lorenzovainigli.foodexpirationdates.view.preview.LanguagePreviews | ||
|
||
@Composable | ||
fun MyBottomAppBar( | ||
navController: NavHostController, | ||
currentBackStackEntry: NavBackStackEntry? | ||
){ | ||
val navigationItems = listOf( | ||
NavigationItem( | ||
label = stringResource(id = R.string.about_this_app), | ||
route = Screen.AboutScreen.route, | ||
selectedIcon = Icons.Filled.Info, | ||
unselectedIcon = Icons.Outlined.Info | ||
), | ||
NavigationItem( | ||
label = stringResource(id = R.string.list), | ||
route = Screen.MainScreen.route, | ||
selectedIcon = Icons.Filled.List, | ||
unselectedIcon = Icons.Outlined.List | ||
), | ||
NavigationItem( | ||
label = stringResource(id = R.string.settings), | ||
route = Screen.SettingsScreen.route, | ||
selectedIcon = Icons.Filled.Settings, | ||
unselectedIcon = Icons.Outlined.Settings | ||
) | ||
) | ||
NavigationBar { | ||
var selectedItem = when (currentBackStackEntry?.destination?.route) { | ||
Screen.AboutScreen.route -> 0 | ||
Screen.SettingsScreen.route -> 2 | ||
else -> 1 | ||
} | ||
navigationItems.forEachIndexed { index, item -> | ||
NavigationBarItem( | ||
selected = selectedItem == index, | ||
onClick = { | ||
selectedItem = index | ||
navController.navigate(item.route) | ||
}, | ||
icon = { | ||
Icon( | ||
imageVector = when (selectedItem == index) { | ||
true -> item.selectedIcon | ||
else -> item.unselectedIcon | ||
}, | ||
contentDescription = item.label | ||
) | ||
}, | ||
label = { | ||
Text( | ||
text = item.label, | ||
maxLines = 1, | ||
overflow = TextOverflow.Ellipsis | ||
) | ||
} | ||
) | ||
} | ||
} | ||
} | ||
|
||
@PreviewLightDark | ||
@LanguagePreviews | ||
@Composable | ||
fun MyBottomAppBarPreview(){ | ||
FoodExpirationDatesTheme { | ||
Surface { | ||
MyBottomAppBar( | ||
navController = rememberNavController(), | ||
currentBackStackEntry = null | ||
) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package com.lorenzovainigli.foodexpirationdates.view.composable | ||
|
||
import android.os.Build | ||
import android.util.Log | ||
import androidx.annotation.RequiresApi | ||
import androidx.compose.foundation.clickable | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material.icons.Icons | ||
import androidx.compose.material.icons.filled.ArrowBack | ||
import androidx.compose.material3.ExperimentalMaterial3Api | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.Scaffold | ||
import androidx.compose.material3.SnackbarDuration | ||
import androidx.compose.material3.SnackbarHost | ||
import androidx.compose.material3.SnackbarHostState | ||
import androidx.compose.material3.SnackbarResult | ||
import androidx.compose.material3.TopAppBarDefaults | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.MutableState | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.rememberCoroutineScope | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.graphics.vector.ImageVector | ||
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 androidx.navigation.NavHostController | ||
import androidx.navigation.compose.currentBackStackEntryAsState | ||
import com.lorenzovainigli.foodexpirationdates.R | ||
import com.lorenzovainigli.foodexpirationdates.view.MainActivity | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.screen.Screen | ||
import kotlinx.coroutines.launch | ||
|
||
data class NavigationItem( | ||
val label: String, | ||
val route: String, | ||
val selectedIcon: ImageVector, | ||
val unselectedIcon: ImageVector | ||
) | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
@OptIn(ExperimentalMaterial3Api::class) | ||
@Composable | ||
fun MyScaffold( | ||
activity: MainActivity? = null, | ||
navController: NavHostController, | ||
showSnackbar: MutableState<Boolean>, | ||
content: @Composable () -> Unit | ||
) { | ||
val snackbarHostState = remember { SnackbarHostState() } | ||
val coroutineScope = rememberCoroutineScope() | ||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() | ||
val currentBackStackEntry by navController.currentBackStackEntryAsState() | ||
val context = LocalContext.current | ||
if (showSnackbar.value){ | ||
coroutineScope.launch { | ||
try { | ||
val deletedItem = activity?.viewModel?.deletedItem?.value | ||
val snackbarResult = | ||
snackbarHostState.showSnackbar( | ||
message = context.resources.getString( | ||
R.string.x_deleted, | ||
deletedItem?.foodName ?: "" | ||
), | ||
actionLabel = context.resources.getString(R.string.undo), | ||
duration = SnackbarDuration.Short | ||
) | ||
when (snackbarResult) { | ||
SnackbarResult.ActionPerformed -> { | ||
deletedItem?.let { | ||
activity.viewModel.addExpirationDate(it) | ||
} | ||
} | ||
|
||
else -> { | ||
Log.i("ERROR", "Error showing snackbar") | ||
} | ||
} | ||
} catch (e: Exception) { | ||
e.printStackTrace() | ||
} | ||
} | ||
showSnackbar.value = false | ||
} | ||
Scaffold( | ||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), | ||
snackbarHost = { SnackbarHost(snackbarHostState) }, | ||
topBar = { | ||
MyTopAppBar( | ||
activity = activity, | ||
title = when (currentBackStackEntry?.destination?.route) { | ||
Screen.AboutScreen.route -> stringResource(id = R.string.about_this_app) | ||
Screen.InsertScreen.route -> stringResource(id = R.string.insert) | ||
Screen.SettingsScreen.route -> stringResource(id = R.string.settings) | ||
else -> stringResource(id = R.string.app_name) | ||
}, | ||
actions = { | ||
AppIcon(size = 48.dp) | ||
}, | ||
navigationIcon = { | ||
if (currentBackStackEntry?.destination?.route == Screen.InsertScreen.route) { | ||
Icon( | ||
modifier = Modifier.clickable { | ||
navController.popBackStack() | ||
}, | ||
imageVector = Icons.Filled.ArrowBack, | ||
contentDescription = stringResource(id = R.string.back), | ||
tint = MaterialTheme.colorScheme.primary | ||
) | ||
} | ||
}, | ||
scrollBehavior = scrollBehavior | ||
) | ||
}, | ||
bottomBar = { | ||
MyBottomAppBar( | ||
navController = navController, | ||
currentBackStackEntry = currentBackStackEntry | ||
) | ||
} | ||
) { padding -> | ||
Column( | ||
modifier = Modifier | ||
.padding(padding) | ||
.fillMaxSize() | ||
) { | ||
content() | ||
} | ||
} | ||
} | ||
|
||
//@RequiresApi(Build.VERSION_CODES.O) | ||
//@PreviewLightDark | ||
//@PreviewScreenSizes | ||
//@Composable | ||
//fun MyScaffoldPreview() { | ||
// val navController = rememberNavController() | ||
// FoodExpirationDatesTheme { | ||
// Surface(modifier = Modifier.fillMaxSize()) { | ||
// MyScaffold( | ||
// navController = navController | ||
// ) | ||
// } | ||
// } | ||
//} |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
package com.lorenzovainigli.foodexpirationdates.view.composable.screen | ||
|
||
import android.content.Context | ||
import android.content.Intent | ||
import androidx.compose.foundation.Image | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Row | ||
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.rememberScrollState | ||
import androidx.compose.foundation.text.ClickableText | ||
import androidx.compose.foundation.verticalScroll | ||
import androidx.compose.material.icons.Icons | ||
import androidx.compose.material.icons.outlined.Edit | ||
import androidx.compose.material.icons.outlined.Email | ||
import androidx.compose.material.icons.outlined.Share | ||
import androidx.compose.material.icons.outlined.Star | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.platform.LocalContext | ||
import androidx.compose.ui.platform.LocalUriHandler | ||
import androidx.compose.ui.res.painterResource | ||
import androidx.compose.ui.res.stringArrayResource | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.text.AnnotatedString | ||
import androidx.compose.ui.text.TextStyle | ||
import androidx.compose.ui.text.style.TextAlign | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.unit.dp | ||
import com.lorenzovainigli.foodexpirationdates.BuildConfig | ||
import com.lorenzovainigli.foodexpirationdates.DEVELOPER_EMAIL | ||
import com.lorenzovainigli.foodexpirationdates.GITHUB_URL | ||
import com.lorenzovainigli.foodexpirationdates.PLAY_STORE_URL | ||
import com.lorenzovainigli.foodexpirationdates.PRIVACY_POLICY_URL | ||
import com.lorenzovainigli.foodexpirationdates.R | ||
import com.lorenzovainigli.foodexpirationdates.model.contributors | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.TextIconButton | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.TextIconButtonData | ||
|
||
@Composable | ||
fun InfoScreen( | ||
context: Context = LocalContext.current | ||
) { | ||
val uriHandler = LocalUriHandler.current | ||
val features = stringArrayResource(id = R.array.features) | ||
.joinToString(separator = "\n") { it.asListItem() } | ||
Column( | ||
modifier = Modifier | ||
.verticalScroll(rememberScrollState()) | ||
.padding(10.dp), | ||
verticalArrangement = Arrangement.spacedBy(16.dp) | ||
) { | ||
Image( | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.height(128.dp) | ||
.padding(top = 16.dp), | ||
painter = painterResource(id = R.drawable.fed_icon), | ||
alignment = Alignment.Center, | ||
contentDescription = null | ||
) | ||
Text( | ||
modifier = Modifier | ||
.align(Alignment.CenterHorizontally), | ||
text = stringResource(id = R.string.app_name), | ||
style = MaterialTheme.typography.headlineMedium, | ||
textAlign = TextAlign.Center | ||
) | ||
Text( | ||
modifier = Modifier.align(Alignment.CenterHorizontally), | ||
style = MaterialTheme.typography.bodySmall, | ||
text = stringResource( | ||
id = R.string.version_x, | ||
BuildConfig.VERSION_NAME | ||
), | ||
textAlign = TextAlign.Center | ||
) | ||
Text( | ||
modifier = Modifier.fillMaxWidth(), | ||
text = stringResource( | ||
id = R.string.app_description, | ||
stringResource(id = R.string.app_name) | ||
) | ||
) | ||
TextIconButton( | ||
modifier = Modifier | ||
.align(Alignment.CenterHorizontally) | ||
.width(256.dp), | ||
onClick = { | ||
uriHandler.openUri( | ||
uri = "https://github.com/lorenzovngl/FoodExpirationDates" | ||
) | ||
}, | ||
imagePainter = painterResource(id = R.drawable.github), | ||
text = stringResource(id = R.string.source_code) | ||
) | ||
Text( | ||
modifier = Modifier.padding(top = 16.dp), | ||
text = stringResource(id = R.string.features), | ||
style = MaterialTheme.typography.headlineMedium, | ||
textAlign = TextAlign.Center | ||
) | ||
Text( | ||
modifier = Modifier.fillMaxWidth(), | ||
text = features | ||
) | ||
/*TextIconButton( | ||
modifier = Modifier.align(CenterHorizontally), | ||
onClick = { | ||
}, | ||
imagePainter = painterResource(id = R.drawable.bug_report), | ||
contentDescription = "Star", | ||
text = stringResource(id = R.string.report_a_bug) | ||
)*/ | ||
Text( | ||
modifier = Modifier.padding(top = 16.dp), | ||
text = stringResource(id = R.string.support_this_project), | ||
style = MaterialTheme.typography.headlineMedium, | ||
textAlign = TextAlign.Center | ||
) | ||
arrayOf( | ||
TextIconButtonData( | ||
iconImageVector = Icons.Outlined.Star, | ||
text = stringResource(id = R.string.leave_a_star_on_github), | ||
onClick = { | ||
uriHandler.openUri( | ||
uri = GITHUB_URL | ||
) | ||
}, | ||
), | ||
TextIconButtonData( | ||
iconImageVector = Icons.Outlined.Edit, | ||
text = stringResource(id = R.string.write_a_review), | ||
onClick = { | ||
uriHandler.openUri( | ||
uri = PLAY_STORE_URL | ||
) | ||
}, | ||
), | ||
TextIconButtonData( | ||
iconImageVector = Icons.Outlined.Share, | ||
text = stringResource(id = R.string.share), | ||
onClick = { | ||
val sendIntent: Intent = Intent().apply { | ||
action = Intent.ACTION_SEND | ||
putExtra(Intent.EXTRA_TEXT, PLAY_STORE_URL) | ||
type = "text/plain" | ||
} | ||
val shareIntent = Intent.createChooser(sendIntent, null) | ||
context.startActivity(shareIntent) | ||
}, | ||
), | ||
).forEach { | ||
Row( | ||
modifier = Modifier.fillMaxWidth(), | ||
horizontalArrangement = Arrangement.Center | ||
) { | ||
TextIconButton( | ||
modifier = Modifier.width(256.dp), | ||
onClick = it.onClick, | ||
iconImageVector = it.iconImageVector, | ||
imagePainter = it.imagePainter, | ||
text = it.text | ||
) | ||
} | ||
} | ||
ContactSection() | ||
ContributorsList() | ||
ClickableText( | ||
modifier = Modifier | ||
.align(Alignment.CenterHorizontally) | ||
.padding(top = 4.dp), | ||
text = AnnotatedString(text = stringResource(id = R.string.privacy_policy)), | ||
style = TextStyle.Default.copy(color = MaterialTheme.colorScheme.primary), | ||
onClick = { | ||
uriHandler.openUri( | ||
uri = PRIVACY_POLICY_URL | ||
) | ||
} | ||
) | ||
} | ||
} | ||
|
||
@Composable | ||
fun ContactSection( | ||
modifier: Modifier = Modifier | ||
) { | ||
Column(modifier = modifier.fillMaxWidth()) { | ||
Text( | ||
modifier = Modifier.padding(top = 16.dp), | ||
text = "Contacts", | ||
style = MaterialTheme.typography.headlineMedium, | ||
textAlign = TextAlign.Center | ||
) | ||
Text( | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.padding(top = 16.dp, bottom = 8.dp), | ||
text = stringResource(id = R.string.contacts_text) | ||
) | ||
val uriHandler = LocalUriHandler.current | ||
TextIconButton( | ||
modifier = Modifier | ||
.align(Alignment.CenterHorizontally) | ||
.width(256.dp), | ||
onClick = { | ||
uriHandler.openUri( | ||
uri = "mailto:$DEVELOPER_EMAIL" | ||
) | ||
}, | ||
iconImageVector = Icons.Outlined.Email, | ||
text = stringResource(id = R.string.send_an_email) | ||
) | ||
} | ||
} | ||
|
||
@Composable | ||
fun ContributorsList( | ||
modifier: Modifier = Modifier | ||
) { | ||
val contributorsText = remember { | ||
contributors.joinToString(separator = "\n") { | ||
"${it.name} (@${it.username})".asListItem() | ||
} | ||
} | ||
Column(modifier = modifier.fillMaxWidth()) { | ||
Text( | ||
modifier = Modifier.padding(top = 16.dp), | ||
text = stringResource(id = R.string.contributors_list_title), | ||
style = MaterialTheme.typography.headlineMedium, | ||
textAlign = TextAlign.Center | ||
) | ||
Text( | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.padding(top = 16.dp, bottom = 8.dp), | ||
text = stringResource(id = R.string.contributors_list_subtitle) | ||
) | ||
Text( | ||
modifier = Modifier.fillMaxWidth(), | ||
text = contributorsText | ||
) | ||
} | ||
} | ||
|
||
private fun String.asListItem() = " ● $this" | ||
|
||
//@PreviewLightDark | ||
//@Composable | ||
//fun InfoScreenPreview() { | ||
// FoodExpirationDatesTheme { | ||
// Surface (modifier = Modifier.fillMaxHeight()) { | ||
// InfoScreen() | ||
// } | ||
// } | ||
//} | ||
|
||
@Preview(showBackground = true) | ||
@Composable | ||
fun ContributorsListPreview() { | ||
ContributorsList() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
package com.lorenzovainigli.foodexpirationdates.view.composable.screen | ||
|
||
import android.os.Build | ||
import android.widget.Toast | ||
import androidx.annotation.RequiresApi | ||
import androidx.compose.foundation.BorderStroke | ||
import androidx.compose.foundation.clickable | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.text.KeyboardOptions | ||
import androidx.compose.material3.Button | ||
import androidx.compose.material3.ButtonDefaults | ||
import androidx.compose.material3.DatePicker | ||
import androidx.compose.material3.DatePickerDialog | ||
import androidx.compose.material3.ExperimentalMaterial3Api | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.OutlinedButton | ||
import androidx.compose.material3.Surface | ||
import androidx.compose.material3.Text | ||
import androidx.compose.material3.TextField | ||
import androidx.compose.material3.TextFieldDefaults | ||
import androidx.compose.material3.rememberDatePickerState | ||
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.graphics.Color | ||
import androidx.compose.ui.platform.testTag | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.text.input.KeyboardCapitalization | ||
import androidx.compose.ui.tooling.preview.PreviewLightDark | ||
import androidx.compose.ui.unit.dp | ||
import androidx.navigation.NavController | ||
import androidx.navigation.compose.rememberNavController | ||
import com.lorenzovainigli.foodexpirationdates.R | ||
import com.lorenzovainigli.foodexpirationdates.model.entity.ExpirationDate | ||
import com.lorenzovainigli.foodexpirationdates.ui.theme.FoodExpirationDatesTheme | ||
import com.lorenzovainigli.foodexpirationdates.view.MainActivity | ||
import java.text.DateFormat | ||
import java.text.SimpleDateFormat | ||
import java.util.Locale | ||
|
||
@OptIn(ExperimentalMaterial3Api::class) | ||
@Composable | ||
fun InsertScreen( | ||
activity: MainActivity? = null, | ||
navController: NavController, | ||
itemId: String? = null, | ||
) { | ||
val itemToEdit = itemId?.let { activity?.viewModel?.getDate(it.toInt()) } | ||
var foodNameToEdit = "" | ||
var expDate: Long? = null | ||
if (itemToEdit != null) { | ||
foodNameToEdit = itemToEdit.foodName | ||
expDate = itemToEdit.expirationDate | ||
} | ||
var foodName by remember { | ||
mutableStateOf(foodNameToEdit) | ||
} | ||
val datePickerState = rememberDatePickerState(expDate) | ||
var isDialogOpen by remember { | ||
mutableStateOf(false) | ||
} | ||
if (isDialogOpen) { | ||
DatePickerDialog( | ||
dismissButton = { | ||
OutlinedButton( | ||
onClick = { isDialogOpen = false }, | ||
border = BorderStroke( | ||
1.dp, | ||
MaterialTheme.colorScheme.tertiary | ||
), | ||
colors = ButtonDefaults.buttonColors( | ||
containerColor = Color.Transparent, | ||
contentColor = MaterialTheme.colorScheme.tertiary | ||
) | ||
) { | ||
Text(text = stringResource(id = R.string.cancel)) | ||
} | ||
}, | ||
confirmButton = { | ||
Button( | ||
modifier = Modifier.testTag("Insert date"), | ||
onClick = { isDialogOpen = false }, | ||
// colors = ButtonDefaults.buttonColors( | ||
// containerColor = MaterialTheme.colorScheme.tertiary, | ||
// contentColor = MaterialTheme.colorScheme.onTertiary | ||
// ) | ||
) { | ||
Text(text = stringResource(id = R.string.insert)) | ||
} | ||
}, | ||
content = { | ||
DatePicker( | ||
state = datePickerState | ||
) | ||
}, | ||
onDismissRequest = { | ||
isDialogOpen = false | ||
} | ||
) | ||
} | ||
Column( | ||
modifier = Modifier | ||
.fillMaxWidth() | ||
.padding(16.dp) | ||
) { | ||
TextField( | ||
label = { | ||
Text( | ||
text = stringResource(id = R.string.food_name), | ||
modifier = Modifier.fillMaxWidth() | ||
) | ||
}, | ||
value = foodName, | ||
onValueChange = { newText -> | ||
foodName = newText | ||
}, | ||
singleLine = true, | ||
keyboardOptions = KeyboardOptions( | ||
capitalization = KeyboardCapitalization.Sentences | ||
) | ||
) | ||
Spacer(modifier = Modifier.height(16.dp)) | ||
TextField( | ||
modifier = Modifier.clickable(onClick = { | ||
isDialogOpen = true | ||
}), | ||
enabled = false, | ||
label = { | ||
Text( | ||
text = stringResource(id = R.string.expiration_date), | ||
modifier = Modifier.fillMaxWidth() | ||
) | ||
}, | ||
value = if (datePickerState.selectedDateMillis == null) "" else { | ||
// TODO What kind of date format is the best here? | ||
val dateFormat = (DateFormat.getDateInstance( | ||
DateFormat.MEDIUM, Locale.getDefault() | ||
) as SimpleDateFormat).toLocalizedPattern() | ||
val sdf = SimpleDateFormat(dateFormat, Locale.getDefault()) | ||
sdf.format(datePickerState.selectedDateMillis) | ||
}, | ||
onValueChange = {}, | ||
colors = TextFieldDefaults.colors( | ||
disabledTextColor = MaterialTheme.colorScheme.onSurface, | ||
disabledLeadingIconColor = MaterialTheme.colorScheme.onSurfaceVariant, | ||
disabledTrailingIconColor = MaterialTheme.colorScheme.onSurfaceVariant, | ||
disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant, | ||
//For Icons | ||
disabledPlaceholderColor = MaterialTheme.colorScheme.onSurfaceVariant, | ||
) | ||
) | ||
Row { | ||
OutlinedButton( | ||
onClick = { navController.popBackStack() }, | ||
modifier = Modifier | ||
.weight(0.5f) | ||
.padding(top = 8.dp, end = 4.dp), | ||
border = BorderStroke( | ||
1.dp, | ||
MaterialTheme.colorScheme.tertiary | ||
), | ||
colors = ButtonDefaults.buttonColors( | ||
containerColor = Color.Transparent, | ||
contentColor = MaterialTheme.colorScheme.tertiary | ||
), | ||
) { | ||
Text(text = stringResource(id = R.string.cancel)) | ||
} | ||
Button( | ||
modifier = Modifier | ||
.testTag("Insert item") | ||
.weight(0.5f) | ||
.padding(top = 8.dp, start = 4.dp), | ||
colors = ButtonDefaults.buttonColors( | ||
containerColor = MaterialTheme.colorScheme.tertiary, | ||
contentColor = MaterialTheme.colorScheme.onTertiary | ||
), | ||
onClick = { | ||
try { | ||
if (foodName.isNotEmpty()) { | ||
if (datePickerState.selectedDateMillis != null) { | ||
var id = 0 | ||
if (itemToEdit != null) { | ||
id = itemId.toInt() | ||
} | ||
val entry = ExpirationDate( | ||
id = id, | ||
foodName = foodName, | ||
expirationDate = datePickerState.selectedDateMillis!! | ||
) | ||
activity?.viewModel?.addExpirationDate(entry) | ||
navController.popBackStack() | ||
} else { | ||
Toast.makeText( | ||
activity, | ||
R.string.please_select_a_date, | ||
Toast.LENGTH_SHORT | ||
).show() | ||
} | ||
} else { | ||
Toast.makeText( | ||
activity, | ||
R.string.please_enter_a_food_name, | ||
Toast.LENGTH_SHORT | ||
).show() | ||
} | ||
} catch (e: Exception) { | ||
Toast.makeText( | ||
activity, | ||
e.message, | ||
Toast.LENGTH_SHORT | ||
).show() | ||
} | ||
} | ||
) { | ||
if (itemToEdit != null) | ||
Text(text = stringResource(id = R.string.update)) | ||
else | ||
Text(text = stringResource(id = R.string.insert)) | ||
} | ||
} | ||
} | ||
} | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
@PreviewLightDark | ||
@Composable | ||
fun InsertScreenPreview() { | ||
FoodExpirationDatesTheme { | ||
Surface { | ||
InsertScreen(navController = rememberNavController()) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
package com.lorenzovainigli.foodexpirationdates.view.composable.screen | ||
|
||
import android.content.Context | ||
import android.os.Build | ||
import androidx.annotation.RequiresApi | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.rememberScrollState | ||
import androidx.compose.foundation.verticalScroll | ||
import androidx.compose.material.icons.Icons | ||
import androidx.compose.material.icons.rounded.Add | ||
import androidx.compose.material3.FloatingActionButton | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.Surface | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.MutableState | ||
import androidx.compose.runtime.collectAsState | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.platform.LocalContext | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.text.style.TextAlign | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.unit.dp | ||
import androidx.navigation.NavHostController | ||
import androidx.navigation.compose.rememberNavController | ||
import com.lorenzovainigli.foodexpirationdates.R | ||
import com.lorenzovainigli.foodexpirationdates.model.entity.ExpirationDate | ||
import com.lorenzovainigli.foodexpirationdates.ui.theme.FoodExpirationDatesTheme | ||
import com.lorenzovainigli.foodexpirationdates.view.MainActivity | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.FoodCard | ||
import java.util.Calendar | ||
import kotlin.math.min | ||
|
||
@Composable | ||
fun MainScreen( | ||
activity: MainActivity? = null, | ||
navController: NavHostController, | ||
showSnackbar: MutableState<Boolean>? = null | ||
) { | ||
Box( | ||
modifier = Modifier | ||
.padding(4.dp) | ||
.fillMaxSize() | ||
) { | ||
val itemsState = activity?.viewModel?.getDates()?.collectAsState(emptyList()) | ||
val items = itemsState?.value ?: getItemsForPreview(LocalContext.current) | ||
if (items.isNotEmpty()) { | ||
ListOfItems( | ||
activity = activity, | ||
items = items, | ||
showSnackbar = showSnackbar, | ||
navController = navController | ||
) | ||
} else { | ||
EmptyList() | ||
} | ||
FloatingActionButton( | ||
modifier = Modifier | ||
.align(Alignment.BottomEnd) | ||
.padding(12.dp), | ||
onClick = { | ||
navController.navigate(Screen.InsertScreen.route) | ||
}, | ||
containerColor = MaterialTheme.colorScheme.tertiaryContainer, | ||
contentColor = MaterialTheme.colorScheme.onTertiaryContainer | ||
) { | ||
Icon( | ||
imageVector = Icons.Rounded.Add, | ||
contentDescription = stringResource(id = R.string.insert) | ||
) | ||
} | ||
} | ||
} | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
@Preview | ||
@Composable | ||
fun MainScreenPreview() { | ||
FoodExpirationDatesTheme { | ||
Surface { | ||
MainScreen( | ||
navController = rememberNavController() | ||
) | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
fun ListOfItems( | ||
activity: MainActivity? = null, | ||
items: List<ExpirationDate>, | ||
navController: NavHostController, | ||
showSnackbar: MutableState<Boolean>? | ||
) { | ||
Column( | ||
modifier = Modifier.verticalScroll(rememberScrollState()) | ||
) { | ||
for (item in items) { | ||
FoodCard( | ||
item = item, | ||
onClickEdit = { | ||
navController.navigate(Screen.InsertScreen.route + "?itemId=${item.id}") | ||
}, | ||
onClickDelete = { | ||
showSnackbar?.value = true | ||
activity?.viewModel?.deleteExpirationDate(item) | ||
} | ||
) | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
@Preview | ||
fun ListOfItemsPreview() { | ||
FoodExpirationDatesTheme { | ||
Surface { | ||
ListOfItems( | ||
items = getItemsForPreview(LocalContext.current), | ||
navController = rememberNavController(), | ||
showSnackbar = null | ||
) | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
fun EmptyList() { | ||
Surface(modifier = Modifier.fillMaxSize()) { | ||
Column( | ||
modifier = Modifier | ||
.fillMaxSize(), | ||
verticalArrangement = Arrangement.spacedBy( | ||
space = 16.dp, | ||
alignment = Alignment.CenterVertically | ||
), | ||
horizontalAlignment = Alignment.CenterHorizontally | ||
) { | ||
Text( | ||
text = stringResource(id = R.string.no_items_found), | ||
style = MaterialTheme.typography.displaySmall, | ||
color = Color.Gray.copy(alpha = 0.5f), | ||
textAlign = TextAlign.Center | ||
) | ||
Text( | ||
text = stringResource(id = R.string.please_insert_one), | ||
textAlign = TextAlign.Center | ||
) | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
@Preview | ||
fun EmptyListPreview() { | ||
FoodExpirationDatesTheme { | ||
Surface { | ||
EmptyList() | ||
} | ||
} | ||
} | ||
|
||
fun getItemsForPreview(context: Context): List<ExpirationDate> { | ||
val items = ArrayList<ExpirationDate>() | ||
val foods = context.resources.getStringArray(R.array.example_foods) | ||
val daysLeft = arrayOf(-1, 0, 1, 3, 7, 10, 30) | ||
for (i in 0 until min(foods.size, daysLeft.size)) { | ||
val cal = Calendar.getInstance() | ||
cal.add(Calendar.DATE, daysLeft[i]) | ||
items.add( | ||
ExpirationDate( | ||
id = 0, | ||
foodName = foods[i], | ||
expirationDate = cal.time.time | ||
) | ||
) | ||
} | ||
return items | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.lorenzovainigli.foodexpirationdates.view.composable.screen | ||
|
||
sealed class Screen(val route: String) { | ||
data object MainScreen : Screen("main_screen") | ||
data object InsertScreen : Screen("insert_screen") | ||
data object AboutScreen : Screen("about_screen") | ||
data object SettingsScreen : Screen("setting_screen") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
package com.lorenzovainigli.foodexpirationdates.view.composable.screen | ||
|
||
import android.os.Build | ||
import androidx.annotation.RequiresApi | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Spacer | ||
import androidx.compose.foundation.layout.fillMaxHeight | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.rememberScrollState | ||
import androidx.compose.foundation.text.ClickableText | ||
import androidx.compose.foundation.verticalScroll | ||
import androidx.compose.material3.Button | ||
import androidx.compose.material3.ExperimentalMaterial3Api | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.OutlinedButton | ||
import androidx.compose.material3.Switch | ||
import androidx.compose.material3.Text | ||
import androidx.compose.material3.rememberTimePickerState | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.collectAsState | ||
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.platform.LocalContext | ||
import androidx.compose.ui.platform.testTag | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.text.AnnotatedString | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.unit.dp | ||
import com.lorenzovainigli.foodexpirationdates.R | ||
import com.lorenzovainigli.foodexpirationdates.model.NotificationManager | ||
import com.lorenzovainigli.foodexpirationdates.model.repository.PreferencesRepository | ||
import com.lorenzovainigli.foodexpirationdates.view.MainActivity | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.AutoResizedText | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.DateFormatDialog | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.NotificationTimeBottomSheet | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.SettingsItem | ||
import java.text.SimpleDateFormat | ||
import java.util.Calendar | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
@OptIn(ExperimentalMaterial3Api::class) | ||
@Composable | ||
fun SettingsScreen( | ||
activity: MainActivity? = null | ||
) { | ||
val context = LocalContext.current | ||
val prefsViewModel = activity?.preferencesViewModel | ||
val darkThemeState = prefsViewModel?.getThemeMode(context)?.collectAsState()?.value | ||
?: PreferencesRepository.Companion.ThemeMode.SYSTEM | ||
val dynamicColorsState = prefsViewModel?.getDynamicColors(context)?.collectAsState()?.value | ||
?: false | ||
val topBarFontState = prefsViewModel?.getTopBarFont(context)?.collectAsState()?.value | ||
?: PreferencesRepository.Companion.TopBarFont.NORMAL.ordinal | ||
|
||
val dateFormat = prefsViewModel?.getDateFormat(context)?.collectAsState()?.value | ||
?: PreferencesRepository.getAvailOtherDateFormats()[0] | ||
var sdf = SimpleDateFormat(dateFormat, context.resources.configuration.locales[0]) | ||
var isDateFormatDialogOpened by remember { | ||
mutableStateOf(false) | ||
} | ||
|
||
val notificationTimeHour = | ||
prefsViewModel?.getNotificationTimeHour(context)?.collectAsState()?.value | ||
?: 11 | ||
val notificationTimeMinute = | ||
prefsViewModel?.getNotificationTimeMinute(context)?.collectAsState()?.value | ||
?: 0 | ||
val timePickerState = | ||
rememberTimePickerState(notificationTimeHour, notificationTimeMinute, true) | ||
var isNotificationTimeBottomSheetOpen by remember { | ||
mutableStateOf(false) | ||
} | ||
prefsViewModel?.let { | ||
DateFormatDialog( | ||
isDialogOpen = isDateFormatDialogOpened, | ||
onDismissRequest = { | ||
sdf = SimpleDateFormat(dateFormat, context.resources.configuration.locales[0]) | ||
isDateFormatDialogOpened = false | ||
}, | ||
onClickDate = it::setDateFormat | ||
) | ||
} | ||
if (isNotificationTimeBottomSheetOpen) { | ||
NotificationTimeBottomSheet( | ||
timePickerState = timePickerState, | ||
onDismissRequest = { | ||
prefsViewModel?.setNotificationTime( | ||
context, | ||
timePickerState.hour, timePickerState.minute | ||
) | ||
NotificationManager.scheduleDailyNotification( | ||
context, | ||
timePickerState.hour, | ||
timePickerState.minute | ||
) | ||
isNotificationTimeBottomSheetOpen = false | ||
} | ||
) | ||
} | ||
Column( | ||
modifier = Modifier | ||
.verticalScroll(rememberScrollState()) | ||
.padding(10.dp), | ||
verticalArrangement = Arrangement.spacedBy(16.dp) | ||
) { | ||
Text( | ||
text = stringResource(R.string.behaviour), | ||
style = MaterialTheme.typography.labelLarge | ||
) | ||
SettingsItem( | ||
label = stringResource(id = R.string.date_format) | ||
) { | ||
ClickableText( | ||
modifier = Modifier.testTag(stringResource(id = R.string.date_format)), | ||
text = AnnotatedString(sdf.format(Calendar.getInstance().time)), | ||
style = MaterialTheme.typography.headlineMedium.copy(color = MaterialTheme.colorScheme.onSurface), | ||
onClick = { | ||
isDateFormatDialogOpened = true | ||
} | ||
) | ||
} | ||
SettingsItem( | ||
label = stringResource(R.string.notification_time) | ||
) { | ||
var text = "" | ||
if (timePickerState.hour < 10) { | ||
text += "0" | ||
} | ||
text = timePickerState.hour.toString() + ":" | ||
if (timePickerState.minute < 10) { | ||
text += "0" | ||
} | ||
text += timePickerState.minute.toString() | ||
ClickableText( | ||
modifier = Modifier.testTag("Notification time"), | ||
text = AnnotatedString(text), | ||
style = MaterialTheme.typography.headlineMedium.copy(color = MaterialTheme.colorScheme.onSurface), | ||
onClick = { | ||
isNotificationTimeBottomSheetOpen = true | ||
} | ||
) | ||
} | ||
Text( | ||
text = stringResource(R.string.appearance), | ||
style = MaterialTheme.typography.labelLarge | ||
) | ||
SettingsItem( | ||
label = stringResource(R.string.theme) | ||
) { | ||
PreferencesRepository.Companion.ThemeMode.entries.forEach { | ||
Spacer( | ||
modifier = Modifier | ||
.fillMaxHeight() | ||
.weight(0.1f) | ||
) | ||
if (it.ordinal == darkThemeState) { | ||
Button(onClick = {}) { | ||
AutoResizedText( | ||
text = context.getString(it.label) | ||
) | ||
} | ||
} | ||
if (it.ordinal != darkThemeState) { | ||
OutlinedButton( | ||
onClick = { | ||
prefsViewModel?.setThemeMode(context, it) | ||
}, | ||
) { | ||
AutoResizedText( | ||
text = context.getString(it.label) | ||
) | ||
} | ||
} | ||
Spacer( | ||
modifier = Modifier | ||
.fillMaxHeight() | ||
.weight(0.1f) | ||
) | ||
} | ||
} | ||
SettingsItem( | ||
label = stringResource(R.string.dynamic_colors) | ||
) { | ||
Spacer( | ||
Modifier | ||
.weight(1f) | ||
.fillMaxHeight() | ||
) | ||
Switch( | ||
checked = dynamicColorsState, | ||
onCheckedChange = { | ||
prefsViewModel?.setDynamicColors(context, it) | ||
} | ||
) | ||
} | ||
SettingsItem( | ||
label = stringResource(R.string.top_bar_font_style) | ||
) { | ||
PreferencesRepository.Companion.TopBarFont.entries.forEach { topBarFont -> | ||
Spacer( | ||
modifier = Modifier | ||
.fillMaxHeight() | ||
.weight(0.1f) | ||
) | ||
if (topBarFont.ordinal != topBarFontState) { | ||
OutlinedButton( | ||
onClick = { | ||
prefsViewModel?.setTopBarFont(context, topBarFont) | ||
}, | ||
) { | ||
AutoResizedText( | ||
text = context.getString(topBarFont.label) | ||
) | ||
} | ||
} | ||
if (topBarFont.ordinal == topBarFontState) { | ||
Button(onClick = {}) { | ||
AutoResizedText( | ||
text = context.getString(topBarFont.label) | ||
) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
@Preview(showBackground = true) | ||
@Composable | ||
fun SettingsScreenPreview() { | ||
SettingsScreen() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,103 @@ | ||
package com.lorenzovainigli.foodexpirationdates.view.preview | ||
|
||
import android.content.res.Configuration | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import android.os.Build | ||
import androidx.annotation.RequiresApi | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.ui.tooling.preview.PreviewDynamicColors | ||
import androidx.compose.ui.tooling.preview.PreviewLightDark | ||
import androidx.navigation.compose.rememberNavController | ||
import com.lorenzovainigli.foodexpirationdates.ui.theme.FoodExpirationDatesTheme | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.screen.InfoScreen | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.screen.MainScreen | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.MyScaffold | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.screen.InsertScreen | ||
import com.lorenzovainigli.foodexpirationdates.view.composable.screen.SettingsScreen | ||
|
||
@Preview(name = "Light mode") | ||
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES) | ||
annotation class DefaultPreviews | ||
class DefaultPreviews { | ||
@RequiresApi(Build.VERSION_CODES.O) | ||
@PreviewLightDark | ||
@Composable | ||
fun MainScreenPreview() { | ||
FoodExpirationDatesTheme( | ||
dynamicColor = false | ||
) { | ||
val navController = rememberNavController() | ||
val showSnackbar = remember { | ||
mutableStateOf(false) | ||
} | ||
MyScaffold(navController = navController, showSnackbar = showSnackbar) { | ||
MainScreen(navController = navController) | ||
} | ||
} | ||
} | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
@PreviewLightDark | ||
@PreviewDynamicColors | ||
@Composable | ||
fun MainScreenDynamicColorsPreview() { | ||
FoodExpirationDatesTheme { | ||
val navController = rememberNavController() | ||
val showSnackbar = remember { | ||
mutableStateOf(false) | ||
} | ||
MyScaffold(navController = navController, showSnackbar = showSnackbar) { | ||
MainScreen(navController = navController) | ||
} | ||
} | ||
} | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
@PreviewLightDark | ||
@Composable | ||
fun InsertScreenPreview() { | ||
FoodExpirationDatesTheme( | ||
dynamicColor = false | ||
) { | ||
val navController = rememberNavController() | ||
val showSnackbar = remember { | ||
mutableStateOf(false) | ||
} | ||
MyScaffold(navController = navController, showSnackbar = showSnackbar) { | ||
InsertScreen(navController = navController) | ||
} | ||
} | ||
} | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
@PreviewLightDark | ||
@Composable | ||
fun SettingsScreenPreview() { | ||
FoodExpirationDatesTheme( | ||
dynamicColor = false | ||
) { | ||
val navController = rememberNavController() | ||
val showSnackbar = remember { | ||
mutableStateOf(false) | ||
} | ||
MyScaffold(navController = navController, showSnackbar = showSnackbar) { | ||
SettingsScreen() | ||
} | ||
} | ||
} | ||
|
||
@RequiresApi(Build.VERSION_CODES.O) | ||
@PreviewLightDark | ||
@Composable | ||
fun InfoScreenPreview() { | ||
FoodExpirationDatesTheme( | ||
dynamicColor = false | ||
) { | ||
val navController = rememberNavController() | ||
val showSnackbar = remember { | ||
mutableStateOf(false) | ||
} | ||
MyScaffold(navController = navController, showSnackbar = showSnackbar) { | ||
InfoScreen() | ||
} | ||
} | ||
} | ||
|
||
} |
This file was deleted.
This file was deleted.