Merge "Make chart time slot not clickable when accessibility servie is enabled" into sc-dev
This commit is contained in:
@@ -15,6 +15,7 @@ package com.android.settings.fuelgauge;
|
|||||||
|
|
||||||
import static java.lang.Math.round;
|
import static java.lang.Math.round;
|
||||||
|
|
||||||
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
@@ -28,23 +29,30 @@ import android.util.Log;
|
|||||||
import android.view.HapticFeedbackConstants;
|
import android.view.HapticFeedbackConstants;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.accessibility.AccessibilityManager;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.appcompat.widget.AppCompatImageView;
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.Utils;
|
import com.android.settingslib.Utils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
/** A widget component to draw chart graph. */
|
/** A widget component to draw chart graph. */
|
||||||
public class BatteryChartView extends AppCompatImageView implements View.OnClickListener {
|
public class BatteryChartView extends AppCompatImageView implements View.OnClickListener {
|
||||||
private static final String TAG = "BatteryChartView";
|
private static final String TAG = "BatteryChartView";
|
||||||
|
private static final List<String> ACCESSIBILITY_SERVICE_NAMES =
|
||||||
|
Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService");
|
||||||
// For drawing the percentage information.
|
// For drawing the percentage information.
|
||||||
private static final String[] PERCENTAGES = new String[] {"100%", "50%", "0%"};
|
private static final String[] PERCENTAGES = new String[] {"100%", "50%", "0%"};
|
||||||
private static final int DEFAULT_TRAPEZOID_COUNT = 12;
|
private static final int DEFAULT_TRAPEZOID_COUNT = 12;
|
||||||
private static final int DEFAULT_TIMESTAMP_COUNT = 4;
|
private static final int DEFAULT_TIMESTAMP_COUNT = 4;
|
||||||
|
|
||||||
/** Selects all trapezoid shapes. */
|
/** Selects all trapezoid shapes. */
|
||||||
public static final int SELECTED_INDEX_ALL = -1;
|
public static final int SELECTED_INDEX_ALL = -1;
|
||||||
public static final int SELECTED_INDEX_INVALID = -2;
|
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 mDividerWidth;
|
||||||
private int mDividerHeight;
|
private int mDividerHeight;
|
||||||
private int mTrapezoidCount;
|
private int mTrapezoidCount;
|
||||||
private int mSelectedIndex;
|
|
||||||
private float mTrapezoidVOffset;
|
private float mTrapezoidVOffset;
|
||||||
private float mTrapezoidHOffset;
|
private float mTrapezoidHOffset;
|
||||||
private boolean mIsSlotsClickable;
|
private boolean mIsSlotsClickabled;
|
||||||
|
|
||||||
|
@VisibleForTesting int mSelectedIndex;
|
||||||
|
|
||||||
// Colors for drawing the trapezoid shape and dividers.
|
// Colors for drawing the trapezoid shape and dividers.
|
||||||
private int mTrapezoidColor;
|
private int mTrapezoidColor;
|
||||||
@@ -247,26 +256,37 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
@Override
|
@Override
|
||||||
public void onAttachedToWindow() {
|
public void onAttachedToWindow() {
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
|
updateClickableState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateClickableState() {
|
||||||
final Context context = mContext;
|
final Context context = mContext;
|
||||||
mIsSlotsClickable =
|
mIsSlotsClickabled =
|
||||||
FeatureFactory.getFactory(context)
|
FeatureFactory.getFactory(context)
|
||||||
.getPowerUsageFeatureProvider(context)
|
.getPowerUsageFeatureProvider(context)
|
||||||
.isChartGraphSlotsEnabled(context);
|
.isChartGraphSlotsEnabled(context)
|
||||||
Log.d(TAG, "isChartGraphSlotsEnabled:" + mIsSlotsClickable);
|
&& !isAccessibilityEnabled(context);
|
||||||
|
Log.d(TAG, "isChartGraphSlotsEnabled:" + mIsSlotsClickabled);
|
||||||
setClickable(isClickable());
|
setClickable(isClickable());
|
||||||
// Initializes the trapezoid curve paint for non-clickable case.
|
// Initializes the trapezoid curve paint for non-clickable case.
|
||||||
if (!mIsSlotsClickable && mTrapezoidCurvePaint == null) {
|
if (!mIsSlotsClickabled && mTrapezoidCurvePaint == null) {
|
||||||
mTrapezoidCurvePaint = new Paint();
|
mTrapezoidCurvePaint = new Paint();
|
||||||
mTrapezoidCurvePaint.setAntiAlias(true);
|
mTrapezoidCurvePaint.setAntiAlias(true);
|
||||||
mTrapezoidCurvePaint.setColor(mTrapezoidSolidColor);
|
mTrapezoidCurvePaint.setColor(mTrapezoidSolidColor);
|
||||||
mTrapezoidCurvePaint.setStyle(Paint.Style.STROKE);
|
mTrapezoidCurvePaint.setStyle(Paint.Style.STROKE);
|
||||||
mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2);
|
mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2);
|
||||||
}
|
}
|
||||||
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setClickable(boolean clickable) {
|
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) {
|
private void initializeColors(Context context) {
|
||||||
@@ -412,7 +432,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
}
|
}
|
||||||
// Configures the trapezoid paint color.
|
// Configures the trapezoid paint color.
|
||||||
final int trapezoidColor =
|
final int trapezoidColor =
|
||||||
!mIsSlotsClickable
|
!mIsSlotsClickabled
|
||||||
? mTrapezoidColor
|
? mTrapezoidColor
|
||||||
: mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL
|
: mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL
|
||||||
? mTrapezoidSolidColor : mTrapezoidColor;
|
? mTrapezoidSolidColor : mTrapezoidColor;
|
||||||
@@ -469,6 +489,29 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
&& mLevels[trapezoidIndex + 1] != 0;
|
&& 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.
|
// A container class for each trapezoid left and right location.
|
||||||
private static final class TrapezoidSlot {
|
private static final class TrapezoidSlot {
|
||||||
public float mLeft;
|
public float mLeft;
|
||||||
|
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user