Implement basic icon picker

This commit is contained in:
MM20
2022-07-26 17:20:34 +02:00
parent 70bef9ac14
commit 32c82d74c3
8 changed files with 244 additions and 11 deletions
@@ -28,6 +28,7 @@ import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.*
import de.mm20.launcher2.ui.ktx.toDp
import de.mm20.launcher2.ui.ktx.toPixels
import de.mm20.launcher2.ui.launcher.search.common.customattrs.CustomizeSearchableSheet
import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled
import de.mm20.launcher2.ui.locals.LocalGridIconSize
import de.mm20.launcher2.ui.locals.LocalSnackbarHostState
@@ -48,6 +49,8 @@ fun AppItem(
val lifecycleOwner = LocalLifecycleOwner.current
val snackbarHostState = LocalSnackbarHostState.current
var edit by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
Column(
modifier = modifier
@@ -217,6 +220,12 @@ fun AppItem(
)
)
toolbarActions.add(DefaultToolbarAction(
label = stringResource(R.string.menu_customize),
icon = Icons.Rounded.Edit,
action = { edit = true }
))
val storeDetails = remember(app) { app.getStoreDetails(context) }
val shareAction = if (storeDetails == null) {
DefaultToolbarAction(
@@ -308,6 +317,13 @@ fun AppItem(
rightActions = toolbarActions
)
}
if (edit) {
CustomizeSearchableSheet(
searchable = app,
onDismiss = { edit = false }
)
}
}
@OptIn(ExperimentalAnimationApi::class)
@@ -0,0 +1,130 @@
package de.mm20.launcher2.ui.launcher.search.common.customattrs
import android.graphics.drawable.InsetDrawable
import androidx.appcompat.content.res.AppCompatResources
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.search.data.Searchable
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.BottomSheetDialog
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
import de.mm20.launcher2.ui.ktx.toPixels
import de.mm20.launcher2.ui.locals.LocalGridColumns
@Composable
fun CustomizeSearchableSheet(
searchable: Searchable,
onDismiss: () -> Unit,
) {
val viewModel: CustomizeSearchableSheetVM =
remember(searchable) { CustomizeSearchableSheetVM(searchable) }
val context = LocalContext.current
val pickIcon by viewModel.isIconPickerOpen.observeAsState(false)
BottomSheetDialog(
onDismissRequest = onDismiss,
title = {
Text(stringResource(if (pickIcon) R.string.icon_picker_title else R.string.menu_customize))
},
confirmButton = {
if (pickIcon) {
OutlinedButton(onClick = { viewModel.closeIconPicker() }) {
Text(stringResource(id = android.R.string.cancel))
}
} else {
OutlinedButton(onClick = onDismiss) {
Text(stringResource(id = R.string.close))
}
}
}
) {
if (!pickIcon) {
Column(
modifier = Modifier
.padding(top = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
val iconSize = 64.dp
val iconSizePx = iconSize.toPixels()
val icon by remember { viewModel.getIcon(iconSizePx.toInt()) }.collectAsState(null)
val primaryColor = MaterialTheme.colorScheme.onSecondary
val badgeDrawable = remember {
InsetDrawable(
AppCompatResources.getDrawable(context, R.drawable.ic_edit),
8
).also {
it.setTint(primaryColor.toArgb())
}
}
ShapedLauncherIcon(
size = iconSize,
icon = icon,
badge = Badge(
icon = badgeDrawable
),
onClick = {
viewModel.openIconPicker()
}
)
OutlinedTextField(
modifier =
Modifier
.fillMaxWidth()
.padding(top = 24.dp),
value = "",
onValueChange = {},
placeholder = {
Text(searchable.label)
},
)
}
} else {
val iconSize = 48.dp
val iconSizePx = iconSize.toPixels()
val suggestions by
remember { viewModel.getIconSuggestions(iconSizePx.toInt()) }
.observeAsState(emptyList())
LazyVerticalGrid(columns = GridCells.Fixed(LocalGridColumns.current)) {
items(suggestions) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.padding(vertical = 8.dp)
) {
ShapedLauncherIcon(
size = iconSize,
icon = it.icon,
onClick = {
viewModel.pickIcon(it.data)
}
)
}
}
}
}
}
}
@@ -0,0 +1,43 @@
package de.mm20.launcher2.ui.launcher.search.common.customattrs
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import de.mm20.launcher2.customattrs.CustomIcon
import de.mm20.launcher2.icons.CustomIconSuggestion
import de.mm20.launcher2.icons.IconRepository
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.search.data.Searchable
import kotlinx.coroutines.flow.Flow
import org.koin.androidx.compose.inject
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class CustomizeSearchableSheetVM(
private val searchable: Searchable
) : KoinComponent {
private val iconRepository: IconRepository by inject()
val isIconPickerOpen = MutableLiveData(false)
fun getIcon(size: Int): Flow<LauncherIcon> {
return iconRepository.getIcon(searchable, size)
}
fun getIconSuggestions(size: Int) = liveData {
emit(iconRepository.getCustomIconSuggestions(searchable, size))
}
fun openIconPicker() {
isIconPickerOpen.value = true
}
fun closeIconPicker() {
isIconPickerOpen.value = false
}
fun pickIcon(icon: CustomIcon?) {
iconRepository.setCustomIcon(searchable, icon)
closeIconPicker()
}
}