diff --git a/res/drawable/accessibility_button_navigation.xml b/res/drawable/accessibility_button_navigation.xml
new file mode 100644
index 00000000000..82e3c70174f
--- /dev/null
+++ b/res/drawable/accessibility_button_navigation.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/drawable/accessibility_button_preview_base.xml b/res/drawable/accessibility_button_preview_base.xml
new file mode 100644
index 00000000000..9e3ec598586
--- /dev/null
+++ b/res/drawable/accessibility_button_preview_base.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/drawable/accessibility_button_preview_large_floating_menu.xml b/res/drawable/accessibility_button_preview_large_floating_menu.xml
new file mode 100644
index 00000000000..e003dc7322c
--- /dev/null
+++ b/res/drawable/accessibility_button_preview_large_floating_menu.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/accessibility_button_preview_small_floating_menu.xml b/res/drawable/accessibility_button_preview_small_floating_menu.xml
new file mode 100644
index 00000000000..3ff8e4b25f4
--- /dev/null
+++ b/res/drawable/accessibility_button_preview_small_floating_menu.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/accessibility_shortcut_type_software_floating.xml b/res/drawable/accessibility_shortcut_type_software_floating.xml
new file mode 100644
index 00000000000..958201515f1
--- /dev/null
+++ b/res/drawable/accessibility_shortcut_type_software_floating.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/accessibility_button_preview.xml b/res/layout/accessibility_button_preview.xml
new file mode 100644
index 00000000000..07cb0ffb8ab
--- /dev/null
+++ b/res/layout/accessibility_button_preview.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 183dd1e579d..2057c50626f 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -966,6 +966,35 @@
- -1
+
+
+ - Floating over other apps
+ - Navigation bar
+
+
+
+
+
+
+ - 1
+
+ - 0
+
+
+
+
+ - Small
+ - Large
+
+
+
+
+
+ - 0
+
+ - 1
+
+
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4fef726d599..dbf21fc8626 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -57,6 +57,8 @@
320dp
+ 200dp
+
4dp
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 89665de59b9..75b7958bf3d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5106,6 +5106,8 @@
Use new accessibility gesture
To use this feature, tap the accessibility button %s on the bottom of your screen.\n\nTo switch between features, touch & hold the accessibility button.
+
+ To use this feature, tap the accessibility button on your screen.
To use this feature, press & hold both volume keys.
@@ -5136,6 +5138,8 @@
Swipe up from the bottom of the screen with 2 fingers.\n\nTo switch between features, swipe up with 2 fingers and hold.
Swipe up from the bottom of the screen with 3 fingers.\n\nTo switch between features, swipe up with 3 fingers and hold.
+
+ Customize accessibility button
Hold volume keys
@@ -5164,6 +5168,26 @@
Shortcut from lock screen
Allow feature shortcut to turn on from the lock screen. Hold both volume keys for a few seconds.
+
+ Accessibility button
+
+ Quickly access accessibility features
+
+ Quickly access accessibility features from any screen. \n\nTo get started, go to accessibility settings and select a feature. Tap on the shortcut and select the accessibility button.
+
+ Location
+
+ Size
+
+ Fade when not in use
+
+ Fades after a few seconds so it\u2019s easier to see your screen
+
+ Transparency when not in use
+
+ Transparent
+
+ Non-transparent
High contrast text
diff --git a/res/xml/accessibility_button_settings.xml b/res/xml/accessibility_button_settings.xml
new file mode 100644
index 00000000000..5e81616bffc
--- /dev/null
+++ b/res/xml/accessibility_button_settings.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/accessibility_shortcuts_settings.xml b/res/xml/accessibility_shortcuts_settings.xml
index 35314e708bb..465f96d74df 100644
--- a/res/xml/accessibility_shortcuts_settings.xml
+++ b/res/xml/accessibility_shortcuts_settings.xml
@@ -21,6 +21,13 @@
android:persistent="false"
android:title="@string/accessibility_shortcuts_settings_title">
+
+
mValueTitleMap = new ArrayMap<>();
+ private int mDefaultLocation;
+
+ public AccessibilityButtonLocationPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ initValueTitleMap();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isGestureNavigateEnabled(mContext)
+ ? DISABLED_DEPENDENT_SETTING : AVAILABLE;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final ListPreference listPreference = (ListPreference) preference;
+ final Integer value = Ints.tryParse((String) newValue);
+ if (value != null) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, value);
+ updateState(listPreference);
+ }
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final ListPreference listPreference = (ListPreference) preference;
+
+ listPreference.setValue(getCurrentAccessibilityButtonMode());
+ }
+
+ private String getCurrentAccessibilityButtonMode() {
+ final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mDefaultLocation);
+ return String.valueOf(mode);
+ }
+
+ private void initValueTitleMap() {
+ if (mValueTitleMap.size() == 0) {
+ final String[] values = mContext.getResources().getStringArray(
+ R.array.accessibility_button_location_selector_values);
+ final String[] titles = mContext.getResources().getStringArray(
+ R.array.accessibility_button_location_selector_titles);
+ final int mapSize = values.length;
+
+ mDefaultLocation = Integer.parseInt(values[0]);
+ for (int i = 0; i < mapSize; i++) {
+ mValueTitleMap.put(values[i], titles[i]);
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java
new file mode 100644
index 00000000000..69a7a46f0c3
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 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 android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.widget.ImageView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.widget.LayoutPreference;
+
+/** Preference controller that controls the preview effect in accessibility button page. */
+public class AccessibilityButtonPreviewPreferenceController extends BasePreferenceController
+ implements LifecycleObserver, OnResume, OnPause {
+
+ private static final int SMALL_SIZE = 0;
+ private static final float DEFAULT_OPACITY = 0.55f;
+ private static final int DEFAULT_SIZE = 0;
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+ private FloatingMenuLayerDrawable mFloatingMenuPreviewDrawable;
+
+ @VisibleForTesting
+ ImageView mPreview;
+
+ public AccessibilityButtonPreviewPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updatePreviewPreference();
+ }
+ };
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ final LayoutPreference preference = screen.findPreference(getPreferenceKey());
+ mPreview = preference.findViewById(R.id.preview_image);
+
+ updatePreviewPreference();
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
+ /* notifyForDescendants= */ false, mContentObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
+ /* notifyForDescendants= */ false, mContentObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY),
+ /* notifyForDescendants= */ false, mContentObserver);
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ private void updatePreviewPreference() {
+ if (AccessibilityUtil.isFloatingMenuEnabled(mContext)) {
+ final int size = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, DEFAULT_SIZE);
+ final int opacity = (int) (Settings.Secure.getFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY) * 100);
+ final int floatingMenuIconId = (size == SMALL_SIZE)
+ ? R.drawable.accessibility_button_preview_small_floating_menu
+ : R.drawable.accessibility_button_preview_large_floating_menu;
+
+ mPreview.setImageDrawable(getFloatingMenuPreviewDrawable(floatingMenuIconId, opacity));
+ // Only change opacity(alpha) would not invoke redraw view, need to invalidate manually.
+ mPreview.invalidate();
+ } else {
+ mPreview.setImageDrawable(
+ mContext.getDrawable(R.drawable.accessibility_button_navigation));
+ }
+ }
+
+ private Drawable getFloatingMenuPreviewDrawable(int resId, int opacity) {
+ if (mFloatingMenuPreviewDrawable == null) {
+ mFloatingMenuPreviewDrawable = FloatingMenuLayerDrawable.createLayerDrawable(
+ mContext, resId, opacity);
+ } else {
+ mFloatingMenuPreviewDrawable.updateLayerDrawable(mContext, resId, opacity);
+ }
+
+ return mFloatingMenuPreviewDrawable;
+ }
+}
diff --git a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
index f349a125085..6b31988ecff 100644
--- a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
+++ b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
@@ -17,6 +17,7 @@
package com.android.settings.accessibility;
import android.app.Dialog;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
@@ -24,6 +25,7 @@ import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
import android.text.style.ImageSpan;
import android.view.LayoutInflater;
import android.view.View;
@@ -40,6 +42,8 @@ import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.utils.AnnotationSpan;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -253,6 +257,8 @@ public class AccessibilityEditDialogUtils {
summary.setVisibility(View.GONE);
} else {
summary.setText(summaryText);
+ summary.setMovementMethod(LinkMovementMethod.getInstance());
+ summary.setFocusable(false);
}
final ImageView image = view.findViewById(R.id.image);
image.setImageResource(imageResId);
@@ -260,10 +266,13 @@ public class AccessibilityEditDialogUtils {
private static void initSoftwareShortcut(Context context, View view) {
final View dialogView = view.findViewById(R.id.software_shortcut);
+ final CharSequence title = context.getText(
+ R.string.accessibility_shortcut_edit_dialog_title_software);
final TextView summary = dialogView.findViewById(R.id.summary);
final int lineHeight = summary.getLineHeight();
- setupShortcutWidget(dialogView, retrieveTitle(context),
- retrieveSummary(context, lineHeight), retrieveImageResId(context));
+
+ setupShortcutWidget(dialogView, title, retrieveSummary(context, lineHeight),
+ retrieveImageResId(context));
}
private static void initHardwareShortcut(Context context, View view) {
@@ -297,35 +306,28 @@ public class AccessibilityEditDialogUtils {
});
}
- private static CharSequence retrieveTitle(Context context) {
- int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
- }
- return context.getText(resId);
- }
-
private static CharSequence retrieveSummary(Context context, int lineHeight) {
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- final int resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_summary_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_summary_software_gesture;
- return context.getText(resId);
- }
- return getSummaryStringWithIcon(context, lineHeight);
+ return AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? getSummaryStringWithLink(context) : getSummaryStringWithIcon(context, lineHeight);
}
private static int retrieveImageResId(Context context) {
- // TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted.
- int resId = R.drawable.accessibility_shortcut_type_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.drawable.accessibility_shortcut_type_software_gesture_talkback
- : R.drawable.accessibility_shortcut_type_software_gesture;
- }
- return resId;
+ return AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? R.drawable.accessibility_shortcut_type_software_floating
+ : R.drawable.accessibility_shortcut_type_software;
+ }
+
+ private static CharSequence getSummaryStringWithLink(Context context) {
+ final View.OnClickListener linkListener = v -> new SubSettingLauncher(context)
+ .setDestination(AccessibilityButtonFragment.class.getName())
+ .setSourceMetricsCategory(
+ SettingsEnums.SWITCH_SHORTCUT_DIALOG_ACCESSIBILITY_BUTTON_SETTINGS)
+ .launch();
+ final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(
+ AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION, linkListener);
+
+ return AnnotationSpan.linkify(context.getText(
+ R.string.accessibility_shortcut_edit_dialog_summary_software_floating), linkInfo);
}
private static SpannableString getSummaryStringWithIcon(Context context, int lineHeight) {
diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
index 482822e4f66..5ea5462c0c8 100644
--- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
+++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
@@ -333,7 +333,8 @@ public final class AccessibilityGestureNavigationTutorial {
}
private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
- final CharSequence title = getSoftwareTitle(context);
+ final CharSequence title = context.getText(
+ R.string.accessibility_tutorial_dialog_title_button);
final ImageView image = createSoftwareImage(context);
final CharSequence instruction = getSoftwareInstruction(context);
final ImageView indicatorIcon =
@@ -390,44 +391,19 @@ public final class AccessibilityGestureNavigationTutorial {
return tutorialPages;
}
- private static CharSequence getSoftwareTitle(Context context) {
- final boolean isGestureNavigationEnabled =
- AccessibilityUtil.isGestureNavigateEnabled(context);
- final int resId = isGestureNavigationEnabled
- ? R.string.accessibility_tutorial_dialog_title_gesture
- : R.string.accessibility_tutorial_dialog_title_button;
-
- return context.getText(resId);
- }
-
private static ImageView createSoftwareImage(Context context) {
- int resId = R.drawable.accessibility_shortcut_type_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.drawable.accessibility_shortcut_type_software_gesture_talkback
- : R.drawable.accessibility_shortcut_type_software_gesture;
- }
+ final int resId = AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? R.drawable.accessibility_shortcut_type_software_floating
+ : R.drawable.accessibility_shortcut_type_software;
return createImageView(context, resId);
}
private static CharSequence getSoftwareInstruction(Context context) {
- final boolean isGestureNavigateEnabled =
- AccessibilityUtil.isGestureNavigateEnabled(context);
- final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context);
- int resId = R.string.accessibility_tutorial_dialog_message_button;
- if (isGestureNavigateEnabled) {
- resId = isTouchExploreEnabled
- ? R.string.accessibility_tutorial_dialog_message_gesture_talkback
- : R.string.accessibility_tutorial_dialog_message_gesture;
- }
-
- CharSequence text = context.getText(resId);
- if (resId == R.string.accessibility_tutorial_dialog_message_button) {
- text = getSoftwareInstructionWithIcon(context, text);
- }
-
- return text;
+ return AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? context.getText(R.string.accessibility_tutorial_dialog_message_floating_button)
+ : getSoftwareInstructionWithIcon(context,
+ context.getText(R.string.accessibility_tutorial_dialog_message_button));
}
private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) {
diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java
index f5472098f90..5c316a40acd 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtil.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtil.java
@@ -16,6 +16,7 @@
package com.android.settings.accessibility;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -143,6 +144,13 @@ final class AccessibilityUtil {
== NAV_BAR_MODE_GESTURAL;
}
+ /** Determines if a accessibility floating menu is being used. */
+ public static boolean isFloatingMenuEnabled(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+ }
+
/** Determines if a touch explore is being used. */
public static boolean isTouchExploreEnabled(Context context) {
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
diff --git a/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java
new file mode 100644
index 00000000000..dd419d0a6e5
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 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 android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+/** Preference controller that controls the fade switch button in accessibility button page. */
+public class FloatingMenuFadePreferenceController extends BasePreferenceController implements
+ Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
+
+ private static final int OFF = 0;
+ private static final int ON = 1;
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+
+ @VisibleForTesting
+ SwitchPreference mPreference;
+
+ public FloatingMenuFadePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateAvailabilityStatus();
+ }
+ };
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isFloatingMenuEnabled(mContext)
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean isEnabled = (boolean) newValue;
+ putFloatingMenuFadeValue(isEnabled);
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final SwitchPreference switchPreference = (SwitchPreference) preference;
+
+ switchPreference.setChecked(getFloatingMenuFadeValue() == ON);
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
+ /* notifyForDescendants= */ false, mContentObserver);
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ private void updateAvailabilityStatus() {
+ mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext));
+ }
+
+ private int getFloatingMenuFadeValue() {
+ return Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, ON);
+ }
+
+ private void putFloatingMenuFadeValue(boolean isEnabled) {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
+ isEnabled ? ON : OFF);
+ }
+}
diff --git a/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java b/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java
new file mode 100644
index 00000000000..bfce114ae13
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 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 android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+
+import java.util.Objects;
+
+/** LayerDrawable that contains device icon as background and floating menu icon as foreground. */
+public class FloatingMenuLayerDrawable extends LayerDrawable {
+
+ private FloatingMenuLayerDrawableState mState;
+
+ /**
+ * Creates a new layer drawable with the list of specified layers.
+ *
+ * @param layers a list of drawables to use as layers in this new drawable,
+ * must be non-null
+ */
+ private FloatingMenuLayerDrawable(@NonNull Drawable[] layers) {
+ super(layers);
+ }
+
+ /**
+ * Create the {@link LayerDrawable} that contains device icon as background and floating menu
+ * icon with given {@code opacity} value as foreground.
+ *
+ * @param context the valid context used to get the icon
+ * @param resId the resource ID of the floating menu icon
+ * @param opacity the opacity to apply to the given icon
+ * @return the drawable that combines the device icon and the floating menu icon
+ */
+ public static FloatingMenuLayerDrawable createLayerDrawable(Context context, int resId,
+ int opacity) {
+ final Drawable bg = context.getDrawable(R.drawable.accessibility_button_preview_base);
+ final FloatingMenuLayerDrawable basicDrawable = new FloatingMenuLayerDrawable(
+ new Drawable[]{bg, null});
+
+ basicDrawable.updateLayerDrawable(context, resId, opacity);
+ return basicDrawable;
+ }
+
+ /**
+ * Update the drawable with given {@code resId} drawable and {@code opacity}(alpha)
+ * value at index 1 layer.
+ *
+ * @param context the valid context used to get the icon
+ * @param resId the resource ID of the floating menu icon
+ * @param opacity the opacity to apply to the given icon
+ */
+ public void updateLayerDrawable(Context context, int resId, int opacity) {
+ final Drawable icon = context.getDrawable(resId);
+ icon.setAlpha(opacity);
+ this.setDrawable(/* index= */ 1, icon);
+ this.setConstantState(context, resId, opacity);
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return mState;
+ }
+
+ /** Stores the constant state and data to the given drawable. */
+ private void setConstantState(Context context, int resId, int opacity) {
+ mState = new FloatingMenuLayerDrawableState(context, resId, opacity);
+ }
+
+ /** {@link ConstantState} to store the data of {@link FloatingMenuLayerDrawable}. */
+ @VisibleForTesting
+ static class FloatingMenuLayerDrawableState extends ConstantState {
+
+ private final Context mContext;
+ private final int mResId;
+ private final int mOpacity;
+
+ FloatingMenuLayerDrawableState(Context context, int resId, int opacity) {
+ mContext = context;
+ mResId = resId;
+ mOpacity = opacity;
+ }
+
+ @NonNull
+ @Override
+ public Drawable newDrawable() {
+ return createLayerDrawable(mContext, mResId, mOpacity);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final FloatingMenuLayerDrawableState that = (FloatingMenuLayerDrawableState) o;
+ return mResId == that.mResId
+ && mOpacity == that.mOpacity
+ && Objects.equals(mContext, that.mContext);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mContext, mResId, mOpacity);
+ }
+ }
+}
diff --git a/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java
new file mode 100644
index 00000000000..fea6fb66c72
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021 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 android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.SliderPreferenceController;
+import com.android.settings.widget.SeekBarPreference;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+/** Preference controller that controls the opacity seekbar in accessibility button page. */
+public class FloatingMenuOpacityPreferenceController extends SliderPreferenceController
+ implements LifecycleObserver, OnResume, OnPause {
+
+ @VisibleForTesting
+ static final float DEFAULT_OPACITY = 0.55f;
+ private static final int FADE_ENABLED = 1;
+ private static final float MIN_PROGRESS = 10f;
+ private static final float MAX_PROGRESS = 100f;
+ @VisibleForTesting
+ static final float PRECISION = 100f;
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+
+ @VisibleForTesting
+ SeekBarPreference mPreference;
+
+ public FloatingMenuOpacityPreferenceController(Context context,
+ String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateAvailabilityStatus();
+ }
+ };
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isFloatingMenuEnabled(mContext)
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = screen.findPreference(getPreferenceKey());
+ mPreference.setContinuousUpdates(true);
+ mPreference.setMax(getMax());
+ mPreference.setMin(getMin());
+ mPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
+
+ updateState(mPreference);
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */
+ false, mContentObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
+ /* notifyForDescendants= */ false, mContentObserver);
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ @Override
+ public int getSliderPosition() {
+ return convertOpacityFloatToInt(getOpacity());
+ }
+
+ @Override
+ public boolean setSliderPosition(int position) {
+ final float value = convertOpacityIntToFloat(position);
+
+ return Settings.Secure.putFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, value);
+ }
+
+ @Override
+ public int getMax() {
+ return (int) MAX_PROGRESS;
+ }
+
+ @Override
+ public int getMin() {
+ return (int) MIN_PROGRESS;
+ }
+
+ private void updateAvailabilityStatus() {
+ final boolean fadeEnabled = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, FADE_ENABLED)
+ == FADE_ENABLED;
+
+ mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext) && fadeEnabled);
+ }
+
+ private int convertOpacityFloatToInt(float value) {
+ return Math.round(value * PRECISION);
+ }
+
+ private float convertOpacityIntToFloat(int value) {
+ return (float) value / PRECISION;
+ }
+
+ private float getOpacity() {
+ float value = Settings.Secure.getFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY);
+ final float minValue = MIN_PROGRESS / PRECISION;
+ final float maxValue = MAX_PROGRESS / PRECISION;
+
+ return (value < minValue || value > maxValue) ? DEFAULT_OPACITY : value;
+ }
+}
+
diff --git a/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java
new file mode 100644
index 00000000000..2f0f833772e
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2021 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 android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.util.ArrayMap;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+import com.google.common.primitives.Ints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Preference controller that controls the preferred size in accessibility button page. */
+public class FloatingMenuSizePreferenceController extends BasePreferenceController
+ implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+
+ @VisibleForTesting
+ ListPreference mPreference;
+
+ private final ArrayMap mValueTitleMap = new ArrayMap<>();
+ private int mDefaultSize;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ Size.SMALL,
+ Size.LARGE,
+ })
+ @VisibleForTesting
+ @interface Size {
+ int SMALL = 0;
+ int LARGE = 1;
+ }
+
+ public FloatingMenuSizePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateAvailabilityStatus();
+ }
+ };
+
+ initValueTitleMap();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isFloatingMenuEnabled(mContext)
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final ListPreference listPreference = (ListPreference) preference;
+ final Integer value = Ints.tryParse((String) newValue);
+ if (value != null) {
+ putAccessibilityFloatingMenuSize(value);
+ updateState(listPreference);
+ }
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final ListPreference listPreference = (ListPreference) preference;
+
+ listPreference.setValue(String.valueOf(getAccessibilityFloatingMenuSize(mDefaultSize)));
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */
+ false, mContentObserver);
+
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ private void updateAvailabilityStatus() {
+ mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext));
+ }
+
+ private void initValueTitleMap() {
+ if (mValueTitleMap.size() == 0) {
+ final String[] values = mContext.getResources().getStringArray(
+ R.array.accessibility_button_size_selector_values);
+ final String[] titles = mContext.getResources().getStringArray(
+ R.array.accessibility_button_size_selector_titles);
+ final int mapSize = values.length;
+
+ mDefaultSize = Integer.parseInt(values[0]);
+ for (int i = 0; i < mapSize; i++) {
+ mValueTitleMap.put(values[i], titles[i]);
+ }
+ }
+ }
+
+ @Size
+ private int getAccessibilityFloatingMenuSize(@Size int defaultValue) {
+ return Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, defaultValue);
+ }
+
+ private void putAccessibilityFloatingMenuSize(@Size int value) {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, value);
+ }
+}
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index ec22a28aeef..cf9c08b73fd 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -608,19 +608,15 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
- int resId = R.string.accessibility_shortcut_edit_summary_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
- }
- final CharSequence softwareTitle = context.getText(resId);
- List list = new ArrayList<>();
- if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
+ final List list = new ArrayList<>();
+ final CharSequence softwareTitle = context.getText(
+ R.string.accessibility_shortcut_edit_summary_software);
+
+ if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) {
list.add(softwareTitle);
}
- if ((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) {
+ if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index dde5be17d74..738d284bda0 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -226,27 +226,23 @@ public class ToggleScreenMagnificationPreferenceFragment extends
return context.getText(R.string.switch_off_text);
}
- final int shortcutType = PreferredShortcuts.retrieveUserShortcutType(context,
+ final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.SOFTWARE);
- int resId = R.string.accessibility_shortcut_edit_summary_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
- }
- final CharSequence softwareTitle = context.getText(resId);
- List list = new ArrayList<>();
- if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
+ final List list = new ArrayList<>();
+ final CharSequence softwareTitle = context.getText(
+ R.string.accessibility_shortcut_edit_summary_software);
+
+ if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) {
list.add(softwareTitle);
}
- if ((shortcutType & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) {
+ if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
}
- if ((shortcutType & UserShortcutType.TRIPLETAP) == UserShortcutType.TRIPLETAP) {
+ if (hasShortcutType(shortcutTypes, UserShortcutType.TRIPLETAP)) {
final CharSequence tripleTapTitle = context.getText(
R.string.accessibility_shortcut_triple_tap_keyword);
list.add(tripleTapTitle);
diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
index 14fe6a664be..388d87ae9a9 100644
--- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
+++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
@@ -30,15 +30,12 @@ import android.content.om.OverlayInfo;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
-import android.text.TextUtils;
import android.util.FeatureFlagUtils;
-import android.view.accessibility.AccessibilityManager;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
-import com.android.settings.SettingsTutorialDialogWrapperActivity;
import com.android.settings.core.FeatureFlags;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
@@ -188,12 +185,7 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i
protected boolean setDefaultKey(String key) {
setCurrentSystemNavigationMode(mOverlayManager, key);
setIllustrationVideo(mVideoPreference, key);
- if (TextUtils.equals(KEY_SYSTEM_NAV_GESTURAL, key) && (
- isAnyServiceSupportAccessibilityButton() || isNavBarMagnificationEnabled())) {
- Intent intent = new Intent(getActivity(), SettingsTutorialDialogWrapperActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- }
+
return true;
}
@@ -267,18 +259,6 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i
}
}
- private boolean isAnyServiceSupportAccessibilityButton() {
- final AccessibilityManager ams = getContext().getSystemService(AccessibilityManager.class);
- final List targets = ams.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- return !targets.isEmpty();
- }
-
- private boolean isNavBarMagnificationEnabled() {
- return Settings.Secure.getInt(getContext().getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1;
- }
-
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.system_navigation_gesture_settings) {
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java
new file mode 100644
index 00000000000..473b56687bf
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 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.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.testutils.XmlTestUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+/** Tests for {@link AccessibilityButtonFragment}. */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilityButtonFragmentTest {
+
+ private Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Test
+ public void getNonIndexableKeys_existInXmlLayout() {
+ final List niks = AccessibilityButtonFragment.SEARCH_INDEX_DATA_PROVIDER
+ .getNonIndexableKeys(mContext);
+ final List keys =
+ XmlTestUtils.getKeysFromPreferenceXml(mContext,
+ R.xml.accessibility_button_settings);
+
+ assertThat(keys).containsAtLeastElementsIn(niks);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java
new file mode 100644
index 00000000000..a67038a6b2d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 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 android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import androidx.preference.ListPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link AccessibilityButtonLocationPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilityButtonLocationPreferenceControllerTest {
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Spy
+ private final Resources mResources = mContext.getResources();
+ private final ContentResolver mContentResolver = mContext.getContentResolver();
+ private final ListPreference mListPreference = new ListPreference(mContext);
+ private AccessibilityButtonLocationPreferenceController mController;
+
+
+ @Before
+ public void setUp() {
+ mController = new AccessibilityButtonLocationPreferenceController(mContext,
+ "test_key");
+ when(mContext.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void getAvailabilityStatus_navigationGestureEnabled_returnDisabledDependentSetting() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_GESTURAL);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void getAvailabilityStatus_navigationGestureDisabled_returnAvailable() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_2BUTTON);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void updateState_a11yBtnModeNavigationBar_navigationBarValue() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.updateState(mListPreference);
+
+ final String navigationBarValue = String.valueOf(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+ assertThat(mListPreference.getValue()).isEqualTo(navigationBarValue);
+ }
+
+ @Test
+ public void onPreferenceChange_a11yBtnModeFloatingMenu_floatingMenuValue() {
+ final String floatingMenuValue = String.valueOf(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ mController.onPreferenceChange(mListPreference, floatingMenuValue);
+
+ assertThat(mListPreference.getValue()).isEqualTo(floatingMenuValue);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java
new file mode 100644
index 00000000000..eb881757b28
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 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 android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.provider.Settings;
+import android.widget.ImageView;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link AccessibilityButtonPreviewPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilityButtonPreviewPreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private AccessibilityButtonPreviewPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new AccessibilityButtonPreviewPreferenceController(mContext, "test_key");
+ mController.mPreview = new ImageView(mContext);
+ }
+
+ @Test
+ public void onChange_a11yBtnModeNavigationBar_getNavigationBarDrawable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.mContentObserver.onChange(false);
+
+ final Drawable navigationBarDrawable = mContext.getDrawable(
+ R.drawable.accessibility_button_navigation);
+ assertThat(mController.mPreview.getDrawable().getConstantState()).isEqualTo(
+ navigationBarDrawable.getConstantState());
+ }
+
+ @Test
+ public void onChange_updatePreviewPreferenceWithConfig_expectedPreviewDrawable() {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, /* small size */ 0);
+ Settings.Secure.putFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.1f);
+
+ mController.mContentObserver.onChange(false);
+
+ final Drawable smallFloatingMenuWithTenOpacityDrawable =
+ FloatingMenuLayerDrawable.createLayerDrawable(mContext,
+ R.drawable.accessibility_button_preview_small_floating_menu, 10);
+ assertThat(mController.mPreview.getDrawable().getConstantState()).isEqualTo(
+ smallFloatingMenuWithTenOpacityDrawable.getConstantState());
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE), false,
+ mController.mContentObserver);
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY),
+ false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java
new file mode 100644
index 00000000000..5cf87ee61a1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 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 android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuFadePreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuFadePreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ private static final int OFF = 0;
+ private static final int ON = 1;
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private final SwitchPreference mSwitchPreference = new SwitchPreference(mContext);
+ private FloatingMenuFadePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new FloatingMenuFadePreferenceController(mContext, "test_key");
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void updateState_keyFloatingMenuFadeDisabled_fadeIsDisabled() {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF);
+
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mSwitchPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void onPreferenceChange_floatingMenuFadeEnabled_keyFloatingMenuFadeIsOn() {
+ mController.onPreferenceChange(mSwitchPreference, Boolean.TRUE);
+
+ final int actualValue = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF);
+ assertThat(actualValue).isEqualTo(ON);
+ }
+
+ @Test
+ public void onChange_floatingMenuFadeChangeToDisabled_preferenceDisabled() {
+ mController.mPreference = mSwitchPreference;
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF);
+
+ mController.mContentObserver.onChange(false);
+
+ assertThat(mController.mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java
new file mode 100644
index 00000000000..ec449d249b5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuLayerDrawable}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuLayerDrawableTest {
+
+ private static final int TEST_RES_ID =
+ com.android.internal.R.drawable.ic_accessibility_magnification;
+ private static final int TEST_RES_ID_2 =
+ com.android.internal.R.drawable.ic_accessibility_color_inversion;
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Test
+ public void createLayerDrawable_configCorrect() {
+ final Drawable expected1stDrawable = mContext.getDrawable(
+ R.drawable.accessibility_button_preview_base);
+ final Drawable expected2ndDrawable = mContext.getDrawable(TEST_RES_ID);
+
+ final FloatingMenuLayerDrawable actualDrawable =
+ FloatingMenuLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID,
+ /* opacity= */ 27);
+
+ final Drawable actual1stDrawable = actualDrawable.getDrawable(0);
+ final Drawable actual2ndDrawable = actualDrawable.getDrawable(1);
+ // These are VectorDrawables, so it can use getConstantState() to compare.
+ assertThat(actual1stDrawable.getConstantState()).isEqualTo(
+ expected1stDrawable.getConstantState());
+ assertThat(actual2ndDrawable.getConstantState()).isEqualTo(
+ expected2ndDrawable.getConstantState());
+ }
+
+ @Test
+ public void updateLayerDrawable_expectedFloatingMenuLayerDrawableState() {
+ final FloatingMenuLayerDrawable originalDrawable =
+ FloatingMenuLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID, /* opacity= */
+ 72);
+
+ originalDrawable.updateLayerDrawable(mContext, TEST_RES_ID_2, /* opacity= */ 27);
+
+ assertThat(originalDrawable.getConstantState()).isEqualTo(
+ new FloatingMenuLayerDrawable.FloatingMenuLayerDrawableState(mContext,
+ TEST_RES_ID_2, /* opacity= */ 27));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java
new file mode 100644
index 00000000000..1638f907407
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2021 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 android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.android.settings.accessibility.FloatingMenuOpacityPreferenceController.DEFAULT_OPACITY;
+import static com.android.settings.accessibility.FloatingMenuOpacityPreferenceController.PRECISION;
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.widget.SeekBarPreference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuOpacityPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuOpacityPreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private FloatingMenuOpacityPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new FloatingMenuOpacityPreferenceController(mContext, "test_key");
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void onChange_a11yBtnModeChangeToNavigationBar_preferenceDisabled() {
+ mController.mPreference = new SeekBarPreference(mContext);
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.mContentObserver.onChange(false);
+
+ assertThat(mController.mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void getSliderPosition_putNormalOpacityValue_expectedValue() {
+ Settings.Secure.putFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.35f);
+
+ assertThat(mController.getSliderPosition()).isEqualTo(35);
+ }
+
+ @Test
+ public void getSliderPosition_putOutOfBoundOpacityValue_defaultValue() {
+ Settings.Secure.putFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.01f);
+
+ final int defaultValue = Math.round(DEFAULT_OPACITY * PRECISION);
+ assertThat(mController.getSliderPosition()).isEqualTo(defaultValue);
+ }
+
+ @Test
+ public void setSliderPosition_expectedValue() {
+ mController.setSliderPosition(27);
+
+ final float value = Settings.Secure.getFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, -1);
+ assertThat(value).isEqualTo(0.27f);
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
+ false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java
new file mode 100644
index 00000000000..4d7d98d7aff
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 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 android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.ListPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuSizePreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuSizePreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private final ListPreference mListPreference = new ListPreference(mContext);
+ private FloatingMenuSizePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new FloatingMenuSizePreferenceController(mContext, "test_key");
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void updateState_floatingMenuLargeSizeAndFullCircle_largeSizeValue() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
+ FloatingMenuSizePreferenceController.Size.LARGE);
+
+ mController.updateState(mListPreference);
+
+ final String largeSize = String.valueOf(FloatingMenuSizePreferenceController.Size.LARGE);
+ assertThat(mListPreference.getValue()).isEqualTo(largeSize);
+ }
+
+ @Test
+ public void onChange_a11yBtnModeChangeToNavigationBar_preferenceDisabled() {
+ mController.mPreference = mListPreference;
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.mContentObserver.onChange(false);
+
+ assertThat(mController.mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}