Merge "feat(#MagnificationJoystick): add preference for enabling Joystick to Settings"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index cc8baf0..5d32022 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4104,6 +4104,10 @@
     <string name="accessibility_screen_magnification_always_on_title">Always On</string>
     <!-- Summary for accessibility magnifier preference where the magnifier never turns off on its own. [CHAR LIMIT=none] -->
     <string name="accessibility_screen_magnification_always_on_summary">Instead of deactivating magnification when changing apps, it simply zooms out to normal size. Pinch to zoom back in whenever you want.</string>
+    <!-- Title for accessibility on-screen joystick controller preference that controls magnification position. [CHAR LIMIT=35] -->
+    <string name="accessibility_screen_magnification_joystick_title">Joystick</string>
+    <!-- Summary for accessibility on-screen joystick controller preference that controls magnification position. [CHAR LIMIT=none] -->
+    <string name="accessibility_screen_magnification_joystick_summary">Activate and move the magnifier with an on-screen joystick. Tap and hold, then drag on the joystick to control the magnifier. Tap and drag to move the joystick itself.</string>
     <!-- Title for screen magnification footer. [CHAR LIMIT=60] -->
     <string name="accessibility_screen_magnification_about_title">About magnification</string>
     <!-- Screen magnification footer link content description [CHAR LIMIT=NONE] -->
diff --git a/src/com/android/settings/accessibility/MagnificationJoystickPreferenceController.java b/src/com/android/settings/accessibility/MagnificationJoystickPreferenceController.java
new file mode 100644
index 0000000..bc9e093
--- /dev/null
+++ b/src/com/android/settings/accessibility/MagnificationJoystickPreferenceController.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accessibility;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * Controller that accesses and switches the preference status of the magnification joystick feature
+ */
+public class MagnificationJoystickPreferenceController extends TogglePreferenceController
+        implements LifecycleObserver {
+
+    private static final String TAG =
+            MagnificationJoystickPreferenceController.class.getSimpleName();
+    static final String PREF_KEY = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED;
+
+    private SwitchPreference mSwitchPreference;
+
+    public MagnificationJoystickPreferenceController(Context context, String preferenceKey) {
+        super(context, preferenceKey);
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+    @Override
+    public boolean isChecked() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED, OFF) == ON;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        return Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED,
+                (isChecked ? ON : OFF));
+    }
+
+    @Override
+    public int getSliceHighlightMenuRes() {
+        return R.string.menu_key_accessibility;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mSwitchPreference = screen.findPreference(getPreferenceKey());
+    }
+
+    // TODO(b/186731461): Remove it when this controller is used in DashBoardFragment only.
+    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+    void onResume() {
+        updateState();
+    }
+
+    /**
+     * Updates the state of preference components which has been displayed by
+     * {@link MagnificationJoystickPreferenceController#displayPreference}.
+     */
+    void updateState() {
+        updateState(mSwitchPreference);
+    }
+}
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index 0558f25..9c2424c 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -217,6 +217,7 @@
         addPreferenceController(mFollowTypingPreferenceController);
 
         addAlwaysOnSetting(generalCategory);
+        addJoystickSetting(generalCategory);
     }
 
     private void addAlwaysOnSetting(PreferenceCategory generalCategory) {
@@ -244,6 +245,34 @@
         addPreferenceController(alwaysOnPreferenceController);
     }
 
+    private void addJoystickSetting(PreferenceCategory generalCategory) {
+        if (!DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                "MagnificationJoystick__enable_magnification_joystick",
+                false
+        )) {
+            return;
+        }
+
+        SwitchPreference joystickPreference = new SwitchPreference(getPrefContext());
+        joystickPreference.setTitle(
+                R.string.accessibility_screen_magnification_joystick_title);
+        joystickPreference.setSummary(
+                R.string.accessibility_screen_magnification_joystick_summary);
+        joystickPreference.setKey(
+                MagnificationJoystickPreferenceController.PREF_KEY);
+        generalCategory.addPreference(joystickPreference);
+
+        MagnificationJoystickPreferenceController joystickPreferenceController =
+                new MagnificationJoystickPreferenceController(
+                        getContext(),
+                        MagnificationJoystickPreferenceController.PREF_KEY
+                );
+        getSettingsLifecycle().addObserver(joystickPreferenceController);
+        joystickPreferenceController.displayPreference(getPreferenceScreen());
+        addPreferenceController(joystickPreferenceController);
+    }
+
     @Override
     public void showDialog(int dialogId) {
         super.showDialog(dialogId);
@@ -349,7 +378,8 @@
 
         var keysToObserve = List.of(
             Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
-            Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED
+            Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
+            Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED
         );
         contentObserver.registerKeysToObserverCallback(keysToObserve,
                 key -> updatePreferencesState());
diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationJoystickPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationJoystickPreferenceControllerTest.java
new file mode 100644
index 0000000..236f18c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationJoystickPreferenceControllerTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accessibility;
+
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class MagnificationJoystickPreferenceControllerTest {
+
+    private static final String KEY_JOYSTICK =
+            Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED;
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+    private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext));
+    private final MagnificationJoystickPreferenceController mController =
+            new MagnificationJoystickPreferenceController(mContext,
+                    MagnificationJoystickPreferenceController.PREF_KEY);
+
+    @Before
+    public void setUp() {
+        final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+        final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
+        mSwitchPreference.setKey(MagnificationJoystickPreferenceController.PREF_KEY);
+        screen.addPreference(mSwitchPreference);
+        mController.displayPreference(screen);
+    }
+
+    @Test
+    public void isChecked_defaultStateForJoystick_onResumeShouldReturnFalse() {
+        mController.onResume();
+
+        assertThat(mController.isChecked()).isFalse();
+        assertThat(mSwitchPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void isChecked_enableJoystick_onResumeShouldReturnTrue() {
+        Settings.Secure.putInt(mContext.getContentResolver(), KEY_JOYSTICK, ON);
+        mController.onResume();
+
+        assertThat(mController.isChecked()).isTrue();
+        assertThat(mSwitchPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void isChecked_disableJoystick_onResumeShouldReturnFalse() {
+        Settings.Secure.putInt(mContext.getContentResolver(), KEY_JOYSTICK, OFF);
+        mController.onResume();
+
+        assertThat(mController.isChecked()).isFalse();
+        assertThat(mSwitchPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void performClick_switchDefaultStateForJoystick_shouldReturnTrue() {
+        mController.onResume();
+
+        mSwitchPreference.performClick();
+
+        verify(mSwitchPreference).setChecked(true);
+        assertThat(mController.isChecked()).isTrue();
+        assertThat(mSwitchPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void updateState_disableJoystick_shouldReturnFalse() {
+        Settings.Secure.putInt(mContext.getContentResolver(), KEY_JOYSTICK, OFF);
+
+        mController.updateState();
+
+        verify(mSwitchPreference).setChecked(false);
+        assertThat(mController.isChecked()).isFalse();
+        assertThat(mSwitchPreference.isChecked()).isFalse();
+    }
+}