diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesTagSelector.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesTagSelector.kt index d3b649758..68a9d194e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesTagSelector.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesTagSelector.kt @@ -46,6 +46,7 @@ fun FavoritesTagSelector( compact: Boolean, expanded: Boolean, onExpand: (Boolean) -> Unit, + showFavorites: Boolean ) { val sheetManager = LocalBottomSheetManager.current @@ -70,40 +71,42 @@ fun FavoritesTagSelector( .horizontalScroll(scrollState) .padding(end = 12.dp), ) { - FilterChip( - modifier = Modifier - .padding(start = 16.dp), - selected = selectedTag == null, - onClick = { onSelectTag(null) }, - leadingIcon = if (compact) null else { - { - Icon( - painter = painterResource(R.drawable.star_20px_filled), - contentDescription = null, - modifier = Modifier.size(FilterChipDefaults.IconSize), - ) + if (showFavorites) { + FilterChip( + modifier = Modifier + .padding(start = 16.dp), + selected = selectedTag == null, + onClick = { onSelectTag(null) }, + leadingIcon = if (compact) null else { + { + Icon( + painter = painterResource(R.drawable.star_20px_filled), + contentDescription = null, + modifier = Modifier.size(FilterChipDefaults.IconSize), + ) + } + }, + label = { + if (compact) { + Icon( + painter = painterResource(R.drawable.star_20px_filled), + contentDescription = null, + modifier = Modifier.size(FilterChipDefaults.IconSize), + ) + } else { + Text(stringResource(R.string.favorites)) + } } - }, - label = { - if (compact) { - Icon( - painter = painterResource(R.drawable.star_20px_filled), - contentDescription = null, - modifier = Modifier.size(FilterChipDefaults.IconSize), - ) - } else { - Text(stringResource(R.string.favorites)) - } - } - ) - for (tag in tags) { + ) + } + for ((i, tag) in tags.withIndex()) { TagChip( modifier = Modifier - .padding(start = 8.dp), + .padding(start = if (!showFavorites && i == 0) 16.dp else 8.dp), tag = tag, selected = selectedTag == tag.tag, onClick = { - if (selectedTag == tag.tag) { + if (selectedTag == tag.tag && showFavorites) { onSelectTag(null) } else { onSelectTag(tag.tag) @@ -184,7 +187,7 @@ fun FavoritesTagSelector( compact = compact, selected = selectedTag == tag.tag, onClick = { - if (selectedTag == tag.tag) { + if (selectedTag == tag.tag && showFavorites) { onSelectTag(null) } else { onSelectTag(tag.tag) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesVM.kt index ebef63710..0994007fa 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesVM.kt @@ -44,7 +44,7 @@ abstract class FavoritesVM : ViewModel(), KoinComponent { minPinnedLevel = PinnedLevel.AutomaticallySorted, ).map { it.filterIsInstance() - } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList()) open val favorites: Flow> = selectedTag.flatMapLatest { tag -> if (tag == null) { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavorites.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavorites.kt index 9440fe861..65a2d4d1e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavorites.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavorites.kt @@ -73,6 +73,7 @@ fun LazyListScope.SearchFavorites( expanded = tagsExpanded, compact = compactTags, onExpand = onExpandTags, + showFavorites = true ) } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt index c925d5c73..9bc70482c 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt @@ -12,33 +12,56 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabsIntent +import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.background import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ButtonGroupDefaults import androidx.compose.material3.CheckboxDefaults +import androidx.compose.material3.DropdownMenuGroup +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.DropdownMenuPopup +import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MenuDefaults import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton +import androidx.compose.material3.ToggleButton +import androidx.compose.material3.ToggleButtonDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -52,20 +75,24 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.isUnspecified +import androidx.compose.ui.zIndex import androidx.core.net.toUri import androidx.lifecycle.compose.LocalLifecycleOwner import de.mm20.launcher2.calendar.CalendarRepository import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.data.customattrs.CustomAttributesRepository import de.mm20.launcher2.ktx.isAtLeastApiLevel import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.plugin.PluginRepository import de.mm20.launcher2.plugin.PluginType +import de.mm20.launcher2.search.Tag import de.mm20.launcher2.search.calendar.CalendarListType import de.mm20.launcher2.themes.colors.atTone import de.mm20.launcher2.ui.R @@ -74,6 +101,9 @@ import de.mm20.launcher2.ui.component.BottomSheetDialog import de.mm20.launcher2.ui.component.DragResizeHandle import de.mm20.launcher2.ui.component.LargeMessage import de.mm20.launcher2.ui.component.MissingPermissionBanner +import de.mm20.launcher2.ui.component.dragndrop.DraggableItem +import de.mm20.launcher2.ui.component.dragndrop.LazyDragAndDropColumn +import de.mm20.launcher2.ui.component.dragndrop.rememberLazyDragAndDropListState import de.mm20.launcher2.ui.component.preferences.CheckboxPreference import de.mm20.launcher2.ui.component.preferences.Preference import de.mm20.launcher2.ui.component.preferences.SwitchPreference @@ -89,8 +119,10 @@ import de.mm20.launcher2.widgets.MusicWidget import de.mm20.launcher2.widgets.NotesWidget import de.mm20.launcher2.widgets.WeatherWidget import de.mm20.launcher2.widgets.Widget +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.map import org.koin.compose.koinInject +import java.text.Collator import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import kotlin.math.roundToInt @@ -157,15 +189,16 @@ fun ColumnScope.ConfigureWeatherWidget( bottom = 8.dp, ), onClick = { - context.startActivity(Intent( - context, - SettingsActivity::class.java - ).apply { - putExtra( - SettingsActivity.EXTRA_ROUTE, - SettingsActivity.ROUTE_WEATHER_INTEGRATION - ) - }) + context.startActivity( + Intent( + context, + SettingsActivity::class.java + ).apply { + putExtra( + SettingsActivity.EXTRA_ROUTE, + SettingsActivity.ROUTE_WEATHER_INTEGRATION + ) + }) }) { Text(stringResource(R.string.widget_config_weather_integration_settings)) Icon( @@ -182,49 +215,342 @@ fun ColumnScope.ConfigureFavoritesWidget( widget: FavoritesWidget, onWidgetUpdated: (FavoritesWidget) -> Unit, ) { + val customAttrRepository = koinInject() + + val allTags by remember { + customAttrRepository + .getAllTags() + .map { + val collator = Collator.getInstance().apply { strength = Collator.SECONDARY } + it + .sortedWith { el1, el2 -> + collator.compare(el1, el2) + } + } + }.collectAsState(emptyList()) + + val tagsListState = rememberLazyListState() + val bottomSheetManager = LocalBottomSheetManager.current - OutlinedCard { - Column( - modifier = Modifier.fillMaxWidth() + + var createTag by remember { mutableStateOf(false) } + + + Row( + horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween), + ) { + ToggleButton( + modifier = Modifier.weight(1f), + checked = !widget.config.customTags, + onCheckedChange = { + if (it) { + onWidgetUpdated(widget.copy(config = widget.config.copy(customTags = false))) + } + }, + shapes = ButtonGroupDefaults.connectedLeadingButtonShapes(), ) { - SwitchPreference( - title = stringResource(R.string.preference_edit_button), - iconPadding = false, - value = widget.config.editButton, - onValueChanged = { - onWidgetUpdated(widget.copy(config = widget.config.copy(editButton = it))) - } + Icon( + painterResource( + if (!widget.config.customTags) R.drawable.check_20px else R.drawable.star_20px + ), + contentDescription = null, + modifier = Modifier + .padding(end = ToggleButtonDefaults.IconSpacing) + .size(ToggleButtonDefaults.IconSize) ) - SwitchPreference( - title = stringResource(R.string.preference_compact_tags), - iconPadding = false, - value = widget.config.compactTags, - onValueChanged = { - onWidgetUpdated(widget.copy(config = widget.config.copy(compactTags = it))) + + Text(stringResource(R.string.favorites)) + } + ToggleButton( + modifier = Modifier.weight(1f), + checked = widget.config.customTags, + onCheckedChange = { + if (it) { + onWidgetUpdated( + widget.copy( + config = widget.config.copy(customTags = true) + ) + ) } + }, + shapes = ButtonGroupDefaults.connectedTrailingButtonShapes(), + ) { + Icon( + painterResource( + if (widget.config.customTags) R.drawable.check_20px else R.drawable.tag_20px + ), + contentDescription = null, + modifier = Modifier + .padding(end = ToggleButtonDefaults.IconSpacing) + .size(ToggleButtonDefaults.IconSize) ) + + Text(stringResource(R.string.preference_screen_tags)) } } - TextButton( - modifier = Modifier - .padding(top = 8.dp) - .align(Alignment.End), - contentPadding = PaddingValues( - end = 16.dp, - top = 8.dp, - start = 24.dp, - bottom = 8.dp, - ), - onClick = { - bottomSheetManager.showEditFavoritesSheet() - }) { - Text(stringResource(R.string.menu_item_edit_favs)) - Icon( + + AnimatedContent(widget.config.customTags) { showTags -> + Column( modifier = Modifier - .padding(start = ButtonDefaults.IconSpacing) - .requiredSize(ButtonDefaults.IconSize), - painter = painterResource(R.drawable.open_in_new_20px), contentDescription = null - ) + .fillMaxWidth(), + ) { + if (showTags) { + var tagList by remember { + mutableStateOf(widget.config.tagList.toImmutableList()) + } + + + val availableTags by remember { + derivedStateOf { allTags - tagList } + } + + LaunchedEffect(tagList) { + onWidgetUpdated( + widget.copy( + config = widget.config.copy( + tagList = tagList + ) + ) + ) + } + + OutlinedCard( + modifier = Modifier.padding(top = 16.dp) + ) { + + val rowState = rememberLazyDragAndDropListState( + listState = tagsListState, + onItemMove = { from, to -> + val newTagList = tagList.toMutableList() + val tag = newTagList.removeAt(from.index) + newTagList.add(to.index, tag) + tagList = newTagList.toImmutableList() + }, + ) + + LazyDragAndDropColumn( + state = rowState, + modifier = Modifier + .heightIn(max = 9999.dp) + .fillMaxWidth() + .animateContentSize(MaterialTheme.motionScheme.defaultSpatialSpec()), + bidirectionalDrag = false, + ) { + items( + tagList, + key = { it } + ) { tag -> + DraggableItem(state = rowState, key = tag) { + val elevation by animateDpAsState(if (it) 4.dp else 0.dp) + Surface( + shadowElevation = elevation, + tonalElevation = elevation, + modifier = Modifier.zIndex(if (it) 1f else 0f) + ) { + Preference( + title = tag, + icon = R.drawable.tag_24px, + controls = { + IconButton( + modifier = Modifier.offset(x = 8.dp), + onClick = { + tagList = (tagList - tag).toImmutableList() + } + ) { + Icon( + painterResource(R.drawable.close_24px), + stringResource(R.string.menu_remove), + ) + } + } + ) + } + } + } + } + } + + + var showAddMenu by remember { mutableStateOf(false) } + + Box { + FilledTonalButton( + modifier = Modifier.padding(top = 8.dp, bottom = 16.dp), + onClick = { + showAddMenu = true + }, + contentPadding = ButtonDefaults.ButtonWithIconContentPadding, + shapes = ButtonDefaults.shapes() + ) { + Icon( + painterResource(R.drawable.add_20px), + null, + modifier = Modifier + .padding(end = ButtonDefaults.IconSpacing) + .size(ButtonDefaults.IconSize) + ) + Text(stringResource(R.string.select_tag)) + } + DropdownMenuPopup( + expanded = showAddMenu, + onDismissRequest = { showAddMenu = false }) { + if (availableTags.isNotEmpty()) { + DropdownMenuGroup( + shapes = MenuDefaults.groupShape(0, 2), + ) { + for ((i, tag) in availableTags.withIndex()) { + DropdownMenuItem( + shape = if (availableTags.size == 1) MenuDefaults.standaloneItemShape + else when (i) { + 0 -> MenuDefaults.leadingItemShape + availableTags.lastIndex -> MenuDefaults.trailingItemShape + else -> MenuDefaults.middleItemShape + }, + leadingIcon = { + Icon( + painterResource(R.drawable.tag_24px), + null + ) + }, + text = { Text(tag) }, + onClick = { + tagList = (tagList + tag).toImmutableList() + showAddMenu = false + }) + } + } + Spacer( + modifier = Modifier.height(MenuDefaults.GroupSpacing) + ) + } + DropdownMenuGroup( + shapes = MenuDefaults.groupShape( + if (availableTags.isEmpty()) 0 else 1, + if (availableTags.isEmpty()) 1 else 2, + ) + ) { + DropdownMenuItem( + shape = MenuDefaults.standaloneItemShape, + leadingIcon = { + Icon(painterResource(R.drawable.add_24px), null) + }, + text = { + Text( + stringResource(R.string.edit_favorites_dialog_new_tag), + ) + }, + onClick = { + createTag = true + showAddMenu = false + } + ) + } + } + } + + + if (createTag) { + EditTagSheet( + tag = null, + onTagSaved = { tag -> + val newTag = Tag(tag) + tagList = (tagList + newTag.tag).toImmutableList() + }, + onDismiss = { + createTag = false + } + ) + } + + OutlinedCard( + modifier = Modifier.padding(top = 16.dp) + ) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + SwitchPreference( + title = stringResource(R.string.preference_compact_tags), + iconPadding = false, + value = widget.config.compactTags, + onValueChanged = { + onWidgetUpdated( + widget.copy( + config = widget.config.copy( + compactTags = it + ) + ) + ) + }, + enabled = widget.config.tagList.size > 1, + ) + } + } + } else { + Column( + modifier = Modifier.fillMaxWidth() + ) { + OutlinedCard( + modifier = Modifier.padding(top = 16.dp) + ) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + + SwitchPreference( + title = stringResource(R.string.preference_edit_button), + iconPadding = false, + value = widget.config.editButton, + onValueChanged = { + onWidgetUpdated( + widget.copy( + config = widget.config.copy( + editButton = it + ) + ) + ) + } + ) + SwitchPreference( + title = stringResource(R.string.customize_item_tags), + iconPadding = false, + value = widget.config.compactTags, + onValueChanged = { + onWidgetUpdated( + widget.copy( + config = widget.config.copy( + compactTags = it + ) + ) + ) + } + ) + } + } + TextButton( + modifier = Modifier + .padding(top = 8.dp) + .align(Alignment.End), + contentPadding = PaddingValues( + end = 16.dp, + top = 8.dp, + start = 24.dp, + bottom = 8.dp, + ), + onClick = { + bottomSheetManager.showEditFavoritesSheet() + }) { + Text(stringResource(R.string.menu_item_edit_favs)) + Icon( + modifier = Modifier + .padding(start = ButtonDefaults.IconSpacing) + .requiredSize(ButtonDefaults.IconSize), + painter = painterResource(R.drawable.open_in_new_20px), + contentDescription = null + ) + } + } + } + } } } @@ -232,7 +558,6 @@ fun ColumnScope.ConfigureFavoritesWidget( fun ColumnScope.ConfigureMusicWidget( widget: MusicWidget, onWidgetUpdated: (MusicWidget) -> Unit, - ) { val context = LocalContext.current @@ -261,15 +586,16 @@ fun ColumnScope.ConfigureMusicWidget( bottom = 8.dp, ), onClick = { - context.startActivity(Intent( - context, - SettingsActivity::class.java - ).apply { - putExtra( - SettingsActivity.EXTRA_ROUTE, - SettingsActivity.ROUTE_MEDIA_INTEGRATION, - ) - }) + context.startActivity( + Intent( + context, + SettingsActivity::class.java + ).apply { + putExtra( + SettingsActivity.EXTRA_ROUTE, + SettingsActivity.ROUTE_MEDIA_INTEGRATION, + ) + }) }) { Text(stringResource(R.string.widget_config_music_integration_settings)) Icon( @@ -504,7 +830,8 @@ fun ColumnScope.ConfigureAppWidget( modifier = Modifier .padding(start = ButtonDefaults.IconSpacing) .requiredSize(ButtonDefaults.IconSize), - painter = painterResource(R.drawable.open_in_new_20px), contentDescription = null + painter = painterResource(R.drawable.open_in_new_20px), + contentDescription = null ) } } @@ -670,6 +997,7 @@ fun ConfigureNotesWidget( onWidgetUpdated: (NotesWidget) -> Unit ) { val context = LocalContext.current + val resources = LocalResources.current val linkFileLauncher = rememberLauncherForActivityResult( ActivityResultContracts.CreateDocument("text/markdown") ) { @@ -733,7 +1061,7 @@ fun ConfigureNotesWidget( icon = R.drawable.link_24px, onClick = { linkFileLauncher.launch( - context.getString( + resources.getString( R.string.notes_widget_export_filename, ZonedDateTime.now().format( DateTimeFormatter.ISO_INSTANT diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/external/AppWidget.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/external/AppWidget.kt index 21569b301..4d93e6aae 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/external/AppWidget.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/external/AppWidget.kt @@ -45,7 +45,7 @@ fun AppWidget( ) { val context = LocalContext.current - val lightBackground = (!LocalDarkTheme.current && widget.config.background) || LocalPreferDarkContentOverWallpaper.current + val lightBackground = if (widget.config.background) !LocalDarkTheme.current else LocalPreferDarkContentOverWallpaper.current val widgetInfo = remember(widget.config.widgetId) { AppWidgetManager.getInstance(context) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidget.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidget.kt index f0810b8c2..5b6b2288d 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidget.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidget.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import de.mm20.launcher2.search.Tag import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.common.FavoritesTagSelector import de.mm20.launcher2.ui.component.Banner @@ -24,10 +25,12 @@ fun FavoritesWidget(widget: FavoritesWidget) { val favorites by remember { viewModel.favorites }.collectAsState(emptyList()) val pinnedTags by viewModel.pinnedTags.collectAsState(emptyList()) val selectedTag by viewModel.selectedTag.collectAsState(null) - val compactTags by viewModel.compactTags.collectAsState(false) - val favoritesEditButton = widget.config.editButton - val tagsExpanded by viewModel.tagsExpanded.collectAsState(false) + val customTags = widget.config.customTags && widget.config.tagList.isNotEmpty() + val favoritesEditButton = !customTags && widget.config.editButton + + val compactTags = widget.config.compactTags + val tagsExpanded = widget.config.tagsMultiline LaunchedEffect(widget) { viewModel.updateWidget(widget) @@ -47,9 +50,9 @@ fun FavoritesWidget(widget: FavoritesWidget) { icon = if (selectedTag == null) R.drawable.star_24px else R.drawable.tag_24px, ) } - if (pinnedTags.isNotEmpty() || favoritesEditButton) { + if (favoritesEditButton || (customTags && widget.config.tagList.size > 1)) { FavoritesTagSelector( - tags = pinnedTags, + tags = if (customTags) widget.config.tagList.map { Tag(it) } else pinnedTags, selectedTag = selectedTag, editButton = favoritesEditButton, reverse = false, @@ -57,7 +60,8 @@ fun FavoritesWidget(widget: FavoritesWidget) { scrollState = rememberScrollState(), expanded = tagsExpanded, compact = compactTags, - onExpand = { viewModel.setTagsExpanded(it) } + onExpand = { viewModel.setTagsExpanded(it) }, + showFavorites = !customTags ) } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt index 80120a04c..cc7eaa688 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt @@ -1,6 +1,5 @@ package de.mm20.launcher2.ui.launcher.widgets.favorites -import androidx.lifecycle.viewModelScope import de.mm20.launcher2.preferences.ui.GridSettings import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.services.widgets.WidgetsService @@ -10,40 +9,16 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn import org.koin.core.component.inject class FavoritesWidgetVM : FavoritesVM() { private val widgetsService: WidgetsService by inject() - private val uiSettings: UiSettings by inject() - private val widget = MutableStateFlow(null) override val tagsExpanded = widget.map { it?.config?.tagsMultiline == true } override val compactTags: Flow = widget.map { it?.config?.compactTags == true } - private val isTopWidget = widgetsService.isFavoritesWidgetFirst() - private val clockWidgetFavSlots = - combine(uiSettings.dock, isTopWidget, uiSettings.gridSettings) { (dock, isTop, grid) -> - dock as Boolean - isTop as Boolean - grid as GridSettings - if (!isTop || !dock) 0 - else { - grid.columnCount - } - } - - override val favorites = super.favorites.combine(clockWidgetFavSlots) { favs, slots -> - if (selectedTag.value == null) { - if (favs.lastIndex < slots) emptyList() - else favs.subList(slots, favs.size) - } else { - favs - } - } - override fun setTagsExpanded(expanded: Boolean) { val widget = this.widget.value ?: return widgetsService.updateWidget( @@ -54,7 +29,11 @@ class FavoritesWidgetVM : FavoritesVM() { } fun updateWidget(widget: FavoritesWidget) { + selectTag(null) + if (widget.config.customTags) { + selectTag(widget.config.tagList.firstOrNull()) + } + this.widget.value = widget } - } \ No newline at end of file diff --git a/core/i18n/src/main/res/values/strings.xml b/core/i18n/src/main/res/values/strings.xml index 4573d9707..072f1d443 100644 --- a/core/i18n/src/main/res/values/strings.xml +++ b/core/i18n/src/main/res/values/strings.xml @@ -1070,4 +1070,5 @@ Preferred transliteration Automatic Disabled + Select tag diff --git a/data/widgets/src/main/java/de/mm20/launcher2/widgets/FavoritesWidget.kt b/data/widgets/src/main/java/de/mm20/launcher2/widgets/FavoritesWidget.kt index f8b8a16b7..542501bcd 100644 --- a/data/widgets/src/main/java/de/mm20/launcher2/widgets/FavoritesWidget.kt +++ b/data/widgets/src/main/java/de/mm20/launcher2/widgets/FavoritesWidget.kt @@ -2,15 +2,16 @@ package de.mm20.launcher2.widgets import de.mm20.launcher2.database.entities.PartialWidgetEntity import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import java.util.UUID @Serializable data class FavoritesWidgetConfig( + val customTags: Boolean = false, val editButton: Boolean = true, val tagsMultiline: Boolean = false, val compactTags: Boolean = false, + val tagList: List = emptyList() ) data class FavoritesWidget(