Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 05bf7858 authored by Zaiyue Xue's avatar Zaiyue Xue Committed by YK Hung
Browse files

Add battery chart view model.

Test: manual
Bug: 239491373
Bug: 236101166
Change-Id: I1ae0e5fcc006855ac552fbbdfb4cd73f3dec52e7
parent efbb0719
Loading
Loading
Loading
Loading
+37 −52
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
@@ -53,7 +54,6 @@ import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.widget.FooterPreference;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -98,13 +98,13 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
    @VisibleForTesting
    boolean mIsExpanded = false;
    @VisibleForTesting
    int[] mBatteryHistoryLevels;
    @VisibleForTesting
    long[] mBatteryHistoryKeys;
    @VisibleForTesting
    int mTrapezoidIndex = BatteryChartViewV2.SELECTED_INDEX_INVALID;
    BatteryChartViewModel mViewModel;
    @VisibleForTesting
    int mTrapezoidIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;

    private boolean mIs24HourFormat = false;
    private boolean mIs24HourFormat;
    private boolean mIsFooterPrefAdded = false;
    private PreferenceScreen mPreferenceScreen;
    private FooterPreference mFooterPreference;
@@ -252,10 +252,11 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
    @Override
    public void onSelect(int trapezoidIndex) {
        Log.d(TAG, "onChartSelect:" + trapezoidIndex);
        refreshUi(trapezoidIndex, /*isForce=*/ false);
        mTrapezoidIndex = trapezoidIndex;
        refreshUi();
        mMetricsFeatureProvider.action(
                mPrefContext,
                trapezoidIndex == BatteryChartViewV2.SELECTED_INDEX_ALL
                trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
                        ? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL
                        : SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT);
    }
@@ -276,18 +277,19 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
        if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
            mBatteryIndexedMap = null;
            mBatteryHistoryKeys = null;
            mBatteryHistoryLevels = null;
            mViewModel = null;
            addFooterPreferenceIfNeeded(false);
            return;
        }
        mBatteryHistoryKeys = getBatteryHistoryKeys(batteryHistoryMap);
        mBatteryHistoryLevels = new int[CHART_LEVEL_ARRAY_SIZE];
        List<Integer> levels = new ArrayList<Integer>();
        for (int index = 0; index < CHART_LEVEL_ARRAY_SIZE; index++) {
            final long timestamp = mBatteryHistoryKeys[index * 2];
            final Map<String, BatteryHistEntry> entryMap = batteryHistoryMap.get(timestamp);
            if (entryMap == null || entryMap.isEmpty()) {
                Log.e(TAG, "abnormal entry list in the timestamp:"
                        + ConvertUtils.utcToLocalTime(mPrefContext, timestamp));
                levels.add(0);
                continue;
            }
            // Averages the battery level in each time slot to avoid corner conditions.
@@ -295,16 +297,17 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
            for (BatteryHistEntry entry : entryMap.values()) {
                batteryLevelCounter += entry.mBatteryLevel;
            }
            mBatteryHistoryLevels[index] =
                    Math.round(batteryLevelCounter / entryMap.size());
            levels.add(Math.round(batteryLevelCounter / entryMap.size()));
        }
        forceRefreshUi();
        final List<String> texts = generateTimestampTexts(mBatteryHistoryKeys, mContext);
        mViewModel = new BatteryChartViewModel(levels, texts, mTrapezoidIndex);
        refreshUi();
        Log.d(TAG, String.format(
                "setBatteryHistoryMap() size=%d key=%s\nlevels=%s",
                "setBatteryHistoryMap() size=%d key=%s\nview model=%s",
                batteryHistoryMap.size(),
                ConvertUtils.utcToLocalTime(mPrefContext,
                        mBatteryHistoryKeys[mBatteryHistoryKeys.length - 1]),
                Arrays.toString(mBatteryHistoryLevels)));
                mViewModel));

        // Loads item icon and label in the background.
        new LoadAllItemsInfoTask(batteryHistoryMap).execute();
@@ -319,35 +322,20 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
    private void setBatteryChartViewInner(final BatteryChartViewV2 batteryChartView) {
        mBatteryChartView = batteryChartView;
        mBatteryChartView.setOnSelectListener(this);
        forceRefreshUi();
    }

    private void forceRefreshUi() {
        final int refreshIndex =
                mTrapezoidIndex == BatteryChartViewV2.SELECTED_INDEX_INVALID
                        ? BatteryChartViewV2.SELECTED_INDEX_ALL
                        : mTrapezoidIndex;
        if (mBatteryChartView != null) {
            mBatteryChartView.setLevels(mBatteryHistoryLevels);
            mBatteryChartView.setSelectedIndex(refreshIndex);
            setTimestampLabel();
        }
        refreshUi(refreshIndex, /*isForce=*/ true);
        refreshUi();
    }

    @VisibleForTesting
    boolean refreshUi(int trapezoidIndex, boolean isForce) {
    boolean refreshUi() {
        // Invalid refresh condition.
        if (mBatteryIndexedMap == null
                || mBatteryChartView == null
                || (mTrapezoidIndex == trapezoidIndex && !isForce)) {
        if (mBatteryIndexedMap == null || mBatteryChartView == null) {
            return false;
        }
        Log.d(TAG, String.format("refreshUi: index=%d size=%d isForce:%b",
                trapezoidIndex, mBatteryIndexedMap.size(), isForce));
        if (mViewModel != null) {
            mViewModel.setSelectedIndex(mTrapezoidIndex);
        }
        mBatteryChartView.setViewModel(mViewModel);

        mTrapezoidIndex = trapezoidIndex;
        mBatteryChartView.setSelectedIndex(mTrapezoidIndex);
        mHandler.post(() -> {
            final long start = System.currentTimeMillis();
            removeAndCacheAllPrefs();
@@ -584,20 +572,6 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
        return !contains(packageName, mNotAllowShowEntryPackages);
    }

    @VisibleForTesting
    void setTimestampLabel() {
        if (mBatteryChartView == null || mBatteryHistoryKeys == null) {
            return;
        }
        final boolean is24HourFormat = DateFormat.is24HourFormat(mContext);
        final String[] labels = new String[mBatteryHistoryKeys.length];
        for (int i = 0; i < mBatteryHistoryKeys.length; i++) {
            labels[i] = ConvertUtils.utcToLocalTimeHour(mContext, mBatteryHistoryKeys[i],
                    is24HourFormat);
        }
        mBatteryChartView.setAxisLabels(labels);
    }

    private void addFooterPreferenceIfNeeded(boolean containAppItems) {
        if (mIsFooterPrefAdded || mFooterPreference == null) {
            return;
@@ -610,6 +584,17 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
        mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference));
    }

    private static List<String> generateTimestampTexts(
            @NonNull long[] timestamps, Context context) {
        final boolean is24HourFormat = DateFormat.is24HourFormat(context);
        final List<String> texts = new ArrayList<String>();
        for (int index = 0; index < CHART_LEVEL_ARRAY_SIZE; index++) {
            texts.add(ConvertUtils.utcToLocalTimeHour(context, timestamps[index * 2],
                    is24HourFormat));
        }
        return texts;
    }

    private static boolean contains(String target, CharSequence[] packageNames) {
        if (target != null && packageNames != null) {
            for (CharSequence packageName : packageNames) {
@@ -654,7 +639,7 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
                        getBatteryHistoryKeys(batteryHistoryMap),
                        batteryHistoryMap,
                        /*purgeLowPercentageAndFakeData=*/ true);
        return batteryIndexedMap.get(BatteryChartViewV2.SELECTED_INDEX_ALL);
        return batteryIndexedMap.get(BatteryChartViewModel.SELECTED_INDEX_ALL);
    }

    /** Used for {@link AppBatteryPreferenceController}. */
@@ -735,7 +720,7 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
            // Posts results back to main thread to refresh UI.
            mHandler.post(() -> {
                mBatteryIndexedMap = indexedUsageMap;
                forceRefreshUi();
                refreshUi();
            });
        }
    }
+98 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.batteryusage;

import androidx.annotation.NonNull;
import androidx.core.util.Preconditions;

import java.util.List;
import java.util.Locale;
import java.util.Objects;

/** The view model of {@code BatteryChartViewV2} */
class BatteryChartViewModel {
    private static final String TAG = "BatteryChartViewModel";

    public static final int SELECTED_INDEX_ALL = -1;
    public static final int SELECTED_INDEX_INVALID = -2;

    // We need at least 2 levels to draw a trapezoid.
    private static final int MIN_LEVELS_DATA_SIZE = 2;

    private final List<Integer> mLevels;
    private final List<String> mTexts;
    private int mSelectedIndex;

    BatteryChartViewModel(
            @NonNull List<Integer> levels, @NonNull List<String> texts, int selectedIndex) {
        Preconditions.checkArgument(
                levels.size() == texts.size()
                        && levels.size() >= MIN_LEVELS_DATA_SIZE
                        && selectedIndex >= SELECTED_INDEX_ALL
                        && selectedIndex < levels.size(),
                String.format(Locale.getDefault(), "Invalid BatteryChartViewModel"
                                + "  levels.size: %d\ntexts.size: %d\nselectedIndex: %d.",
                        levels.size(), texts.size(), selectedIndex));
        mLevels = levels;
        mTexts = texts;
        mSelectedIndex = selectedIndex;
    }

    public int size() {
        return mLevels.size();
    }

    public List<Integer> levels() {
        return mLevels;
    }

    public List<String> texts() {
        return mTexts;
    }

    public int selectedIndex() {
        return mSelectedIndex;
    }

    public void setSelectedIndex(int index) {
        mSelectedIndex = index;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mLevels, mTexts, mSelectedIndex);
    }

    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        } else if (!(other instanceof BatteryChartViewModel)) {
            return false;
        }
        final BatteryChartViewModel batteryChartViewModel = (BatteryChartViewModel) other;
        return Objects.equals(mLevels, batteryChartViewModel.mLevels)
                && Objects.equals(mTexts, batteryChartViewModel.mTexts)
                && mSelectedIndex == batteryChartViewModel.mSelectedIndex;
    }

    @Override
    public String toString() {
        return String.format(Locale.getDefault(), "levels: %s\ntexts: %s\nselectedIndex: %d",
                Objects.toString(mLevels), Objects.toString(mTexts), mSelectedIndex);
    }
}
+69 −78
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import static com.android.settings.Utils.formatPercentage;
import static java.lang.Math.round;

import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -38,6 +37,7 @@ import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.widget.AppCompatImageView;

@@ -61,16 +61,14 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
    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;
    public static final int SELECTED_INDEX_INVALID = -2;

    /** A callback listener for selected group index is updated. */
    public interface OnSelectListener {
        /** The callback function for selected group index is updated. */
        void onSelect(int trapezoidIndex);
    }

    private BatteryChartViewModel mViewModel;

    private int mDividerWidth;
    private int mDividerHeight;
    private float mTrapezoidVOffset;
@@ -79,9 +77,7 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
    private String[] mPercentages = getPercentages();

    @VisibleForTesting
    int mHoveredIndex = SELECTED_INDEX_INVALID;
    @VisibleForTesting
    int mSelectedIndex = SELECTED_INDEX_INVALID;
    int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
    @VisibleForTesting
    String[] mAxisLabels;

@@ -103,7 +99,6 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
    @VisibleForTesting
    final Runnable mUpdateClickableStateRun = () -> updateClickableState();

    private int[] mLevels;
    private Paint mTextPaint;
    private Paint mDividerPaint;
    private Paint mTrapezoidPaint;
@@ -126,44 +121,26 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
        initializeColors(context);
        // Registers the click event listener.
        setOnClickListener(this);
        setSelectedIndex(SELECTED_INDEX_ALL);
        setClickable(false);
        requestLayout();
    }

    /** Sets all levels value to draw the trapezoid shape */
    public void setLevels(int[] levels) {
        Log.d(TAG, "setLevels() " + (levels == null ? "null" : levels.length));
        // At least 2 levels to draw a trapezoid.
        if (levels == null || levels.length < 2) {
            mLevels = null;
    /** Sets the data model of this view. */
    public void setViewModel(BatteryChartViewModel viewModel) {
        if (viewModel == null) {
            mViewModel = null;
            invalidate();
            return;
        }
        mLevels = levels;

        // Initialize trapezoid slots.
        mTrapezoidSlots = new TrapezoidSlot[mLevels.length - 1];
        for (int index = 0; index < mTrapezoidSlots.length; index++) {
            mTrapezoidSlots[index] = new TrapezoidSlot();
        }
        Log.d(TAG, String.format("setViewModel(): size: %d, selectedIndex: %d.",
                viewModel.size(), viewModel.selectedIndex()));
        mViewModel = viewModel;

        setClickable(false);
        invalidate();
        // Sets the chart is clickable if there is at least one valid item in it.
        for (int index = 0; index < mLevels.length - 1; index++) {
            if (mLevels[index] != 0 && mLevels[index + 1] != 0) {
                setClickable(true);
                break;
            }
        }
    }

    /** Sets the selected group index to draw highlight effect. */
    public void setSelectedIndex(int index) {
        if (mSelectedIndex != index) {
            mSelectedIndex = index;
            invalidate();
        }
        initializeTrapezoidSlots(viewModel.size() - 1);
        initializeAxisLabels(viewModel.texts());
        setClickable(hasNonZeroTrapezoid(viewModel.levels()));
        requestLayout();
    }

    /** Sets the callback to monitor the selected group index. */
@@ -184,26 +161,6 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
        requestLayout();
    }

    /**
     * Sets the X-axis labels list for each level. This class will choose some labels among the
     * input list to show.
     *
     * @param labels The length of this parameter should be the same as the length of
     *               {@code levels}.
     */
    public void setAxisLabels(@NonNull String[] labels) {
        if (mAxisLabels == null) {
            mAxisLabels = new String[DEFAULT_AXIS_LABEL_COUNT];
        }
        // Current logic is always showing {@code AXIS_LABEL_GAPS_COUNT} labels.
        // TODO: Support different count of labels for different levels sizes.
        final int step = (labels.length - 1) / AXIS_LABEL_GAPS_COUNT;
        for (int index = 0; index < DEFAULT_AXIS_LABEL_COUNT; index++) {
            mAxisLabels[index] = labels[index * step];
        }
        requestLayout();
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -240,7 +197,7 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
        // Before mLevels initialized, the count of trapezoids is unknown. Only draws the
        // horizontal percentages and dividers.
        drawHorizontalDividers(canvas);
        if (mLevels == null) {
        if (mViewModel == null) {
            return;
        }
        drawVerticalDividers(canvas);
@@ -282,7 +239,7 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
    public void onHoverChanged(boolean hovered) {
        super.onHoverChanged(hovered);
        if (!hovered) {
            mHoveredIndex = SELECTED_INDEX_INVALID; // reset
            mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset
            invalidate();
        }
    }
@@ -295,13 +252,15 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
        }
        final int trapezoidIndex = getTrapezoidIndex(mTouchUpEventX);
        // Ignores the click event if the level is zero.
        if (trapezoidIndex == SELECTED_INDEX_INVALID
        if (trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID
                || !isValidToDraw(trapezoidIndex)) {
            return;
        }
        if (mOnSelectListener != null) {
            // Selects all if users click the same trapezoid item two times.
            mOnSelectListener.onSelect(
                    trapezoidIndex == mSelectedIndex ? SELECTED_INDEX_ALL : trapezoidIndex);
                    trapezoidIndex == mViewModel.selectedIndex()
                            ? BatteryChartViewModel.SELECTED_INDEX_ALL : trapezoidIndex);
        }
        view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
    }
@@ -350,8 +309,8 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
            mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2);
        } else if (mIsSlotsClickabled) {
            mTrapezoidCurvePaint = null;
            // Sets levels again to force update the click state.
            setLevels(mLevels);
            // Sets view model again to force update the click state.
            setViewModel(mViewModel);
        }
        invalidate();
    }
@@ -366,6 +325,28 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
        super.setClickable(clickable);
    }

    private void initializeTrapezoidSlots(int count) {
        mTrapezoidSlots = new TrapezoidSlot[count];
        for (int index = 0; index < mTrapezoidSlots.length; index++) {
            mTrapezoidSlots[index] = new TrapezoidSlot();
        }
    }

    /**
     * Initializes the displayed X-axis labels list selected from the model all texts list.
     */
    private void initializeAxisLabels(@NonNull List<String> allTexts) {
        if (mAxisLabels == null) {
            mAxisLabels = new String[DEFAULT_AXIS_LABEL_COUNT];
        }
        // Current logic is always showing {@code AXIS_LABEL_GAPS_COUNT} labels.
        // TODO: Support different count of labels for different levels sizes.
        final int step = (allTexts.size() - 1) / AXIS_LABEL_GAPS_COUNT;
        for (int index = 0; index < DEFAULT_AXIS_LABEL_COUNT; index++) {
            mAxisLabels[index] = allTexts.get(index * step);
        }
    }

    private void initializeColors(Context context) {
        setBackgroundColor(Color.TRANSPARENT);
        mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context);
@@ -498,7 +479,7 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli

    private void drawTrapezoids(Canvas canvas) {
        // Ignores invalid trapezoid data.
        if (mLevels == null) {
        if (mViewModel == null) {
            return;
        }
        final float trapezoidBottom =
@@ -519,17 +500,17 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
                continue;
            }
            // Configures the trapezoid paint color.
            final int trapezoidColor =
                    !mIsSlotsClickabled
                            ? mTrapezoidColor
                            : mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL
            final int trapezoidColor = mIsSlotsClickabled && (mViewModel.selectedIndex() == index
                    || mViewModel.selectedIndex() == BatteryChartViewModel.SELECTED_INDEX_ALL)
                    ? mTrapezoidSolidColor : mTrapezoidColor;
            final boolean isHoverState =
                    mIsSlotsClickabled && mHoveredIndex == index && isValidToDraw(mHoveredIndex);
            mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor);

            final float leftTop = round(trapezoidBottom - mLevels[index] * unitHeight);
            final float rightTop = round(trapezoidBottom - mLevels[index + 1] * unitHeight);
            final float leftTop = round(
                    trapezoidBottom - mViewModel.levels().get(index) * unitHeight);
            final float rightTop = round(
                    trapezoidBottom - mViewModel.levels().get(index + 1) * unitHeight);
            trapezoidPath.reset();
            trapezoidPath.moveTo(mTrapezoidSlots[index].mLeft, trapezoidBottom);
            trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop);
@@ -568,15 +549,25 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
                return index;
            }
        }
        return SELECTED_INDEX_INVALID;
        return BatteryChartViewModel.SELECTED_INDEX_INVALID;
    }

    private boolean isValidToDraw(int trapezoidIndex) {
        return mLevels != null
        return mViewModel != null
                && trapezoidIndex >= 0
                && trapezoidIndex < mLevels.length - 1
                && mLevels[trapezoidIndex] != 0
                && mLevels[trapezoidIndex + 1] != 0;
                && trapezoidIndex < mViewModel.size() - 1
                && mViewModel.levels().get(trapezoidIndex) != 0
                && mViewModel.levels().get(trapezoidIndex + 1) != 0;
    }

    private static boolean hasNonZeroTrapezoid(List<Integer> levels) {
        // Sets the chart is clickable if there is at least one valid item in it.
        for (int index = 0; index < levels.size() - 1; index++) {
            if (levels.get(index) != 0 && levels.get(index + 1) != 0) {
                return true;
            }
        }
        return false;
    }

    private static String[] getPercentages() {
+32 −80

File changed.

Preview size limit exceeded, changes collapsed.

+13 −7
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import org.robolectric.RuntimeEnvironment;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

@RunWith(RobolectricTestRunner.class)
@@ -100,13 +101,15 @@ public final class BatteryChartViewV2Test {

    @Test
    public void onClick_invokesCallback() {
        mBatteryChartView.setLevels(new int[] {90, 80, 70, 60});
        final int originalSelectedIndex = 2;
        mBatteryChartView.setViewModel(
                new BatteryChartViewModel(List.of(90, 80, 70, 60), List.of("", "", "", ""),
                        originalSelectedIndex));
        for (int i = 0; i < mBatteryChartView.mTrapezoidSlots.length; i++) {
            mBatteryChartView.mTrapezoidSlots[i] = new BatteryChartViewV2.TrapezoidSlot();
            mBatteryChartView.mTrapezoidSlots[i].mLeft = i;
            mBatteryChartView.mTrapezoidSlots[i].mRight = i + 0.5f;
        }
        mBatteryChartView.mSelectedIndex = 2;
        final int[] selectedIndex = new int[1];
        mBatteryChartView.setOnSelectListener(
                trapezoidIndex -> {
@@ -123,7 +126,7 @@ public final class BatteryChartViewV2Test {
        mBatteryChartView.mTouchUpEventX = 2;
        selectedIndex[0] = Integer.MIN_VALUE;
        mBatteryChartView.onClick(mMockView);
        assertThat(selectedIndex[0]).isEqualTo(BatteryChartViewV2.SELECTED_INDEX_ALL);
        assertThat(selectedIndex[0]).isEqualTo(BatteryChartViewModel.SELECTED_INDEX_ALL);
    }

    @Test
@@ -178,11 +181,14 @@ public final class BatteryChartViewV2Test {

    @Test
    public void clickable_restoreFromNonClickableState() {
        final int[] levels = new int[13];
        for (int index = 0; index < levels.length; index++) {
            levels[index] = index + 1;
        }
        mBatteryChartView.setLevels(levels);
        final List<Integer> levels = new ArrayList<Integer>();
        final List<String> texts = new ArrayList<String>();
        for (int index = 0; index < 13; index++) {
            levels.add(index + 1);
            texts.add("");
        }
        mBatteryChartView.setViewModel(new BatteryChartViewModel(
                levels, texts, BatteryChartViewModel.SELECTED_INDEX_ALL));
        mBatteryChartView.setClickableForce(true);
        when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
                .thenReturn(true);