diff --git a/i18n/src/main/res/values-de/strings.xml b/i18n/src/main/res/values-de/strings.xml index d059f51d..b583ade8 100644 --- a/i18n/src/main/res/values-de/strings.xml +++ b/i18n/src/main/res/values-de/strings.xml @@ -459,6 +459,8 @@ Google Drive Sie sind im Moment nicht angemeldet %1$ss Dateien auf Google Drive durchsuchen + Stil + Erscheinungsbild der Suchleiste anpassen Wikipedia-URL diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 2aedad7a..1efe0556 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -502,6 +502,9 @@ Restrict to music apps Ignore media sessions of apps that are not music apps + Style + Customize search bar appearance + Wikipedia URL %1$s is playing media diff --git a/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt b/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt index be3e5834..d0e3060f 100644 --- a/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt +++ b/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt @@ -101,5 +101,10 @@ fun createFactorySettings(context: Context): Settings { .setColumnCount(context.resources.getInteger(R.integer.config_columnCount)) .build() ) + .setSearchBar( + Settings.SearchBarSettings.newBuilder() + .setSearchBarStyle(Settings.SearchBarSettings.SearchBarStyle.Transparent) + .build() + ) .build() } \ No newline at end of file diff --git a/preferences/src/main/proto/settings.proto b/preferences/src/main/proto/settings.proto index a0f904c2..4f17cd3a 100644 --- a/preferences/src/main/proto/settings.proto +++ b/preferences/src/main/proto/settings.proto @@ -123,4 +123,14 @@ message Settings { } GridSettings grid = 19; + message SearchBarSettings { + enum SearchBarStyle { + Transparent = 0; + Solid = 1; + Hidden = 2; + } + SearchBarStyle search_bar_style = 1; + } + SearchBarSettings search_bar = 20; + } \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchBar.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchBar.kt index 90e6885a..89c29369 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchBar.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchBar.kt @@ -30,6 +30,7 @@ import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.focus.onFocusEvent @@ -41,11 +42,16 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.rememberImagePainter import de.mm20.launcher2.ktx.tryStartActivity +import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.Settings.SearchBarSettings import de.mm20.launcher2.search.data.Websearch import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.LauncherCard import de.mm20.launcher2.ui.launcher.LauncherActivityVM import de.mm20.launcher2.ui.settings.SettingsActivity +import kotlinx.coroutines.flow.map +import org.koin.androidx.compose.inject import java.io.File @Composable @@ -56,6 +62,11 @@ fun SearchBar( val searchViewModel: SearchVM = viewModel() val activityViewModel: LauncherActivityVM = viewModel() + val dataStore: LauncherDataStore by inject() + + val style by remember { dataStore.data.map { it.searchBar.searchBarStyle } } + .collectAsState(SearchBarSettings.SearchBarStyle.Hidden) + val context = LocalContext.current val query by searchViewModel.searchQuery.observeAsState("") @@ -69,6 +80,7 @@ fun SearchBar( onValueChange = { searchViewModel.search(it) }, + style = style, overflowMenu = { show, onDismissRequest -> DropdownMenu(expanded = show, onDismissRequest = onDismissRequest) { DropdownMenuItem(onClick = { @@ -125,6 +137,7 @@ fun SearchBar( websearches: List, overflowMenu: @Composable (show: Boolean, onDismissRequest: () -> Unit) -> Unit = { _, _ -> }, value: String, + style: SearchBarSettings.SearchBarStyle, onValueChange: (String) -> Unit, onFocus: () -> Unit = {} ) { @@ -148,10 +161,10 @@ fun SearchBar( } } ) { - when (it) { - SearchBarLevel.Resting -> 0.dp - SearchBarLevel.Active -> 2.dp - SearchBarLevel.Raised -> 8.dp + when { + it == SearchBarLevel.Resting && style != SearchBarSettings.SearchBarStyle.Solid -> 0.dp + it == SearchBarLevel.Raised -> 8.dp + else -> 2.dp } } @@ -166,7 +179,11 @@ fun SearchBar( else -> tween(durationMillis = 500) } }) { - if (it == SearchBarLevel.Resting) 0f else 1f + when { + style != SearchBarSettings.SearchBarStyle.Transparent -> 1f + it == SearchBarLevel.Resting -> 0f + else -> 1f + } } val contentColor by transition.animateColor(label = "textColor", @@ -180,7 +197,16 @@ fun SearchBar( else -> tween(durationMillis = 500) } }) { - if (it == SearchBarLevel.Resting) Color.White else LocalContentColor.current + when { + style != SearchBarSettings.SearchBarStyle.Transparent -> LocalContentColor.current + it == SearchBarLevel.Resting -> Color.White + else -> LocalContentColor.current + } + } + + val opacity by transition.animateFloat(label = "opacity") { + if (style == SearchBarSettings.SearchBarStyle.Hidden && it == SearchBarLevel.Resting) 0f + else 1f } val rightIcon = AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_menu_clear) @@ -189,6 +215,7 @@ fun SearchBar( modifier = Modifier .fillMaxWidth() .wrapContentHeight() + .alpha(opacity) .padding(8.dp), backgroundOpacity = backgroundOpacity, elevation = elevation diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt index e28ee44e..5820fc8d 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt @@ -1,15 +1,33 @@ package de.mm20.launcher2.ui.settings.appearance import androidx.appcompat.app.AppCompatActivity +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.google.accompanist.pager.ExperimentalPagerApi +import com.google.accompanist.pager.HorizontalPager +import com.google.accompanist.pager.HorizontalPagerIndicator +import com.google.accompanist.pager.rememberPagerState import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme +import de.mm20.launcher2.preferences.Settings.SearchBarSettings import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.preferences.* +import de.mm20.launcher2.ui.launcher.search.SearchBar +import de.mm20.launcher2.ui.launcher.search.SearchBarLevel +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive @Composable fun AppearanceSettingsScreen() { @@ -78,6 +96,96 @@ fun AppearanceSettingsScreen() { } ) } + PreferenceCategory(stringResource(R.string.preference_category_searchbar)) { + val searchBarStyle by viewModel.searchBarStyle.observeAsState() + SearchBarStylePreference( + title = stringResource(R.string.preference_search_bar_style), + summary = stringResource(R.string.preference_search_bar_style_summary), + value = searchBarStyle, + onValueChanged = { + viewModel.setSearchBarStyle(it) + } + ) + } } } +} + +@OptIn(ExperimentalPagerApi::class) +@Composable +fun SearchBarStylePreference( + title: String, + summary: String? = null, + value: SearchBarSettings.SearchBarStyle?, + onValueChanged: (SearchBarSettings.SearchBarStyle) -> Unit +) { + var showDialog by remember { mutableStateOf(false) } + Preference(title = title, summary = summary, onClick = { showDialog = true }) + if (showDialog && value != null) { + val styles = remember { + SearchBarSettings.SearchBarStyle.values().filter { it != SearchBarSettings.SearchBarStyle.UNRECOGNIZED } + } + val pagerState = rememberPagerState(styles.indexOf(value)) + + var level by remember { mutableStateOf(SearchBarLevel.Resting) } + var previewSearchValue by remember { mutableStateOf("") } + LaunchedEffect(null) { + while(isActive) { + delay(2000) + level = SearchBarLevel.Active + delay(1000) + previewSearchValue = "A" + delay(100) + previewSearchValue = "AB" + delay(100) + previewSearchValue = "ABC" + delay(800) + level = SearchBarLevel.Raised + delay(2000) + level = SearchBarLevel.Resting + previewSearchValue = "" + } + } + + AlertDialog( + onDismissRequest = { showDialog = false }, + confirmButton = { + TextButton(onClick = { + showDialog = false + onValueChanged(styles[pagerState.currentPage]) + }) { + Text( + text = stringResource(android.R.string.ok), + style = MaterialTheme.typography.labelLarge + ) + } + }, + dismissButton = { + TextButton(onClick = { showDialog = false }) { + Text( + text = stringResource(android.R.string.cancel), + style = MaterialTheme.typography.labelLarge + ) + } + }, + + text = { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + HorizontalPager( + count = styles.size, + state = pagerState, + modifier = Modifier + .height(150.dp) + .padding(bottom = 16.dp) + .background(MaterialTheme.colorScheme.secondary) + ) { + SearchBar(level = level, style = styles[it], websearches = emptyList(), value = previewSearchValue, onValueChange = {}) + } + HorizontalPagerIndicator(pagerState = pagerState) + } + } + ) + } } \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt index 0fa2fc37..03f3dfdb 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme +import de.mm20.launcher2.preferences.Settings.SearchBarSettings import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent @@ -63,4 +64,17 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent { fun openWallpaperChooser(context: AppCompatActivity) { context.startActivity(Intent.createChooser(Intent(Intent.ACTION_SET_WALLPAPER), null)) } + + val searchBarStyle = dataStore.data.map { it.searchBar.searchBarStyle }.asLiveData() + fun setSearchBarStyle(searchBarStyle: SearchBarSettings.SearchBarStyle) { + viewModelScope.launch { + dataStore.updateData { + it.toBuilder() + .setSearchBar(it.searchBar.toBuilder() + .setSearchBarStyle(searchBarStyle) + ) + .build() + } + } + } } \ No newline at end of file