diff --git a/src/com/android/settings/fuelgauge/BatteryChartView.java b/src/com/android/settings/fuelgauge/BatteryChartView.java index 104801704d1..1590a57818d 100644 --- a/src/com/android/settings/fuelgauge/BatteryChartView.java +++ b/src/com/android/settings/fuelgauge/BatteryChartView.java @@ -24,6 +24,7 @@ import android.graphics.CornerPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; +import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.view.HapticFeedbackConstants; @@ -44,7 +45,8 @@ import java.util.List; import java.util.Locale; /** A widget component to draw chart graph. */ -public class BatteryChartView extends AppCompatImageView implements View.OnClickListener { +public class BatteryChartView extends AppCompatImageView implements View.OnClickListener, + AccessibilityManager.AccessibilityStateChangeListener { private static final String TAG = "BatteryChartView"; private static final List ACCESSIBILITY_SERVICE_NAMES = Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService"); @@ -52,6 +54,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick 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; + private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5"); + private static final long UPDATE_STATE_DELAYED_TIME = 500L; /** Selects all trapezoid shapes. */ public static final int SELECTED_INDEX_ALL = -1; @@ -74,7 +78,6 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick // Colors for drawing the trapezoid shape and dividers. private int mTrapezoidColor; private int mTrapezoidSolidColor; - private final int mDividerColor = Color.parseColor("#CDCCC5"); // For drawing the percentage information. private int mTextPadding; private final Rect mIndent = new Rect(); @@ -85,11 +88,17 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private final Rect[] mTimestampsBounds = new Rect[] {new Rect(), new Rect(), new Rect(), new Rect()}; + @VisibleForTesting + Handler mHandler = new Handler(); + @VisibleForTesting + final Runnable mUpdateClickableStateRun = () -> updateClickableState(); + private int[] mLevels; private Paint mTextPaint; private Paint mDividerPaint; private Paint mTrapezoidPaint; - private Paint mTrapezoidCurvePaint = null; + @VisibleForTesting + Paint mTrapezoidCurvePaint = null; private TrapezoidSlot[] mTrapezoidSlots; // Records the location to calculate selected index. private MotionEvent mTouchUpEvent; @@ -257,6 +266,26 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick public void onAttachedToWindow() { super.onAttachedToWindow(); updateClickableState(); + mContext.getSystemService(AccessibilityManager.class) + .addAccessibilityStateChangeListener(/*listener=*/ this); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mContext.getSystemService(AccessibilityManager.class) + .removeAccessibilityStateChangeListener(/*listener=*/ this); + mHandler.removeCallbacks(mUpdateClickableStateRun); + } + + @Override + public void onAccessibilityStateChanged(boolean enabled) { + Log.d(TAG, "onAccessibilityStateChanged:" + enabled); + mHandler.removeCallbacks(mUpdateClickableStateRun); + // We should delay it a while since accessibility manager will spend + // some times to bind with new enabled accessibility services. + mHandler.postDelayed( + mUpdateClickableStateRun, UPDATE_STATE_DELAYED_TIME); } private void updateClickableState() { @@ -275,6 +304,10 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick mTrapezoidCurvePaint.setColor(mTrapezoidSolidColor); mTrapezoidCurvePaint.setStyle(Paint.Style.STROKE); mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2); + } else if (mIsSlotsClickabled) { + mTrapezoidCurvePaint = null; + // Sets levels again to force update the click state. + setLevels(mLevels); } invalidate(); } @@ -299,7 +332,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick mDividerHeight = resources.getDimensionPixelSize(R.dimen.chartview_divider_height); mDividerPaint = new Paint(); mDividerPaint.setAntiAlias(true); - mDividerPaint.setColor(mDividerColor); + mDividerPaint.setColor(DIVIDER_COLOR); mDividerPaint.setStyle(Paint.Style.STROKE); mDividerPaint.setStrokeWidth(mDividerWidth); Log.i(TAG, "mDividerWidth:" + mDividerWidth); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java index 877ebc28734..3998a332630 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java @@ -130,6 +130,7 @@ public final class BatteryChartViewTest { mBatteryChartView.onAttachedToWindow(); assertThat(mBatteryChartView.isClickable()).isFalse(); + assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull(); } @Test @@ -141,6 +142,7 @@ public final class BatteryChartViewTest { mBatteryChartView.onAttachedToWindow(); assertThat(mBatteryChartView.isClickable()).isTrue(); + assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull(); } @Test @@ -155,6 +157,7 @@ public final class BatteryChartViewTest { mBatteryChartView.onAttachedToWindow(); assertThat(mBatteryChartView.isClickable()).isTrue(); + assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull(); } @Test @@ -166,5 +169,62 @@ public final class BatteryChartViewTest { mBatteryChartView.onAttachedToWindow(); assertThat(mBatteryChartView.isClickable()).isFalse(); + assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull(); + } + + @Test + public void testClickable_restoreFromNonClickableState() { + final int[] levels = new int[13]; + for (int index = 0; index < levels.length; index++) { + levels[index] = index + 1; + } + mBatteryChartView.setTrapezoidCount(12); + mBatteryChartView.setLevels(levels); + mBatteryChartView.setClickableForce(true); + when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) + .thenReturn(true); + doReturn(true).when(mockAccessibilityManager).isEnabled(); + mBatteryChartView.onAttachedToWindow(); + // Ensures the testing environment is correct. + assertThat(mBatteryChartView.isClickable()).isFalse(); + // Turns off accessibility service. + doReturn(false).when(mockAccessibilityManager).isEnabled(); + + mBatteryChartView.onAttachedToWindow(); + + assertThat(mBatteryChartView.isClickable()).isTrue(); + } + + @Test + public void testOnAttachedToWindow_addAccessibilityStateChangeListener() { + mBatteryChartView.onAttachedToWindow(); + verify(mockAccessibilityManager) + .addAccessibilityStateChangeListener(mBatteryChartView); + } + + @Test + public void testOnDetachedFromWindow_removeAccessibilityStateChangeListener() { + mBatteryChartView.onAttachedToWindow(); + mBatteryChartView.mHandler.postDelayed( + mBatteryChartView.mUpdateClickableStateRun, 1000); + + mBatteryChartView.onDetachedFromWindow(); + + verify(mockAccessibilityManager) + .removeAccessibilityStateChangeListener(mBatteryChartView); + assertThat(mBatteryChartView.mHandler.hasCallbacks( + mBatteryChartView.mUpdateClickableStateRun)) + .isFalse(); + } + + @Test + public void testOnAccessibilityStateChanged_postUpdateStateRunnable() { + mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler); + mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true); + + verify(mBatteryChartView.mHandler) + .removeCallbacks(mBatteryChartView.mUpdateClickableStateRun); + verify(mBatteryChartView.mHandler) + .postDelayed(mBatteryChartView.mUpdateClickableStateRun, 500L); } }