mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-27 07:16:54 +00:00
feat(drawer): implement app reordering in folder settings (#6173)
* feat(drawer): implement app reordering in folder settings - Add drag-and-drop reordering for apps within App Drawer folders - Split folder editing UI into "Selected apps" and "Add apps" sections - Add "remove" button (cross icon) to selected apps - Update FolderService * style: Fix spotless check violations in SelectAppsForDrawerFolder Add missing trailing commas to comply with Kotlin coding style
This commit is contained in:
@@ -125,6 +125,8 @@
|
||||
<string name="caddy">Caddy</string>
|
||||
|
||||
<string name="app_drawer_folder">App drawer folders</string>
|
||||
<string name="selected_apps">Selected apps</string>
|
||||
<string name="add_apps">Add apps</string>
|
||||
<string name="app_drawer_folder_settings">Drawer folder</string>
|
||||
<string name="add_folder">Create folder</string>
|
||||
<string name="edit_folder">Edit folder</string>
|
||||
|
||||
@@ -60,8 +60,12 @@ class FolderViewModel(
|
||||
fun updateFolderItems(id: Int, title: String, appInfo: List<AppInfo>) {
|
||||
viewModelScope.launch {
|
||||
repository.updateFolderWithItems(id, title, appInfo)
|
||||
// Update the local state flow so UI can observe changes without full reload if needed,
|
||||
// though for now we just rely on reloadGrid to refresh the launcher.
|
||||
// We call reloadGrid *after* the DB update is complete.
|
||||
_folderInfo.value = repository.getFolderInfo(id, true)
|
||||
reloadHelper.reloadGrid()
|
||||
}
|
||||
reloadHelper.reloadGrid()
|
||||
}
|
||||
|
||||
fun createFolder(folderInfo: FolderInfo) {
|
||||
|
||||
@@ -38,8 +38,8 @@ class FolderService(val context: Context) : SafeCloseable {
|
||||
suspend fun updateFolderWithItems(folderInfoId: Int, title: String, appInfos: List<AppInfo>) = withContext(Dispatchers.IO) {
|
||||
folderDao.insertFolderWithItems(
|
||||
FolderInfoEntity(id = folderInfoId, title = title),
|
||||
appInfos.map {
|
||||
it.toEntity(folderInfoId)
|
||||
appInfos.mapIndexed { index, appInfo ->
|
||||
appInfo.toEntity(folderInfoId).copy(rank = index)
|
||||
}.toList(),
|
||||
)
|
||||
}
|
||||
@@ -70,7 +70,7 @@ class FolderService(val context: Context) : SafeCloseable {
|
||||
title = folderWithItems.folder.title
|
||||
}
|
||||
|
||||
folderWithItems.items.forEach { itemEntity ->
|
||||
folderWithItems.items.sortedBy { it.rank }.forEach { itemEntity ->
|
||||
// Consider caching toItemInfo results if componentKey lookups are slow
|
||||
// and items don't change frequently without folder data changing
|
||||
toItemInfo(itemEntity.componentKey)?.let { appInfo ->
|
||||
|
||||
@@ -36,34 +36,6 @@ import app.lawnchair.ui.preferences.components.layout.PreferenceTemplate
|
||||
import app.lawnchair.util.App
|
||||
import com.android.launcher3.model.data.AppInfo
|
||||
|
||||
@Composable
|
||||
fun AppItem(
|
||||
app: App,
|
||||
onClick: (app: App) -> Unit,
|
||||
widget: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
AppItem(
|
||||
label = app.label,
|
||||
icon = app.icon,
|
||||
onClick = { onClick(app) },
|
||||
widget = widget,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppItem(
|
||||
appInfo: AppInfo,
|
||||
onClick: (appInfo: AppInfo) -> Unit,
|
||||
widget: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
AppItem(
|
||||
label = appInfo.title.toString(),
|
||||
icon = appInfo.bitmap.icon,
|
||||
onClick = { onClick(appInfo) },
|
||||
widget = widget,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppItem(
|
||||
label: String,
|
||||
@@ -71,11 +43,13 @@ fun AppItem(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
widget: (@Composable () -> Unit)? = null,
|
||||
endWidget: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
AppItemLayout(
|
||||
modifier = modifier
|
||||
.clickable(onClick = onClick),
|
||||
widget = widget,
|
||||
endWidget = endWidget,
|
||||
icon = {
|
||||
Image(
|
||||
bitmap = icon.asImageBitmap(),
|
||||
@@ -87,6 +61,38 @@ fun AppItem(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppItem(
|
||||
app: App,
|
||||
onClick: (app: App) -> Unit,
|
||||
widget: (@Composable () -> Unit)? = null,
|
||||
endWidget: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
AppItem(
|
||||
label = app.label,
|
||||
icon = app.icon,
|
||||
onClick = { onClick(app) },
|
||||
widget = widget,
|
||||
endWidget = endWidget,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppItem(
|
||||
appInfo: AppInfo,
|
||||
onClick: (appInfo: AppInfo) -> Unit,
|
||||
widget: (@Composable () -> Unit)? = null,
|
||||
endWidget: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
AppItem(
|
||||
label = appInfo.title.toString(),
|
||||
icon = appInfo.bitmap.icon,
|
||||
onClick = { onClick(appInfo) },
|
||||
widget = widget,
|
||||
endWidget = endWidget,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppItemPlaceholder(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -125,6 +131,7 @@ private fun AppItemLayout(
|
||||
title: @Composable () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
widget: (@Composable () -> Unit)? = null,
|
||||
endWidget: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
PreferenceTemplate(
|
||||
title = title,
|
||||
@@ -136,6 +143,7 @@ private fun AppItemLayout(
|
||||
}
|
||||
icon()
|
||||
},
|
||||
endWidget = endWidget,
|
||||
verticalPadding = 12.dp,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -55,6 +56,7 @@ fun <T> DraggablePreferenceGroup(
|
||||
defaultList: List<T>,
|
||||
onOrderChange: (List<T>) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onSettle: ((List<T>) -> Unit)? = null,
|
||||
itemContent: @Composable ReorderableScope.(
|
||||
item: T,
|
||||
index: Int,
|
||||
@@ -62,7 +64,14 @@ fun <T> DraggablePreferenceGroup(
|
||||
onDraggingChange: (Boolean) -> Unit,
|
||||
) -> Unit,
|
||||
) {
|
||||
var localItems = items
|
||||
var localItems by remember { mutableStateOf(items) }
|
||||
|
||||
LaunchedEffect(items) {
|
||||
if (localItems != items) {
|
||||
localItems = items
|
||||
}
|
||||
}
|
||||
|
||||
var isAnyDragging by remember { mutableStateOf(false) }
|
||||
|
||||
val color by animateColorAsState(
|
||||
@@ -85,12 +94,15 @@ fun <T> DraggablePreferenceGroup(
|
||||
modifier = Modifier,
|
||||
list = localItems,
|
||||
onSettle = { fromIndex, toIndex ->
|
||||
localItems = localItems.toMutableList().apply {
|
||||
val newItems = localItems.toMutableList().apply {
|
||||
add(toIndex, removeAt(fromIndex))
|
||||
}.toList().also { newItems ->
|
||||
onOrderChange(newItems)
|
||||
isAnyDragging = false
|
||||
}.toList()
|
||||
localItems = newItems
|
||||
onOrderChange(newItems)
|
||||
if (onSettle != null) {
|
||||
onSettle(newItems)
|
||||
}
|
||||
isAnyDragging = false
|
||||
},
|
||||
onMove = {
|
||||
isAnyDragging = true
|
||||
@@ -107,8 +119,20 @@ fun <T> DraggablePreferenceGroup(
|
||||
.a11yDrag(
|
||||
index = index,
|
||||
items = items,
|
||||
onMoveUp = { localItems = it },
|
||||
onMoveDown = { localItems = it },
|
||||
onMoveUp = {
|
||||
localItems = it
|
||||
onOrderChange(it)
|
||||
if (onSettle != null) {
|
||||
onSettle(it)
|
||||
}
|
||||
},
|
||||
onMoveDown = {
|
||||
localItems = it
|
||||
onOrderChange(it)
|
||||
if (onSettle != null) {
|
||||
onSettle(it)
|
||||
}
|
||||
},
|
||||
),
|
||||
) {
|
||||
itemContent(
|
||||
@@ -132,7 +156,11 @@ fun <T> DraggablePreferenceGroup(
|
||||
ExpandAndShrink(visible = localItems != defaultList) {
|
||||
PreferenceGroup {
|
||||
ClickablePreference(label = stringResource(id = R.string.action_reset)) {
|
||||
onOrderChange(defaultList)
|
||||
val resetList = defaultList
|
||||
onOrderChange(resetList)
|
||||
if (onSettle != null) {
|
||||
onSettle(resetList)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
package app.lawnchair.ui.preferences.destinations
|
||||
package app.lawnchair.ui.preferences.destinations
|
||||
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Check
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.Close
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -27,19 +25,19 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import app.lawnchair.data.folder.model.FolderViewModel
|
||||
import app.lawnchair.ui.OverflowMenu
|
||||
import app.lawnchair.ui.preferences.LocalIsExpandedScreen
|
||||
import app.lawnchair.ui.preferences.components.AppItem
|
||||
import app.lawnchair.ui.preferences.components.AppItemPlaceholder
|
||||
import app.lawnchair.ui.preferences.components.layout.PreferenceDivider
|
||||
import app.lawnchair.ui.preferences.components.layout.PreferenceLazyColumn
|
||||
import app.lawnchair.ui.preferences.components.layout.PreferenceScaffold
|
||||
import app.lawnchair.ui.preferences.components.DragHandle
|
||||
import app.lawnchair.ui.preferences.components.DraggablePreferenceGroup
|
||||
import app.lawnchair.ui.preferences.components.layout.PreferenceLayoutLazyColumn
|
||||
import app.lawnchair.ui.preferences.components.layout.preferenceGroupItems
|
||||
import app.lawnchair.util.App
|
||||
import app.lawnchair.util.appsState
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.model.data.AppInfo
|
||||
import com.android.launcher3.model.data.ItemInfo
|
||||
import com.android.launcher3.util.ComponentKey
|
||||
|
||||
@Composable
|
||||
fun SelectAppsForDrawerFolder(
|
||||
@@ -57,223 +55,128 @@ fun SelectAppsForDrawerFolder(
|
||||
|
||||
val folders by viewModel.folders.collectAsStateWithLifecycle()
|
||||
val folderInfo by viewModel.folderInfo.collectAsStateWithLifecycle()
|
||||
var allFolderPackages by remember { mutableStateOf(emptySet<String>()) }
|
||||
var selectedAppsInFolder by remember { mutableStateOf(setOf<ItemInfo>()) }
|
||||
var filterNonUniqueItems by remember { mutableStateOf(true) }
|
||||
|
||||
LaunchedEffect(folders) {
|
||||
allFolderPackages = folders.flatMap { it.getContents() }
|
||||
.mapNotNull { it.targetPackage }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
LaunchedEffect(folderInfo) {
|
||||
selectedAppsInFolder = folderInfo?.getContents()?.toMutableSet() ?: emptySet()
|
||||
}
|
||||
val selectedAppsInFolder = remember { mutableStateListOf<App>() }
|
||||
|
||||
LaunchedEffect(folderInfoId) {
|
||||
viewModel.setFolderInfo(folderInfoId, false)
|
||||
}
|
||||
|
||||
val apps by appsState()
|
||||
val filteredApps = apps.filter { app ->
|
||||
if (filterNonUniqueItems) {
|
||||
!allFolderPackages.contains(app.key.componentName.packageName) || selectedAppsInFolder.map { it.targetPackage }.contains(app.key.componentName.packageName)
|
||||
} else {
|
||||
true
|
||||
var isInitialLoad by remember { mutableStateOf(true) }
|
||||
|
||||
LaunchedEffect(folderInfo, apps) {
|
||||
if (isInitialLoad && folderInfo != null && apps.isNotEmpty()) {
|
||||
val currentContent = folderInfo!!.getContents()
|
||||
val orderedApps = currentContent.sortedBy { it.rank }.mapNotNull { item ->
|
||||
val key = ComponentKey(item.targetComponent, item.user)
|
||||
apps.find { it.key == key }
|
||||
}
|
||||
selectedAppsInFolder.clear()
|
||||
selectedAppsInFolder.addAll(orderedApps)
|
||||
isInitialLoad = false
|
||||
}
|
||||
}
|
||||
|
||||
val loading = folderInfo == null && apps.isEmpty()
|
||||
|
||||
PreferenceScaffold(
|
||||
PreferenceLayoutLazyColumn(
|
||||
label = if (loading) {
|
||||
stringResource(R.string.loading)
|
||||
} else {
|
||||
stringResource(R.string.x_with_y_count, folderInfo?.title.toString(), selectedAppsInFolder.size)
|
||||
},
|
||||
modifier = modifier,
|
||||
actions = {
|
||||
if (!loading) {
|
||||
ListSortingOptions(
|
||||
originalList = apps,
|
||||
filteredList = selectedAppsInFolder,
|
||||
onUpdateList = { newSet ->
|
||||
selectedAppsInFolder = newSet
|
||||
isExpandedScreen = LocalIsExpandedScreen.current,
|
||||
) {
|
||||
if (loading) {
|
||||
preferenceGroupItems(
|
||||
count = 20,
|
||||
isFirstChild = true,
|
||||
dividerStartIndent = 40.dp,
|
||||
) {
|
||||
AppItemPlaceholder()
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
if (selectedAppsInFolder.isNotEmpty()) {
|
||||
DraggablePreferenceGroup<App>(
|
||||
label = stringResource(R.string.selected_apps),
|
||||
items = selectedAppsInFolder,
|
||||
defaultList = selectedAppsInFolder,
|
||||
onOrderChange = { newOrder ->
|
||||
selectedAppsInFolder.clear()
|
||||
selectedAppsInFolder.addAll(newOrder)
|
||||
},
|
||||
onSettle = { newOrder ->
|
||||
// Update the database when drag settles
|
||||
viewModel.updateFolderItems(
|
||||
folderInfoId,
|
||||
folderInfo?.title.toString(),
|
||||
newOrder.map { it.toAppInfo(context) },
|
||||
)
|
||||
},
|
||||
) { app, _, _, onDraggingChange ->
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
AppItem(
|
||||
app = app,
|
||||
onClick = { },
|
||||
widget = {
|
||||
DragHandle(
|
||||
scope = this,
|
||||
interactionSource = interactionSource,
|
||||
onDragStop = {
|
||||
onDraggingChange(false)
|
||||
},
|
||||
)
|
||||
},
|
||||
endWidget = {
|
||||
IconButton(onClick = {
|
||||
val newList = selectedAppsInFolder.toMutableList()
|
||||
newList.remove(app)
|
||||
selectedAppsInFolder.clear()
|
||||
selectedAppsInFolder.addAll(newList)
|
||||
|
||||
viewModel.updateFolderItems(
|
||||
folderInfoId,
|
||||
folderInfo?.title.toString(),
|
||||
newList.map { it.toAppInfo(context) },
|
||||
)
|
||||
}) {
|
||||
Icon(Icons.Rounded.Close, contentDescription = stringResource(R.string.delete_label))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val unselectedApps = apps.filter { !selectedAppsInFolder.contains(it) }
|
||||
|
||||
preferenceGroupItems(
|
||||
items = unselectedApps,
|
||||
heading = if (selectedAppsInFolder.isNotEmpty()) {
|
||||
{ stringResource(R.string.add_apps) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
isFirstChild = selectedAppsInFolder.isEmpty(),
|
||||
dividerStartIndent = 40.dp,
|
||||
) { _, app ->
|
||||
AppItem(
|
||||
app = app,
|
||||
onClick = {
|
||||
selectedAppsInFolder.add(app)
|
||||
viewModel.updateFolderItems(
|
||||
folderInfoId,
|
||||
folderInfo?.title.toString(),
|
||||
newSet.toList(),
|
||||
selectedAppsInFolder.map { it.toAppInfo(context) },
|
||||
)
|
||||
},
|
||||
filterUniqueItems = filterNonUniqueItems,
|
||||
onToggleFilterUniqueItems = {
|
||||
filterNonUniqueItems = it
|
||||
widget = {
|
||||
Icon(Icons.Rounded.Add, contentDescription = null)
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
isExpandedScreen = LocalIsExpandedScreen.current,
|
||||
) {
|
||||
Crossfade(targetState = loading, label = "") { isLoading ->
|
||||
if (isLoading) {
|
||||
PreferenceLazyColumn(it, enabled = false, state = rememberLazyListState()) {
|
||||
preferenceGroupItems(
|
||||
count = 20,
|
||||
isFirstChild = true,
|
||||
dividerStartIndent = 40.dp,
|
||||
) {
|
||||
AppItemPlaceholder {
|
||||
Spacer(Modifier.width(24.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PreferenceLazyColumn(it, state = rememberLazyListState()) {
|
||||
preferenceGroupItems(
|
||||
filteredApps,
|
||||
isFirstChild = true,
|
||||
dividerStartIndent = 40.dp,
|
||||
) { _, app ->
|
||||
key(app.toString()) {
|
||||
AppItem(
|
||||
app,
|
||||
onClick = {
|
||||
updateFolderItems(
|
||||
app = it,
|
||||
items = selectedAppsInFolder,
|
||||
context = context,
|
||||
onSetChange = { newSet ->
|
||||
selectedAppsInFolder = newSet
|
||||
|
||||
viewModel.updateFolderItems(
|
||||
folderInfoId,
|
||||
folderInfo?.title.toString(),
|
||||
newSet.filterIsInstance<AppInfo>().toList(),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
) {
|
||||
Checkbox(
|
||||
checked = selectedAppsInFolder.any {
|
||||
val appInfo = it as? AppInfo
|
||||
appInfo?.targetPackage == app.key.componentName.packageName && appInfo.user == app.key.user
|
||||
},
|
||||
onCheckedChange = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ListSortingOptions(
|
||||
originalList: List<App>,
|
||||
filteredList: Set<ItemInfo>,
|
||||
onUpdateList: (Set<AppInfo>) -> Unit,
|
||||
filterUniqueItems: Boolean,
|
||||
onToggleFilterUniqueItems: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
OverflowMenu(modifier) {
|
||||
val originalListPackageNames = originalList
|
||||
.map { it.key.componentName.packageName }
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
val inverseSelectionPackageNames = originalListPackageNames
|
||||
.filter { items ->
|
||||
!filteredList.map { it.targetPackage }.contains(items)
|
||||
}
|
||||
.toSet()
|
||||
|
||||
val inverseSelection = originalList
|
||||
.filter {
|
||||
inverseSelectionPackageNames.contains(it.key.componentName.packageName)
|
||||
}
|
||||
.map {
|
||||
it.toAppInfo(context)
|
||||
}
|
||||
.toSet()
|
||||
|
||||
onUpdateList(inverseSelection)
|
||||
hideMenu()
|
||||
},
|
||||
text = {
|
||||
Text(stringResource(R.string.inverse_selection))
|
||||
},
|
||||
)
|
||||
|
||||
val selectedAll = originalListPackageNames == filteredList.map { it.targetPackage }
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
onUpdateList(
|
||||
if (selectedAll) {
|
||||
emptySet()
|
||||
} else {
|
||||
originalList.map { app ->
|
||||
app.toAppInfo(context)
|
||||
}.toSet()
|
||||
},
|
||||
)
|
||||
hideMenu()
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
stringResource(if (selectedAll) R.string.deselect_all else R.string.select_all),
|
||||
)
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
onToggleFilterUniqueItems(!filterUniqueItems)
|
||||
hideMenu()
|
||||
},
|
||||
trailingIcon = {
|
||||
if (filterUniqueItems) {
|
||||
Icon(Icons.Rounded.Check, contentDescription = null)
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Text(stringResource(R.string.folders_filter_duplicates))
|
||||
},
|
||||
)
|
||||
PreferenceDivider(modifier = Modifier.padding(vertical = 8.dp))
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
onUpdateList(
|
||||
emptySet(),
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(stringResource(R.string.action_reset))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateFolderItems(
|
||||
app: App,
|
||||
items: Set<ItemInfo>,
|
||||
context: Context,
|
||||
onSetChange: (Set<ItemInfo>) -> Unit,
|
||||
) {
|
||||
val newSet = items.toMutableSet().apply {
|
||||
val isChecked = any { it is AppInfo && it.targetPackage == app.key.componentName.packageName && it.user == app.key.user }
|
||||
if (isChecked) {
|
||||
removeIf { it is AppInfo && it.targetPackage == app.key.componentName.packageName && it.user == app.key.user }
|
||||
} else {
|
||||
add(
|
||||
app.toAppInfo(context),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
onSetChange(newSet)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user