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

Commit d2a34ce6 authored by yyalan's avatar yyalan
Browse files

[3FT] When the chosen app is uninstalled

Use a LauncherApps callback get updates when a package is removed. Only switch to default gesture (i.e. mid click) when the gesture is used to launch app and the removed package matches the chosen app.

Demo in comment.

Bug: 409003996
Flag: com.android.settings.flags.touchpad_settings_design_update
Flag: com.android.settings.flags.three_finger_tap_app_launch
Test: TouchpadThreeFingerTapAppSelectionPreferenceControllerTest
Change-Id: I44369824b3ca289990b752a32f5b6a387ddcde8d
parent 1e7c306f
Loading
Loading
Loading
Loading
+63 −14
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import android.hardware.input.AppLaunchData.ComponentData;
import android.hardware.input.InputGestureData;
import android.hardware.input.InputManager;
import android.hardware.input.InputSettings;
import android.hardware.input.KeyGestureEvent;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -80,24 +79,73 @@ public class TouchpadThreeFingerTapAppSelectionPreferenceController extends Base

    @Nullable private PreferenceScreen mPreferenceScreen;

    private final LauncherApps.Callback mLauncherAppsCallback = new LauncherApps.Callback() {
        @Override
        public void onPackageRemoved(@Nullable String packageName, @Nullable UserHandle user) {
            if (packageName != null && isAppSelected(packageName)) {
                setGestureToDefault();
                repopulateApps();
            }
        }

        @Override
        public void onPackageAdded(@Nullable String packageName, @Nullable UserHandle user) {}

        @Override
        public void onPackageChanged(@Nullable String packageName, @Nullable UserHandle user) {}

        @Override
        public void onPackagesAvailable(
                @Nullable String[] packageNames, @Nullable UserHandle user, boolean replacing) {}

        @Override
        public void onPackagesUnavailable(
                @Nullable String[] packageNames, @Nullable UserHandle user, boolean replacing) {
            if (packageNames == null) {
                return;
            }
            for (String packageName : packageNames) {
                if (isAppSelected(packageName)) {
                    setGestureToDefault();
                    repopulateApps();
                }
            }
        }
    };

    public TouchpadThreeFingerTapAppSelectionPreferenceController(@NonNull Context context,
            @NonNull String key) {
        super(context, key);
        mLauncherApps = context.getSystemService(LauncherApps.class);
        mInputManager = context.getSystemService(InputManager.class);
        mContentResolver = context.getContentResolver();
        mLauncherApps.registerCallback(mLauncherAppsCallback);
    }

    @VisibleForTesting
    TouchpadThreeFingerTapAppSelectionPreferenceController(@NonNull Context context,
            @NonNull String key,
            LauncherApps launcherApps,
            InputManager inputManager,
            ContentObserver contentObserver) {
            @NonNull LauncherApps launcherApps,
            @NonNull InputManager inputManager,
            @NonNull ContentObserver contentObserver) {
        this(context, key);
        mLauncherApps = launcherApps;
        mInputManager = inputManager;
        mObserver = contentObserver;
        mLauncherApps.registerCallback(mLauncherAppsCallback);
    }

    private void setGestureToDefault() {
        mInputManager.removeAllCustomInputGestures(InputGestureData.Filter.TOUCHPAD);
        TouchpadThreeFingerTapUtils.setDefaultGestureType(mContentResolver);
    }

    private boolean isAppSelected(@NonNull String packageName) {
        if (TouchpadThreeFingerTapUtils.isGestureTypeLaunchApp(mContentResolver)) {
            ComponentName componentName = getSelectedAppComponent();
            return componentName != null && packageName.equals(componentName.getPackageName());
        }
        return false;
    }

    @Override
@@ -119,13 +167,15 @@ public class TouchpadThreeFingerTapAppSelectionPreferenceController extends Base
    }

    private void updateAppListSelection() {
        int current = TouchpadThreeFingerTapUtils.getCurrentGestureType(mContentResolver);

        // When the current gesture state is not app launching, the key is set to null so that no
        // app Preference will be selected
        String matchingKey =
                current == KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION
                        ? parsePreferenceKey() : null;
        String matchingKey = null;
        if (TouchpadThreeFingerTapUtils.isGestureTypeLaunchApp(mContentResolver)) {
            ComponentName componentName = getSelectedAppComponent();
            if (componentName != null) {
                matchingKey = parsePreferenceKeyFromComponent(componentName);
            }
        }
        updateCheckStatus(matchingKey);
    }

@@ -145,15 +195,14 @@ public class TouchpadThreeFingerTapAppSelectionPreferenceController extends Base
    }

    @Nullable
    private String parsePreferenceKey() {
    private ComponentName getSelectedAppComponent() {
        InputGestureData gestureData =
                mInputManager.getInputGesture(TouchpadThreeFingerTapUtils.TRIGGER);
        if (gestureData != null) {
            ComponentData componentData = (ComponentData) gestureData.getAction().appLaunchData();
            if (componentData != null) {
                ComponentName component = new ComponentName(
                return new ComponentName(
                        componentData.getPackageName(), componentData.getClassName());
                return parsePreferenceKeyFromComponent(component);
            }
        }
        return null;
@@ -178,10 +227,10 @@ public class TouchpadThreeFingerTapAppSelectionPreferenceController extends Base
    public void displayPreference(@NonNull PreferenceScreen screen) {
        super.displayPreference(screen);
        mPreferenceScreen = screen;
        populateApps();
        repopulateApps();
    }

    private void populateApps() {
    private void repopulateApps() {
        if (mPreferenceScreen == null) {
            return;
        }
+23 −1
Original line number Diff line number Diff line
@@ -41,6 +41,10 @@ public final class TouchpadThreeFingerTapUtils {
    static final InputGestureData.Trigger TRIGGER =
            createTouchpadTrigger(TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP);

    // Note that KEY_GESTURE_TYPE_UNSPECIFIED is the "mouse mid click" action
    static final Integer DEFAULT_GESTURE_TYPE =
            KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;

    private static final Map<String, Integer> PREF_KEY_TO_GESTURE_TYPE = Map.ofEntries(
            Map.entry("launch_gemini", KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT),
            Map.entry("go_home", KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
@@ -60,6 +64,15 @@ public final class TouchpadThreeFingerTapUtils {
                ActivityManager.getCurrentUser());
    }

    /**
     * Return if KEY_GESTURE_TYPE_LAUNCH_APPLICATION is the cuurent the gesture type
     * @param resolver ContentResolver
     */
    public static boolean isGestureTypeLaunchApp(@NonNull ContentResolver resolver) {
        return getCurrentGestureType(resolver)
                == KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION;
    }

    /**
     * @param prefKey Preference key for input_touchpad_three_finger_tap_action
     * @return the corresponding KeyGestureEvent of the given key, or KEY_GESTURE_TYPE_UNSPECIFIED
@@ -80,7 +93,16 @@ public final class TouchpadThreeFingerTapUtils {
    }

    /**
     * Set KEY_GESTURE_TYPE_LAUNCH_APPLICATION as the gesture type
     * Set KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED as the gesture type
     * @param resolver ContentResolver
     */
    public static void setDefaultGestureType(@NonNull ContentResolver resolver) {
        Settings.System.putIntForUser(
                resolver, TARGET_ACTION, DEFAULT_GESTURE_TYPE, ActivityManager.getCurrentUser());
    }

    /**
     * Set KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION as the gesture type
     * @param resolver ContentResolver
     */
    public static void setLaunchAppAsGestureType(@NonNull ContentResolver resolver) {
+41 −5
Original line number Diff line number Diff line
@@ -104,7 +104,8 @@ public class TouchpadThreeFingerTapAppSelectionPreferenceControllerTest {
    private final Context mContext = RuntimeEnvironment.application;
    private ContentResolver mContentResolver;
    private TouchpadThreeFingerTapAppSelectionPreferenceController mController;
    private InputGestureData mCustomInputGesture = null;
    private InputGestureData mCustomInputGesture;
    private LauncherApps.Callback mLauncherAppsCallback;

    @Before
    public void setup() {
@@ -185,7 +186,7 @@ public class TouchpadThreeFingerTapAppSelectionPreferenceControllerTest {
    public void updateState_whenActionIsLaunchApp_correspondingAppChecked() {
        ArgumentCaptor<SelectorWithWidgetPreference> captor = capturePrefs();

        setupLaunchingApp(/* matchingIndex = */ 1);
        setupAppSelection(/* matchingIndex = */ 1);
        mController.updateState(mMockPreferenceScreen);

        List<SelectorWithWidgetPreference> prefs = captor.getAllValues();
@@ -212,8 +213,7 @@ public class TouchpadThreeFingerTapAppSelectionPreferenceControllerTest {
        mController.onRadioButtonClicked(captor.getAllValues().get(clickingIndex));

        // Settings key is updated
        int gesture = TouchpadThreeFingerTapUtils.getCurrentGestureType(mContentResolver);
        assertThat(gesture).isEqualTo(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION);
        assertTrue(TouchpadThreeFingerTapUtils.isGestureTypeLaunchApp(mContentResolver));

        // InputManager gesture is updated
        assertThat(mCustomInputGesture).isNotNull();
@@ -229,6 +229,42 @@ public class TouchpadThreeFingerTapAppSelectionPreferenceControllerTest {
        assertThat(prefs.get(1).isChecked()).isFalse();
    }

    @Test
    public void onPackageRemoved_isNotSelectedApp_doNothing() {
        ArgumentCaptor<LauncherApps.Callback> captor =
                ArgumentCaptor.forClass(LauncherApps.Callback.class);
        verify(mMockLauncherApps).registerCallback(captor.capture());
        mLauncherAppsCallback = captor.getValue();

        int selectedAppIndex = 0;
        setupAppSelection(/* matchingIndex = */ selectedAppIndex);
        mController.updateState(mMockPreferenceScreen);

        mLauncherAppsCallback.onPackageRemoved(
                TEST_PACKAGE_PREFIX + 1, UserHandle.CURRENT);

        assertTrue(TouchpadThreeFingerTapUtils.isGestureTypeLaunchApp(mContentResolver));
    }

    @Test
    public void onPackageRemoved_isSelectedApp_setToDefaultGesture() {
        ArgumentCaptor<LauncherApps.Callback> captor =
                ArgumentCaptor.forClass(LauncherApps.Callback.class);
        verify(mMockLauncherApps).registerCallback(captor.capture());
        mLauncherAppsCallback = captor.getValue();

        int selectedAppIndex = 0;
        setupAppSelection(/* matchingIndex = */ selectedAppIndex);
        mController.updateState(mMockPreferenceScreen);

        mLauncherAppsCallback.onPackageRemoved(
                TEST_PACKAGE_PREFIX + selectedAppIndex, UserHandle.CURRENT);

        // Settings key is updated
        int gesture = TouchpadThreeFingerTapUtils.getCurrentGestureType(mContentResolver);
        assertThat(gesture).isEqualTo(TouchpadThreeFingerTapUtils.DEFAULT_GESTURE_TYPE);
    }

    private ArgumentCaptor<SelectorWithWidgetPreference> capturePrefs() {
        ArgumentCaptor<SelectorWithWidgetPreference> captor =
                ArgumentCaptor.forClass(SelectorWithWidgetPreference.class);
@@ -242,7 +278,7 @@ public class TouchpadThreeFingerTapAppSelectionPreferenceControllerTest {
        return captor;
    }

    private void setupLaunchingApp(int matchingIndex) {
    private void setupAppSelection(int matchingIndex) {
        AppLaunchData appLaunchData = createLaunchDataForComponent(
                TEST_PACKAGE_PREFIX + matchingIndex, TEST_CLASS_PREFIX + matchingIndex);