Merge "Make chart time slot not clickable when accessibility servie is enabled" into sc-dev

This commit is contained in:
YUKAI HUNG
2021-05-13 06:34:36 +00:00
committed by Android (Google) Code Review
2 changed files with 222 additions and 9 deletions

View File

@@ -15,6 +15,7 @@ package com.android.settings.fuelgauge;
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.util.Log;
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 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
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 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
@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);
.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 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
}
// Configures the trapezoid paint color.
final int trapezoidColor =
!mIsSlotsClickable
!mIsSlotsClickabled
? mTrapezoidColor
: mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL
? mTrapezoidSolidColor : mTrapezoidColor;
@@ -469,6 +489,29 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
&& 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;

View File

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