diff --git a/database/src/main/java/de/mm20/launcher2/database/SearchDao.kt b/database/src/main/java/de/mm20/launcher2/database/SearchDao.kt index f209add6..2962bfb7 100644 --- a/database/src/main/java/de/mm20/launcher2/database/SearchDao.kt +++ b/database/src/main/java/de/mm20/launcher2/database/SearchDao.kt @@ -160,4 +160,7 @@ interface SearchDao { @Query("UPDATE Searchable SET `pinned` = 0") fun unpinAll() + + @Query("UPDATE Searchable Set `pinned` = 0, `launchCount` = 0 WHERE `key` = :key") + suspend fun resetPinStatusAndLaunchCounter(key: String) } diff --git a/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt index 810d33c5..3141cbc4 100644 --- a/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt +++ b/favorites/src/main/java/de/mm20/launcher2/favorites/FavoritesRepository.kt @@ -54,14 +54,23 @@ interface FavoritesRepository { fun unhideItem(searchable: Searchable) fun incrementLaunchCounter(searchable: Searchable) fun updateFavorites( - manuallySorted: List, - automaticallySorted: List, + manuallySorted: List, + automaticallySorted: List, ) fun getHiddenItems(): Flow> fun getHiddenItemKeys(): Flow> + + /** + * Remove this item from the Searchable database + */ fun remove(searchable: Searchable) + /** + * Remove this item from favorites and reset launch counter + */ + fun removeFromFavorites(searchable: Searchable) + /** * Ensure that this searchable exists in the Favorites table. * If it doesn't exist, insert it with 0 launch count, not pinned and not hidden @@ -287,6 +296,12 @@ internal class FavoritesRepositoryImpl( } } + override fun removeFromFavorites(searchable: Searchable) { + scope.launch { + database.searchDao().resetPinStatusAndLaunchCounter(searchable.key) + } + } + override fun save(searchable: Searchable) { scope.launch { withContext(Dispatchers.IO) { @@ -322,17 +337,18 @@ internal class FavoritesRepositoryImpl( entity.pinPosition = manuallySorted.size - index + 1 entity } - val updatedAutomaticallySorted = automaticallySorted.mapIndexedNotNull { index, searchable -> - val entity = entities.find { searchable.key == it.key } ?: FavoritesItem( - key = searchable.key, - searchable = searchable, - launchCount = 0, - pinPosition = 0, - hidden = false, - ).toDatabaseEntity() ?: return@mapIndexedNotNull null - entity.pinPosition = 1 - entity - } + val updatedAutomaticallySorted = + automaticallySorted.mapIndexedNotNull { index, searchable -> + val entity = entities.find { searchable.key == it.key } ?: FavoritesItem( + key = searchable.key, + searchable = searchable, + launchCount = 0, + pinPosition = 0, + hidden = false, + ).toDatabaseEntity() ?: return@mapIndexedNotNull null + entity.pinPosition = 1 + entity + } database.runInTransaction { dao.unpinAll() dao.insertAllReplaceExisting(updatedManuallySorted) @@ -439,7 +455,10 @@ internal class FavoritesRepositoryImpl( if (item.searchable == null || item.searchable.key != item.key) { removeInvalidItem(item.key) removed++ - Log.i("MM20", "SearchableDatabase cleanup: removed invalid item ${item.key}") + Log.i( + "MM20", + "SearchableDatabase cleanup: removed invalid item ${item.key}" + ) } } page++ diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/helper/DragAndDropGrid.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/helper/DragAndDropGrid.kt index 7aa793ff..c93de836 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/helper/DragAndDropGrid.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/helper/DragAndDropGrid.kt @@ -31,6 +31,7 @@ import kotlin.coroutines.coroutineContext fun rememberLazyDragAndDropGridState( gridState: LazyGridState = rememberLazyGridState(), onDragStart: (item: LazyGridItemInfo) -> Boolean = { true }, + onDrag: (item: LazyGridItemInfo, offset: Offset) -> Unit = {_, _ ->}, onDragEnd: (item: LazyGridItemInfo) -> Unit = {}, onDragCancel: (item: LazyGridItemInfo) -> Unit = {}, onItemMove: (from: LazyGridItemInfo, to: LazyGridItemInfo) -> Unit @@ -39,6 +40,7 @@ fun rememberLazyDragAndDropGridState( LazyDragAndDropGridState( gridState, onDragStart, + onDrag, onDragEnd, onDragCancel, onItemMove @@ -49,6 +51,7 @@ fun rememberLazyDragAndDropGridState( data class LazyDragAndDropGridState( val gridState: LazyGridState, val onDragStart: (item: LazyGridItemInfo) -> Boolean = { true }, + val onDrag: (item: LazyGridItemInfo, offset: Offset) -> Unit = {_, _ ->}, val onDragEnd: (item: LazyGridItemInfo) -> Unit = {}, val onDragCancel: (item: LazyGridItemInfo) -> Unit = {}, val onItemMove: (from: LazyGridItemInfo, to: LazyGridItemInfo) -> Unit @@ -259,7 +262,7 @@ fun Modifier.dragAndDrop( ).contains(draggedCenter) } - if (dragOver != null && dragOver.key != state.draggedItem?.key) { + if (dragOver != null && dragOver.key != draggedItem.key) { scope.launch { state.attemptMove(dragOver) } @@ -280,6 +283,8 @@ fun Modifier.dragAndDrop( } else { state.endScrolling() } + + state.draggedItemOffset?.let { state.onDrag(draggedItem, it) } } }, onDragCancel = { diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/modals/EditFavoritesSheet.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/modals/EditFavoritesSheet.kt index 36de44fc..22237c97 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/modals/EditFavoritesSheet.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/modals/EditFavoritesSheet.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Add +import androidx.compose.material.icons.rounded.Delete import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState @@ -112,9 +113,23 @@ fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM) { val items by viewModel.gridItems.observeAsState(emptyList()) val columns = LocalGridColumns.current + var contextMenuItemKey by remember { mutableStateOf(null) } + + val contextMenuCloseDistance = 8.dp.toPixels() + val state = rememberLazyDragAndDropGridState( onDragStart = { - items.getOrNull(it.index) is FavoritesSheetGridItem.Favorite + val item = items.getOrNull(it.index) + + if (item !is FavoritesSheetGridItem.Favorite) return@rememberLazyDragAndDropGridState false + + contextMenuItemKey = item.item.key + true + }, + onDrag = { _, offset -> + if (offset.getDistanceSquared() > contextMenuCloseDistance) { + contextMenuItemKey = null + } } ) { from, to -> viewModel.moveItem(from, to) @@ -163,6 +178,23 @@ fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM) { icon = icon, badge = badge ) + if (contextMenuItemKey == it.item.key) { + DropdownMenu( + expanded = true, + onDismissRequest = { contextMenuItemKey = null }) { + DropdownMenuItem( + leadingIcon = { + Icon(imageVector = Icons.Rounded.Delete, contentDescription = null) + }, + text = { + Text("Remove") + }, onClick = { + contextMenuItemKey?.let { viewModel.remove(it) } + contextMenuItemKey = null + } + ) + } + } } } is FavoritesSheetGridItem.Divider -> { @@ -331,8 +363,11 @@ fun ShortcutPicker(viewModel: EditFavoritesSheetVM) { .fillMaxWidth() .padding(vertical = 4.dp), onClick = { - val launcherApps = context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps - val sender = launcherApps.getShortcutConfigActivityIntent(it.launcherActivityInfo) ?: return@OutlinedCard + val launcherApps = + context.getSystemService(Context.LAUNCHER_APPS_SERVICE) as LauncherApps + val sender = + launcherApps.getShortcutConfigActivityIntent(it.launcherActivityInfo) + ?: return@OutlinedCard activityLauncher.launch(IntentSenderRequest.Builder(sender).build(), null) }) { Row( diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/modals/EditFavoritesSheetVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/modals/EditFavoritesSheetVM.kt index 91d6d1c5..b74f8933 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/modals/EditFavoritesSheetVM.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/modals/EditFavoritesSheetVM.kt @@ -202,5 +202,15 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent { createShortcutTarget.value = null } + fun remove(key: String) { + val gridItems = gridItems.value?.toMutableList() ?: return + val item = gridItems.find { it is FavoritesSheetGridItem.Favorite && it.item.key == key } as FavoritesSheetGridItem.Favorite? + if (item != null) { + repository.removeFromFavorites(item.item) + gridItems.remove(item) + this.gridItems.value = gridItems + } + } + } \ No newline at end of file