Make chart time slot not clickable when accessibility servie is enabled

after discuss with Shuan & Peggy, we will make the chat view is not
clickable since accessibility service is enabled for talkback, switch
access and voice access, since some gestures are conflict with our
design (double click to show all contents is conflict with a11y behavior)

Bug: 187814675
Test: make SettingsRoboTests
Change-Id: I7eecafc42cf4b4a0374ab46f07461e77907fc03a
This commit is contained in:
ykhung
2021-05-13 15:35:01 +08:00
committed by YUKAI HUNG
parent 4cb355f8e5
commit 11de1a6452
2 changed files with 97 additions and 4 deletions

View File

@@ -24,6 +24,7 @@ import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.HapticFeedbackConstants;
@@ -44,7 +45,8 @@ import java.util.List;
import java.util.Locale;
/** A widget component to draw chart graph. */
public class BatteryChartView extends AppCompatImageView implements View.OnClickListener {
public class BatteryChartView extends AppCompatImageView implements View.OnClickListener,
AccessibilityManager.AccessibilityStateChangeListener {
private static final String TAG = "BatteryChartView";
private static final List<String> ACCESSIBILITY_SERVICE_NAMES =
Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService");
@@ -52,6 +54,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private static final String[] PERCENTAGES = new String[] {"100%", "50%", "0%"};
private static final int DEFAULT_TRAPEZOID_COUNT = 12;
private static final int DEFAULT_TIMESTAMP_COUNT = 4;
private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5");
private static final long UPDATE_STATE_DELAYED_TIME = 500L;
/** Selects all trapezoid shapes. */
public static final int SELECTED_INDEX_ALL = -1;
@@ -74,7 +78,6 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
// Colors for drawing the trapezoid shape and dividers.
private int mTrapezoidColor;
private int mTrapezoidSolidColor;
private final int mDividerColor = Color.parseColor("#CDCCC5");
// For drawing the percentage information.
private int mTextPadding;
private final Rect mIndent = new Rect();
@@ -85,11 +88,17 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private final Rect[] mTimestampsBounds =
new Rect[] {new Rect(), new Rect(), new Rect(), new Rect()};
@VisibleForTesting
Handler mHandler = new Handler();
@VisibleForTesting
final Runnable mUpdateClickableStateRun = () -> updateClickableState();
private int[] mLevels;
private Paint mTextPaint;
private Paint mDividerPaint;
private Paint mTrapezoidPaint;
private Paint mTrapezoidCurvePaint = null;
@VisibleForTesting
Paint mTrapezoidCurvePaint = null;
private TrapezoidSlot[] mTrapezoidSlots;
// Records the location to calculate selected index.
private MotionEvent mTouchUpEvent;
@@ -257,6 +266,26 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
public void onAttachedToWindow() {
super.onAttachedToWindow();
updateClickableState();
mContext.getSystemService(AccessibilityManager.class)
.addAccessibilityStateChangeListener(/*listener=*/ this);
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
mContext.getSystemService(AccessibilityManager.class)
.removeAccessibilityStateChangeListener(/*listener=*/ this);
mHandler.removeCallbacks(mUpdateClickableStateRun);
}
@Override
public void onAccessibilityStateChanged(boolean enabled) {
Log.d(TAG, "onAccessibilityStateChanged:" + enabled);
mHandler.removeCallbacks(mUpdateClickableStateRun);
// We should delay it a while since accessibility manager will spend
// some times to bind with new enabled accessibility services.
mHandler.postDelayed(
mUpdateClickableStateRun, UPDATE_STATE_DELAYED_TIME);
}
private void updateClickableState() {
@@ -275,6 +304,10 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
mTrapezoidCurvePaint.setColor(mTrapezoidSolidColor);
mTrapezoidCurvePaint.setStyle(Paint.Style.STROKE);
mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2);
} else if (mIsSlotsClickabled) {
mTrapezoidCurvePaint = null;
// Sets levels again to force update the click state.
setLevels(mLevels);
}
invalidate();
}
@@ -299,7 +332,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
mDividerHeight = resources.getDimensionPixelSize(R.dimen.chartview_divider_height);
mDividerPaint = new Paint();
mDividerPaint.setAntiAlias(true);
mDividerPaint.setColor(mDividerColor);
mDividerPaint.setColor(DIVIDER_COLOR);
mDividerPaint.setStyle(Paint.Style.STROKE);
mDividerPaint.setStrokeWidth(mDividerWidth);
Log.i(TAG, "mDividerWidth:" + mDividerWidth);

View File

@@ -130,6 +130,7 @@ public final class BatteryChartViewTest {
mBatteryChartView.onAttachedToWindow();
assertThat(mBatteryChartView.isClickable()).isFalse();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
}
@Test
@@ -141,6 +142,7 @@ public final class BatteryChartViewTest {
mBatteryChartView.onAttachedToWindow();
assertThat(mBatteryChartView.isClickable()).isTrue();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
}
@Test
@@ -155,6 +157,7 @@ public final class BatteryChartViewTest {
mBatteryChartView.onAttachedToWindow();
assertThat(mBatteryChartView.isClickable()).isTrue();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
}
@Test
@@ -166,5 +169,62 @@ public final class BatteryChartViewTest {
mBatteryChartView.onAttachedToWindow();
assertThat(mBatteryChartView.isClickable()).isFalse();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
}
@Test
public void testClickable_restoreFromNonClickableState() {
final int[] levels = new int[13];
for (int index = 0; index < levels.length; index++) {
levels[index] = index + 1;
}
mBatteryChartView.setTrapezoidCount(12);
mBatteryChartView.setLevels(levels);
mBatteryChartView.setClickableForce(true);
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
.thenReturn(true);
doReturn(true).when(mockAccessibilityManager).isEnabled();
mBatteryChartView.onAttachedToWindow();
// Ensures the testing environment is correct.
assertThat(mBatteryChartView.isClickable()).isFalse();
// Turns off accessibility service.
doReturn(false).when(mockAccessibilityManager).isEnabled();
mBatteryChartView.onAttachedToWindow();
assertThat(mBatteryChartView.isClickable()).isTrue();
}
@Test
public void testOnAttachedToWindow_addAccessibilityStateChangeListener() {
mBatteryChartView.onAttachedToWindow();
verify(mockAccessibilityManager)
.addAccessibilityStateChangeListener(mBatteryChartView);
}
@Test
public void testOnDetachedFromWindow_removeAccessibilityStateChangeListener() {
mBatteryChartView.onAttachedToWindow();
mBatteryChartView.mHandler.postDelayed(
mBatteryChartView.mUpdateClickableStateRun, 1000);
mBatteryChartView.onDetachedFromWindow();
verify(mockAccessibilityManager)
.removeAccessibilityStateChangeListener(mBatteryChartView);
assertThat(mBatteryChartView.mHandler.hasCallbacks(
mBatteryChartView.mUpdateClickableStateRun))
.isFalse();
}
@Test
public void testOnAccessibilityStateChanged_postUpdateStateRunnable() {
mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler);
mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true);
verify(mBatteryChartView.mHandler)
.removeCallbacks(mBatteryChartView.mUpdateClickableStateRun);
verify(mBatteryChartView.mHandler)
.postDelayed(mBatteryChartView.mUpdateClickableStateRun, 500L);
}
}