Make magnification mode switch accessible

To support accessibility of magnification mode switch,
We provide content description, state description and
click action.

Bug: 168278448
Test: Enable Talkback to do manually test
      atest MagnificationModeSwitchTest

Change-Id: I1b94f3849f85db4f6dc2b801d0dd6ff40d0c87ed
This commit is contained in:
ryanlwlin
2020-09-23 22:47:55 +08:00
parent 58686805ae
commit 28e2938771
3 changed files with 92 additions and 7 deletions

View File

@@ -2678,6 +2678,14 @@
<string name="accessibility_control_move_left">Move left</string>
<!-- Action in accessibility menu to move the magnification window right. [CHAR LIMIT=30] -->
<string name="accessibility_control_move_right">Move right</string>
<!-- Content description for magnification mode switch. [CHAR LIMIT=NONE] -->
<string name="magnification_mode_switch_description">Magnification switch</string>
<!-- A11y state description for magnification mode switch that device is in full-screen mode. [CHAR LIMIT=NONE] -->
<string name="magnification_mode_switch_state_full_screen">Magnify entire screen</string>
<!-- A11y state description for magnification mode switch that device is in window mode. [CHAR LIMIT=NONE] -->
<string name="magnification_mode_switch_state_window">Magnify part of screen</string>
<!-- Click action label for magnification switch. [CHAR LIMIT=NONE] -->
<string name="magnification_mode_switch_click_label">Switch</string>
<!-- Device Controls strings -->
<!-- Device Controls empty state, title [CHAR LIMIT=30] -->

View File

@@ -16,11 +16,14 @@
package com.android.systemui.accessibility;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.os.Bundle;
import android.provider.Settings;
import android.util.MathUtils;
import android.view.Gravity;
@@ -28,6 +31,8 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.ImageView;
import com.android.internal.annotations.VisibleForTesting;
@@ -71,6 +76,29 @@ class MagnificationModeSwitch {
applyResourcesValues();
mImageView.setImageResource(getIconResId(mMagnificationMode));
mImageView.setOnTouchListener(this::onTouch);
mImageView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setStateDescription(formatStateDescription());
info.setContentDescription(mContext.getResources().getString(
R.string.magnification_mode_switch_description));
final AccessibilityAction clickAction = new AccessibilityAction(
AccessibilityAction.ACTION_CLICK.getId(), mContext.getResources().getString(
R.string.magnification_mode_switch_click_label));
info.addAction(clickAction);
info.setClickable(true);
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (action == AccessibilityAction.ACTION_CLICK.getId()) {
handleSingleTap();
return true;
}
return super.performAccessibilityAction(host, action, args);
}
});
mAnimationTask = () -> {
mImageView.animate()
@@ -81,6 +109,13 @@ class MagnificationModeSwitch {
};
}
private CharSequence formatStateDescription() {
final int stringId = mMagnificationMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
? R.string.magnification_mode_switch_state_window
: R.string.magnification_mode_switch_state_full_screen;
return mContext.getResources().getString(stringId);
}
private void applyResourcesValues() {
final int padding = mContext.getResources().getDimensionPixelSize(
R.dimen.magnification_switch_button_padding);

View File

@@ -22,11 +22,14 @@ import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK;
import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId;
import static junit.framework.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -45,10 +48,12 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewPropertyAnimator;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -142,13 +147,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
resetMockImageViewAndAnimator();
listener.onTouch(mSpyImageView, MotionEvent.obtain(
0, ViewConfiguration.getTapTimeout(), ACTION_UP, 100, 100, 0));
verify(mViewPropertyAnimator).cancel();
verify(mSpyImageView).setImageResource(
getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
verify(mWindowManager).removeView(mSpyImageView);
final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, actualMode);
verifyTapAction(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
}
@Test
@@ -222,6 +221,34 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
assertShowButtonAnimation();
}
@Test
public void initializeA11yNode_showWindowModeButton_expectedValues() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
mSpyImageView.onInitializeAccessibilityNodeInfo(nodeInfo);
assertEquals(mContext.getString(R.string.magnification_mode_switch_description),
nodeInfo.getContentDescription());
assertEquals(mContext.getString(R.string.magnification_mode_switch_state_window),
nodeInfo.getStateDescription());
assertThat(nodeInfo.getActionList(),
hasItems(new AccessibilityNodeInfo.AccessibilityAction(
ACTION_CLICK.getId(), mContext.getResources().getString(
R.string.magnification_mode_switch_click_label))));
}
@Test
public void performA11yActions_showWindowModeButton_verifyTapAction() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
resetMockImageViewAndAnimator();
mSpyImageView.performAccessibilityAction(
ACTION_CLICK.getId(), null);
verifyTapAction(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
}
private void assertModeUnchanged(int expectedMode) {
final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
@@ -254,4 +281,19 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
Mockito.reset(mSpyImageView);
initMockImageViewAndAnimator();
}
/**
* Verifies the tap behaviour including the image of the button and the magnification mode.
*
* @param expectedMode the expected mode after tapping
*/
private void verifyTapAction(int expectedMode) {
verify(mViewPropertyAnimator).cancel();
verify(mSpyImageView).setImageResource(
getIconResId(expectedMode));
verify(mWindowManager).removeView(mSpyImageView);
final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
assertEquals(expectedMode, actualMode);
}
}