diff --git a/res/drawable/ic_keyboard_arrow_down.xml b/res/drawable/ic_keyboard_arrow_down.xml
new file mode 100644
index 00000000000..d72f3dfd78a
--- /dev/null
+++ b/res/drawable/ic_keyboard_arrow_down.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/res/layout/accessibility_edit_shortcut.xml b/res/layout/accessibility_edit_shortcut.xml
new file mode 100644
index 00000000000..753adb1b8a5
--- /dev/null
+++ b/res/layout/accessibility_edit_shortcut.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/accessibility_edit_shortcut_component.xml b/res/layout/accessibility_edit_shortcut_component.xml
new file mode 100644
index 00000000000..2e1ad2de3d3
--- /dev/null
+++ b/res/layout/accessibility_edit_shortcut_component.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/accessibility_edit_shortcut_magnification.xml b/res/layout/accessibility_edit_shortcut_magnification.xml
new file mode 100644
index 00000000000..76a1fadfc02
--- /dev/null
+++ b/res/layout/accessibility_edit_shortcut_magnification.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 503da8732d2..641c2537763 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4732,6 +4732,24 @@
To turn an accessibility service on or off, swipe up from the bottom of the screen with three fingers.\n\nTo switch between services, swipe up with three fingers and hold.
Got it
+
+ Accessibility Button
+
+ 2-finger swipe up from bottom
+
+ Tap the %s button at the bottom of your screen
+
+ Swipe up with 2 fingers from edge of screen
+
+ Hold volume keys
+
+ Press and hold both volume keys for 1 second
+
+ Triple tap screen
+
+ Quickly tap screen 3 times (this can slow down your device)
+
+ Advanced
The Accessibility button is set to %1$s. To use magnification, touch & hold the Accessibility button, then select magnification.
diff --git a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
new file mode 100644
index 00000000000..62d6d340098
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2019 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_GESTURAL;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.ImageSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.IntDef;
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.content.ContextCompat;
+
+import com.android.settings.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Utility class for creating the edit dialog.
+ */
+public class AccessibilityEditDialogUtils {
+
+ /**
+ * IntDef enum for dialog type that indicates different dialog for user to choose the shortcut
+ * type.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DialogType.EDIT_SHORTCUT_GENERIC,
+ DialogType.EDIT_SHORTCUT_MAGNIFICATION,
+ })
+
+ private @interface DialogType {
+ int EDIT_SHORTCUT_GENERIC = 0;
+ int EDIT_SHORTCUT_MAGNIFICATION = 1;
+ }
+
+ /**
+ * Method to show the edit shortcut dialog.
+ *
+ * @param context A valid context
+ * @param dialogTitle The title of edit shortcut dialog
+ * @param listener The listener to determine the action of edit shortcut dialog
+ * @return A edit shortcut dialog for showing
+ */
+ public static AlertDialog showEditShortcutDialog(Context context, CharSequence dialogTitle,
+ DialogInterface.OnClickListener listener) {
+ final AlertDialog alertDialog = createDialog(context, DialogType.EDIT_SHORTCUT_GENERIC,
+ dialogTitle, listener);
+ alertDialog.show();
+
+ return alertDialog;
+ }
+
+ /**
+ * Method to show the edit shortcut dialog in Magnification.
+ *
+ * @param context A valid context
+ * @param dialogTitle The title of edit shortcut dialog
+ * @param listener The listener to determine the action of edit shortcut dialog
+ * @return A edit shortcut dialog for showing in Magnification
+ */
+ public static AlertDialog showMagnificationEditShortcutDialog(Context context,
+ CharSequence dialogTitle, DialogInterface.OnClickListener listener) {
+ final AlertDialog alertDialog = createDialog(context,
+ DialogType.EDIT_SHORTCUT_MAGNIFICATION, dialogTitle, listener);
+ alertDialog.show();
+
+ return alertDialog;
+ }
+
+ private static AlertDialog createDialog(Context context, int dialogType,
+ CharSequence dialogTitle, DialogInterface.OnClickListener listener) {
+
+ final AlertDialog alertDialog = new AlertDialog.Builder(context)
+ .setView(createEditDialogContentView(context, dialogType))
+ .setTitle(dialogTitle)
+ .setPositiveButton(R.string.save, listener)
+ .setNegativeButton(R.string.cancel,
+ (DialogInterface dialog, int which) -> dialog.dismiss())
+ .create();
+
+ return alertDialog;
+ }
+
+ /**
+ * Get a content View for the edit shortcut dialog.
+ *
+ * @param context A valid context
+ * @param dialogType The type of edit shortcut dialog
+ * @return A content view suitable for viewing
+ */
+ private static View createEditDialogContentView(Context context, int dialogType) {
+ final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ View contentView = null;
+
+ switch (dialogType) {
+ case DialogType.EDIT_SHORTCUT_GENERIC:
+ contentView = inflater.inflate(
+ R.layout.accessibility_edit_shortcut, null);
+ initSoftwareShortcut(context, contentView);
+ initHardwareShortcut(context, contentView);
+ break;
+ case DialogType.EDIT_SHORTCUT_MAGNIFICATION:
+ contentView = inflater.inflate(
+ R.layout.accessibility_edit_shortcut_magnification, null);
+ initSoftwareShortcut(context, contentView);
+ initHardwareShortcut(context, contentView);
+ initMagnifyShortcut(context, contentView);
+ initAdvancedWidget(contentView);
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ return contentView;
+ }
+
+ private static void setupShortcutWidget(View view, CharSequence titleText,
+ CharSequence summaryText, int imageResId) {
+ final CheckBox checkBox = view.findViewById(R.id.checkbox);
+ checkBox.setText(titleText);
+ final TextView summary = view.findViewById(R.id.summary);
+ summary.setText(summaryText);
+ final ImageView image = view.findViewById(R.id.image);
+ image.setImageResource(imageResId);
+ }
+
+ private static void initSoftwareShortcut(Context context, View view) {
+ final View dialogView = view.findViewById(R.id.software_shortcut);
+ final TextView summary = dialogView.findViewById(R.id.summary);
+ final int lineHeight = summary.getLineHeight();
+ setupShortcutWidget(dialogView, retrieveTitle(context),
+ retrieveSummary(context, lineHeight), retrieveImageResId(context));
+ }
+
+ private static void initHardwareShortcut(Context context, View view) {
+ final View dialogView = view.findViewById(R.id.hardware_shortcut);
+ final String title = context.getString(
+ R.string.accessibility_shortcut_edit_dialog_title_hardware);
+ final String summary = context.getString(
+ R.string.accessibility_shortcut_edit_dialog_summary_hardware);
+ setupShortcutWidget(dialogView, title, summary,
+ R.drawable.illustration_accessibility_button);
+ }
+
+ private static void initMagnifyShortcut(Context context, View view) {
+ final View dialogView = view.findViewById(R.id.triple_tap_shortcut);
+ final String title = context.getString(
+ R.string.accessibility_shortcut_edit_dialog_title_triple_tap);
+ final String summary = context.getString(
+ R.string.accessibility_shortcut_edit_dialog_summary_triple_tap);
+ setupShortcutWidget(dialogView, title, summary,
+ R.drawable.illustration_accessibility_button);
+ }
+
+ private static void initAdvancedWidget(View view) {
+ final LinearLayout advanced = view.findViewById(R.id.advanced_shortcut);
+ final View tripleTap = view.findViewById(R.id.triple_tap_shortcut);
+ advanced.setOnClickListener((View v) -> {
+ advanced.setVisibility(View.GONE);
+ tripleTap.setVisibility(View.VISIBLE);
+ });
+ }
+
+ private static boolean isGestureNavigateEnabled(Context context) {
+ return context.getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode)
+ == NAV_BAR_MODE_GESTURAL;
+ }
+
+ private static CharSequence retrieveTitle(Context context) {
+ return context.getString(isGestureNavigateEnabled(context)
+ ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture
+ : R.string.accessibility_shortcut_edit_dialog_title_software);
+ }
+
+ private static CharSequence retrieveSummary(Context context, int lineHeight) {
+ return isGestureNavigateEnabled(context)
+ ? context.getString(
+ R.string.accessibility_shortcut_edit_dialog_summary_software_gesture)
+ : getSummaryStringWithIcon(context, lineHeight);
+ }
+
+ private static int retrieveImageResId(Context context) {
+ return isGestureNavigateEnabled(context)
+ ? R.drawable.illustration_accessibility_button
+ : R.drawable.illustration_accessibility_button;
+ }
+
+ private static SpannableString getSummaryStringWithIcon(Context context, int lineHeight) {
+ final String summary = context
+ .getString(R.string.accessibility_shortcut_edit_dialog_summary_software);
+ final SpannableString spannableMessage = SpannableString.valueOf(summary);
+
+ // Icon
+ final int indexIconStart = summary.indexOf("%s");
+ final int indexIconEnd = indexIconStart + 2;
+ final Drawable icon = context.getDrawable(R.drawable.ic_accessibility_new);
+ final ImageSpan imageSpan = new ImageSpan(icon);
+ imageSpan.setContentDescription("");
+ icon.setTint(getThemeAttrColor(context, android.R.attr.textColorPrimary));
+ icon.setBounds(0, 0, lineHeight, lineHeight);
+ spannableMessage.setSpan(
+ imageSpan, indexIconStart, indexIconEnd,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ return spannableMessage;
+ }
+
+ /**
+ * Returns the color associated with the specified attribute in the context's theme.
+ */
+ @ColorInt
+ private static int getThemeAttrColor(final Context context, final int attributeColor) {
+ final int colorResId = getAttrResourceId(context, attributeColor);
+ return ContextCompat.getColor(context, colorResId);
+ }
+
+ /**
+ * Returns the identifier of the resolved resource assigned to the given attribute.
+ */
+ private static int getAttrResourceId(final Context context, final int attributeColor) {
+ final int[] attrs = {attributeColor};
+ final TypedArray typedArray = context.obtainStyledAttributes(attrs);
+ final int colorResId = typedArray.getResourceId(0, 0);
+ typedArray.recycle();
+ return colorResId;
+ }
+}