diff --git a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/MainActivity.kt b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/MainActivity.kt index 5ff101ae..fe652c64 100644 --- a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/MainActivity.kt +++ b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/MainActivity.kt @@ -70,6 +70,8 @@ class MainActivity : ComponentActivity() { darkScrim = android.graphics.Color.TRANSPARENT, detectDarkMode = { _ -> isInDarkTheme } ) + val searchQuery = remember { mutableStateOf("") } + enableEdgeToEdge( statusBarStyle = systemBarStyle, navigationBarStyle = systemBarStyle @@ -90,12 +92,14 @@ class MainActivity : ComponentActivity() { MyScaffold( activity = this, navController = navController, - showSnackbar = showSnackbar + showSnackbar = showSnackbar, + searchQuery = searchQuery ) { Navigation( activity = this, navController = navController, - showSnackbar = showSnackbar + showSnackbar = showSnackbar, + searchQuery = searchQuery ) } } diff --git a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/Navigation.kt b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/Navigation.kt index 697eea38..977199b3 100644 --- a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/Navigation.kt +++ b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/Navigation.kt @@ -23,7 +23,8 @@ fun Navigation( activity: MainActivity? = null, navController: NavHostController, startDestination: String = Screen.MainScreen.route, - showSnackbar: MutableState + showSnackbar: MutableState, + searchQuery: MutableState ) { NavHost( modifier = Modifier.fillMaxSize(), @@ -34,7 +35,8 @@ fun Navigation( MainScreen( activity = activity, navController = navController, - showSnackbar = showSnackbar + showSnackbar = showSnackbar, + searchQuery ) } composable( diff --git a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/MainScreenMenu.kt b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/MainScreenMenu.kt index 61957f47..804f107b 100644 --- a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/MainScreenMenu.kt +++ b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/MainScreenMenu.kt @@ -3,22 +3,36 @@ package com.lorenzovainigli.foodexpirationdates.view.composable import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.text.BasicTextField import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.filled.Search import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.SearchBar +import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState 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.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.sp import com.lorenzovainigli.foodexpirationdates.R import com.lorenzovainigli.foodexpirationdates.util.FirebaseUtils import com.lorenzovainigli.foodexpirationdates.util.OperationResult @@ -30,9 +44,11 @@ data class MenuItem( val onClick: () -> Unit = {} ) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun MainScreenMenu( activity: MainActivity? = null, + searchQuery: MutableState ) { val viewModel = activity?.viewModel val exportTaskSuccess = viewModel?.exportTaskSuccess?.value @@ -53,92 +69,127 @@ fun MainScreenMenu( ?: OperationResult(state = OperationResult.State.NOT_PERFORMED) } } - IconButton( - onClick = { isExpanded = true } - ) { - Icon( - imageVector = Icons.Default.MoreVert, - contentDescription = stringResource(id = R.string.back), - tint = MaterialTheme.colorScheme.primary - ) - } - DropdownMenu( - expanded = isExpanded, - onDismissRequest = { - isExpanded = false - } - ) { - arrayOf( - MenuItem( - iconId = R.drawable.ic_export, - label = stringResource(R.string.export_data), - onClick = { - if (viewModel != null) { - viewModel.exportData(context) - } else { - FirebaseUtils.logToCrashlytics("Cannot export data, viewModel is null") - } - isExpanded = false - } - ), - MenuItem( - iconId = R.drawable.ic_import, - label = stringResource(R.string.import_data), - onClick = { - if (filePickerLauncher != null){ - filePickerLauncher.launch(arrayOf("*/*")) - } else { - FirebaseUtils.logToCrashlytics("Cannot import data, filePickerLauncher is null") - } + + +// TopAppBar( +// title = { +// BasicTextField( +// value = searchQuery, +// onValueChange = { searchQuery = it }, +// textStyle = TextStyle(color = Color.White, fontSize = 18.sp), +// modifier = Modifier.fillMaxWidth() +// ) +// }, +// backgroundColor = MaterialTheme.colorScheme.primary +// ) + + + var isSearchBarExpanded by remember { mutableStateOf(false) } + + SearchBar( + query = searchQuery.value, + onQueryChange = { searchQuery.value = it }, + onSearch = { /* Trigger search logic if needed */ }, + active = isSearchBarExpanded, + onActiveChange = { isSearchBarExpanded = it }, + placeholder = { Text("Search...") }, + leadingIcon = { + Icon(Icons.Default.Search, contentDescription = null) + }, + trailingIcon = { + IconButton( + onClick = { isExpanded = true } + ) { + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = stringResource(id = R.string.back), + tint = MaterialTheme.colorScheme.primary + ) + } + DropdownMenu( + expanded = isExpanded, + onDismissRequest = { isExpanded = false } - ) - ).forEach { - DropdownMenuItem( - leadingIcon = { - Icon( - painter = painterResource(id = it.iconId), - contentDescription = stringResource(id = R.string.back), - tint = MaterialTheme.colorScheme.primary + ) { + arrayOf( + MenuItem( + iconId = R.drawable.ic_export, + label = stringResource(R.string.export_data), + onClick = { + if (viewModel != null) { + viewModel.exportData(context) + } else { + FirebaseUtils.logToCrashlytics("Cannot export data, viewModel is null") + } + isExpanded = false + } + ), + MenuItem( + iconId = R.drawable.ic_import, + label = stringResource(R.string.import_data), + onClick = { + if (filePickerLauncher != null){ + filePickerLauncher.launch(arrayOf("*/*")) + } else { + FirebaseUtils.logToCrashlytics("Cannot import data, filePickerLauncher is null") + } + isExpanded = false + } ) - }, - text = { - Text(it.label) - }, - onClick = it.onClick - ) - } - } - if (notifyExportTaskDone == true) { - if (exportTaskSuccess == true) { + ).forEach { + DropdownMenuItem( + leadingIcon = { + Icon( + painter = painterResource(id = it.iconId), + contentDescription = stringResource(id = R.string.back), + tint = MaterialTheme.colorScheme.primary + ) + }, + text = { + Text(it.label) + }, + onClick = it.onClick + ) + } + } + if (notifyExportTaskDone == true) { + if (exportTaskSuccess == true) { // SuccessDialog( // onDismiss = { // viewModel.resetNotifyExportTaskDone() // }, // message = stringResource(id = R.string.data_export_success) // ) - } else { - ErrorDialog( - onDismiss = { - viewModel.resetNotifyExportTaskDone() - }, - message = stringResource(id = R.string.data_export_error) - ) - } - } - when (operationResult.value.state){ - OperationResult.State.FAILURE -> ErrorDialog( - onDismiss = { - operationResult.value = OperationResult() - }, - message = operationResult.value.message - ) - OperationResult.State.SUCCESS -> SuccessDialog( - onDismiss = { - operationResult.value = OperationResult() - }, - message = operationResult.value.message - ) - OperationResult.State.NOT_PERFORMED -> {} + } else { + ErrorDialog( + onDismiss = { + viewModel.resetNotifyExportTaskDone() + }, + message = stringResource(id = R.string.data_export_error) + ) + } + } + when (operationResult.value.state){ + OperationResult.State.FAILURE -> ErrorDialog( + onDismiss = { + operationResult.value = OperationResult() + }, + message = operationResult.value.message + ) + OperationResult.State.SUCCESS -> SuccessDialog( + onDismiss = { + operationResult.value = OperationResult() + }, + message = operationResult.value.message + ) + OperationResult.State.NOT_PERFORMED -> {} + } + }, + colors = SearchBarDefaults.colors(), + modifier = Modifier.fillMaxWidth() + ) { + } + } diff --git a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/MyScaffold.kt b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/MyScaffold.kt index 24640962..11344a0e 100644 --- a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/MyScaffold.kt +++ b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/MyScaffold.kt @@ -1,5 +1,6 @@ package com.lorenzovainigli.foodexpirationdates.view.composable +import android.annotation.SuppressLint import android.os.Build import android.util.Log import androidx.annotation.RequiresApi @@ -8,16 +9,20 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.Chat import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField 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.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextField import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState @@ -57,13 +62,15 @@ fun MyScaffold( navController: NavHostController, navDestination: String? = null, showSnackbar: MutableState, - content: @Composable () -> Unit + searchQuery : MutableState, + 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 { @@ -118,7 +125,7 @@ fun MyScaffold( }, actions = { if (destination?.contains(Screen.MainScreen.route) == true) { - MainScreenMenu(activity) + MainScreenMenu(activity, searchQuery) } }, navigationIcon = { @@ -154,6 +161,7 @@ fun MyScaffold( } } +@SuppressLint("UnrememberedMutableState") @RequiresApi(Build.VERSION_CODES.O) @PreviewLightDark @PreviewScreenSizes @@ -167,10 +175,13 @@ fun MyScaffoldPreview() { } MyScaffold( navController = navController, - showSnackbar = showSnackbar + showSnackbar = showSnackbar, + searchQuery = mutableStateOf("") ) { + MainScreen( - navController = rememberNavController() + navController = rememberNavController(), + searchQuery = mutableStateOf("") ) } } diff --git a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/screen/InfoScreen.kt b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/screen/InfoScreen.kt index 57462b5d..c6d8fd81 100644 --- a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/screen/InfoScreen.kt +++ b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/screen/InfoScreen.kt @@ -38,7 +38,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.lorenzovainigli.foodexpirationdates.BuildConfig +import com.google.android.datatransport.BuildConfig import com.lorenzovainigli.foodexpirationdates.DEVELOPER_EMAIL import com.lorenzovainigli.foodexpirationdates.GITHUB_URL import com.lorenzovainigli.foodexpirationdates.PLAY_STORE_URL diff --git a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/screen/MainScreen.kt b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/screen/MainScreen.kt index bdcca9fd..ba7de333 100644 --- a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/screen/MainScreen.kt +++ b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/composable/screen/MainScreen.kt @@ -1,5 +1,6 @@ package com.lorenzovainigli.foodexpirationdates.view.composable.screen +import android.annotation.SuppressLint import android.content.Context import android.os.Build import androidx.annotation.RequiresApi @@ -22,6 +23,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -45,7 +47,8 @@ import kotlin.math.min fun MainScreen( activity: MainActivity? = null, navController: NavHostController, - showSnackbar: MutableState? = null + showSnackbar: MutableState? = null, + searchQuery: MutableState ) { Box( modifier = Modifier @@ -54,16 +57,31 @@ fun MainScreen( ) { val itemsState = activity?.viewModel?.getDates()?.collectAsState(emptyList()) val items = itemsState?.value ?: getItemsForPreview(LocalContext.current) - if (items.isNotEmpty()) { + + val filteredItems = items.filter { + it.foodName.contains(searchQuery.value, ignoreCase = true) + } + if (filteredItems.isNotEmpty()) { ListOfItems( activity = activity, - items = items, - showSnackbar = showSnackbar, - navController = navController + items = filteredItems, + navController = navController, + showSnackbar = showSnackbar ) } else { EmptyList() } + +// if (items.isNotEmpty()) { +// ListOfItems( +// activity = activity, +// items = items, +// showSnackbar = showSnackbar, +// navController = navController +// ) +// } else { +// EmptyList() +// } FloatingActionButton( modifier = Modifier .align(Alignment.BottomEnd) @@ -82,6 +100,7 @@ fun MainScreen( } } +@SuppressLint("UnrememberedMutableState") @RequiresApi(Build.VERSION_CODES.O) @Preview @Composable @@ -89,7 +108,8 @@ fun MainScreenPreview() { FoodExpirationDatesTheme { Surface { MainScreen( - navController = rememberNavController() + navController = rememberNavController(), + searchQuery = mutableStateOf("") ) } } diff --git a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/preview/DefaultPreviews.kt b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/preview/DefaultPreviews.kt index 65680cc8..1885fbea 100644 --- a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/preview/DefaultPreviews.kt +++ b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/preview/DefaultPreviews.kt @@ -1,5 +1,6 @@ package com.lorenzovainigli.foodexpirationdates.view.preview +import android.annotation.SuppressLint import android.os.Build import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable @@ -18,6 +19,7 @@ import com.lorenzovainigli.foodexpirationdates.view.composable.screen.Screen import com.lorenzovainigli.foodexpirationdates.view.composable.screen.SettingsScreen class DefaultPreviews { + @SuppressLint("UnrememberedMutableState") @RequiresApi(Build.VERSION_CODES.O) @PreviewLightDark @Composable @@ -29,12 +31,13 @@ class DefaultPreviews { val showSnackbar = remember { mutableStateOf(false) } - MyScaffold(navController = navController, showSnackbar = showSnackbar) { - Navigation(navController = navController, showSnackbar = showSnackbar, startDestination = Screen.AboutScreen.route) + MyScaffold(navController = navController, showSnackbar = showSnackbar, searchQuery = mutableStateOf("")) { + Navigation(navController = navController, showSnackbar = showSnackbar, startDestination = Screen.AboutScreen.route, searchQuery = mutableStateOf("")) } } } + @SuppressLint("UnrememberedMutableState") @RequiresApi(Build.VERSION_CODES.O) @PreviewLightDark @PreviewDynamicColors @@ -45,12 +48,13 @@ class DefaultPreviews { val showSnackbar = remember { mutableStateOf(false) } - MyScaffold(navController = navController, showSnackbar = showSnackbar) { - MainScreen(navController = navController) + MyScaffold(navController = navController, showSnackbar = showSnackbar, searchQuery = mutableStateOf("")) { + MainScreen(navController = navController, searchQuery = mutableStateOf("")) } } } + @SuppressLint("UnrememberedMutableState") @RequiresApi(Build.VERSION_CODES.O) @PreviewLightDark @Composable @@ -62,12 +66,13 @@ class DefaultPreviews { val showSnackbar = remember { mutableStateOf(false) } - MyScaffold(navController = navController, showSnackbar = showSnackbar) { + MyScaffold(navController = navController, showSnackbar = showSnackbar, searchQuery = mutableStateOf("")) { InsertScreen(navController = navController) } } } + @SuppressLint("UnrememberedMutableState") @RequiresApi(Build.VERSION_CODES.O) @PreviewLightDark @Composable @@ -79,12 +84,13 @@ class DefaultPreviews { val showSnackbar = remember { mutableStateOf(false) } - MyScaffold(navController = navController, showSnackbar = showSnackbar) { + MyScaffold(navController = navController, showSnackbar = showSnackbar, searchQuery = mutableStateOf("")) { SettingsScreen() } } } + @SuppressLint("UnrememberedMutableState") @RequiresApi(Build.VERSION_CODES.O) @PreviewLightDark @Composable @@ -96,7 +102,7 @@ class DefaultPreviews { val showSnackbar = remember { mutableStateOf(false) } - MyScaffold(navController = navController, showSnackbar = showSnackbar) { + MyScaffold(navController = navController, showSnackbar = showSnackbar, searchQuery = mutableStateOf("")) { InfoScreen() } } diff --git a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/preview/PlayStoreScreenshot.kt b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/preview/PlayStoreScreenshot.kt index 60788d81..5e14459a 100644 --- a/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/preview/PlayStoreScreenshot.kt +++ b/app/src/main/java/com/lorenzovainigli/foodexpirationdates/view/preview/PlayStoreScreenshot.kt @@ -1,5 +1,6 @@ package com.lorenzovainigli.foodexpirationdates.view.preview +import android.annotation.SuppressLint import android.os.Build import androidx.annotation.RequiresApi import androidx.compose.foundation.Image @@ -74,6 +75,7 @@ fun PlayStoreScreenshot( } } +@SuppressLint("UnrememberedMutableState") @RequiresApi(Build.VERSION_CODES.O) @Preview(showBackground = true) @Composable @@ -86,11 +88,12 @@ fun PlayStoreScreenshotPreview() { val showSnackbar = remember { mutableStateOf(false) } - MyScaffold(navController = navController, showSnackbar = showSnackbar) { + MyScaffold(navController = navController, showSnackbar = showSnackbar, searchQuery = mutableStateOf("")) { Navigation( navController = navController , showSnackbar = showSnackbar, - startDestination = Screen.AboutScreen.route + startDestination = Screen.AboutScreen.route, + searchQuery = mutableStateOf("") ) } }