From 3d7b8b3bfedeaa4a38d78aae62f4da55a466ad54 Mon Sep 17 00:00:00 2001 From: Zaiyue Xue Date: Mon, 5 Dec 2022 14:51:46 +0800 Subject: [PATCH] [Battery usage U] Show battery usage info immediately up till as close to the current moment as possible. screen_record: https://drive.google.com/file/d/1Wdm8Wpn39k6E9Yo4bbTENf5VANP337QA/view?usp=share_link&resourcekey=0-1LNmaTaZI13DUmjNfkBehQ Bug: 252407178 Fix: 252407178 Test: maunal Change-Id: Ia08dea791bb72113719fd1316e8e9587a96eaef1 --- res/values/strings.xml | 2 + .../BatteryChartPreferenceController.java | 29 +++++- .../fuelgauge/batteryusage/DataProcessor.java | 89 +++++++++++++++---- .../BatteryChartPreferenceControllerTest.java | 28 ++++-- .../batteryusage/DataProcessorTest.java | 68 +++++++++----- 5 files changed, 167 insertions(+), 49 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 0cd0c8661ab..795c118ca1b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4987,6 +4987,8 @@ Background: %s Battery usage data will be available in a few hours once fully charged + + now Battery usage chart diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java index 7e1bb381224..707c5b2fa94 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java @@ -47,6 +47,8 @@ import com.android.settingslib.core.lifecycle.events.OnDestroy; import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState; +import com.google.common.base.Objects; + import java.util.ArrayList; import java.util.Calendar; import java.util.List; @@ -238,7 +240,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll hourlyBatteryLevelsPerDay.getLevels(), hourlyBatteryLevelsPerDay.getTimestamps(), BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS, - mHourlyChartLabelTextGenerator)); + mHourlyChartLabelTextGenerator.setLatestTimestamp(getLast(getLast( + batteryLevelData.getHourlyBatteryLevelsPerDay()).getTimestamps())))); } refreshUi(); } @@ -514,6 +517,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll return allBatteryDiffData == null ? null : allBatteryDiffData.getAppDiffEntryList(); } + private static T getLast(List list) { + if (list == null || list.isEmpty()) { + return null; + } + return list.get(list.size() - 1); + } + /** Used for {@link AppBatteryPreferenceController}. */ public static BatteryDiffEntry getAppBatteryUsageData( Context context, String packageName, int userId) { @@ -553,18 +563,33 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll private final class HourlyChartLabelTextGenerator implements BatteryChartViewModel.LabelTextGenerator { + private Long mLatestTimestamp; + @Override public String generateText(List timestamps, int index) { + if (Objects.equal(timestamps.get(index), mLatestTimestamp)) { + // Replaces the latest timestamp text to "now". + return mContext.getString(R.string.battery_usage_chart_label_now); + } return ConvertUtils.utcToLocalTimeHour(mContext, timestamps.get(index), mIs24HourFormat); } @Override public String generateFullText(List timestamps, int index) { + if (Objects.equal(timestamps.get(index), mLatestTimestamp)) { + // Replaces the latest timestamp text to "now". + return mContext.getString(R.string.battery_usage_chart_label_now); + } return index == timestamps.size() - 1 ? generateText(timestamps, index) : String.format("%s%s%s", generateText(timestamps, index), - mIs24HourFormat ? "-" : " - ", generateText(timestamps, index + 1)); + mIs24HourFormat ? "-" : " - ", generateText(timestamps, index + 1)); + } + + public HourlyChartLabelTextGenerator setLatestTimestamp(Long latestTimestamp) { + this.mLatestTimestamp = latestTimestamp; + return this; } } } diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java index 282781893cc..b510e5b19e2 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java @@ -21,6 +21,7 @@ import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLoca import android.app.settings.SettingsEnums; import android.content.ContentValues; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; import android.os.AsyncTask; import android.os.BatteryConsumer; @@ -86,6 +87,12 @@ public final class DataProcessor { static final double PERCENTAGE_OF_TOTAL_THRESHOLD = 1f; @VisibleForTesting static final int SELECTED_INDEX_ALL = BatteryChartViewModel.SELECTED_INDEX_ALL; + @VisibleForTesting + static final String CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER = + "CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER"; + + @VisibleForTesting + static long sFakeCurrentTimeMillis = 0; /** A callback listener when battery usage loading async task is executed. */ public interface UsageMapAsyncResponse { @@ -239,11 +246,12 @@ public final class DataProcessor { return resultMap; } Collections.sort(rawTimestampList); - final List expectedTimestampList = getTimestampSlots(rawTimestampList); + final long currentTime = getCurrentTimeMillis(); + final List expectedTimestampList = getTimestampSlots(rawTimestampList, currentTime); final boolean isFromFullCharge = isFromFullCharge(batteryHistoryMap.get(rawTimestampList.get(0))); interpolateHistory( - context, rawTimestampList, expectedTimestampList, isFromFullCharge, + context, rawTimestampList, expectedTimestampList, currentTime, isFromFullCharge, batteryHistoryMap, resultMap); Log.d(TAG, String.format("getHistoryMapWithExpectedTimestamps() size=%d in %d/ms", resultMap.size(), (System.currentTimeMillis() - startTime))); @@ -278,19 +286,16 @@ public final class DataProcessor { * between start and end two even hour values. */ @VisibleForTesting - static List getTimestampSlots(final List rawTimestampList) { + static List getTimestampSlots(final List rawTimestampList, final long currentTime) { final List timestampSlots = new ArrayList<>(); - final int rawTimestampListSize = rawTimestampList.size(); - // If timestamp number is smaller than 2, the following computation is not necessary. - if (rawTimestampListSize < MIN_TIMESTAMP_DATA_SIZE) { + if (rawTimestampList.isEmpty()) { return timestampSlots; } final long rawStartTimestamp = rawTimestampList.get(0); - final long rawEndTimestamp = rawTimestampList.get(rawTimestampListSize - 1); // No matter the start is from last full charge or 6 days ago, use the nearest even hour. final long startTimestamp = getNearestEvenHourTimestamp(rawStartTimestamp); - // Use the even hour before the raw end timestamp as the end. - final long endTimestamp = getLastEvenHourBeforeTimestamp(rawEndTimestamp); + // Use the first even hour after the current time as the end. + final long endTimestamp = getFirstEvenHourAfterTimestamp(currentTime); // If the start timestamp is later or equal the end one, return the empty list. if (startTimestamp >= endTimestamp) { return timestampSlots; @@ -451,10 +456,7 @@ public final class DataProcessor { @Nullable static BatteryDiffData generateBatteryDiffData( final Context context, - @Nullable final List batteryEntryList, - final BatteryUsageStats batteryUsageStats) { - final List batteryHistEntryList = - convertToBatteryHistEntry(batteryEntryList, batteryUsageStats); + final List batteryHistEntryList) { if (batteryHistEntryList == null || batteryHistEntryList.isEmpty()) { Log.w(TAG, "batteryHistEntryList is null or empty in generateBatteryDiffData()"); return null; @@ -531,7 +533,7 @@ public final class DataProcessor { final Map allUsageMap = new HashMap<>(); // Always construct the map whether the value is null or not. allUsageMap.put(SELECTED_INDEX_ALL, - getBatteryDiffDataFromBatteryStatsService(context)); + generateBatteryDiffData(context, getBatteryHistListFromFromStatsService(context))); resultMap.put(SELECTED_INDEX_ALL, allUsageMap); // Compute the apps number before purge. Must put before purgeLowPercentageAndFakeData. @@ -545,24 +547,33 @@ public final class DataProcessor { } @Nullable - private static BatteryDiffData getBatteryDiffDataFromBatteryStatsService( + private static List getBatteryHistListFromFromStatsService( final Context context) { - BatteryDiffData batteryDiffData = null; + List batteryHistEntryList = null; try { final BatteryUsageStats batteryUsageStats = getBatteryUsageStats(context); final List batteryEntryList = generateBatteryEntryListFromBatteryUsageStats(context, batteryUsageStats); - batteryDiffData = generateBatteryDiffData(context, batteryEntryList, batteryUsageStats); + batteryHistEntryList = convertToBatteryHistEntry(batteryEntryList, batteryUsageStats); closeBatteryUsageStats(batteryUsageStats); } catch (RuntimeException e) { Log.e(TAG, "load batteryUsageStats:" + e); } - return batteryDiffData; + return batteryHistEntryList; } + private static Map getCurrentBatteryHistoryMapFromStatsService( + final Context context) { + final List batteryHistEntryList = + getBatteryHistListFromFromStatsService(context); + return batteryHistEntryList == null ? new HashMap<>() + : batteryHistEntryList.stream().collect(Collectors.toMap(e -> e.getKey(), e -> e)); + } + + @VisibleForTesting @Nullable - private static List convertToBatteryHistEntry( + static List convertToBatteryHistEntry( @Nullable final List batteryEntryList, final BatteryUsageStats batteryUsageStats) { if (batteryEntryList == null || batteryEntryList.isEmpty()) { @@ -591,6 +602,7 @@ public final class DataProcessor { Context context, final List rawTimestampList, final List expectedTimestampSlots, + final long currentTime, final boolean isFromFullCharge, final Map> batteryHistoryMap, final Map> resultMap) { @@ -611,6 +623,17 @@ public final class DataProcessor { final int expectedTimestampSlotsSize = expectedTimestampSlots.size(); for (int index = startIndex; index < expectedTimestampSlotsSize; index++) { final long currentSlot = expectedTimestampSlots.get(index); + if (currentSlot > currentTime) { + // The slot timestamp is greater than the current time. Puts a placeholder first, + // then in the async task, loads the real time battery usage data from the battery + // stats service. + // If current time is odd hour, one placeholder is added. If the current hour is + // even hour, two placeholders are added. This is because the method + // insertHourlyUsageDiffDataPerSlot() requires continuing three hours data. + resultMap.put(currentSlot, + Map.of(CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER, EMPTY_BATTERY_HIST_ENTRY)); + continue; + } final boolean isStartOrEnd = index == 0 || index == expectedTimestampSlotsSize - 1; interpolateHistoryForSlot( context, currentSlot, rawTimestampList, batteryHistoryMap, resultMap, @@ -732,6 +755,13 @@ public final class DataProcessor { return getEvenHourTimestamp(rawTimestamp, /*addHourOfDay*/ 1); } + /** + * @return Returns the fist even hour timestamp after the given timestamp. + */ + private static long getFirstEvenHourAfterTimestamp(long rawTimestamp) { + return getLastEvenHourBeforeTimestamp(rawTimestamp + DateUtils.HOUR_IN_MILLIS * 2); + } + /** * @return Returns the last even hour timestamp before the given timestamp. */ @@ -809,6 +839,12 @@ public final class DataProcessor { + utcToLocalTime(context, timestamp)); return null; } + // The current time battery history hasn't been loaded yet, returns the current battery + // level. + if (entryMap.containsKey(CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER)) { + final Intent intent = BatteryUtils.getBatteryIntent(context); + return BatteryStatus.getBatteryLevel(intent); + } // Averages the battery level in each time slot to avoid corner conditions. float batteryLevelCounter = 0; for (BatteryHistEntry entry : entryMap.values()) { @@ -1394,6 +1430,10 @@ public final class DataProcessor { return batteryDiffEntry; } + private static long getCurrentTimeMillis() { + return sFakeCurrentTimeMillis > 0 ? sFakeCurrentTimeMillis : System.currentTimeMillis(); + } + private static void logAppCountMetrics( Context context, final int countOfAppBeforePurge, final int countOfAppAfterPurge) { context = context.getApplicationContext(); @@ -1465,6 +1505,17 @@ public final class DataProcessor { return null; } final long startTime = System.currentTimeMillis(); + // Loads the current battery usage data from the battery stats service and replaces the + // placeholder in mBatteryHistoryMap. + Map currentBatteryHistoryMap = + getCurrentBatteryHistoryMapFromStatsService(mApplicationContext); + for (Map.Entry> mapEntry + : mBatteryHistoryMap.entrySet()) { + if (mapEntry.getValue().containsKey(CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER)) { + mapEntry.setValue(currentBatteryHistoryMap); + } + } + final Map> batteryUsageMap = getBatteryUsageMap( mApplicationContext, mHourlyBatteryLevelsPerDay, mBatteryHistoryMap); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java index 42eb1c080ba..95e3bad12b5 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java @@ -19,7 +19,9 @@ package com.android.settings.fuelgauge.batteryusage; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; @@ -30,7 +32,9 @@ import static org.mockito.Mockito.verify; import android.content.ContentValues; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; +import android.os.BatteryManager; import android.os.Bundle; import android.os.LocaleList; import android.text.format.DateUtils; @@ -57,6 +61,8 @@ import java.util.TimeZone; @RunWith(RobolectricTestRunner.class) public final class BatteryChartPreferenceControllerTest { + @Mock + private Intent mIntent; @Mock private SettingsActivity mSettingsActivity; @Mock @@ -90,6 +96,9 @@ public final class BatteryChartPreferenceControllerTest { .when(mFeatureFactory.powerUsageFeatureProvider) .getHideApplicationEntries(mContext); doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams(); + doReturn(mIntent).when(mContext).registerReceiver(any(), any()); + doReturn(100).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_SCALE), anyInt()); + doReturn(66).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_LEVEL), anyInt()); setupHourlyChartViewAnimationMock(); mBatteryChartPreferenceController = createController(); mBatteryChartPreferenceController.mPrefContext = mContext; @@ -144,10 +153,11 @@ public final class BatteryChartPreferenceControllerTest { // Ignore fast refresh ui from the data processor callback. verify(mHourlyChartView, atLeast(0)).setViewModel(null); verify(mHourlyChartView, atLeastOnce()).setViewModel(new BatteryChartViewModel( - List.of(100, 97, 95), + List.of(100, 97, 95, 66), List.of(1619251200000L /* 8 AM */, 1619258400000L /* 10 AM */, - 1619265600000L /* 12 PM */), + 1619265600000L /* 12 PM */, + 1619272800000L /* 2 PM */), BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS, mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator)); } @@ -159,12 +169,12 @@ public final class BatteryChartPreferenceControllerTest { setupHourlyChartViewAnimationMock(); BatteryChartViewModel expectedDailyViewModel = new BatteryChartViewModel( - List.of(100, 83, 59, 41), + List.of(100, 83, 59, 66), // "Sat", "Sun", "Mon", "Mon" List.of(1619251200000L /* Sat */, 1619308800000L /* Sun */, 1619395200000L /* Mon */, - 1619460000000L /* Mon */), + 1619467200000L /* Mon */), BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS, mBatteryChartPreferenceController.mDailyChartLabelTextGenerator); @@ -244,7 +254,7 @@ public final class BatteryChartPreferenceControllerTest { expectedDailyViewModel.setSelectedIndex(2); verify(mDailyChartView).setViewModel(expectedDailyViewModel); verify(mHourlyChartView).setViewModel(new BatteryChartViewModel( - List.of(59, 57, 55, 53, 51, 49, 47, 45, 43, 41), + List.of(59, 57, 55, 53, 51, 49, 47, 45, 43, 41, 66), List.of(1619395200000L /* 12 AM */, 1619402400000L /* 2 AM */, 1619409600000L /* 4 AM */, @@ -254,9 +264,11 @@ public final class BatteryChartPreferenceControllerTest { 1619438400000L /* 12 PM */, 1619445600000L /* 2 PM */, 1619452800000L /* 4 PM */, - 1619460000000L /* 6 PM */), + 1619460000000L /* 6 PM */, + 1619467200000L /* 8 PM */), BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS, mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator)); + } @Test @@ -364,7 +376,7 @@ public final class BatteryChartPreferenceControllerTest { final int totalHour = BatteryChartPreferenceController.getTotalHours(batteryLevelData); // Only calculate the even hours. - assertThat(totalHour).isEqualTo(58); + assertThat(totalHour).isEqualTo(60); } private static Long generateTimestamp(int index) { @@ -395,6 +407,8 @@ public final class BatteryChartPreferenceControllerTest { entryMap.put("fake_entry_key" + index, entry); batteryHistoryMap.put(generateTimestamp(index), entryMap); } + DataProcessor.sFakeCurrentTimeMillis = + generateTimestamp(numOfHours - 1) + DateUtils.MINUTE_IN_MILLIS; return batteryHistoryMap; } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java index 0926d146db9..ce6a917cab5 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java @@ -18,7 +18,10 @@ package com.android.settings.fuelgauge.batteryusage; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -27,7 +30,9 @@ import static org.mockito.Mockito.when; import android.app.settings.SettingsEnums; import android.content.ContentValues; import android.content.Context; +import android.content.Intent; import android.os.BatteryConsumer; +import android.os.BatteryManager; import android.os.BatteryUsageStats; import android.text.format.DateUtils; @@ -63,6 +68,7 @@ public class DataProcessorTest { private MetricsFeatureProvider mMetricsFeatureProvider; private PowerUsageFeatureProvider mPowerUsageFeatureProvider; + @Mock private Intent mIntent; @Mock private BatteryUsageStats mBatteryUsageStats; @Mock private BatteryEntry mMockBatteryEntry1; @Mock private BatteryEntry mMockBatteryEntry2; @@ -79,6 +85,10 @@ public class DataProcessorTest { mFeatureFactory = FakeFeatureFactory.setupForTest(); mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider; + + doReturn(mIntent).when(mContext).registerReceiver(any(), any()); + doReturn(100).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_SCALE), anyInt()); + doReturn(66).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_LEVEL), anyInt()); } @Test @@ -100,11 +110,15 @@ public class DataProcessorTest { @Test public void getBatteryLevelData_notEnoughData_returnNull() { - // The timestamps are within 1 hour. - final long[] timestamps = {1000000L, 2000000L, 3000000L}; + // The timestamps and the current time are within half hour before an even hour. + final long[] timestamps = { + DateUtils.HOUR_IN_MILLIS * 2 - 300L, + DateUtils.HOUR_IN_MILLIS * 2 - 200L, + DateUtils.HOUR_IN_MILLIS * 2 - 100L}; final int[] levels = {100, 99, 98}; final Map> batteryHistoryMap = createHistoryMap(timestamps, levels); + DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1]; assertThat(DataProcessor.getBatteryLevelData( mContext, /*handler=*/ null, batteryHistoryMap, /*asyncResponseDelegate=*/ null)) @@ -117,11 +131,12 @@ public class DataProcessorTest { @Test public void getBatteryLevelData_returnExpectedResult() { - // Timezone GMT+8: 2022-01-01 00:00:00, 2022-01-01 01:00:00, 2022-01-01 02:00:00 - final long[] timestamps = {1640966400000L, 1640970000000L, 1640973600000L}; - final int[] levels = {100, 99, 98}; + // Timezone GMT+8: 2022-01-01 00:00:00, 2022-01-01 01:00:00 + final long[] timestamps = {1640966400000L, 1640970000000L}; + final int[] levels = {100, 99}; final Map> batteryHistoryMap = createHistoryMap(timestamps, levels); + DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1]; final BatteryLevelData resultData = DataProcessor.getBatteryLevelData( @@ -130,8 +145,10 @@ public class DataProcessorTest { batteryHistoryMap, /*asyncResponseDelegate=*/ null); - final List expectedDailyTimestamps = List.of(timestamps[0], timestamps[2]); - final List expectedDailyLevels = List.of(levels[0], levels[2]); + final List expectedDailyTimestamps = List.of( + 1640966400000L, // 2022-01-01 00:00:00 + 1640973600000L); // 2022-01-01 02:00:00 + final List expectedDailyLevels = List.of(100, 66); final List> expectedHourlyTimestamps = List.of(expectedDailyTimestamps); final List> expectedHourlyLevels = List.of(expectedDailyLevels); verifyExpectedBatteryLevelData( @@ -162,6 +179,7 @@ public class DataProcessorTest { final int[] levels = {100, 94, 90, 82, 50}; final Map> batteryHistoryMap = createHistoryMap(timestamps, levels); + DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1]; final Map> resultMap = DataProcessor.getHistoryMapWithExpectedTimestamps(mContext, batteryHistoryMap); @@ -172,14 +190,20 @@ public class DataProcessorTest { 1640970000000L, // 2022-01-01 01:00:00 1640973600000L, // 2022-01-01 02:00:00 1640977200000L, // 2022-01-01 03:00:00 - 1640980800000L // 2022-01-01 04:00:00 + 1640980800000L, // 2022-01-01 04:00:00 + 1640984400000L, // 2022-01-01 05:00:00 + 1640988000000L // 2022-01-01 06:00:00 }; - final int[] expectedLevels = {100, 94, 90, 84, 56}; + final int[] expectedLevels = {100, 94, 90, 84, 56, 98, 98}; assertThat(resultMap).hasSize(expectedLevels.length); - for (int index = 0; index < expectedLevels.length; index++) { + for (int index = 0; index < 5; index++) { assertThat(resultMap.get(expectedTimestamps[index]).get(FAKE_ENTRY_KEY).mBatteryLevel) .isEqualTo(expectedLevels[index]); } + for (int index = 5; index < 7; index++) { + assertThat(resultMap.get(expectedTimestamps[index]).containsKey( + DataProcessor.CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER)).isTrue(); + } } @Test @@ -188,6 +212,7 @@ public class DataProcessorTest { final int[] levels = {100}; final Map> batteryHistoryMap = createHistoryMap(timestamps, levels); + DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1]; assertThat( DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap)) @@ -207,6 +232,7 @@ public class DataProcessorTest { final int[] levels = {100, 94, 90, 82, 50}; final Map> batteryHistoryMap = createHistoryMap(timestamps, levels); + DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1]; final BatteryLevelData resultData = DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap); @@ -239,6 +265,7 @@ public class DataProcessorTest { final int[] levels = {100, 94, 90, 82}; final Map> batteryHistoryMap = createHistoryMap(timestamps, levels); + DataProcessor.sFakeCurrentTimeMillis = timestamps[timestamps.length - 1]; final BatteryLevelData resultData = DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap); @@ -290,8 +317,8 @@ public class DataProcessorTest { @Test public void getTimestampSlots_emptyRawList_returnEmptyList() { - final List resultList = - DataProcessor.getTimestampSlots(new ArrayList<>()); + final List resultList = DataProcessor.getTimestampSlots( + new ArrayList<>(), 1641038400000L); // 2022-01-01 20:00:00 assertThat(resultList).isEmpty(); } @@ -305,7 +332,7 @@ public class DataProcessorTest { final Calendar expectedStartCalendar = Calendar.getInstance(); expectedStartCalendar.set(2022, 6, 5, 6, 0, 0); // 2022-07-05 06:00:00 final Calendar expectedEndCalendar = Calendar.getInstance(); - expectedEndCalendar.set(2022, 6, 5, 22, 0, 0); // 2022-07-05 22:00:00 + expectedEndCalendar.set(2022, 6, 6, 0, 0, 0); // 2022-07-05 22:00:00 verifyExpectedTimestampSlots( startCalendar, endCalendar, expectedStartCalendar, expectedEndCalendar); } @@ -320,7 +347,7 @@ public class DataProcessorTest { final Calendar expectedStartCalendar = Calendar.getInstance(); expectedStartCalendar.set(2022, 6, 5, 6, 00, 00); // 2022-07-05 06:00:00 final Calendar expectedEndCalendar = Calendar.getInstance(); - expectedEndCalendar.set(2022, 6, 6, 20, 00, 00); // 2022-07-06 20:00:00 + expectedEndCalendar.set(2022, 6, 6, 22, 00, 00); // 2022-07-06 20:00:00 verifyExpectedTimestampSlots( startCalendar, endCalendar, expectedStartCalendar, expectedEndCalendar); } @@ -1110,8 +1137,8 @@ public class DataProcessorTest { @Test public void generateBatteryDiffData_emptyBatteryEntryList_returnNull() { - assertThat(DataProcessor.generateBatteryDiffData( - mContext, null, mBatteryUsageStats)).isNull(); + assertThat(DataProcessor.generateBatteryDiffData(mContext, + DataProcessor.convertToBatteryHistEntry(null, mBatteryUsageStats))).isNull(); } @Test @@ -1161,8 +1188,8 @@ public class DataProcessorTest { doReturn(BatteryConsumer.POWER_COMPONENT_CAMERA) .when(mMockBatteryEntry4).getPowerComponentId(); - final BatteryDiffData batteryDiffData = DataProcessor.generateBatteryDiffData( - mContext, batteryEntryList, mBatteryUsageStats); + final BatteryDiffData batteryDiffData = DataProcessor.generateBatteryDiffData(mContext, + DataProcessor.convertToBatteryHistEntry(batteryEntryList, mBatteryUsageStats)); assertBatteryDiffEntry( batteryDiffData.getAppDiffEntryList().get(0), 0, /*uid=*/ 2L, @@ -1284,16 +1311,15 @@ public class DataProcessorTest { private static void verifyExpectedTimestampSlots( final Calendar start, - final Calendar end, + final Calendar current, final Calendar expectedStart, final Calendar expectedEnd) { expectedStart.set(Calendar.MILLISECOND, 0); expectedEnd.set(Calendar.MILLISECOND, 0); final ArrayList timestampSlots = new ArrayList<>(); timestampSlots.add(start.getTimeInMillis()); - timestampSlots.add(end.getTimeInMillis()); final List resultList = - DataProcessor.getTimestampSlots(timestampSlots); + DataProcessor.getTimestampSlots(timestampSlots, current.getTimeInMillis()); for (int index = 0; index < resultList.size(); index++) { final long expectedTimestamp =