Show notifications when default launcher changes

Shows a notification when we have to switch away from Gesture Nav
because the new launcher doesn't support it.

Also shows another notification when previously we had to switch away
from Gesture Nav, but now the new launcher supports it.

Bug: 129532605
Test: Manual test
Change-Id: Ie954dd5dea1b45795f5c0e22ab7cc1cffde4aabe
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index 7cd8f7b..77eb469 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -24,6 +24,9 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
 
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -38,13 +41,16 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseBooleanArray;
 
 import com.android.systemui.Dumpable;
+import com.android.systemui.R;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.NotificationChannels;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -63,6 +69,9 @@
     private static final String TAG = NavigationModeController.class.getSimpleName();
     private static final boolean DEBUG = false;
 
+    static final String SHARED_PREFERENCES_NAME = "navigation_mode_controller_preferences";
+    static final String PREFS_SWITCHED_FROM_GESTURE_NAV_KEY = "switched_from_gesture_nav";
+
     public interface ModeChangedListener {
         void onNavigationModeChanged(int mode);
     }
@@ -78,6 +87,8 @@
     private int mMode = NAV_BAR_MODE_3BUTTON;
     private ArrayList<ModeChangedListener> mListeners = new ArrayList<>();
 
+    private String mLastDefaultLauncher;
+
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -92,7 +103,13 @@
                     if (DEBUG) {
                         Log.d(TAG, "ACTION_PREFERRED_ACTIVITY_CHANGED");
                     }
-                    switchFromGestureNavModeIfNotSupportedByDefaultLauncher();
+                    final String launcher = getDefaultLauncherPackageName(mCurrentUserContext);
+                    // Check if it is a default launcher change
+                    if (!TextUtils.equals(mLastDefaultLauncher, launcher)) {
+                        switchFromGestureNavModeIfNotSupportedByDefaultLauncher();
+                        showNotificationIfDefaultLauncherSupportsGestureNav();
+                        mLastDefaultLauncher = launcher;
+                    }
                     break;
             }
         }
@@ -157,6 +174,8 @@
         IntentFilter preferredActivityFilter = new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, preferredActivityFilter, null,
                 null);
+        // We are only interested in launcher changes, so keeping track of the current default.
+        mLastDefaultLauncher = getDefaultLauncherPackageName(mContext);
 
         updateCurrentInteractionMode(false /* notify */);
         switchFromGestureNavModeIfNotSupportedByDefaultLauncher();
@@ -287,6 +306,84 @@
         });
     }
 
+    private void switchFromGestureNavModeIfNotSupportedByDefaultLauncher() {
+        if (getCurrentInteractionMode(mCurrentUserContext) != NAV_BAR_MODE_GESTURAL) {
+            return;
+        }
+        final Boolean supported = isGestureNavSupportedByDefaultLauncher(mCurrentUserContext);
+        if (supported == null || supported) {
+            return;
+        }
+
+        setModeOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, USER_CURRENT);
+        showNotification(mCurrentUserContext, R.string.notification_content_system_nav_changed);
+        mCurrentUserContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
+                .edit().putBoolean(PREFS_SWITCHED_FROM_GESTURE_NAV_KEY, true).apply();
+    }
+
+    private void showNotificationIfDefaultLauncherSupportsGestureNav() {
+        boolean previouslySwitchedFromGestureNav = mCurrentUserContext
+                .getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
+                .getBoolean(PREFS_SWITCHED_FROM_GESTURE_NAV_KEY, false);
+        if (!previouslySwitchedFromGestureNav) {
+            return;
+        }
+        if (getCurrentInteractionMode(mCurrentUserContext) == NAV_BAR_MODE_GESTURAL) {
+            return;
+        }
+        final Boolean supported = isGestureNavSupportedByDefaultLauncher(mCurrentUserContext);
+        if (supported == null || !supported) {
+            return;
+        }
+
+        showNotification(mCurrentUserContext, R.string.notification_content_gesture_nav_available);
+        mCurrentUserContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
+                .edit().putBoolean(PREFS_SWITCHED_FROM_GESTURE_NAV_KEY, false).apply();
+    }
+
+    /**
+     * Returns null if there is no default launcher set for the current user. Returns true if the
+     * current default launcher supports Gesture Navigation. Returns false otherwise.
+     */
+    private Boolean isGestureNavSupportedByDefaultLauncher(Context context) {
+        final String defaultLauncherPackageName = getDefaultLauncherPackageName(context);
+        if (DEBUG) {
+            Log.d(TAG, "isGestureNavSupportedByDefaultLauncher:"
+                    + " defaultLauncher=" + defaultLauncherPackageName
+                    + " contextUser=" + context.getUserId());
+        }
+        if (defaultLauncherPackageName == null) {
+            return null;
+        }
+        ComponentName recentsComponentName = ComponentName.unflattenFromString(
+                context.getString(com.android.internal.R.string.config_recentsComponentName));
+        return recentsComponentName.getPackageName().equals(defaultLauncherPackageName);
+    }
+
+    private String getDefaultLauncherPackageName(Context context) {
+        final ComponentName cn = context.getPackageManager().getHomeActivities(new ArrayList<>());
+        if (cn == null) {
+            return null;
+        }
+        return cn.getPackageName();
+    }
+
+    private void showNotification(Context context, int resId) {
+        final CharSequence message = context.getResources().getString(resId);
+        if (DEBUG) {
+            Log.d(TAG, "showNotification: message=" + message);
+        }
+
+        final Notification.Builder builder =
+                new Notification.Builder(mContext, NotificationChannels.ALERTS)
+                        .setContentText(message)
+                        .setStyle(new Notification.BigTextStyle())
+                        .setSmallIcon(R.drawable.ic_info)
+                        .setAutoCancel(true)
+                        .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0));
+        context.getSystemService(NotificationManager.class).notify(TAG, 0, builder.build());
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("NavigationModeController:");
@@ -299,6 +396,12 @@
         }
         pw.println("  defaultOverlays=" + defaultOverlays);
         dumpAssetPaths(mCurrentUserContext);
+
+        pw.println("  defaultLauncher=" + mLastDefaultLauncher);
+        boolean previouslySwitchedFromGestureNav = mCurrentUserContext
+                .getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
+                .getBoolean(PREFS_SWITCHED_FROM_GESTURE_NAV_KEY, false);
+        pw.println("  previouslySwitchedFromGestureNav=" + previouslySwitchedFromGestureNav);
     }
 
     private void dumpAssetPaths(Context context) {
@@ -308,27 +411,4 @@
             Log.d(TAG, "    " + a.getAssetPath());
         }
     }
-
-    private void switchFromGestureNavModeIfNotSupportedByDefaultLauncher() {
-        if (getCurrentInteractionMode(mCurrentUserContext) == NAV_BAR_MODE_GESTURAL
-                && !isGestureNavSupportedByDefaultLauncher(mCurrentUserContext)) {
-            setModeOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, USER_CURRENT);
-        }
-    }
-
-    private boolean isGestureNavSupportedByDefaultLauncher(Context context) {
-        final ComponentName cn = context.getPackageManager().getHomeActivities(new ArrayList<>());
-        if (cn == null) {
-            // There is no default home app set for the current user, don't make any changes yet.
-            return true;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "isGestureNavSupportedByDefaultLauncher: launcher=" + cn.getPackageName()
-                    + " contextUser=" + context.getUserId());
-        }
-
-        ComponentName recentsComponentName = ComponentName.unflattenFromString(context.getString(
-                com.android.internal.R.string.config_recentsComponentName));
-        return recentsComponentName.getPackageName().equals(cn.getPackageName());
-    }
 }