diff --git a/flexera/res/drawable/ic_flexera_hide.xml b/flexera/res/drawable/ic_flexera_hide.xml new file mode 100644 index 0000000000..414ab1ea06 --- /dev/null +++ b/flexera/res/drawable/ic_flexera_hide.xml @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/flexera/res/values/flexera_strings.xml b/flexera/res/values/flexera_strings.xml index 5a23abfc49..7d1e245415 100644 --- a/flexera/res/values/flexera_strings.xml +++ b/flexera/res/values/flexera_strings.xml @@ -93,6 +93,9 @@ Slide up or down along the alphabet\'s edge of the home screen to open the app list at a letter. Swipe up and hold does the same from anywhere. + Hide app + + %1$s is hidden Blur behind open folders Blurs the home screen while a folder is open Quick launch diff --git a/flexera/src/app/flexera/drawer/HideApp.kt b/flexera/src/app/flexera/drawer/HideApp.kt new file mode 100644 index 0000000000..6de740a78a --- /dev/null +++ b/flexera/src/app/flexera/drawer/HideApp.kt @@ -0,0 +1,78 @@ +package app.flexera.drawer + +import android.view.View +import androidx.lifecycle.lifecycleScope +import app.lawnchair.LawnchairLauncher +import app.lawnchair.preferences2.PreferenceManager2 +import app.lawnchair.preferences2.firstCached +import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS +import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION +import com.android.launcher3.R +import com.android.launcher3.model.data.ItemInfo +import com.android.launcher3.popup.SystemShortcut +import com.android.launcher3.util.ComponentKey +import com.android.launcher3.views.Snackbar +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch + +/** + * "Hide app" in the menu a long press on an app opens in the app list. + * + * It puts the app on the hidden list Lawnchair already has: the one the app list and its search + * leave out, kept under Settings > App drawer > Hidden apps, where an app is brought back. Before + * this, hiding meant going through that settings page or through Customize. A message with Undo + * follows, because the app is gone from the list the moment it is hidden. + */ +object HideApp { + + /** + * Offered for apps in the app list only: hiding does nothing to an icon on the home screen, + * so offering it there would promise something else. Not offered while the home screen is + * locked against changes, like Customize and Uninstall. + */ + @JvmField + val SHORTCUT = SystemShortcut.Factory { launcher: LawnchairLauncher, itemInfo: ItemInfo, originalView: View -> + val component = itemInfo.targetComponent + when { + itemInfo.container != CONTAINER_ALL_APPS -> null + itemInfo.itemType != ITEM_TYPE_APPLICATION || component == null -> null + PreferenceManager2.getInstance(launcher).lockHomeScreen.firstCached() -> null + else -> Shortcut(launcher, itemInfo, originalView, ComponentKey(component, itemInfo.user)) + } + } + + private class Shortcut( + launcher: LawnchairLauncher, + itemInfo: ItemInfo, + originalView: View, + private val app: ComponentKey, + ) : SystemShortcut( + R.drawable.ic_flexera_hide, + R.string.flexera_hide_app, + launcher, + itemInfo, + originalView, + ) { + override fun onClick(view: View) { + val launcher = mTarget + val name = mItemInfo.title?.toString().orEmpty() + dismissTaskMenuView() + setHidden(launcher, app, hidden = true) + Snackbar.show( + launcher, + launcher.getString(R.string.flexera_app_hidden, name), + R.string.undo, + null, + ) { setHidden(launcher, app, hidden = false) } + } + } + + private fun setHidden(launcher: LawnchairLauncher, app: ComponentKey, hidden: Boolean) { + val hiddenApps = PreferenceManager2.getInstance(launcher).hiddenApps + val key = app.toString() + launcher.lifecycleScope.launch { + val now = hiddenApps.get().first() + hiddenApps.set(if (hidden) now + key else now - key) + } + } +} diff --git a/lawnchair/src/app/lawnchair/LawnchairLauncher.kt b/lawnchair/src/app/lawnchair/LawnchairLauncher.kt index 77264a141c..05a5acc0ca 100644 --- a/lawnchair/src/app/lawnchair/LawnchairLauncher.kt +++ b/lawnchair/src/app/lawnchair/LawnchairLauncher.kt @@ -283,7 +283,8 @@ class LawnchairLauncher : QuickstepLauncher() { super.getSupportedShortcuts(container), Stream.concat( Stream.of(LawnchairShortcut.UNINSTALL, LawnchairShortcut.CUSTOMIZE, LawnchairShortcut.OPEN_IN_STORE), - Stream.of(LawnchairShortcut.PAUSE_APPS), + // Flexera Launcher: "Hide app", which its factory offers in the app list only. + Stream.of(LawnchairShortcut.PAUSE_APPS, app.flexera.drawer.HideApp.SHORTCUT), ), ) diff --git a/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt b/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt index a048c7761d..f9ddae8435 100644 --- a/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt +++ b/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt @@ -919,10 +919,16 @@ class PreferenceManager2 @Inject constructor( .onEach { cachedPreferences = it } .launchIn(scope) + // Flexera Launcher: in the five collectors below, distinctUntilChanged comes before + // drop(1). Upstream has them the other way round, which lets the first emission after + // the dropped one through whatever its value: the first write of any preference after + // the launcher started then reloaded the model, rebuilt the grid three times and + // recreated the launcher. Hiding an app from the app list is such a write, made while + // the owner looks at the launcher. initializeIconShape(iconShape.firstCached(this)) iconShape.get() - .drop(1) .distinctUntilChanged() + .drop(1) .onEach { shape -> initializeIconShape(shape) L3ThemeManager.INSTANCE.get(context) @@ -931,14 +937,14 @@ class PreferenceManager2 @Inject constructor( .launchIn(scope) hotseatMode.get() - .drop(1) .distinctUntilChanged() + .drop(1) .onEach { reloadHelper.restart() } .launchIn(scope) isHotseatEnabled.get() - .drop(1) .distinctUntilChanged() + .drop(1) .onEach { reloadHelper.recreate() reloadHelper.reloadGrid() @@ -946,14 +952,14 @@ class PreferenceManager2 @Inject constructor( .launchIn(scope) hotseatQsbProvider.get() - .drop(1) .distinctUntilChanged() + .drop(1) .onEach { reloadHelper.recreate() } .launchIn(scope) enableLabelInDock.get() - .drop(1) .distinctUntilChanged() + .drop(1) .onEach { reloadHelper.reloadGrid() } .launchIn(scope) }