DragToEdit Dynaimc Title
Bug: 323388734
Test: atest EditShortcutsPreferenceFragmentTest
Flag: aconfig edit_shortcut_dynamic_title true
Change-Id: I74785054e4f846db04d13a54bd6695e4690f5b8b
diff --git a/res/values/accessibility_shortcut_keys.xml b/res/values/accessibility_shortcut_keys.xml
index 0d409a8..4992a54 100644
--- a/res/values/accessibility_shortcut_keys.xml
+++ b/res/values/accessibility_shortcut_keys.xml
@@ -16,6 +16,7 @@
-->
<resources>
+ <string name="accessibility_shortcut_description_pref" translatable="false">shortcut_description</string>
<string name="accessibility_shortcut_volume_keys_pref" translatable="false">shortcut_volume_keys_pref</string>
<string name="accessibility_shortcut_gesture_pref" translatable="false">shortcut_gesture_pref</string>
<string name="accessibility_shortcut_nav_button_pref" translatable="false">shortcut_nav_button_pref</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 45305ec..533c9b5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5096,6 +5096,10 @@
<string name="accessibility_shortcut_type_hardware">Hold volume keys</string>
<!-- Summary for accessibility shortcut preference for magnification triple tap shortcut type. [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_type_triple_tap">Triple tap screen</string>
+ <!-- Generic title for editing the shortcuts of multiple accessibility features. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_shortcut_edit_screen_title">Edit accessibility shortcuts</string>
+ <!-- Prompt for editing the shortcuts of multiple accessibility features. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_shortcut_edit_screen_prompt">Chose your shortcut for %1$s</string>
<!-- Button text for the accessibility dialog continue to the next screen for hearing aid. [CHAR LIMIT=32] -->
<string name="accessibility_hearingaid_instruction_continue_button">Continue</string>
diff --git a/res/xml/accessibility_edit_shortcuts.xml b/res/xml/accessibility_edit_shortcuts.xml
index 06cbedd..8be0ee5 100644
--- a/res/xml/accessibility_edit_shortcuts.xml
+++ b/res/xml/accessibility_edit_shortcuts.xml
@@ -18,62 +18,66 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto">
- <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
- android:key="@string/accessibility_shortcut_fab_pref"
- android:persistent="false"
- android:selectable="true"
- settings:allowDividerAbove="false"
- settings:allowDividerBelow="false"
- settings:controller="com.android.settings.accessibility.shortcuts.FloatingButtonShortcutOptionController" />
+ <PreferenceCategory
+ android:key="@string/accessibility_shortcut_description_pref">
- <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
- android:key="@string/accessibility_shortcut_gesture_pref"
- android:persistent="false"
- android:selectable="true"
- settings:allowDividerAbove="false"
- settings:allowDividerBelow="false"
- settings:controller="com.android.settings.accessibility.shortcuts.GestureShortcutOptionController" />
+ <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
+ android:key="@string/accessibility_shortcut_fab_pref"
+ android:persistent="false"
+ android:selectable="true"
+ settings:allowDividerAbove="false"
+ settings:allowDividerBelow="false"
+ settings:controller="com.android.settings.accessibility.shortcuts.FloatingButtonShortcutOptionController" />
- <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
- android:key="@string/accessibility_shortcut_nav_button_pref"
- android:persistent="false"
- android:selectable="true"
- settings:allowDividerAbove="false"
- settings:allowDividerBelow="false"
- settings:controller="com.android.settings.accessibility.shortcuts.NavButtonShortcutOptionController" />
+ <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
+ android:key="@string/accessibility_shortcut_gesture_pref"
+ android:persistent="false"
+ android:selectable="true"
+ settings:allowDividerAbove="false"
+ settings:allowDividerBelow="false"
+ settings:controller="com.android.settings.accessibility.shortcuts.GestureShortcutOptionController" />
- <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
- android:key="@string/accessibility_shortcut_volume_keys_pref"
- android:persistent="false"
- android:selectable="true"
- settings:allowDividerAbove="false"
- settings:allowDividerBelow="false"
- settings:controller="com.android.settings.accessibility.shortcuts.VolumeKeysShortcutOptionController" />
+ <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
+ android:key="@string/accessibility_shortcut_nav_button_pref"
+ android:persistent="false"
+ android:selectable="true"
+ settings:allowDividerAbove="false"
+ settings:allowDividerBelow="false"
+ settings:controller="com.android.settings.accessibility.shortcuts.NavButtonShortcutOptionController" />
- <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
- android:key="@string/accessibility_shortcut_two_fingers_double_tap_pref"
- android:persistent="false"
- android:selectable="true"
- settings:allowDividerAbove="false"
- settings:allowDividerBelow="false"
- settings:controller="com.android.settings.accessibility.shortcuts.TwoFingersDoubleTapShortcutOptionController" />
+ <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
+ android:key="@string/accessibility_shortcut_volume_keys_pref"
+ android:persistent="false"
+ android:selectable="true"
+ settings:allowDividerAbove="false"
+ settings:allowDividerBelow="false"
+ settings:controller="com.android.settings.accessibility.shortcuts.VolumeKeysShortcutOptionController" />
- <Preference
- android:icon="@drawable/ic_keyboard_arrow_down"
- android:key="@string/accessibility_shortcuts_advanced_collapsed"
- android:persistent="false"
- android:selectable="true"
- android:title="@string/accessibility_shortcut_edit_dialog_title_advance"
- settings:allowDividerAbove="false"
- settings:allowDividerBelow="false"
- settings:controller="com.android.settings.accessibility.shortcuts.AdvancedShortcutsPreferenceController" />
+ <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
+ android:key="@string/accessibility_shortcut_two_fingers_double_tap_pref"
+ android:persistent="false"
+ android:selectable="true"
+ settings:allowDividerAbove="false"
+ settings:allowDividerBelow="false"
+ settings:controller="com.android.settings.accessibility.shortcuts.TwoFingersDoubleTapShortcutOptionController" />
- <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
- android:key="@string/accessibility_shortcut_triple_tap_pref"
- android:persistent="false"
- android:selectable="true"
- settings:allowDividerAbove="false"
- settings:allowDividerBelow="false"
- settings:controller="com.android.settings.accessibility.shortcuts.TripleTapShortcutOptionController" />
+ <Preference
+ android:icon="@drawable/ic_keyboard_arrow_down"
+ android:key="@string/accessibility_shortcuts_advanced_collapsed"
+ android:persistent="false"
+ android:selectable="true"
+ android:title="@string/accessibility_shortcut_edit_dialog_title_advance"
+ settings:allowDividerAbove="false"
+ settings:allowDividerBelow="false"
+ settings:controller="com.android.settings.accessibility.shortcuts.AdvancedShortcutsPreferenceController" />
+
+ <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
+ android:key="@string/accessibility_shortcut_triple_tap_pref"
+ android:persistent="false"
+ android:selectable="true"
+ settings:allowDividerAbove="false"
+ settings:allowDividerBelow="false"
+ settings:controller="com.android.settings.accessibility.shortcuts.TripleTapShortcutOptionController" />
+ </PreferenceCategory>
</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java b/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
index a3cbb57..5a3b13a 100644
--- a/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
@@ -27,16 +27,21 @@
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE;
+import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.database.ContentObserver;
+import android.icu.text.ListFormatter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -48,9 +53,12 @@
import androidx.preference.Preference;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
+import com.android.internal.accessibility.dialog.AccessibilityTargetHelper;
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
import com.android.settings.accessibility.AccessibilitySetupWizardUtils;
+import com.android.settings.accessibility.Flags;
import com.android.settings.accessibility.PreferredShortcuts;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
@@ -60,7 +68,10 @@
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.GlifPreferenceLayout;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -171,6 +182,36 @@
registerSettingsObserver();
}
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ super.onCreatePreferences(savedInstanceState, rootKey);
+
+ Activity activity = getActivity();
+
+ if (!activity.getIntent().getAction().equals(
+ Settings.ACTION_ACCESSIBILITY_SHORTCUT_SETTINGS)
+ || !Flags.editShortcutsInFullScreen()) {
+ return;
+ }
+
+ // TODO(b/325664350): Implement shortcut type for "all shortcuts"
+ List<AccessibilityTarget> accessibilityTargets =
+ AccessibilityTargetHelper.getInstalledTargets(
+ activity.getBaseContext(), AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY);
+
+ Pair<String, String> titles = getTitlesFromAccessibilityTargetList(
+ mShortcutTargets,
+ accessibilityTargets,
+ activity.getResources()
+ );
+
+ activity.setTitle(titles.first);
+
+ String categoryKey = activity.getResources().getString(
+ R.string.accessibility_shortcut_description_pref);
+ findPreference(categoryKey).setTitle(titles.second);
+ }
+
@NonNull
@Override
public RecyclerView onCreateRecyclerView(
@@ -275,7 +316,6 @@
}
mShortcutTargets = Set.of(targets);
- // TODO(318748373): use 'targets' to populate title when no title is given
}
@Override
@@ -356,4 +396,52 @@
// A11y Nav Button
refreshPreferenceController(NavButtonShortcutOptionController.class);
}
+
+ /**
+ * Generates a title & subtitle pair describing the features whose shortcuts are being edited.
+ *
+ * @param shortcutTargets string list of component names corresponding to
+ * the relevant shortcut targets.
+ * @param accessibilityTargets list of accessibility targets
+ * to try and find corresponding labels in.
+ * @return pair of strings to be used as page title and subtitle.
+ * If there is only one shortcut label, It is displayed in the title and the subtitle is null.
+ * Otherwise, the title is a generic prompt and the subtitle lists all shortcut labels.
+ */
+ @VisibleForTesting
+ static Pair<String, String> getTitlesFromAccessibilityTargetList(
+ Set<String> shortcutTargets,
+ List<AccessibilityTarget> accessibilityTargets,
+ Resources resources) {
+ ArrayList<CharSequence> featureLabels = new ArrayList<>();
+
+ Map<String, CharSequence> accessibilityTargetLabels = new ArrayMap<>();
+ accessibilityTargets.forEach((target) -> accessibilityTargetLabels.put(
+ target.getId(), target.getLabel()));
+
+ for (String target: shortcutTargets) {
+ if (accessibilityTargetLabels.containsKey(target)) {
+ featureLabels.add(accessibilityTargetLabels.get(target));
+ } else {
+ throw new IllegalStateException("Shortcut target does not have a label: " + target);
+ }
+ }
+
+ if (featureLabels.size() == 1) {
+ return new Pair<>(
+ resources.getString(
+ R.string.accessibility_shortcut_title, featureLabels.get(0)),
+ null
+ );
+ } else if (featureLabels.size() == 0) {
+ throw new IllegalStateException("Found no labels for any shortcut targets.");
+ } else {
+ return new Pair<>(
+ resources.getString(R.string.accessibility_shortcut_edit_screen_title),
+ resources.getString(
+ R.string.accessibility_shortcut_edit_screen_prompt,
+ ListFormatter.getInstance().format(featureLabels))
+ );
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragmentTest.java
index 13f0b24..b2ddb6c 100644
--- a/tests/robotests/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragmentTest.java
@@ -28,6 +28,9 @@
import static com.google.android.setupcompat.util.WizardManagerHelper.EXTRA_IS_SETUP_FLOW;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.content.ComponentName;
@@ -35,7 +38,9 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
+import android.util.Pair;
import android.view.accessibility.AccessibilityManager;
import androidx.fragment.app.FragmentActivity;
@@ -46,6 +51,7 @@
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
@@ -60,6 +66,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
@@ -89,6 +96,9 @@
private static final String TARGET = MAGNIFICATION_CONTROLLER_NAME;
private static final Set<String> TARGETS = Set.of(TARGET);
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private final Context mContext = ApplicationProvider.getApplicationContext();
private FragmentActivity mActivity;
private FragmentScenario<EditShortcutsPreferenceFragment> mFragmentScenario;
@@ -414,6 +424,60 @@
}
+ @Test
+ public void findTitles_withSingleTarget_hasNullSubtitle() {
+ final String fake_label = "FAKE";
+ List<AccessibilityTarget> accessibilityTargets = List.of(
+ generateAccessibilityTargetMock(TARGET_FAKE_COMPONENT, fake_label));
+
+ Pair<String, String> titles = EditShortcutsPreferenceFragment
+ .getTitlesFromAccessibilityTargetList(
+ Set.of(TARGET_FAKE_COMPONENT.flattenToString()),
+ accessibilityTargets, mActivity.getResources()
+ );
+
+ assertThat(titles.first).isNotNull();
+ assertThat(titles.first).contains(fake_label);
+ assertThat(titles.second).isNull();
+ }
+
+ @Test
+ public void findTitles_withMoreTargets_hasSubtitle() {
+ final String fake_label = "FAKE";
+ final String magnification_label = "MAGNIFICATION";
+ List<AccessibilityTarget> accessibilityTargets = List.of(
+ generateAccessibilityTargetMock(TARGET_FAKE_COMPONENT, fake_label),
+ generateAccessibilityTargetMock(MAGNIFICATION_COMPONENT_NAME, magnification_label));
+
+ Pair<String, String> titles = EditShortcutsPreferenceFragment
+ .getTitlesFromAccessibilityTargetList(
+ Set.of(TARGET_FAKE_COMPONENT.flattenToString(),
+ MAGNIFICATION_COMPONENT_NAME.flattenToString()),
+ accessibilityTargets, mActivity.getResources()
+ );
+
+ assertThat(titles.first).isNotNull();
+ assertThat(titles.second).isNotNull();
+ assertThat(titles.second).contains(fake_label);
+ assertThat(titles.second).contains(magnification_label);
+ }
+
+ @Test
+ public void findTitles_targetMissing_labelNotInTitles() {
+ final String fake_label = "FAKE";
+ List<AccessibilityTarget> accessibilityTargets = List.of(
+ generateAccessibilityTargetMock(TARGET_FAKE_COMPONENT, fake_label));
+
+ assertThrows(IllegalStateException.class,
+ () -> EditShortcutsPreferenceFragment
+ .getTitlesFromAccessibilityTargetList(
+ Set.of(MAGNIFICATION_COMPONENT_NAME.flattenToString()),
+ accessibilityTargets, mActivity.getResources()
+ ));
+ }
+
+
+
private void assertLaunchSubSettingWithCurrentTargetComponents(
String componentName, boolean isInSuw) {
Intent intent = shadowOf(mActivity.getApplication()).getNextStartedActivity();
@@ -480,4 +544,12 @@
intent.putExtra(EXTRA_IS_DEFERRED_SETUP, isInSuw);
return intent;
}
+
+ private AccessibilityTarget generateAccessibilityTargetMock(
+ ComponentName componentName, String label) {
+ AccessibilityTarget target = mock(AccessibilityTarget.class);
+ when(target.getComponentName()).thenReturn(componentName);
+ when(target.getLabel()).thenReturn(label);
+ return target;
+ }
}