diff --git a/res/drawable/accessibility_button_preview_three_finger.xml b/res/drawable/accessibility_button_preview_three_finger.xml
new file mode 100644
index 00000000000..0fe6710df66
--- /dev/null
+++ b/res/drawable/accessibility_button_preview_three_finger.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/drawable/accessibility_button_preview_two_finger.xml b/res/drawable/accessibility_button_preview_two_finger.xml
new file mode 100644
index 00000000000..e61e3e84fc7
--- /dev/null
+++ b/res/drawable/accessibility_button_preview_two_finger.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9ee75794802..a9fbc8b360e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5427,9 +5427,9 @@
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.\n\nTo use the accessibility button in the navigation bar instead, switch to 2-button navigation or 3-button navigation.
+ 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 or gesture.
- 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.
+ 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.
Use button or gesture
diff --git a/res/xml/accessibility_button_settings.xml b/res/xml/accessibility_button_settings.xml
index 02b1c7d4693..9f32714f328 100644
--- a/res/xml/accessibility_button_settings.xml
+++ b/res/xml/accessibility_button_settings.xml
@@ -16,8 +16,7 @@
+ xmlns:settings="http://schemas.android.com/apk/res-auto">
+
+
+ android:summary="@string/accessibility_button_summary"
+ settings:controller="com.android.settings.accessibility.AccessibilityButtonPreferenceController"/>
mDefaultGesture = Optional.empty();
+
+ public AccessibilityButtonGesturePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isGestureNavigateEnabled(mContext)
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @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, getDefaultGestureValue());
+ return String.valueOf(mode);
+ }
+
+ private int getDefaultGestureValue() {
+ if (!mDefaultGesture.isPresent()) {
+ final String[] valuesList = mContext.getResources().getStringArray(
+ R.array.accessibility_button_gesture_selector_values);
+ mDefaultGesture = Optional.of(Integer.parseInt(valuesList[0]));
+ }
+ return mDefaultGesture.get();
+ }
+}
diff --git a/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceController.java
index ed7cb27bff6..167e08faec7 100644
--- a/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceController.java
@@ -18,7 +18,6 @@ package com.android.settings.accessibility;
import android.content.Context;
import android.provider.Settings;
-import android.util.ArrayMap;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
@@ -28,16 +27,16 @@ import com.android.settings.core.BasePreferenceController;
import com.google.common.primitives.Ints;
+import java.util.Optional;
+
/** Preference controller that controls the preferred location in accessibility button page. */
public class AccessibilityButtonLocationPreferenceController extends BasePreferenceController
implements Preference.OnPreferenceChangeListener {
- private final ArrayMap mValueTitleMap = new ArrayMap<>();
- private int mDefaultLocation;
+ private Optional mDefaultLocation = Optional.empty();
public AccessibilityButtonLocationPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
- initValueTitleMap();
}
@Override
@@ -68,22 +67,16 @@ public class AccessibilityButtonLocationPreferenceController extends BasePrefere
private String getCurrentAccessibilityButtonMode() {
final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mDefaultLocation);
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, getDefaultLocationValue());
return String.valueOf(mode);
}
- private void initValueTitleMap() {
- if (mValueTitleMap.size() == 0) {
- final String[] values = mContext.getResources().getStringArray(
+ private int getDefaultLocationValue() {
+ if (!mDefaultLocation.isPresent()) {
+ final String[] valuesList = 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]);
- }
+ mDefaultLocation = Optional.of(Integer.parseInt(valuesList[0]));
}
+ return mDefaultLocation.get();
}
}
diff --git a/src/com/android/settings/accessibility/AccessibilityButtonPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityButtonPreferenceController.java
new file mode 100644
index 00000000000..f0764721dcd
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilityButtonPreferenceController.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 android.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Preference controller for accessibility button preference.
+ */
+public class AccessibilityButtonPreferenceController extends BasePreferenceController {
+
+ public AccessibilityButtonPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ final int titleResource = AccessibilityUtil.isGestureNavigateEnabled(mContext)
+ ? R.string.accessibility_button_gesture_title : R.string.accessibility_button_title;
+ final Preference preference = screen.findPreference(getPreferenceKey());
+ preference.setTitle(titleResource);
+
+ }
+}
diff --git a/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java
index 69a7a46f0c3..8a840a1cb70 100644
--- a/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java
+++ b/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java
@@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
+import android.view.accessibility.AccessibilityManager;
import android.widget.ImageView;
import androidx.annotation.VisibleForTesting;
@@ -46,11 +47,14 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
private final ContentResolver mContentResolver;
@VisibleForTesting
final ContentObserver mContentObserver;
- private FloatingMenuLayerDrawable mFloatingMenuPreviewDrawable;
+ private AccessibilityLayerDrawable mAccessibilityPreviewDrawable;
@VisibleForTesting
ImageView mPreview;
+ private AccessibilityManager.TouchExplorationStateChangeListener
+ mTouchExplorationStateChangeListener;
+
public AccessibilityButtonPreviewPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mContentResolver = context.getContentResolver();
@@ -60,6 +64,9 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
updatePreviewPreference();
}
};
+ mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
+ updatePreviewPreference();
+ };
}
@Override
@@ -78,6 +85,9 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
@Override
public void onResume() {
+ final AccessibilityManager am = mContext.getSystemService(AccessibilityManager.class);
+ am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
/* notifyForDescendants= */ false, mContentObserver);
@@ -91,6 +101,9 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
@Override
public void onPause() {
+ final AccessibilityManager am = mContext.getSystemService(AccessibilityManager.class);
+ am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
mContentResolver.unregisterContentObserver(mContentObserver);
}
@@ -103,9 +116,14 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
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.setImageDrawable(getAccessibilityPreviewDrawable(floatingMenuIconId, opacity));
+ mPreview.invalidate();
+ } else if (AccessibilityUtil.isGestureNavigateEnabled(mContext)) {
+ // TODO(b/193081959): Use static illustartion instead.
+ final int resId = AccessibilityUtil.isTouchExploreEnabled(mContext)
+ ? R.drawable.accessibility_button_preview_three_finger
+ : R.drawable.accessibility_button_preview_two_finger;
+ mPreview.setImageDrawable(getAccessibilityPreviewDrawable(resId, /* opacity= */ 100));
mPreview.invalidate();
} else {
mPreview.setImageDrawable(
@@ -113,14 +131,14 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
}
}
- private Drawable getFloatingMenuPreviewDrawable(int resId, int opacity) {
- if (mFloatingMenuPreviewDrawable == null) {
- mFloatingMenuPreviewDrawable = FloatingMenuLayerDrawable.createLayerDrawable(
+ private Drawable getAccessibilityPreviewDrawable(int resId, int opacity) {
+ if (mAccessibilityPreviewDrawable == null) {
+ mAccessibilityPreviewDrawable = AccessibilityLayerDrawable.createLayerDrawable(
mContext, resId, opacity);
} else {
- mFloatingMenuPreviewDrawable.updateLayerDrawable(mContext, resId, opacity);
+ mAccessibilityPreviewDrawable.updateLayerDrawable(mContext, resId, opacity);
}
- return mFloatingMenuPreviewDrawable;
+ return mAccessibilityPreviewDrawable;
}
}
diff --git a/src/com/android/settings/accessibility/AccessibilityDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityDialogUtils.java
index ffe5d6db59b..a9ba14a7646 100644
--- a/src/com/android/settings/accessibility/AccessibilityDialogUtils.java
+++ b/src/com/android/settings/accessibility/AccessibilityDialogUtils.java
@@ -344,12 +344,11 @@ public class AccessibilityDialogUtils {
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, title,
+ setupShortcutWidget(dialogView,
+ retrieveTitle(context),
retrieveSoftwareShortcutSummary(context, lineHeight),
retrieveSoftwareShortcutImageResId(context));
}
@@ -398,20 +397,49 @@ public class AccessibilityDialogUtils {
return sb;
}
+ private static CharSequence retrieveTitle(Context context) {
+ int resId;
+ if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
+ resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+ } else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = R.string.accessibility_shortcut_edit_dialog_title_software_by_gesture;
+ } else {
+ resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+ }
+ return context.getText(resId);
+ }
+
private static CharSequence retrieveSoftwareShortcutSummary(Context context, int lineHeight) {
final SpannableStringBuilder sb = new SpannableStringBuilder();
- if (!AccessibilityUtil.isFloatingMenuEnabled(context)) {
+ if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
+ sb.append(getCustomizeAccessibilityButtonLink(context));
+ } else 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;
+ sb.append(context.getText(resId));
+ sb.append("\n\n");
+ sb.append(getCustomizeAccessibilityButtonLink(context));
+ } else {
sb.append(getSummaryStringWithIcon(context, lineHeight));
sb.append("\n\n");
+ sb.append(getCustomizeAccessibilityButtonLink(context));
}
- sb.append(getCustomizeAccessibilityButtonLink(context));
return sb;
}
private static int retrieveSoftwareShortcutImageResId(Context context) {
- return AccessibilityUtil.isFloatingMenuEnabled(context)
- ? R.drawable.accessibility_shortcut_type_software_floating
- : R.drawable.accessibility_shortcut_type_software;
+ int resId;
+ if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
+ resId = R.drawable.accessibility_shortcut_type_software_floating;
+ } else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.drawable.accessibility_shortcut_type_software_gesture_talkback
+ : R.drawable.accessibility_shortcut_type_software_gesture;
+ } else {
+ resId = R.drawable.accessibility_shortcut_type_software;
+ }
+ return resId;
}
private static CharSequence getCustomizeAccessibilityButtonLink(Context context) {
@@ -422,7 +450,6 @@ public class AccessibilityDialogUtils {
.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);
}
diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
index f8cdcb3388a..bc2e05d3258 100644
--- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
+++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
@@ -27,6 +27,7 @@ import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
import android.text.style.ImageSpan;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -323,8 +324,7 @@ public final class AccessibilityGestureNavigationTutorial {
}
private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
- final CharSequence title = context.getText(
- R.string.accessibility_tutorial_dialog_title_button);
+ final CharSequence title = getSoftwareTitle(context);
final ImageView image = createSoftwareImage(context);
final CharSequence instruction = getSoftwareInstruction(context);
final ImageView indicatorIcon =
@@ -382,18 +382,46 @@ public final class AccessibilityGestureNavigationTutorial {
}
private static ImageView createSoftwareImage(Context context) {
- final int resId = AccessibilityUtil.isFloatingMenuEnabled(context)
- ? R.drawable.accessibility_shortcut_type_software_floating
- : R.drawable.accessibility_shortcut_type_software;
-
+ int resId;
+ if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
+ resId = R.drawable.accessibility_shortcut_type_software_floating;
+ } else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.drawable.accessibility_shortcut_type_software_gesture_talkback
+ : R.drawable.accessibility_shortcut_type_software_gesture;
+ } else {
+ resId = R.drawable.accessibility_shortcut_type_software;
+ }
return createImageView(context, resId);
}
+ private static CharSequence getSoftwareTitle(Context context) {
+ int resId;
+ if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
+ resId = R.string.accessibility_tutorial_dialog_title_button;
+ } else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = R.string.accessibility_tutorial_dialog_title_gesture;
+ } else {
+ resId = R.string.accessibility_tutorial_dialog_title_button;
+ }
+ return context.getText(resId);
+ }
+
private static CharSequence getSoftwareInstruction(Context context) {
- 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));
+ final SpannableStringBuilder sb = new SpannableStringBuilder();
+ if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
+ final int resId = R.string.accessibility_tutorial_dialog_message_floating_button;
+ sb.append(context.getText(resId));
+ } else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ final int resId = AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.string.accessibility_tutorial_dialog_message_gesture_talkback
+ : R.string.accessibility_tutorial_dialog_message_gesture;
+ sb.append(context.getText(resId));
+ } else {
+ final int resId = R.string.accessibility_tutorial_dialog_message_button;
+ sb.append(getSoftwareInstructionWithIcon(context, context.getText(resId)));
+ }
+ return sb;
}
private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) {
diff --git a/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java b/src/com/android/settings/accessibility/AccessibilityLayerDrawable.java
similarity index 76%
rename from src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java
rename to src/com/android/settings/accessibility/AccessibilityLayerDrawable.java
index bfce114ae13..b04a969885a 100644
--- a/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java
+++ b/src/com/android/settings/accessibility/AccessibilityLayerDrawable.java
@@ -27,10 +27,10 @@ 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 {
+/** LayerDrawable that contains device icon as background and given icon as foreground. */
+public class AccessibilityLayerDrawable extends LayerDrawable {
- private FloatingMenuLayerDrawableState mState;
+ private AccessibilityLayerDrawableState mState;
/**
* Creates a new layer drawable with the list of specified layers.
@@ -38,23 +38,23 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
* @param layers a list of drawables to use as layers in this new drawable,
* must be non-null
*/
- private FloatingMenuLayerDrawable(@NonNull Drawable[] layers) {
+ private AccessibilityLayerDrawable(@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.
+ * Create the {@link LayerDrawable} that contains device icon as background and given 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 resId the resource ID of the given icon
* @param opacity the opacity to apply to the given icon
- * @return the drawable that combines the device icon and the floating menu icon
+ * @return the drawable that combines the device icon and the given icon
*/
- public static FloatingMenuLayerDrawable createLayerDrawable(Context context, int resId,
+ public static AccessibilityLayerDrawable createLayerDrawable(Context context, int resId,
int opacity) {
final Drawable bg = context.getDrawable(R.drawable.accessibility_button_preview_base);
- final FloatingMenuLayerDrawable basicDrawable = new FloatingMenuLayerDrawable(
+ final AccessibilityLayerDrawable basicDrawable = new AccessibilityLayerDrawable(
new Drawable[]{bg, null});
basicDrawable.updateLayerDrawable(context, resId, opacity);
@@ -66,7 +66,7 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
* 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 resId the resource ID of the given icon
* @param opacity the opacity to apply to the given icon
*/
public void updateLayerDrawable(Context context, int resId, int opacity) {
@@ -83,18 +83,18 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
/** 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);
+ mState = new AccessibilityLayerDrawableState(context, resId, opacity);
}
- /** {@link ConstantState} to store the data of {@link FloatingMenuLayerDrawable}. */
+ /** {@link ConstantState} to store the data of {@link AccessibilityLayerDrawable}. */
@VisibleForTesting
- static class FloatingMenuLayerDrawableState extends ConstantState {
+ static class AccessibilityLayerDrawableState extends ConstantState {
private final Context mContext;
private final int mResId;
private final int mOpacity;
- FloatingMenuLayerDrawableState(Context context, int resId, int opacity) {
+ AccessibilityLayerDrawableState(Context context, int resId, int opacity) {
mContext = context;
mResId = resId;
mOpacity = opacity;
@@ -119,7 +119,7 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
if (o == null || getClass() != o.getClass()) {
return false;
}
- final FloatingMenuLayerDrawableState that = (FloatingMenuLayerDrawableState) o;
+ final AccessibilityLayerDrawableState that = (AccessibilityLayerDrawableState) o;
return mResId == that.mResId
&& mOpacity == that.mOpacity
&& Objects.equals(mContext, that.mContext);
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 7cefcedff28..245f362e093 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -584,6 +584,18 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
return value;
}
+ private static CharSequence getSoftwareShortcutTypeSummary(Context context) {
+ int resId;
+ if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
+ resId = R.string.accessibility_shortcut_edit_summary_software;
+ } else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = R.string.accessibility_shortcut_edit_summary_software_gesture;
+ } else {
+ resId = R.string.accessibility_shortcut_edit_summary_software;
+ }
+ return context.getText(resId);
+ }
+
protected CharSequence getShortcutTypeSummary(Context context) {
if (!mShortcutPreference.isSettingsEditable()) {
return context.getText(R.string.accessibility_shortcut_edit_dialog_title_hardware);
@@ -597,11 +609,8 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
mComponentName.flattenToString(), 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);
+ list.add(getSoftwareShortcutTypeSummary(context));
}
if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
@@ -611,7 +620,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
// Show software shortcut if first time to use.
if (list.isEmpty()) {
- list.add(softwareTitle);
+ list.add(getSoftwareShortcutTypeSummary(context));
}
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index 4f25430e8d2..1c7ce39bab9 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -265,6 +265,18 @@ public class ToggleScreenMagnificationPreferenceFragment extends
return (value & type) == type;
}
+ private static CharSequence getSoftwareShortcutTypeSummary(Context context) {
+ int resId;
+ if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
+ resId = R.string.accessibility_shortcut_edit_summary_software;
+ } else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = R.string.accessibility_shortcut_edit_summary_software_gesture;
+ } else {
+ resId = R.string.accessibility_shortcut_edit_summary_software;
+ }
+ return context.getText(resId);
+ }
+
@Override
protected CharSequence getShortcutTypeSummary(Context context) {
if (!mShortcutPreference.isChecked()) {
@@ -275,18 +287,14 @@ public class ToggleScreenMagnificationPreferenceFragment extends
MAGNIFICATION_CONTROLLER_NAME, 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);
+ list.add(getSoftwareShortcutTypeSummary(context));
}
if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
}
-
if (hasShortcutType(shortcutTypes, UserShortcutType.TRIPLETAP)) {
final CharSequence tripleTapTitle = context.getText(
R.string.accessibility_shortcut_triple_tap_keyword);
@@ -295,7 +303,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends
// Show software shortcut if first time to use.
if (list.isEmpty()) {
- list.add(softwareTitle);
+ list.add(getSoftwareShortcutTypeSummary(context));
}
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
index 4e783c96407..4b9cfa36f2f 100644
--- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
+++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
@@ -17,6 +17,7 @@
package com.android.settings.gestures;
import static android.os.UserHandle.USER_CURRENT;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
@@ -30,11 +31,14 @@ import android.content.om.OverlayInfo;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.text.TextUtils;
+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.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
@@ -173,7 +177,14 @@ 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)
+ && !isAccessibilityFloatingMenuEnabled()
+ && (isAnyServiceSupportAccessibilityButton() || isNavBarMagnificationEnabled())) {
+ final Intent intent = new Intent(getActivity(),
+ SettingsTutorialDialogWrapperActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
return true;
}
@@ -246,6 +257,24 @@ 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;
+ }
+
+ private boolean isAccessibilityFloatingMenuEnabled() {
+ return Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+ }
+
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/AccessibilityButtonFooterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFooterPreferenceControllerTest.java
index 010b444f914..7354555a719 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFooterPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFooterPreferenceControllerTest.java
@@ -16,6 +16,7 @@
package com.android.settings.accessibility;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static com.google.common.truth.Truth.assertThat;
@@ -46,7 +47,6 @@ public class AccessibilityButtonFooterPreferenceControllerTest {
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
-
@Spy
private final Context mContext = ApplicationProvider.getApplicationContext();
@Spy
@@ -76,4 +76,15 @@ public class AccessibilityButtonFooterPreferenceControllerTest {
assertThat(mPreference.getTitle()).isEqualTo(
mContext.getText(R.string.accessibility_button_gesture_description));
}
+
+ @Test
+ public void displayPreference_navigationGestureDisabled_setCorrectTitle() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_2BUTTON);
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.getTitle()).isEqualTo(
+ mContext.getText(R.string.accessibility_button_description));
+ }
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonGesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonGesturePreferenceControllerTest.java
new file mode 100644
index 00000000000..da442281e9e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonGesturePreferenceControllerTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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_GESTURE;
+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.CONDITIONALLY_UNAVAILABLE;
+
+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 AccessibilityButtonGesturePreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilityButtonGesturePreferenceControllerTest {
+
+ @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 AccessibilityButtonGesturePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ mController = new AccessibilityButtonGesturePreferenceController(mContext,
+ "test_key");
+ when(mContext.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void getAvailabilityStatus_navigationGestureEnabled_returnAvailable() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_GESTURAL);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_navigationGestureDisabled_returnConditionallyUnavailable() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_2BUTTON);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void updateState_a11yBtnModeGesture_navigationBarValue() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_GESTURE);
+
+ mController.updateState(mListPreference);
+
+ final String gestureValue = String.valueOf(ACCESSIBILITY_BUTTON_MODE_GESTURE);
+ assertThat(mListPreference.getValue()).isEqualTo(gestureValue);
+ }
+
+ @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/AccessibilityButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreferenceControllerTest.java
new file mode 100644
index 00000000000..03f7887b9d0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreferenceControllerTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+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 AccessibilityButtonPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilityButtonPreferenceControllerTest {
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Spy
+ private final Resources mResources = mContext.getResources();
+ @Mock
+ private PreferenceScreen mScreen;
+ private Preference mPreference;
+ private AccessibilityButtonPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ mController = new AccessibilityButtonPreferenceController(mContext, "test_key");
+ mPreference = new Preference(mContext);
+ mPreference.setKey("test_key");
+
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+ when(mContext.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void displayPreference_navigationGestureEnabled_setCorrectTitle() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_GESTURAL);
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.getTitle()).isEqualTo(
+ mContext.getText(R.string.accessibility_button_gesture_title));
+ }
+
+ @Test
+ public void displayPreference_navigationGestureDisabled_setCorrectTitle() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_2BUTTON);
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mPreference.getTitle()).isEqualTo(
+ mContext.getText(R.string.accessibility_button_title));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java
index 306503040ce..61808bd4c1b 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java
@@ -90,7 +90,7 @@ public class AccessibilityButtonPreviewPreferenceControllerTest {
mController.mContentObserver.onChange(false);
final Drawable smallFloatingMenuWithTenOpacityDrawable =
- FloatingMenuLayerDrawable.createLayerDrawable(mContext,
+ AccessibilityLayerDrawable.createLayerDrawable(mContext,
R.drawable.accessibility_button_preview_small_floating_menu, 10);
assertThat(mController.mPreview.getDrawable().getConstantState()).isEqualTo(
smallFloatingMenuWithTenOpacityDrawable.getConstantState());
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityLayerDrawableTest.java
similarity index 83%
rename from tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java
rename to tests/robotests/src/com/android/settings/accessibility/AccessibilityLayerDrawableTest.java
index 45cefe482b0..915c788c3bd 100644
--- a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityLayerDrawableTest.java
@@ -30,9 +30,9 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
-/** Tests for {@link FloatingMenuLayerDrawable}. */
+/** Tests for {@link AccessibilityLayerDrawable}. */
@RunWith(RobolectricTestRunner.class)
-public class FloatingMenuLayerDrawableTest {
+public class AccessibilityLayerDrawableTest {
private static final int TEST_RES_ID =
com.android.internal.R.drawable.ic_accessibility_magnification;
@@ -46,8 +46,8 @@ public class FloatingMenuLayerDrawableTest {
R.drawable.accessibility_button_preview_base);
final Drawable expected2ndDrawable = mContext.getDrawable(TEST_RES_ID);
- final FloatingMenuLayerDrawable actualDrawable =
- FloatingMenuLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID,
+ final AccessibilityLayerDrawable actualDrawable =
+ AccessibilityLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID,
/* opacity= */ 27);
final Drawable actual1stDrawable = actualDrawable.getDrawable(0);
@@ -60,14 +60,14 @@ public class FloatingMenuLayerDrawableTest {
@Test
public void updateLayerDrawable_expectedFloatingMenuLayerDrawableState() {
- final FloatingMenuLayerDrawable originalDrawable =
- FloatingMenuLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID, /* opacity= */
+ final AccessibilityLayerDrawable originalDrawable =
+ AccessibilityLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID, /* opacity= */
72);
originalDrawable.updateLayerDrawable(mContext, TEST_RES_ID_2, /* opacity= */ 27);
assertThat(originalDrawable.getConstantState()).isEqualTo(
- new FloatingMenuLayerDrawable.FloatingMenuLayerDrawableState(mContext,
+ new AccessibilityLayerDrawable.AccessibilityLayerDrawableState(mContext,
TEST_RES_ID_2, /* opacity= */ 27));
}
}