Merge "Save the user preferred shortcut on Edit A11yShortcut screen" into main
diff --git a/src/com/android/settings/accessibility/PreferredShortcuts.java b/src/com/android/settings/accessibility/PreferredShortcuts.java
index 2c9840d..d4e8e0c 100644
--- a/src/com/android/settings/accessibility/PreferredShortcuts.java
+++ b/src/com/android/settings/accessibility/PreferredShortcuts.java
@@ -19,10 +19,17 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.os.UserHandle;
+import android.util.ArrayMap;
 
+import androidx.annotation.NonNull;
+
+import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.util.ShortcutUtils;
 import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
 
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 /** Static utility methods relating to {@link PreferredShortcut} */
@@ -81,6 +88,41 @@
     }
 
     /**
+     * Update the user preferred shortcut from Settings data
+     *
+     * @param context    {@link Context} to access the {@link SharedPreferences}
+     * @param components contains a set of {@link ComponentName} the service or activity. The
+     *                   string
+     *                   representation of the ComponentName should be in the format of
+     *                   {@link ComponentName#flattenToString()}.
+     */
+    public static void updatePreferredShortcutsFromSettings(
+            @NonNull Context context, @NonNull Set<String> components) {
+        final Map<Integer, Set<String>> shortcutTypeToTargets = new ArrayMap<>();
+        for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) {
+            shortcutTypeToTargets.put(
+                    shortcutType,
+                    ShortcutUtils.getShortcutTargetsFromSettings(
+                            context, shortcutType, UserHandle.myUserId()));
+        }
+
+        for (String target : components) {
+            int shortcutTypes = ShortcutConstants.UserShortcutType.DEFAULT;
+            for (Map.Entry<Integer, Set<String>> entry : shortcutTypeToTargets.entrySet()) {
+                if (entry.getValue().contains(target)) {
+                    shortcutTypes |= entry.getKey();
+                }
+            }
+
+            if (shortcutTypes != ShortcutConstants.UserShortcutType.DEFAULT) {
+                final PreferredShortcut shortcut = new PreferredShortcut(
+                        target, shortcutTypes);
+                PreferredShortcuts.saveUserShortcutType(context, shortcut);
+            }
+        }
+    }
+
+    /**
      * Returns a immutable set of {@link PreferredShortcut#toString()} list from
      * SharedPreferences.
      */
diff --git a/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java b/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
index 7a1dd4b..a3cbb57 100644
--- a/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
@@ -51,6 +51,7 @@
 import com.android.settings.R;
 import com.android.settings.SetupWizardUtils;
 import com.android.settings.accessibility.AccessibilitySetupWizardUtils;
+import com.android.settings.accessibility.PreferredShortcuts;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settingslib.core.AbstractPreferenceController;
@@ -161,6 +162,9 @@
                 } else if (TWO_FINGERS_DOUBLE_TAP_SHORTCUT_SETTING.equals(uri)) {
                     refreshPreferenceController(TwoFingersDoubleTapShortcutOptionController.class);
                 }
+
+                PreferredShortcuts.updatePreferredShortcutsFromSettings(
+                        getContext(), mShortcutTargets);
             }
         };
 
@@ -212,6 +216,7 @@
         final AccessibilityManager am = getSystemService(
                 AccessibilityManager.class);
         am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+        PreferredShortcuts.updatePreferredShortcutsFromSettings(getContext(), mShortcutTargets);
     }
 
     @Override
diff --git a/tests/robotests/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragmentTest.java
index 53a87ba..7586954 100644
--- a/tests/robotests/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragmentTest.java
@@ -51,6 +51,7 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.SubSettings;
 import com.android.settings.accessibility.AccessibilityUtil;
+import com.android.settings.accessibility.PreferredShortcuts;
 import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
@@ -369,6 +370,50 @@
         });
     }
 
+    @Test
+    public void fragmentResumed_preferredShortcutsUpdated() {
+        mFragmentScenario = createFragScenario(/* isInSuw= */ false);
+        mFragmentScenario.moveToState(Lifecycle.State.RESUMED);
+        // Move the fragment to the background
+        mFragmentScenario.moveToState(Lifecycle.State.CREATED);
+        assertThat(
+                PreferredShortcuts.retrieveUserShortcutType(
+                        mContext, TARGET, ShortcutConstants.UserShortcutType.SOFTWARE)
+        ).isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE);
+        // Update the chosen shortcut type to Volume keys while the fragment is in the background
+        ShortcutUtils.optInValueToSettings(
+                mContext, ShortcutConstants.UserShortcutType.HARDWARE, TARGET);
+
+        mFragmentScenario.moveToState(Lifecycle.State.RESUMED);
+
+        assertThat(
+                PreferredShortcuts.retrieveUserShortcutType(
+                        mContext, TARGET, ShortcutConstants.UserShortcutType.SOFTWARE)
+        ).isEqualTo(ShortcutConstants.UserShortcutType.HARDWARE);
+    }
+
+    @Test
+    public void onVolumeKeysShortcutSettingChanged_preferredShortcutsUpdated() {
+        mFragmentScenario = createFragScenario(/* isInSuw= */ false);
+        mFragmentScenario.moveToState(Lifecycle.State.CREATED);
+        assertThat(
+                PreferredShortcuts.retrieveUserShortcutType(
+                        mContext, TARGET, ShortcutConstants.UserShortcutType.SOFTWARE)
+        ).isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE);
+
+        ShortcutUtils.optInValueToSettings(
+                mContext, ShortcutConstants.UserShortcutType.HARDWARE, TARGET);
+
+        // Calls onFragment so that the change to Setting is notified to its observer
+        mFragmentScenario.onFragment(fragment ->
+                assertThat(
+                        PreferredShortcuts.retrieveUserShortcutType(
+                                mContext, TARGET, ShortcutConstants.UserShortcutType.SOFTWARE)
+                ).isEqualTo(ShortcutConstants.UserShortcutType.HARDWARE)
+        );
+
+    }
+
     private void assertLaunchSubSettingWithCurrentTargetComponents(
             String componentName, boolean isInSuw) {
         Intent intent = shadowOf(mActivity.getApplication()).getNextStartedActivity();
diff --git a/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java b/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java
index 95a0b83..e7dfb5b 100644
--- a/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java
+++ b/tests/unit/src/com/android/settings/accessibility/PreferredShortcutsTest.java
@@ -16,17 +16,30 @@
 
 package com.android.settings.accessibility;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.provider.Settings;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.util.ShortcutUtils;
+
+import org.junit.AfterClass;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Set;
+
 /** Tests for {@link PreferredShortcuts} */
 @RunWith(AndroidJUnit4.class)
 public class PreferredShortcutsTest {
@@ -39,8 +52,20 @@
     private static final String CLASS_NAME_2 = PACKAGE_NAME_2 + ".test2";
     private static final ComponentName COMPONENT_NAME_2 = new ComponentName(PACKAGE_NAME_2,
             CLASS_NAME_2);
+    private static final ContentResolver sContentResolver =
+            ApplicationProvider.getApplicationContext().getContentResolver();
 
-    private Context mContext = ApplicationProvider.getApplicationContext();
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    @Before
+    public void setUp() {
+        clearShortcuts();
+    }
+
+    @AfterClass
+    public static void cleanUp() {
+        clearShortcuts();
+    }
 
     @Test
     public void retrieveUserShortcutType_fromSingleData_matchSavedType() {
@@ -71,4 +96,88 @@
 
         assertThat(retrieveType).isEqualTo(type1);
     }
+
+    @Test
+    public void updatePreferredShortcutsFromSetting_magnificationWithTripleTapAndVolumeKeyShortcuts_preferredShortcutsMatches() {
+        ShortcutUtils.optInValueToSettings(mContext, ShortcutConstants.UserShortcutType.HARDWARE,
+                MAGNIFICATION_CONTROLLER_NAME);
+        Settings.Secure.putInt(
+                sContentResolver,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+                AccessibilityUtil.State.ON);
+
+        PreferredShortcuts.updatePreferredShortcutsFromSettings(mContext,
+                Set.of(MAGNIFICATION_CONTROLLER_NAME));
+        int expectedShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE
+                | ShortcutConstants.UserShortcutType.TRIPLETAP;
+
+        assertThat(
+                PreferredShortcuts.retrieveUserShortcutType(
+                        mContext, MAGNIFICATION_CONTROLLER_NAME,
+                        ShortcutConstants.UserShortcutType.SOFTWARE))
+                .isEqualTo(expectedShortcutTypes);
+    }
+
+    @Test
+    public void updatePreferredShortcutsFromSetting_magnificationWithNoActiveShortcuts_noChangesOnPreferredShortcutTypes() {
+        int expectedShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE
+                | ShortcutConstants.UserShortcutType.SOFTWARE;
+        PreferredShortcuts.saveUserShortcutType(mContext,
+                new PreferredShortcut(MAGNIFICATION_CONTROLLER_NAME, expectedShortcutTypes));
+
+
+        PreferredShortcuts.updatePreferredShortcutsFromSettings(mContext,
+                Set.of(MAGNIFICATION_CONTROLLER_NAME));
+
+
+        assertThat(
+                PreferredShortcuts.retrieveUserShortcutType(
+                        mContext, MAGNIFICATION_CONTROLLER_NAME,
+                        ShortcutConstants.UserShortcutType.SOFTWARE))
+                .isEqualTo(expectedShortcutTypes);
+    }
+
+    @Test
+    public void updatePreferredShortcutsFromSetting_multipleComponents_preferredShortcutsMatches() {
+        String target1 = COLOR_INVERSION_COMPONENT_NAME.flattenToString();
+        String target2 = DALTONIZER_COMPONENT_NAME.flattenToString();
+
+        Settings.Secure.putString(sContentResolver,
+                Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, target1);
+        Settings.Secure.putString(sContentResolver,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                target1 + ShortcutConstants.SERVICES_SEPARATOR + target2);
+
+        int target1ShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE
+                | ShortcutConstants.UserShortcutType.SOFTWARE;
+        int target2ShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE;
+
+        PreferredShortcuts.updatePreferredShortcutsFromSettings(mContext, Set.of(target1, target2));
+
+        assertThat(
+                PreferredShortcuts.retrieveUserShortcutType(
+                        mContext, target1,
+                        ShortcutConstants.UserShortcutType.SOFTWARE))
+                .isEqualTo(target1ShortcutTypes);
+        assertThat(
+                PreferredShortcuts.retrieveUserShortcutType(
+                        mContext, target2,
+                        ShortcutConstants.UserShortcutType.SOFTWARE))
+                .isEqualTo(target2ShortcutTypes);
+    }
+
+    private static void clearShortcuts() {
+        Settings.Secure.putString(sContentResolver,
+                Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "");
+        Settings.Secure.putString(sContentResolver,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
+        Settings.Secure.putInt(
+                sContentResolver,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+                AccessibilityUtil.State.OFF);
+        Settings.Secure.putInt(
+                sContentResolver,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
+                AccessibilityUtil.State.OFF);
+    }
 }