Feature: Lock Single Tag for Favorites Widget (#1355)
* single tag for favorites * fixing * don't need this one * cleanup * new features - select for favorites/tags customization * bug fix for tags * bug fix to show pinned tags * make sure pinned tag works on load * better way to work with viewmodel (learning as i go) * back to working state.. can be improved * adding some comments * cleanup --------- Co-authored-by: MM20 <15646950+MM2-0@users.noreply.github.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -44,7 +44,7 @@ abstract class FavoritesVM : ViewModel(), KoinComponent {
|
||||
minPinnedLevel = PinnedLevel.AutomaticallySorted,
|
||||
).map {
|
||||
it.filterIsInstance<Tag>()
|
||||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
|
||||
|
||||
open val favorites: Flow<List<SavableSearchable>> = selectedTag.flatMapLatest { tag ->
|
||||
if (tag == null) {
|
||||
|
||||
@@ -73,6 +73,7 @@ fun LazyListScope.SearchFavorites(
|
||||
expanded = tagsExpanded,
|
||||
compact = compactTags,
|
||||
onExpand = onExpandTags,
|
||||
showFavorites = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<CustomAttributesRepository>()
|
||||
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FavoritesWidget?>(null)
|
||||
override val tagsExpanded = widget.map { it?.config?.tagsMultiline == true }
|
||||
override val compactTags: Flow<Boolean> = 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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1070,4 +1070,5 @@
|
||||
<string name="preference_transliteration">Preferred transliteration</string>
|
||||
<string name="preference_transliteration_auto">Automatic</string>
|
||||
<string name="preference_transliteration_disabled">Disabled</string>
|
||||
<string name="select_tag">Select tag</string>
|
||||
</resources>
|
||||
|
||||
@@ -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<String> = emptyList()
|
||||
)
|
||||
|
||||
data class FavoritesWidget(
|
||||
|
||||
Reference in New Issue
Block a user