Hide app from the app list's long-press menu; stop the restart on the first preference write
Hide app: a new entry in the menu a long press on an app opens in the app list. It adds the app to Lawnchair's hidden list (hidden_apps), which the list and its search already leave out and which Settings > App drawer > Hidden apps already shows, so that page is where an app is brought back. A message with Undo follows. The entry is offered in the app list only (not for home screen icons, which hiding does not touch) and not while the home screen is locked. Preference collectors: five collectors in PreferenceManager2 (icon shape, dock mode, dock on/off, dock search provider, labels in the dock) had drop(1) before distinctUntilChanged. The first emission after the dropped one then always passed, so the first write of any preference after the launcher started reloaded the model, rebuilt the grid three times and recreated the launcher. Hiding an app is such a write, made while the launcher is on screen: the list went blank for a moment and the Undo message was lost with the old activity. The operators are swapped, so only a changed value passes. Checked on the emulator: both menu layouts (labelled rows and the icon row), hiding right after a fresh start (no relaunch in the log, message stays), Undo, and unticking the apps under Hidden apps. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
1 parent
81c5c8914a
commit
77278a11e0
5 files changed
+124
-6
No files matched your search
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- "Hide app" in an app's long-press menu: an eye with a line through it. Drawn as strokes;
|
||||
the menu tints it like its other icons. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#000000"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<!-- The eye's outline, open on the side the line crosses first. -->
|
||||
<path
|
||||
android:pathData="M9.9,5.2C10.6,5.07 11.3,5 12,5C16.2,5 19.7,7.6 21.5,12C20.9,13.4 20.1,14.6 19.1,15.6M16.4,17.6C15.1,18.5 13.6,19 12,19C7.8,19 4.3,16.4 2.5,12C3.3,10 4.5,8.4 6,7.2"
|
||||
android:strokeColor="@android:color/white"
|
||||
android:strokeWidth="1.8"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<!-- What the line leaves of the pupil. -->
|
||||
<path
|
||||
android:pathData="M14.1,14.1C13.56,14.66 12.82,15 12,15C10.34,15 9,13.66 9,12C9,11.18 9.34,10.44 9.9,9.9"
|
||||
android:strokeColor="@android:color/white"
|
||||
android:strokeWidth="1.8"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" />
|
||||
<!-- The line. -->
|
||||
<path
|
||||
android:pathData="M4,4L20,20"
|
||||
android:strokeColor="@android:color/white"
|
||||
android:strokeWidth="1.8"
|
||||
android:strokeLineCap="round" />
|
||||
</vector>
|
||||
@@ -93,6 +93,9 @@
|
||||
<string name="flexera_scrub_from_home_description">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.</string>
|
||||
|
||||
<!-- Quick launch: a swipe on past the first or the last home screen page (app.flexera.home.QuickLaunch) -->
|
||||
<string name="flexera_hide_app">Hide app</string>
|
||||
<!-- Shown after an app was hidden from the app list. %1$s is the app's name -->
|
||||
<string name="flexera_app_hidden">%1$s is hidden</string>
|
||||
<string name="flexera_folder_blur">Blur behind open folders</string>
|
||||
<string name="flexera_folder_blur_description">Blurs the home screen while a folder is open</string>
|
||||
<string name="flexera_quick_launch">Quick launch</string>
|
||||
|
||||
@@ -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<LawnchairLauncher>(
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user