Merge "Allow uninstallation of current home app" into main
diff --git a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
index 0ccbe5b..b80de32 100644
--- a/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppButtonsPreferenceController.java
@@ -32,6 +32,7 @@
 import android.content.om.OverlayInfo;
 import android.content.om.OverlayManager;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -434,10 +435,17 @@
                     // No preferred default, so permit uninstall only when
                     // there is more than one candidate
                     enabled = (mHomePackages.size() > 1);
-                } else {
-                    // There is an explicit default home app -- forbid uninstall of
-                    // that one, but permit it for installed-but-inactive ones.
-                    enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName());
+                } else if (mPackageInfo.packageName.equals(currentDefaultHome.getPackageName())) {
+                    if (Flags.improveHomeAppBehavior()) {
+                        // Allow uninstallation of current home app if it is a non-system app
+                        // and/or there are other candidate apps available.
+                        if (mPackageInfo.applicationInfo.isSystemApp()
+                                || mHomePackages.size() == 1) {
+                            enabled = false;
+                        }
+                    } else {
+                        enabled = false;
+                    }
                 }
             }
         }
diff --git a/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt b/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt
index 5fa9436..b8019fb 100644
--- a/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt
@@ -21,6 +21,7 @@
 import android.content.Context
 import android.content.om.OverlayManager
 import android.content.pm.ApplicationInfo
+import android.content.pm.Flags
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
 import android.util.Log
@@ -95,7 +96,7 @@
 
             isDisallowControl(app) -> return false
 
-            uninstallDisallowedDueToHomeApp(app.packageName) -> return false
+            uninstallDisallowedDueToHomeApp(app) -> return false
 
             // Resource overlays can be uninstalled iff they are public (installed on /data) and
             // disabled. ("Enabled" means they are in use by resource management.)
@@ -113,7 +114,8 @@
      * can go to Home settings and pick a different one, after which we'll permit uninstallation
      * of the now-not-default one.
      */
-    private fun uninstallDisallowedDueToHomeApp(packageName: String): Boolean {
+    fun uninstallDisallowedDueToHomeApp(applicationInfo: ApplicationInfo): Boolean {
+        val packageName = applicationInfo.packageName
         val homePackageInfo = getHomePackageInfo()
         return when {
             packageName !in homePackageInfo.homePackages -> false
@@ -121,8 +123,17 @@
             // Disallow uninstall when this is the only home app.
             homePackageInfo.homePackages.size == 1 -> true
 
-            // Disallow if this is the explicit default home app.
-            else -> packageName == homePackageInfo.currentDefaultHome?.packageName
+            packageName == homePackageInfo.currentDefaultHome?.packageName -> {
+                if (Flags.improveHomeAppBehavior()) {
+                    // Disallow the uninstallation of the current home app if it is a system app.
+                    return applicationInfo.isSystemApp()
+                } else {
+                    // Disallow if this is the explicit default home app.
+                    return true
+                }
+            }
+
+            else -> false
         }
     }
 
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java
index 54c2d6e..6fc01fc 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppButtonsPreferenceControllerTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -38,15 +39,18 @@
 import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.settings.SettingsEnums;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.om.OverlayInfo;
 import android.content.om.OverlayManager;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.util.ArraySet;
 import android.view.View;
 
@@ -107,7 +111,7 @@
     @Mock
     private OverlayManager mOverlayManager;
     @Mock
-    private PackageManager mPackageManger;
+    private PackageManager mPackageManager;
     @Mock
     private DevicePolicyManager mDpm;
     @Mock
@@ -132,7 +136,7 @@
         mContext = RuntimeEnvironment.application;
         doReturn(mDpm).when(mSettingsActivity).getSystemService(Context.DEVICE_POLICY_SERVICE);
         doReturn(mUserManager).when(mSettingsActivity).getSystemService(Context.USER_SERVICE);
-        doReturn(mPackageManger).when(mSettingsActivity).getPackageManager();
+        doReturn(mPackageManager).when(mSettingsActivity).getPackageManager();
         doReturn(mAm).when(mSettingsActivity).getSystemService(Context.ACTIVITY_SERVICE);
         doReturn(mOverlayManager).when(mSettingsActivity).
                 getSystemService(OverlayManager.class);
@@ -184,7 +188,7 @@
     @Test
     public void retrieveAppEntry_hasAppEntry_notNull()
             throws PackageManager.NameNotFoundException {
-        doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
+        doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(anyString(), anyInt());
 
         mController.retrieveAppEntry();
 
@@ -195,7 +199,7 @@
     @Test
     public void retrieveAppEntry_noAppEntry_null() throws PackageManager.NameNotFoundException {
         doReturn(null).when(mState).getEntry(eq(PACKAGE_NAME), anyInt());
-        doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
+        doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(anyString(), anyInt());
 
         mController.retrieveAppEntry();
 
@@ -207,7 +211,7 @@
     public void retrieveAppEntry_throwException_null() throws
             PackageManager.NameNotFoundException {
         doReturn(mAppEntry).when(mState).getEntry(anyString(), anyInt());
-        doThrow(new PackageManager.NameNotFoundException()).when(mPackageManger).getPackageInfo(
+        doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager).getPackageInfo(
                 anyString(), anyInt());
 
         mController.retrieveAppEntry();
@@ -225,7 +229,7 @@
 
     @Test
     public void updateOpenButton_haveLaunchIntent_buttonShouldBeEnable() {
-        doReturn(new Intent()).when(mPackageManger).getLaunchIntentForPackage(anyString());
+        doReturn(new Intent()).when(mPackageManager).getLaunchIntentForPackage(anyString());
 
         mController.updateOpenButton();
 
@@ -347,6 +351,35 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
+    public void updateUninstallButton_isNotSystemAndIsCurrentHomeAndHasOneHome_setButtonDisable() {
+        doReturn(false).when(mController).isSystemPackage(any(), any(), any());
+        doReturn(new ComponentName(PACKAGE_NAME, "cls")).when(mPackageManager).getHomeActivities(
+                anyList());
+
+        mController.mHomePackages.add(PACKAGE_NAME);
+
+        mController.updateUninstallButton();
+
+        verify(mButtonPrefs).setButton2Enabled(false);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
+    public void updateUninstallButton_isNotSystemAndIsCurrentHomeAndHasOtherHome_setButtonEnable() {
+        doReturn(false).when(mController).isSystemPackage(any(), any(), any());
+        doReturn(new ComponentName(PACKAGE_NAME, "cls")).when(mPackageManager).getHomeActivities(
+                anyList());
+
+        mController.mHomePackages.add(PACKAGE_NAME);
+        mController.mHomePackages.add("com.android.home.fake");
+
+        mController.updateUninstallButton();
+
+        verify(mButtonPrefs).setButton2Enabled(true);
+    }
+
+    @Test
     public void updateUninstallButton_isSystemRro_setButtonDisable() {
         mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
 
@@ -477,7 +510,7 @@
             throws PackageManager.NameNotFoundException {
         doReturn(AppButtonsPreferenceController.AVAILABLE)
                 .when(mController).getAvailabilityStatus();
-        doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
+        doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(anyString(), anyInt());
         doReturn(mButtonPrefs).when(mScreen).findPreference(anyString());
         mController.displayPreference(mScreen);
         mController.mButtonsPref = null;
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonRepositoryTest.kt
index 49b60fb..1eb1a7f 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonRepositoryTest.kt
@@ -20,8 +20,11 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.Flags
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
+import android.platform.test.annotations.RequiresFlagsEnabled
 import androidx.core.os.bundleOf
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -126,9 +129,62 @@
         assertThat(homePackageInfo.homePackages).containsExactly(PACKAGE_NAME)
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
+    fun uninstallDisallowedDueToHomeApp_isNotSystemAndIsCurrentHomeAndHasOnlyOneHomeApp() {
+        val app = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+        }
+
+        mockGetHomeActivities(
+            homeActivities = listOf(RESOLVE_INFO),
+            currentDefaultHome = COMPONENT_NAME,
+        )
+
+        val value = appButtonRepository.uninstallDisallowedDueToHomeApp(app)
+
+        assertThat(value).isTrue()
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
+    fun uninstallDisallowedDueToHomeApp_isNotSystemAndIsCurrentHomeAndHasOtherHomeApps() {
+        val app = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+        }
+
+        mockGetHomeActivities(
+            homeActivities = listOf(RESOLVE_INFO, RESOLVE_INFO_FAKE),
+            currentDefaultHome = COMPONENT_NAME,
+        )
+
+        val value = appButtonRepository.uninstallDisallowedDueToHomeApp(app)
+
+        assertThat(value).isFalse()
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
+    fun uninstallDisallowedDueToHomeApp_isSystemAndIsCurrentHomeAndHasOtherHomeApps() {
+        val app = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            flags = ApplicationInfo.FLAG_SYSTEM
+        }
+
+        mockGetHomeActivities(
+            homeActivities = listOf(RESOLVE_INFO, RESOLVE_INFO_FAKE),
+            currentDefaultHome = COMPONENT_NAME,
+        )
+
+        val value = appButtonRepository.uninstallDisallowedDueToHomeApp(app)
+
+        assertThat(value).isTrue()
+    }
+
     private companion object {
         const val PACKAGE_NAME = "packageName"
         const val PACKAGE_NAME_ALTERNATE = "packageName.alternate"
+        const val PACKAGE_NAME_FAKE = "packageName.fake"
         const val ACTIVITY_NAME = "activityName"
         val COMPONENT_NAME = ComponentName(PACKAGE_NAME, ACTIVITY_NAME)
         val RESOLVE_INFO = ResolveInfo().apply {
@@ -136,6 +192,11 @@
                 packageName = PACKAGE_NAME
             }
         }
+        val RESOLVE_INFO_FAKE = ResolveInfo().apply {
+            activityInfo = ActivityInfo().apply {
+                packageName = PACKAGE_NAME_FAKE
+            }
+        }
         val RESOLVE_INFO_WITH_ALTERNATE = ResolveInfo().apply {
             activityInfo = ActivityInfo().apply {
                 packageName = PACKAGE_NAME