Merge "Make chart time slot not clickable when accessibility servie is enabled" into sc-dev
diff --git a/src/com/android/settings/fuelgauge/BatteryChartView.java b/src/com/android/settings/fuelgauge/BatteryChartView.java
index cd76d0e..1048017 100644
--- a/src/com/android/settings/fuelgauge/BatteryChartView.java
+++ b/src/com/android/settings/fuelgauge/BatteryChartView.java
@@ -15,6 +15,7 @@
import static java.lang.Math.round;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -28,23 +29,30 @@
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
+import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
import androidx.appcompat.widget.AppCompatImageView;
+import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.Utils;
+import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
/** A widget component to draw chart graph. */
public class BatteryChartView extends AppCompatImageView implements View.OnClickListener {
private static final String TAG = "BatteryChartView";
+ private static final List<String> ACCESSIBILITY_SERVICE_NAMES =
+ Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService");
// For drawing the percentage information.
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;
+
/** Selects all trapezoid shapes. */
public static final int SELECTED_INDEX_ALL = -1;
public static final int SELECTED_INDEX_INVALID = -2;
@@ -57,10 +65,11 @@
private int mDividerWidth;
private int mDividerHeight;
private int mTrapezoidCount;
- private int mSelectedIndex;
private float mTrapezoidVOffset;
private float mTrapezoidHOffset;
- private boolean mIsSlotsClickable;
+ private boolean mIsSlotsClickabled;
+
+ @VisibleForTesting int mSelectedIndex;
// Colors for drawing the trapezoid shape and dividers.
private int mTrapezoidColor;
@@ -247,26 +256,37 @@
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
+ updateClickableState();
+ }
+
+ private void updateClickableState() {
final Context context = mContext;
- mIsSlotsClickable =
+ mIsSlotsClickabled =
FeatureFactory.getFactory(context)
- .getPowerUsageFeatureProvider(context)
- .isChartGraphSlotsEnabled(context);
- Log.d(TAG, "isChartGraphSlotsEnabled:" + mIsSlotsClickable);
+ .getPowerUsageFeatureProvider(context)
+ .isChartGraphSlotsEnabled(context)
+ && !isAccessibilityEnabled(context);
+ Log.d(TAG, "isChartGraphSlotsEnabled:" + mIsSlotsClickabled);
setClickable(isClickable());
// Initializes the trapezoid curve paint for non-clickable case.
- if (!mIsSlotsClickable && mTrapezoidCurvePaint == null) {
+ if (!mIsSlotsClickabled && mTrapezoidCurvePaint == null) {
mTrapezoidCurvePaint = new Paint();
mTrapezoidCurvePaint.setAntiAlias(true);
mTrapezoidCurvePaint.setColor(mTrapezoidSolidColor);
mTrapezoidCurvePaint.setStyle(Paint.Style.STROKE);
mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2);
}
+ invalidate();
}
@Override
public void setClickable(boolean clickable) {
- super.setClickable(mIsSlotsClickable && clickable);
+ super.setClickable(mIsSlotsClickabled && clickable);
+ }
+
+ @VisibleForTesting
+ void setClickableForce(boolean clickable) {
+ super.setClickable(clickable);
}
private void initializeColors(Context context) {
@@ -412,7 +432,7 @@
}
// Configures the trapezoid paint color.
final int trapezoidColor =
- !mIsSlotsClickable
+ !mIsSlotsClickabled
? mTrapezoidColor
: mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL
? mTrapezoidSolidColor : mTrapezoidColor;
@@ -469,6 +489,29 @@
&& mLevels[trapezoidIndex + 1] != 0;
}
+ @VisibleForTesting
+ static boolean isAccessibilityEnabled(Context context) {
+ final AccessibilityManager accessibilityManager =
+ context.getSystemService(AccessibilityManager.class);
+ if (!accessibilityManager.isEnabled()) {
+ return false;
+ }
+ final List<AccessibilityServiceInfo> serviceInfoList =
+ accessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_SPOKEN
+ | AccessibilityServiceInfo.FEEDBACK_GENERIC);
+ for (AccessibilityServiceInfo info : serviceInfoList) {
+ for (String serviceName : ACCESSIBILITY_SERVICE_NAMES) {
+ final String serviceId = info.getId();
+ if (serviceId != null && serviceId.contains(serviceName)) {
+ Log.d(TAG, "acccessibilityEnabled:" + serviceId);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
// A container class for each trapezoid left and right location.
private static final class TrapezoidSlot {
public float mLeft;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java
new file mode 100644
index 0000000..877ebc2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+package com.android.settings.fuelgauge;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.Context;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+
+@RunWith(RobolectricTestRunner.class)
+public final class BatteryChartViewTest {
+
+ private Context mContext;
+ private BatteryChartView mBatteryChartView;
+ private FakeFeatureFactory mFeatureFactory;
+ private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
+
+ @Mock private AccessibilityServiceInfo mockAccessibilityServiceInfo;
+ @Mock private AccessibilityManager mockAccessibilityManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+ mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
+ mContext = spy(RuntimeEnvironment.application);
+ mBatteryChartView = new BatteryChartView(mContext);
+ doReturn(mockAccessibilityManager).when(mContext)
+ .getSystemService(AccessibilityManager.class);
+ doReturn("TalkBackService").when(mockAccessibilityServiceInfo).getId();
+ doReturn(Arrays.asList(mockAccessibilityServiceInfo))
+ .when(mockAccessibilityManager)
+ .getEnabledAccessibilityServiceList(anyInt());
+ }
+
+ @Test
+ public void testIsAccessibilityEnabled_disable_returnFalse() {
+ doReturn(false).when(mockAccessibilityManager).isEnabled();
+ assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ public void testIsAccessibilityEnabled_emptyInfo_returnFalse() {
+ doReturn(true).when(mockAccessibilityManager).isEnabled();
+ doReturn(new ArrayList<AccessibilityServiceInfo>())
+ .when(mockAccessibilityManager)
+ .getEnabledAccessibilityServiceList(anyInt());
+
+ assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isFalse();
+ }
+
+ @Test
+ public void testIsAccessibilityEnabled_validServiceId_returnTrue() {
+ doReturn(true).when(mockAccessibilityManager).isEnabled();
+ assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isTrue();
+ }
+
+ @Test
+ public void testSetSelectedIndex_invokesCallback() {
+ final int selectedIndex[] = new int[1];
+ final int expectedIndex = 2;
+ mBatteryChartView.mSelectedIndex = 1;
+ mBatteryChartView.setOnSelectListener(
+ trapezoidIndex -> {
+ selectedIndex[0] = trapezoidIndex;
+ });
+
+ mBatteryChartView.setSelectedIndex(expectedIndex);
+
+ assertThat(mBatteryChartView.mSelectedIndex)
+ .isEqualTo(expectedIndex);
+ assertThat(selectedIndex[0]).isEqualTo(expectedIndex);
+ }
+
+ @Test
+ public void testSetSelectedIndex_sameIndex_notInvokesCallback() {
+ final int selectedIndex[] = new int[1];
+ final int expectedIndex = 1;
+ mBatteryChartView.mSelectedIndex = expectedIndex;
+ mBatteryChartView.setOnSelectListener(
+ trapezoidIndex -> {
+ selectedIndex[0] = trapezoidIndex;
+ });
+
+ mBatteryChartView.setSelectedIndex(expectedIndex);
+
+ assertThat(selectedIndex[0]).isNotEqualTo(expectedIndex);
+ }
+
+ @Test
+ public void testClickable_isChartGraphSlotsEnabledIsFalse_notClickable() {
+ mBatteryChartView.setClickableForce(true);
+ when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
+ .thenReturn(false);
+
+ mBatteryChartView.onAttachedToWindow();
+ assertThat(mBatteryChartView.isClickable()).isFalse();
+ }
+
+ @Test
+ public void testClickable_accessibilityIsDisabled_clickable() {
+ mBatteryChartView.setClickableForce(true);
+ when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
+ .thenReturn(true);
+ doReturn(false).when(mockAccessibilityManager).isEnabled();
+
+ mBatteryChartView.onAttachedToWindow();
+ assertThat(mBatteryChartView.isClickable()).isTrue();
+ }
+
+ @Test
+ public void testClickable_accessibilityIsEnabledWithoutValidId_clickable() {
+ mBatteryChartView.setClickableForce(true);
+ when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
+ .thenReturn(true);
+ doReturn(true).when(mockAccessibilityManager).isEnabled();
+ doReturn(new ArrayList<AccessibilityServiceInfo>())
+ .when(mockAccessibilityManager)
+ .getEnabledAccessibilityServiceList(anyInt());
+
+ mBatteryChartView.onAttachedToWindow();
+ assertThat(mBatteryChartView.isClickable()).isTrue();
+ }
+
+ @Test
+ public void testClickable_accessibilityIsEnabledWithValidId_notClickable() {
+ mBatteryChartView.setClickableForce(true);
+ when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
+ .thenReturn(true);
+ doReturn(true).when(mockAccessibilityManager).isEnabled();
+
+ mBatteryChartView.onAttachedToWindow();
+ assertThat(mBatteryChartView.isClickable()).isFalse();
+ }
+}