[Mag] Keyboard shortcut info shown when physical keyboard present
Shows keyboard shortcut info in magnification settings when a
physical keyboard is present. See bug for screenshots.
Hides touchscreen info in magnification settings when a touchscreen
is not present.
Adds ShadowInputDevice support for physical full keyboards.
Bug: b/388847050
Test: Manual, atest ToggleScreenMagnificationPreferenceFragmentTest
Flag: com.android.server.accessibility.enable_magnification_keyboard_control
Change-Id: Ib53fbd8f929d1cc8e294f6f04bab405c9bb576a9
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c22ce6c..f8937f7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5330,6 +5330,16 @@
{4,number,integer}. Lift finger to stop magnification
]]>
</string>
+ <!-- Instructions on the accessibility preference screen teaching the user how to control magnification with a keyboard. [CHAR LIMIT=none] -->
+ <string name="accessibility_screen_magnification_keyboard_summary">
+ <![CDATA[
+ <b>To zoom with the keyboard:</b><br/>
+ {0,number,integer}. Use the shortcut to start magnification<br/>
+ {1,number,integer}. Hold down <xliff:g id="meta1">%1$s</xliff:g> and <xliff:g id="alt1">%2$s</xliff:g> and press + or - to zoom in or out<br/>
+ {2,number,integer}. Hold down <xliff:g id="meta2">%3$s</xliff:g> and <xliff:g id="alt2">%4$s</xliff:g> and press the arrow keys to move around the screen<br/>
+ {3,number,integer}. Use the shortcut to stop magnification
+ ]]>
+ </string>
<!-- Instructions on the accessibility preference screen teaching the user how to interact with screen magnification when one finger panning feature is turned off. [CHAR LIMIT=none] -->
<string name="accessibility_screen_magnification_summary_one_finger_panning_off">
<![CDATA[
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index c9b8b2b..8b52507 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -35,6 +35,7 @@
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
+import android.view.InputDevice;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -231,9 +232,32 @@
if (!arguments.containsKey(AccessibilitySettings.EXTRA_HTML_DESCRIPTION)
&& !Flags.enableMagnificationOneFingerPanningGesture()) {
- String summary = MessageFormat.format(
- context.getString(R.string.accessibility_screen_magnification_summary),
- new Object[]{1, 2, 3, 4, 5});
+ String summary = "";
+ boolean hasTouchscreen = hasTouchscreen();
+ if (Flags.enableMagnificationKeyboardControl() && hasHardKeyboard()) {
+ // Include the keyboard summary when a keyboard is plugged in.
+ final String meta = context.getString(R.string.modifier_keys_meta);
+ final String alt = context.getString(R.string.modifier_keys_alt);
+ summary += MessageFormat.format(
+ context.getString(
+ R.string.accessibility_screen_magnification_keyboard_summary,
+ meta, alt, meta, alt),
+ new Object[]{1, 2, 3, 4});
+ if (hasTouchscreen) {
+ // Add a newline before the touchscreen text.
+ summary += "<br/><br/>";
+ }
+
+ }
+ if (hasTouchscreen || TextUtils.isEmpty(summary)) {
+ // Always show the touchscreen summary if there is no summary yet, even if the
+ // touchscreen is missing.
+ // If the keyboard summary is present and there is no touchscreen, then we can
+ // ignore the touchscreen summary.
+ summary += MessageFormat.format(
+ context.getString(R.string.accessibility_screen_magnification_summary),
+ new Object[]{1, 2, 3, 4, 5});
+ }
arguments.putCharSequence(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, summary);
}
@@ -610,6 +634,25 @@
getPrefContext(), MAGNIFICATION_CONTROLLER_NAME);
}
+ private boolean hasHardKeyboard() {
+ final int[] devices = InputDevice.getDeviceIds();
+ for (int i = 0; i < devices.length; i++) {
+ InputDevice device = InputDevice.getDevice(devices[i]);
+ if (device == null || device.isVirtual() || !device.isFullKeyboard()) {
+ continue;
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ private boolean hasTouchscreen() {
+ return getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
+ || getPackageManager().hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);
+ }
+
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
// LINT.IfChange(search_data)
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
index 4b28085..3c136f0 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
@@ -51,6 +51,7 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.view.InputDevice;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -68,6 +69,7 @@
import com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode;
import com.android.settings.testutils.shadow.ShadowAccessibilityManager;
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
+import com.android.settings.testutils.shadow.ShadowInputDevice;
import com.android.settings.testutils.shadow.ShadowStorageManager;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -169,6 +171,7 @@
@After
public void tearDown() {
ShadowDeviceConfig.reset();
+ ShadowInputDevice.reset();
}
@Test
@@ -672,6 +675,36 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_KEYBOARD_CONTROL)
+ public void getCurrentHtmlDescription_doesNotIncludeKeyboardInfoIfNoKeyboardAttached() {
+ ToggleScreenMagnificationPreferenceFragment fragment =
+ mFragController.create(
+ R.id.main_content, /* bundle= */ null).start().resume().get();
+
+ String htmlDescription = fragment.getCurrentHtmlDescription().toString();
+ assertThat(htmlDescription).isNotEmpty();
+ assertThat(htmlDescription).doesNotContain("keyboard");
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_KEYBOARD_CONTROL)
+ @Config(shadows = ShadowInputDevice.class)
+ public void getCurrentHtmlDescription_includesKeyboardInfoIfKeyboardAttached() {
+ int deviceId = 1;
+ ShadowInputDevice.sDeviceIds = new int[]{deviceId};
+ InputDevice device = ShadowInputDevice.makeFullKeyboardInputDevicebyId(deviceId);
+ ShadowInputDevice.addDevice(deviceId, device);
+
+ ToggleScreenMagnificationPreferenceFragment fragment =
+ mFragController.create(
+ R.id.main_content, /* bundle= */ null).start().resume().get();
+
+ String htmlDescription = fragment.getCurrentHtmlDescription().toString();
+ assertThat(htmlDescription).isNotEmpty();
+ assertThat(htmlDescription).contains("keyboard");
+ }
+
+ @Test
public void getSummary_magnificationEnabled_returnShortcutOnWithSummary() {
mShadowAccessibilityManager.setAccessibilityShortcutTargets(
TRIPLETAP, List.of(MAGNIFICATION_CONTROLLER_NAME));
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowInputDevice.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowInputDevice.java
index 145c2e9..a448e17 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowInputDevice.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowInputDevice.java
@@ -37,6 +37,8 @@
private int mSources;
+ private boolean mIsFullKeyboard;
+
@Implementation
protected static int[] getDeviceIds() {
return sDeviceIds;
@@ -62,6 +64,28 @@
return mDeviceId;
}
+ public void setId(int id) {
+ mDeviceId = id;
+ }
+
+ @Implementation
+ public int getSources() {
+ return mSources;
+ }
+
+ public void setSources(int sources) {
+ mSources = sources;
+ }
+
+ @Implementation
+ public boolean isFullKeyboard() {
+ return mIsFullKeyboard;
+ }
+
+ public void setFullKeyboard(boolean isFullKeyboard) {
+ mIsFullKeyboard = isFullKeyboard;
+ }
+
public static InputDevice makeInputDevicebyId(int id) {
final InputDevice inputDevice = Shadow.newInstanceOf(InputDevice.class);
final ShadowInputDevice shadowInputDevice = Shadow.extract(inputDevice);
@@ -69,10 +93,6 @@
return inputDevice;
}
- public void setId(int id) {
- mDeviceId = id;
- }
-
public static InputDevice makeInputDevicebyIdWithSources(int id, int sources) {
final InputDevice inputDevice = Shadow.newInstanceOf(InputDevice.class);
final ShadowInputDevice shadowInputDevice = Shadow.extract(inputDevice);
@@ -81,12 +101,17 @@
return inputDevice;
}
- @Implementation
- public int getSources() {
- return mSources;
- }
-
- public void setSources(int sources) {
- mSources = sources;
+ /**
+ * Create a full keyboard input device shadow.
+ * @param id The ID to use. If the ID is < 1, the device is considered virtual.
+ * @return The shadow InputDevice
+ */
+ public static InputDevice makeFullKeyboardInputDevicebyId(int id) {
+ final InputDevice inputDevice = Shadow.newInstanceOf(InputDevice.class);
+ final ShadowInputDevice shadowInputDevice = Shadow.extract(inputDevice);
+ shadowInputDevice.setId(id);
+ shadowInputDevice.setFullKeyboard(true);
+ shadowInputDevice.setSources(InputDevice.SOURCE_KEYBOARD);
+ return inputDevice;
}
}