diff --git a/lawnchair/res/values/strings.xml b/lawnchair/res/values/strings.xml
index f214852995..99007ab6e5 100644
--- a/lawnchair/res/values/strings.xml
+++ b/lawnchair/res/values/strings.xml
@@ -166,6 +166,8 @@
Home screen
Feed, grid, icons
+ Home screen actions
+
Dock
Search bar, icon count
@@ -277,6 +279,9 @@
Reset custom icons
All custom icons will be reset. Do you want to continue?
+ Clear home screen
+ Home screen will be cleared. Do you want to continue?
+
Icons
Reset to default
@@ -777,7 +782,7 @@
-->
- Search apps, web, and more
+ Search web and more
Search apps
No apps found matching \"%1$s\"
@@ -909,4 +914,6 @@
Grant permissions
Permissions needed
Tap to grant permissions
+
+ Home screen cleared
diff --git a/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt b/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt
index ff0d253e67..d7db44fedb 100644
--- a/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt
+++ b/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt
@@ -7,10 +7,10 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import app.lawnchair.data.folder.model.FolderOrderUtils
import app.lawnchair.data.folder.model.FolderViewModel
-import app.lawnchair.flowerpot.Flowerpot
import app.lawnchair.launcher
import app.lawnchair.preferences.PreferenceManager
import app.lawnchair.preferences2.PreferenceManager2
+import app.lawnchair.util.categorizeAppsWithSystemAndGoogle
import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener
import com.android.launcher3.allapps.AllAppsStore
import com.android.launcher3.allapps.AlphabeticalAppsList
@@ -45,7 +45,6 @@ class LawnchairAlphabeticalAppsList(
private val filteredList = mutableListOf()
private val folderOrder = FolderOrderUtils.stringToIntList(prefs.drawerListOrder.get())
- private val potsManager = Flowerpot.Manager.getInstance(context)
init {
context.launcher.deviceProfile.inv.addOnChangeListener(this)
@@ -88,8 +87,10 @@ class LawnchairAlphabeticalAppsList(
if (isWorkOrPrivateSpace(appList)) return super.addAppsWithSections(appList, position)
if (!drawerListDefault) {
- val categorizedApps = potsManager.categorizeApps(appList)
- categorizedApps.forEach { (category, apps) ->
+ val validApps = appList.mapNotNull { it }
+ val finalCategorizedApps = categorizeAppsWithSystemAndGoogle(validApps, context)
+
+ finalCategorizedApps.forEach { (category, apps) ->
if (apps.size == 1) {
mAdapterItems.add(AdapterItem.asApp(apps.first()))
} else {
diff --git a/lawnchair/src/app/lawnchair/deck/LawndeckManager.kt b/lawnchair/src/app/lawnchair/deck/LawndeckManager.kt
index d7a7c74c3f..c0cd9f3cf7 100644
--- a/lawnchair/src/app/lawnchair/deck/LawndeckManager.kt
+++ b/lawnchair/src/app/lawnchair/deck/LawndeckManager.kt
@@ -6,6 +6,7 @@ import app.lawnchair.LawnchairLauncher
import app.lawnchair.flowerpot.Flowerpot
import app.lawnchair.launcher
import app.lawnchair.launcherNullable
+import app.lawnchair.util.categorizeAppsWithSystemAndGoogle
import app.lawnchair.util.restartLauncher
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherAppState
@@ -17,6 +18,7 @@ import com.android.launcher3.model.data.FolderInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.util.ComponentKey
+import com.android.launcher3.util.PackageManagerHelper
import java.io.File
import java.util.Locale
import kotlinx.coroutines.CompletableDeferred
@@ -101,9 +103,8 @@ class LawndeckManager(private val context: Context) {
onProgress?.invoke("Categorizing apps...")
- // Use flowerpot to categorize apps
- val potsManager = Flowerpot.Manager.getInstance(context)
- val categorizedApps = potsManager.categorizeApps(apps.map { it as? AppInfo })
+ val validApps = apps.mapNotNull { it as? AppInfo }
+ val finalCategorizedApps = categorizeAppsWithSystemAndGoogle(validApps, context)
onProgress?.invoke("Adding apps to workspace...")
@@ -115,7 +116,7 @@ class LawndeckManager(private val context: Context) {
var singleAppCount = 0
// Process each category
- categorizedApps.forEach { (category, categoryApps) ->
+ finalCategorizedApps.forEach { (category, categoryApps) ->
if (categoryApps.isEmpty()) return@forEach
if (categoryApps.size == 1) {
@@ -188,22 +189,32 @@ class LawndeckManager(private val context: Context) {
val activityInfo = activities[0]
val appInfo = AppInfo(context, activityInfo, user)
- // Use flowerpot to categorize the app
- val potsManager = Flowerpot.Manager.getInstance(context)
- val categorizedApps = potsManager.categorizeApps(listOf(appInfo))
+ val intent = appInfo.intent
- if (categorizedApps.isEmpty()) {
- // No category found, add directly to workspace
- ItemInstallQueue.INSTANCE.get(context).queueItem(packageName, user)
- return
- }
+ // Determine category: Google Apps > System Apps > Flowerpot categories
+ val category = when {
+ packageName.startsWith("com.google.") -> "Google Apps"
- // Get the category for this app (categorizedApps is a Map>)
- val categoryEntry = categorizedApps.entries.firstOrNull() ?: run {
- ItemInstallQueue.INSTANCE.get(context).queueItem(packageName, user)
- return
+ intent != null && PackageManagerHelper.isSystemApp(context, intent) -> "System Apps"
+
+ else -> {
+ // Use flowerpot to categorize the app
+ val potsManager = Flowerpot.Manager.getInstance(context)
+ val categorizedApps = potsManager.categorizeApps(listOf(appInfo))
+
+ if (categorizedApps.isEmpty()) {
+ // No category found, add directly to workspace
+ ItemInstallQueue.INSTANCE.get(context).queueItem(packageName, user)
+ return
+ }
+
+ // Get the category from flowerpot
+ categorizedApps.entries.firstOrNull()?.key ?: run {
+ ItemInstallQueue.INSTANCE.get(context).queueItem(packageName, user)
+ return
+ }
+ }
}
- val category = categoryEntry.key
// Check if there's already a folder for this category on workspace
val existingFolder = findFolderByCategory(category, dataModel)
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt
index caee77a71c..0dbee1ad34 100644
--- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt
+++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt
@@ -44,6 +44,7 @@ import app.lawnchair.ui.preferences.components.layout.PreferenceGroup
import app.lawnchair.ui.preferences.components.layout.PreferenceLayout
import app.lawnchair.ui.preferences.navigation.HomeScreenGrid
import app.lawnchair.util.collectAsStateBlocking
+import com.android.launcher3.LauncherAppState
import com.android.launcher3.R
import com.android.launcher3.Utilities
import kotlinx.coroutines.launch
@@ -67,6 +68,7 @@ fun HomeScreenPreferences(
) {
val lockHomeScreenAdapter = prefs2.lockHomeScreen.getAdapter()
val showDeckLayout = prefs2.showDeckLayout.getAdapter().state.value
+ val context = LocalContext.current
if (showDeckLayout) {
HomeLayoutSettings()
@@ -101,6 +103,20 @@ fun HomeScreenPreferences(
)
}
}
+ PreferenceGroup(heading = stringResource(id = R.string.home_screen_actions)) {
+ Item {
+ ClickablePreference(
+ label = stringResource(id = R.string.remove_all_views_from_home_screen),
+ confirmationText = stringResource(id = R.string.remove_all_views_from_home_screen_desc),
+ onClick = {
+ scope.launch {
+ LauncherAppState.getInstance(context).clearAllViewsFromHomeScreen()
+ }
+ },
+ )
+ }
+ }
+ PreferenceGroup(heading = stringResource(id = R.string.minus_one)) {
val feedAvailable = OverlayCallbackImpl.minusOneAvailable(LocalContext.current)
val enableFeedAdapter = prefs2.enableFeed.getAdapter()
PreferenceGroup(heading = stringResource(id = R.string.minus_one)) {
diff --git a/lawnchair/src/app/lawnchair/util/AppCategorizationUtils.kt b/lawnchair/src/app/lawnchair/util/AppCategorizationUtils.kt
new file mode 100644
index 0000000000..db1c7d0cc3
--- /dev/null
+++ b/lawnchair/src/app/lawnchair/util/AppCategorizationUtils.kt
@@ -0,0 +1,54 @@
+package app.lawnchair.util
+
+import android.content.Context
+import app.lawnchair.flowerpot.Flowerpot
+import com.android.launcher3.model.data.AppInfo
+import com.android.launcher3.util.PackageManagerHelper
+
+/**
+ * Categorizes apps into System Apps, Google Apps, and Flowerpot categories.
+ *
+ * @param apps List of apps to categorize
+ * @param context Context for checking system apps and accessing Flowerpot
+ * @return Map of category names to lists of apps in that category
+ */
+fun categorizeAppsWithSystemAndGoogle(
+ apps: List,
+ context: Context,
+): Map> {
+ val systemApps = mutableListOf()
+ val googleApps = mutableListOf()
+ val otherApps = mutableListOf()
+
+ apps.forEach { app ->
+ val packageName = app.targetPackage ?: return@forEach
+ val intent = app.intent
+
+ // Check if it's a Google app first (Google apps can also be system apps)
+ when {
+ packageName.startsWith("com.google.") -> googleApps.add(app)
+ intent != null && PackageManagerHelper.isSystemApp(context, intent) -> systemApps.add(app)
+ else -> otherApps.add(app)
+ }
+ }
+
+ // Use flowerpot to categorize other apps (non-system, non-Google)
+ val potsManager = Flowerpot.Manager.getInstance(context)
+ val categorizedApps = potsManager.categorizeApps(otherApps)
+
+ // Build final categorized apps map
+ val finalCategorizedApps = mutableMapOf>()
+
+ if (systemApps.isNotEmpty()) {
+ finalCategorizedApps["System Apps"] = systemApps
+ }
+
+ if (googleApps.isNotEmpty()) {
+ finalCategorizedApps["Google Apps"] = googleApps
+ }
+
+ // Add flowerpot categorized apps
+ finalCategorizedApps.putAll(categorizedApps)
+
+ return finalCategorizedApps
+}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index f885daedf9..f9d1e2d7fb 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -153,6 +153,35 @@ public class ModelWriter {
throw e;
}
}
+
+ /**
+ * Clears all views from the home screen.
+ */
+ public boolean clearAllHomeScreenViewsByType(int type) {
+ final ArrayList itemsToRemove = new ArrayList<>();
+
+ for (ItemInfo item : mBgDataModel.itemsIdMap) {
+ if (item.container == type) {
+ itemsToRemove.add(item);
+ }
+ }
+
+ if (itemsToRemove.isEmpty()) {
+ return false;
+ }
+
+ enqueueDeleteRunnable(newModelTask(() -> {
+ final ModelDbController db = mModel.getModelDbController();
+
+ for (ItemInfo item : itemsToRemove) {
+ db.delete(TABLE_NAME, itemIdMatch(item.id), null);
+ mBgDataModel.removeItem(mContext, item);
+ }
+ }));
+
+ mModel.forceReload();
+ return true;
+ }
/**
* Move an item in the DB to a new