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

Commit edfa085b authored by YUKAI HUNG's avatar YUKAI HUNG Committed by Android (Google) Code Review
Browse files

Merge "Refine logic and add simple test for BatteryChartPreferenceController" into sc-dev

parents 20e30c30 ab5e1801
Loading
Loading
Loading
Loading
+50 −30
Original line number Diff line number Diff line
@@ -50,16 +50,15 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
    private static final int CHART_LEVEL_ARRAY_SIZE = 13;

    @VisibleForTesting
    PreferenceGroup mAppListPrefGroup;
    Map<Integer, List<BatteryDiffEntry>> mBatteryIndexedMap;

    private Context mPrefContext;
    private BatteryChartView mBatteryChartView;
    // Battery history relative data.
    private int[] mBatteryHistoryLevels;
    private long[] mBatteryHistoryKeys;
    private Map<Long, List<BatteryHistEntry>> mBatteryHistoryMap;
    @VisibleForTesting Context mPrefContext;
    @VisibleForTesting PreferenceGroup mAppListPrefGroup;
    @VisibleForTesting BatteryChartView mBatteryChartView;

    private int mTrapezoidIndex = BatteryChartView.SELECTED_INDEX_INVALID;
    @VisibleForTesting int[] mBatteryHistoryLevels;
    @VisibleForTesting long[] mBatteryHistoryKeys;
    @VisibleForTesting int mTrapezoidIndex = BatteryChartView.SELECTED_INDEX_INVALID;

    private final String mPreferenceKey;
    private final SettingsActivity mActivity;
@@ -84,6 +83,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll

    @Override
    public void onDestroy() {
        if (mActivity.isChangingConfigurations()) {
            BatteryDiffEntry.clearCache();
        }
    }

    @Override
@@ -110,30 +112,34 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll

    @Override
    public void onSelect(int trapezoidIndex) {
        Log.d(TAG, "onSelect:" + trapezoidIndex);
        refreshUi(trapezoidIndex);
        Log.d(TAG, "onChartSelect:" + trapezoidIndex);
        refreshUi(trapezoidIndex, /*isForce=*/ false);
    }

    void setBatteryHistoryMap(Map<Long, List<BatteryHistEntry>> batteryHistoryMap) {
        // Assumes all timestamp data is consecutive and aligns to hourly time slot.
        mBatteryHistoryMap = batteryHistoryMap;
        // Resets all battery history data relative variables.
        if (batteryHistoryMap == null) {
            mBatteryIndexedMap = null;
            mBatteryHistoryKeys = null;
            mBatteryHistoryLevels = null;
            return;
        }
        // Generates battery history keys.
        final List<Long> batteryHistoryKeyList =
            new ArrayList<Long>(mBatteryHistoryMap.keySet());
        // Sorts all timestamp keys ordered by ASC from the map keys.
            new ArrayList<Long>(batteryHistoryMap.keySet());
        Collections.sort(batteryHistoryKeyList);
        mBatteryHistoryKeys = new long[CHART_KEY_ARRAY_SIZE];
        final int elementSize = Math.min(
            batteryHistoryKeyList.size(), CHART_KEY_ARRAY_SIZE);
        final int elementSize = Math.min(batteryHistoryKeyList.size(), CHART_KEY_ARRAY_SIZE);
        final int offset = CHART_KEY_ARRAY_SIZE - elementSize;
        for (int index = 0; index < elementSize; index++) {
            mBatteryHistoryKeys[index + offset] = batteryHistoryKeyList.get(index);
        }

        // Generates the battery history levels.
        mBatteryHistoryLevels = new int[CHART_LEVEL_ARRAY_SIZE];
        for (int index = 0; index < CHART_LEVEL_ARRAY_SIZE; index++) {
            final Long timestamp = Long.valueOf(mBatteryHistoryKeys[index * 2]);
            final List<BatteryHistEntry> entryList = mBatteryHistoryMap.get(timestamp);
            final List<BatteryHistEntry> entryList = batteryHistoryMap.get(timestamp);
            if (entryList != null && !entryList.isEmpty()) {
                // All battery levels are the same in the same timestamp snapshot.
                mBatteryHistoryLevels[index] = entryList.get(0).mBatteryLevel;
@@ -142,10 +148,15 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
                    ConvertUtils.utcToLocalTime(timestamp));
            }
        }
        if (mBatteryChartView != null) {
            mBatteryChartView.setLevels(mBatteryHistoryLevels);
        }
        Log.d(TAG, String.format("setBatteryHistoryMap() size=%d\nkeys=%s\nlevels=%s",
        // Generates indexed usage map for chart.
        mBatteryIndexedMap =
            ConvertUtils.getIndexedUsageMap(
                mPrefContext, /*timeSlotSize=*/ CHART_LEVEL_ARRAY_SIZE - 1,
                mBatteryHistoryKeys, batteryHistoryMap);
        forceRefreshUi();

        Log.d(TAG, String.format(
            "setBatteryHistoryMap() size=%d\nkeys=%s\nlevels=%s",
            batteryHistoryKeyList.size(),
            utcToLocalTime(mBatteryHistoryKeys),
            Arrays.toString(mBatteryHistoryLevels)));
@@ -154,20 +165,29 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
    void setBatteryChartView(BatteryChartView batteryChartView) {
        mBatteryChartView = batteryChartView;
        mBatteryChartView.setOnSelectListener(this);
        if (mBatteryHistoryLevels != null) {
            mBatteryChartView.setLevels(mBatteryHistoryLevels);
        forceRefreshUi();
    }

    private void forceRefreshUi() {
        final int refreshIndex =
            mTrapezoidIndex == BatteryChartView.SELECTED_INDEX_INVALID
                ? BatteryChartView.SELECTED_INDEX_ALL
                : mTrapezoidIndex;
        refreshUi(refreshIndex, /*isForce=*/ true);
    }

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

    private static String utcToLocalTime(long[] timestamps) {
+1 −1
Original line number Diff line number Diff line
@@ -200,7 +200,7 @@ public final class BatteryDiffEntry {

        final BatteryEntry.NameAndIcon nameAndIcon =
            BatteryEntry.loadNameAndIcon(
                mContext, uid, /*uid=*/ null, /*batteryEntry=*/ null,
                mContext, uid, /*handler=*/ null, /*batteryEntry=*/ null,
                packageName, mAppLabel, mAppIcon);
        // Clears BatteryEntry internal cache since we will have another one.
        BatteryEntry.clearUidCache();
+1 −1
Original line number Diff line number Diff line
@@ -385,7 +385,7 @@ public class BatteryEntry {

        sUidCache.put(uidString, utd);
        if (handler != null) {
            handler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, batteryEntry));
            handler.sendMessage(handler.obtainMessage(MSG_UPDATE_NAME_ICON, batteryEntry));
        }
        return new NameAndIcon(name, defaultPackageName, icon, /*iconId=*/ 0);
    }
+194 −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.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.ContentValues;
import android.content.pm.PackageManager;

import androidx.preference.PreferenceGroup;

import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
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;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RunWith(RobolectricTestRunner.class)
public final class BatteryChartPreferenceControllerTest {

    @Mock private InstrumentedPreferenceFragment mFragment;
    @Mock private SettingsActivity mSettingsActivity;
    @Mock private PreferenceGroup mAppListGroup;
    @Mock private PackageManager mPackageManager;
    @Mock private BatteryChartView mBatteryChartView;

    private Context mContext;
    private BatteryChartPreferenceController mBatteryChartPreferenceController;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mContext = spy(RuntimeEnvironment.application);
        mBatteryChartPreferenceController =
            new BatteryChartPreferenceController(
                mContext, "app_list", /*lifecycle=*/ null,
                mSettingsActivity, mFragment);
        mBatteryChartPreferenceController.mPrefContext = mContext;
        mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup;
        mBatteryChartPreferenceController.mBatteryChartView = mBatteryChartView;
        // Adds fake testing data.
        BatteryDiffEntry.sResourceCache.put(
            "fakeBatteryDiffEntryKey",
            new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1));
        mBatteryChartPreferenceController.setBatteryHistoryMap(
            createBatteryHistoryMap(/*size=*/ 5));
    }

    @Test
    public void testOnDestroy_activityIsChanging_clearBatteryEntryCache() {
        doReturn(true).when(mSettingsActivity).isChangingConfigurations();
        // Ensures the testing environment is correct.
        assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);

        mBatteryChartPreferenceController.onDestroy();
        assertThat(BatteryDiffEntry.sResourceCache).isEmpty();
    }

    @Test
    public void testOnDestroy_activityIsNotChanging_notClearBatteryEntryCache() {
        doReturn(false).when(mSettingsActivity).isChangingConfigurations();
        // Ensures the testing environment is correct.
        assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);

        mBatteryChartPreferenceController.onDestroy();
        assertThat(BatteryDiffEntry.sResourceCache).isNotEmpty();
    }

    @Test
    public void testSetBatteryHistoryMap_createExpectedKeysAndLevels() {
        mBatteryChartPreferenceController.setBatteryHistoryMap(
            createBatteryHistoryMap(/*size=*/ 5));

        // Verifies the created battery keys array.
        for (int index = 0; index < 25; index++) {
            assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index])
                // These values is are calculated by hand from createBatteryHistoryMap().
                .isEqualTo(index < 20 ? 0 : (index - 20 + 1));
        }
        // Verifies the created battery levels array.
        for (int index = 0; index < 13; index++) {
            assertThat(mBatteryChartPreferenceController.mBatteryHistoryLevels[index])
                // These values is are calculated by hand from createBatteryHistoryMap().
                .isEqualTo(index < 10 ? 0 : (100 - (index - 10) * 2));
        }
        assertThat(mBatteryChartPreferenceController.mBatteryIndexedMap).hasSize(13);
    }

    @Test
    public void testSetBatteryHistoryMap_largeSize_createExpectedKeysAndLevels() {
        mBatteryChartPreferenceController.setBatteryHistoryMap(
            createBatteryHistoryMap(/*size=*/ 25));

        // Verifies the created battery keys array.
        for (int index = 0; index < 25; index++) {
          assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index])
              // These values is are calculated by hand from createBatteryHistoryMap().
              .isEqualTo(index + 1);
        }
        // Verifies the created battery levels array.
        for (int index = 0; index < 13; index++) {
          assertThat(mBatteryChartPreferenceController.mBatteryHistoryLevels[index])
              // These values is are calculated by hand from createBatteryHistoryMap().
              .isEqualTo(100 - index * 2);
        }
        assertThat(mBatteryChartPreferenceController.mBatteryIndexedMap).hasSize(13);
    }

    @Test
    public void testRefreshUi_batteryIndexedMapIsNull_ignoreRefresh() {
        mBatteryChartPreferenceController.setBatteryHistoryMap(null);
        assertThat(mBatteryChartPreferenceController.refreshUi(
            /*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse();
    }

    @Test
    public void testRefreshUi_batteryChartViewIsNull_ignoreRefresh() {
        mBatteryChartPreferenceController.mBatteryChartView = null;
        assertThat(mBatteryChartPreferenceController.refreshUi(
            /*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse();
    }

    @Test
    public void testRefreshUi_trapezoidIndexIsNotChanged_ignoreRefresh() {
        final int trapezoidIndex = 1;
        mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex;
        assertThat(mBatteryChartPreferenceController.refreshUi(
            trapezoidIndex, /*isForce=*/ false)).isFalse();
    }

    @Test
    public void testRefreshUi_forceUpdate_refreshUi() {
        final int trapezoidIndex = 1;
        mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex;
        assertThat(mBatteryChartPreferenceController.refreshUi(
            trapezoidIndex, /*isForce=*/ true)).isTrue();
    }

    @Test
    public void testForceRefreshUi_updateTrapezoidIndexIntoSelectAll() {
        mBatteryChartPreferenceController.mTrapezoidIndex =
            BatteryChartView.SELECTED_INDEX_INVALID;
        mBatteryChartPreferenceController.setBatteryHistoryMap(
            createBatteryHistoryMap(/*size=*/ 25));


        assertThat(mBatteryChartPreferenceController.mTrapezoidIndex)
            .isEqualTo(BatteryChartView.SELECTED_INDEX_ALL);
    }

    private Map<Long, List<BatteryHistEntry>> createBatteryHistoryMap(int size) {
        final Map<Long, List<BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
        for (int index = 0; index < size; index++) {
            final ContentValues values = new ContentValues();
            values.put("batteryLevel", Integer.valueOf(100 - index));
            final BatteryHistEntry entry = new BatteryHistEntry(values);
            batteryHistoryMap.put(Long.valueOf(index + 1), Arrays.asList(entry));
        }
        return batteryHistoryMap;
    }
}
+15 −13
Original line number Diff line number Diff line
@@ -199,16 +199,16 @@ public final class ConvertUtilsTest {
                createBatteryHistEntry(
                    "package2", "label2", 15.0, 2L, 25L, 35L),
                createBatteryHistEntry(
                    "package3", "label3", 5.0, 2L, 5L, 5L)));
                    "package3", "label3", 5.0, 3L, 5L, 5L)));
        batteryHistoryMap.put(
            Long.valueOf(batteryHistoryKeys[4]),
            Arrays.asList(
                createBatteryHistEntry(
                    "package2", "label2", 30.0, 2L, 30L, 40L),
                createBatteryHistEntry(
                    "package2", "label2", 75.0, 3L, 40L, 50L),
                    "package2", "label2", 75.0, 4L, 40L, 50L),
                createBatteryHistEntry(
                    "package3", "label3", 5.0, 2L, 5L, 5L)));
                    "package3", "label3", 5.0, 3L, 5L, 5L)));

        final Map<Integer, List<BatteryDiffEntry>> resultMap =
            ConvertUtils.getIndexedUsageMap(
@@ -222,28 +222,30 @@ public final class ConvertUtilsTest {
        // Verifies the second timestamp result.
        entryList = resultMap.get(Integer.valueOf(1));
        assertThat(entryList).hasSize(3);
        assertBatteryDiffEntry(entryList.get(0), 5, 5L, 5L);
        assertBatteryDiffEntry(entryList.get(1), 75, 40L, 50L);
        assertBatteryDiffEntry(entryList.get(2), 20, 15L, 15L);
        assertBatteryDiffEntry(entryList.get(1), 5, 5L, 5L);
        assertBatteryDiffEntry(entryList.get(2), 75, 40L, 50L);
        assertBatteryDiffEntry(entryList.get(0), 20, 15L, 15L);
        // Verifies the last 24 hours aggregate result.
        entryList = resultMap.get(Integer.valueOf(-1));
        assertThat(entryList).hasSize(3);
        assertBatteryDiffEntry(entryList.get(0), 4, 5L, 5L);
        assertBatteryDiffEntry(entryList.get(1), 68, 40L, 50L);
        assertBatteryDiffEntry(entryList.get(2), 27, 30L, 40L);
        assertBatteryDiffEntry(entryList.get(1), 4, 5L, 5L);
        assertBatteryDiffEntry(entryList.get(2), 68, 40L, 50L);
        assertBatteryDiffEntry(entryList.get(0), 27, 30L, 40L);
    }

    private static BatteryHistEntry createBatteryHistEntry(
            String packageName, String appLabel, double consumePower,
            long userId, long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) {
            long uid, long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) {
        // Only insert required fields.
        final ContentValues values = new ContentValues();
        values.put("packageName", packageName);
        values.put("appLabel", appLabel);
        values.put("userId", userId);
        values.put("uid", Long.valueOf(uid));
        values.put("consumerType",
            Integer.valueOf(ConvertUtils.CONSUMER_TYPE_UID_BATTERY));
        values.put("consumePower", consumePower);
        values.put("foregroundUsageTimeInMs", foregroundUsageTimeInMs);
        values.put("backgroundUsageTimeInMs", backgroundUsageTimeInMs);
        values.put("foregroundUsageTimeInMs", Long.valueOf(foregroundUsageTimeInMs));
        values.put("backgroundUsageTimeInMs", Long.valueOf(backgroundUsageTimeInMs));
        return new BatteryHistEntry(values);
    }