Rework search provider selection UI

This commit is contained in:
Patryk Michalik
2022-06-13 19:02:58 +02:00
parent 5ecc2f6678
commit d00a183033
4 changed files with 195 additions and 151 deletions

View File

@@ -319,4 +319,6 @@
<string name="gesture_home_tap">Home Button Tap</string>
<string name="gesture_back_tap">Back Button Tap</string>
<string name="x_and_y">%s &amp; %s</string>
<string name="app_label">App</string>
<string name="website_label">Website</string>
</resources>

View File

@@ -16,19 +16,30 @@
package app.lawnchair.ui.preferences
import androidx.compose.animation.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavGraphBuilder
import app.lawnchair.preferences.getAdapter
import app.lawnchair.preferences.preferenceManager
import app.lawnchair.preferences2.preferenceManager2
import app.lawnchair.qsb.providers.QsbSearchProviderType
import app.lawnchair.ui.preferences.components.*
import app.lawnchair.qsb.providers.QsbSearchProvider
import app.lawnchair.ui.preferences.components.DividerColumn
import app.lawnchair.ui.preferences.components.ExpandAndShrink
import app.lawnchair.ui.preferences.components.NavigationActionPreference
import app.lawnchair.ui.preferences.components.PreferenceGroup
import app.lawnchair.ui.preferences.components.PreferenceLayout
import app.lawnchair.ui.preferences.components.SliderPreference
import app.lawnchair.ui.preferences.components.SwitchPreference
import com.android.launcher3.R
object DockRoutes {
const val SEARCH_PROVIDER = "searchProvider"
}
fun NavGraphBuilder.dockGraph(route: String) {
preferenceGraph(route, { DockPreferences() })
preferenceGraph(route, { DockPreferences() }) { subRoute ->
searchProviderGraph(subRoute(DockRoutes.SEARCH_PROVIDER))
}
}
@Composable
@@ -55,16 +66,16 @@ fun DockPreferences() {
valueRange = 0F..1F,
showAsPercentage = true,
)
QsbProviderPreference()
val hotseatQsbProviderAdapter =
preferenceManager2().hotseatQsbProvider.getAdapter()
ExpandAndShrink(visible = hotseatQsbProviderAdapter.state.value.type == QsbSearchProviderType.APP_AND_WEBSITE) {
SwitchPreference(
adapter = prefs2.hotseatQsbForceWebsite.getAdapter(),
label = stringResource(R.string.always_open_website_label),
description = stringResource(R.string.always_open_website_description),
)
}
val hotseatQsbProviderAdapter by preferenceManager2().hotseatQsbProvider.getAdapter()
NavigationActionPreference(
label = stringResource(R.string.search_provider),
destination = subRoute(DockRoutes.SEARCH_PROVIDER),
subtitle = stringResource(
id = QsbSearchProvider.values()
.first { it == hotseatQsbProviderAdapter }
.name,
),
)
}
}
}

View File

@@ -0,0 +1,168 @@
package app.lawnchair.ui.preferences
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavGraphBuilder
import app.lawnchair.preferences.getAdapter
import app.lawnchair.preferences2.preferenceManager2
import app.lawnchair.qsb.providers.QsbSearchProvider
import app.lawnchair.qsb.providers.QsbSearchProviderType
import app.lawnchair.ui.preferences.components.ClickableIcon
import app.lawnchair.ui.preferences.components.DividerColumn
import app.lawnchair.ui.preferences.components.ExpandAndShrink
import app.lawnchair.ui.preferences.components.PreferenceDivider
import app.lawnchair.ui.preferences.components.PreferenceGroup
import app.lawnchair.ui.preferences.components.PreferenceLayout
import app.lawnchair.ui.preferences.components.PreferenceTemplate
import com.android.launcher3.R
fun NavGraphBuilder.searchProviderGraph(route: String) {
preferenceGraph(route, { SearchProviderPreferences() })
}
@Composable
fun SearchProviderPreferences() {
val context = LocalContext.current
val adapter = preferenceManager2().hotseatQsbProvider.getAdapter()
val forceWebsiteAdapter = preferenceManager2().hotseatQsbForceWebsite.getAdapter()
PreferenceLayout(label = stringResource(R.string.search_provider)) {
PreferenceGroup {
QsbSearchProvider.values().forEach { qsbSearchProvider ->
val appInstalled = qsbSearchProvider.isDownloaded(context)
val selected = adapter.state.value == qsbSearchProvider
val hasAppAndWebsite = qsbSearchProvider.type == QsbSearchProviderType.APP_AND_WEBSITE
val showDownloadButton = qsbSearchProvider.type == QsbSearchProviderType.APP && !appInstalled
Column {
ListItem(
title = stringResource(id = qsbSearchProvider.name),
showDownloadButton = showDownloadButton,
enabled = qsbSearchProvider.type != QsbSearchProviderType.APP || appInstalled,
selected = selected,
onClick = { adapter.onChange(newValue = qsbSearchProvider) },
onDownloadClick = { qsbSearchProvider.launchOnAppMarket(context = context) },
description = if (showDownloadButton) {
stringResource(id = R.string.qsb_search_provider_app_required)
} else null,
)
ExpandAndShrink(visible = selected && hasAppAndWebsite) {
Options(
appEnabled = appInstalled,
appSelected = !forceWebsiteAdapter.state.value && appInstalled,
onAppClick = { forceWebsiteAdapter.onChange(newValue = false) },
onAppDownloadClick = { qsbSearchProvider.launchOnAppMarket(context = context) },
onWebsiteClick = { forceWebsiteAdapter.onChange(newValue = true) },
showAppDownloadButton = !appInstalled,
)
}
}
}
}
}
}
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun ListItem(
title: String,
description: String?,
showDownloadButton: Boolean,
enabled: Boolean,
selected: Boolean,
onClick: () -> Unit,
onDownloadClick: () -> Unit,
) {
Column {
PreferenceTemplate(
title = { Text(text = title) },
verticalPadding = if (showDownloadButton) 12.dp else 16.dp,
horizontalPadding = 0.dp,
enabled = enabled,
modifier = Modifier.clickable(enabled = enabled, onClick = onClick),
description = { if (description != null) Text(text = description) },
startWidget = {
RadioButton(
selected = selected,
onClick = null,
enabled = enabled,
modifier = Modifier.padding(start = 16.dp)
)
},
endWidget = {
if (showDownloadButton) {
ClickableIcon(
painter = painterResource(id = R.drawable.ic_download),
onClick = onDownloadClick,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(end = 4.dp),
)
}
},
)
}
}
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun Options(
appEnabled: Boolean,
appSelected: Boolean,
showAppDownloadButton: Boolean,
onAppClick: () -> Unit,
onAppDownloadClick: () -> Unit,
onWebsiteClick: () -> Unit,
) {
PreferenceDivider(startIndent = 40.dp)
DividerColumn(startIndent = 40.dp) {
PreferenceTemplate(
title = { Text(stringResource(id = R.string.app_label)) },
enabled = appEnabled,
verticalPadding = if (!appEnabled) 4.dp else 16.dp,
horizontalPadding = 0.dp,
modifier = Modifier.clickable(
enabled = appEnabled,
onClick = onAppClick,
),
startWidget = {
RadioButton(
selected = appSelected,
onClick = null,
enabled = appEnabled,
modifier = Modifier.padding(start = 56.dp),
)
},
endWidget = {
if (showAppDownloadButton) {
ClickableIcon(
painter = painterResource(R.drawable.ic_download),
onClick = onAppDownloadClick,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(end = 4.dp),
)
}
},
)
PreferenceTemplate(
title = { Text(text = stringResource(id = R.string.website_label)) },
modifier = Modifier.clickable(onClick = onWebsiteClick),
horizontalPadding = 0.dp,
startWidget = {
RadioButton(
selected = !appSelected,
onClick = null,
modifier = Modifier.padding(start = 56.dp),
)
},
)
}
}

View File

@@ -1,137 +0,0 @@
package app.lawnchair.ui.preferences.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.RadioButton
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import app.lawnchair.preferences.getAdapter
import app.lawnchair.preferences2.preferenceManager2
import app.lawnchair.qsb.providers.QsbSearchProvider
import app.lawnchair.qsb.providers.QsbSearchProviderType
import app.lawnchair.ui.AlertBottomSheetContent
import app.lawnchair.ui.util.addIf
import app.lawnchair.ui.util.bottomSheetHandler
import com.android.launcher3.R
@Composable
fun QsbProviderPreference() {
val adapter = preferenceManager2().hotseatQsbProvider.getAdapter()
val context = LocalContext.current
val entries = remember {
QsbSearchProvider.values().map {
// Enabled is true if provider type is not app or it is app & the app is installed
val enabled = it.type != QsbSearchProviderType.APP || it.isDownloaded(context)
ListPreferenceEntry(it, enabled) { stringResource(id = it.name) }
}
}
QsbProviderListPreference(
entries = entries,
value = adapter.state.value,
onValueChange = adapter::onChange,
label = stringResource(R.string.search_provider),
)
}
@Composable
fun QsbProviderListPreference(
entries: List<ListPreferenceEntry<QsbSearchProvider>>,
value: QsbSearchProvider,
onValueChange: (QsbSearchProvider) -> Unit,
label: String,
enabled: Boolean = true,
description: String? = null,
) {
val context = LocalContext.current
val bottomSheetHandler = bottomSheetHandler
val currentDescription = description ?: entries
.firstOrNull { it.value == value }
?.label?.invoke()
val paddingVertical = 16.dp
val paddingHorizontal = 16.dp
PreferenceTemplate(
title = { Text(text = label) },
description = { currentDescription?.let { Text(text = it) } },
enabled = enabled,
modifier = Modifier.clickable(enabled) {
bottomSheetHandler.show {
AlertBottomSheetContent(
title = { Text(label) },
buttons = {
OutlinedButton(onClick = { bottomSheetHandler.hide() }) {
Text(text = stringResource(id = android.R.string.cancel))
}
}
) {
LazyColumn {
itemsIndexed(entries) { index, item ->
if (index > 0) {
PreferenceDivider(startIndent = 40.dp)
}
PreferenceTemplate(
enabled = item.enabled,
title = { Text(item.label()) },
description = {
if (item.value.type == QsbSearchProviderType.APP) {
Text(stringResource(id = R.string.qsb_search_provider_app_required))
}
},
applyPaddings = false,
modifier = Modifier
.clickable(item.enabled) {
onValueChange(item.value)
bottomSheetHandler.hide()
},
startWidget = {
RadioButton(
modifier = Modifier.padding(
horizontal = paddingHorizontal,
vertical = paddingVertical
),
selected = item.value == value,
onClick = null,
enabled = item.enabled,
)
},
endWidget = {
if (item.value.type.downloadable) {
val downloaded = item.value.isDownloaded(context)
Icon(
modifier = Modifier
.addIf(!downloaded) {
clickable {
item.value.launchOnAppMarket(context = context)
bottomSheetHandler.hide()
}
}
.padding(
horizontal = paddingHorizontal,
vertical = paddingVertical
),
painter = painterResource(id = if (downloaded) R.drawable.ic_tick else R.drawable.ic_download),
contentDescription = null,
)
}
}
)
}
}
}
}
}
)
}