Loading res/values/accessibility_shortcut_keys.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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> Loading res/values/strings.xml +4 −0 Original line number Diff line number Diff line Loading @@ -5112,6 +5112,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> Loading res/xml/accessibility_edit_shortcuts.xml +55 −51 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto"> <PreferenceCategory android:key="@string/accessibility_shortcut_description_pref"> <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference android:key="@string/accessibility_shortcut_fab_pref" android:persistent="false" Loading Loading @@ -75,5 +78,6 @@ settings:allowDividerAbove="false" settings:allowDividerBelow="false" settings:controller="com.android.settings.accessibility.shortcuts.TripleTapShortcutOptionController" /> </PreferenceCategory> </PreferenceScreen> No newline at end of file src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java +89 −1 Original line number Diff line number Diff line Loading @@ -27,16 +27,21 @@ import static com.android.internal.accessibility.AccessibilityShortcutController 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; Loading @@ -48,9 +53,12 @@ import androidx.annotation.VisibleForTesting; 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; Loading @@ -60,7 +68,10 @@ import com.google.android.setupcompat.template.FooterBarMixin; 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; /** Loading Loading @@ -171,6 +182,36 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment { 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( Loading Loading @@ -275,7 +316,6 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment { } mShortcutTargets = Set.of(targets); // TODO(318748373): use 'targets' to populate title when no title is given } @Override Loading Loading @@ -356,4 +396,52 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment { // 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)) ); } } } tests/robotests/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragmentTest.java +72 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,9 @@ import static com.google.android.setupcompat.util.WizardManagerHelper.EXTRA_IS_P 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; Loading @@ -35,7 +38,9 @@ import android.content.Context; 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; Loading @@ -46,6 +51,7 @@ import androidx.preference.TwoStatePreference; 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; Loading @@ -60,6 +66,7 @@ import com.google.android.setupcompat.util.WizardManagerHelper; 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; Loading Loading @@ -89,6 +96,9 @@ public class EditShortcutsPreferenceFragmentTest { 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; Loading Loading @@ -414,6 +424,60 @@ public class EditShortcutsPreferenceFragmentTest { } @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(); Loading Loading @@ -480,4 +544,12 @@ public class EditShortcutsPreferenceFragmentTest { 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; } } Loading
res/values/accessibility_shortcut_keys.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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> Loading
res/values/strings.xml +4 −0 Original line number Diff line number Diff line Loading @@ -5112,6 +5112,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> Loading
res/xml/accessibility_edit_shortcuts.xml +55 −51 Original line number Diff line number Diff line Loading @@ -18,6 +18,9 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res-auto"> <PreferenceCategory android:key="@string/accessibility_shortcut_description_pref"> <com.android.settings.accessibility.shortcuts.ShortcutOptionPreference android:key="@string/accessibility_shortcut_fab_pref" android:persistent="false" Loading Loading @@ -75,5 +78,6 @@ settings:allowDividerAbove="false" settings:allowDividerBelow="false" settings:controller="com.android.settings.accessibility.shortcuts.TripleTapShortcutOptionController" /> </PreferenceCategory> </PreferenceScreen> No newline at end of file
src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java +89 −1 Original line number Diff line number Diff line Loading @@ -27,16 +27,21 @@ import static com.android.internal.accessibility.AccessibilityShortcutController 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; Loading @@ -48,9 +53,12 @@ import androidx.annotation.VisibleForTesting; 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; Loading @@ -60,7 +68,10 @@ import com.google.android.setupcompat.template.FooterBarMixin; 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; /** Loading Loading @@ -171,6 +182,36 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment { 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( Loading Loading @@ -275,7 +316,6 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment { } mShortcutTargets = Set.of(targets); // TODO(318748373): use 'targets' to populate title when no title is given } @Override Loading Loading @@ -356,4 +396,52 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment { // 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)) ); } } }
tests/robotests/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragmentTest.java +72 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,9 @@ import static com.google.android.setupcompat.util.WizardManagerHelper.EXTRA_IS_P 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; Loading @@ -35,7 +38,9 @@ import android.content.Context; 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; Loading @@ -46,6 +51,7 @@ import androidx.preference.TwoStatePreference; 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; Loading @@ -60,6 +66,7 @@ import com.google.android.setupcompat.util.WizardManagerHelper; 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; Loading Loading @@ -89,6 +96,9 @@ public class EditShortcutsPreferenceFragmentTest { 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; Loading Loading @@ -414,6 +424,60 @@ public class EditShortcutsPreferenceFragmentTest { } @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(); Loading Loading @@ -480,4 +544,12 @@ public class EditShortcutsPreferenceFragmentTest { 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; } }