Loading src/com/android/settings/fuelgauge/BatteryChartView.java +52 −9 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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; Loading tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java 0 → 100644 +170 −0 Original line number Diff line number Diff line /* * 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(); } } Loading
src/com/android/settings/fuelgauge/BatteryChartView.java +52 −9 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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; Loading
tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java 0 → 100644 +170 −0 Original line number Diff line number Diff line /* * 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(); } }