Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dc664edb authored by Mehdi Alizadeh's avatar Mehdi Alizadeh
Browse files

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
parent e5383718
Loading
Loading
Loading
Loading
+104 −24
Original line number Diff line number Diff line
@@ -24,6 +24,9 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVE
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.ServiceManager;
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 @@ public class NavigationModeController implements Dumpable {
    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 @@ public class NavigationModeController implements Dumpable {
    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 @@ public class NavigationModeController implements Dumpable {
                    if (DEBUG) {
                        Log.d(TAG, "ACTION_PREFERRED_ACTIVITY_CHANGED");
                    }
                    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 @@ public class NavigationModeController implements Dumpable {
        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 @@ public class NavigationModeController implements Dumpable {
        });
    }

    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 @@ public class NavigationModeController implements Dumpable {
        }
        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 @@ public class NavigationModeController implements Dumpable {
            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());
    }
}