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();
+    }
+}