Fix b/273175976: Screen time counts the time before full charge.
Use the raw start timestamp instead of the first timestamp in the level map to query app usage time. Bug: 273175976 Fix: 273175976 Test: manual Change-Id: Idb43b2bd5378e2f34ec722354408754f4a439c6d
This commit is contained in:
@@ -77,10 +77,8 @@ public class DataProcessManager {
|
||||
private List<BatteryLevelData.PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
|
||||
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
|
||||
|
||||
// The start timestamp of battery level data. As we don't know when is the full charge cycle
|
||||
// start time when loading app usage data, this value is used as the start time of querying app
|
||||
// usage data.
|
||||
private long mStartTimestampOfLevelData;
|
||||
// Raw start timestamp with round to the nearest hour.
|
||||
private long mRawStartTimestamp;
|
||||
|
||||
private boolean mIsCurrentBatteryHistoryLoaded = false;
|
||||
private boolean mIsCurrentAppUsageLoaded = false;
|
||||
@@ -105,6 +103,7 @@ public class DataProcessManager {
|
||||
DataProcessManager(
|
||||
Context context,
|
||||
Handler handler,
|
||||
final long rawStartTimestamp,
|
||||
@NonNull final DataProcessor.UsageMapAsyncResponse callbackFunction,
|
||||
@NonNull final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
|
||||
@NonNull final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
||||
@@ -114,7 +113,7 @@ public class DataProcessManager {
|
||||
mCallbackFunction = callbackFunction;
|
||||
mHourlyBatteryLevelsPerDay = hourlyBatteryLevelsPerDay;
|
||||
mBatteryHistoryMap = batteryHistoryMap;
|
||||
mStartTimestampOfLevelData = getStartTimestampOfBatteryLevelData();
|
||||
mRawStartTimestamp = rawStartTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,21 +152,6 @@ public class DataProcessManager {
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
long getStartTimestampOfBatteryLevelData() {
|
||||
for (int dailyIndex = 0; dailyIndex < mHourlyBatteryLevelsPerDay.size(); dailyIndex++) {
|
||||
if (mHourlyBatteryLevelsPerDay.get(dailyIndex) == null) {
|
||||
continue;
|
||||
}
|
||||
final List<Long> timestamps =
|
||||
mHourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
|
||||
if (timestamps.size() > 0) {
|
||||
return timestamps.get(0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
List<AppUsageEvent> getAppUsageEventList() {
|
||||
return mAppUsageEventList;
|
||||
@@ -251,7 +235,7 @@ public class DataProcessManager {
|
||||
final int workProfileUserId = getWorkProfileUserId();
|
||||
final UsageEvents usageEventsForCurrentUser =
|
||||
DataProcessor.getAppUsageEventsForUser(
|
||||
mContext, currentUserId, mStartTimestampOfLevelData);
|
||||
mContext, currentUserId, mRawStartTimestamp);
|
||||
// If fail to load usage events for current user, return null directly and screen-on
|
||||
// time will not be shown in the UI.
|
||||
if (usageEventsForCurrentUser == null) {
|
||||
@@ -262,7 +246,7 @@ public class DataProcessManager {
|
||||
if (workProfileUserId != Integer.MIN_VALUE) {
|
||||
usageEventsForWorkProfile =
|
||||
DataProcessor.getAppUsageEventsForUser(
|
||||
mContext, workProfileUserId, mStartTimestampOfLevelData);
|
||||
mContext, workProfileUserId, mRawStartTimestamp);
|
||||
} else {
|
||||
Log.d(TAG, "there is no work profile");
|
||||
}
|
||||
@@ -309,7 +293,7 @@ public class DataProcessManager {
|
||||
final List<AppUsageEvent> appUsageEventList =
|
||||
DatabaseUtils.getAppUsageEventForUsers(
|
||||
mContext, Calendar.getInstance(), getCurrentUserIds(),
|
||||
mStartTimestampOfLevelData);
|
||||
mRawStartTimestamp);
|
||||
Log.d(TAG, String.format("execute loadDatabaseAppUsageList size=%d in %d/ms",
|
||||
appUsageEventList.size(), (System.currentTimeMillis() - startTime)));
|
||||
return appUsageEventList;
|
||||
@@ -376,7 +360,7 @@ public class DataProcessManager {
|
||||
// Generates the indexed AppUsagePeriod list data for each corresponding time slot for
|
||||
// further use.
|
||||
mAppUsagePeriodMap = DataProcessor.generateAppUsagePeriodMap(
|
||||
mHourlyBatteryLevelsPerDay, mAppUsageEventList);
|
||||
mRawStartTimestamp, mHourlyBatteryLevelsPerDay, mAppUsageEventList);
|
||||
}
|
||||
|
||||
private void tryToGenerateFinalDataAndApplyCallback() {
|
||||
@@ -489,10 +473,13 @@ public class DataProcessManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
final long rawStartTimestamp =
|
||||
batteryHistoryMap.keySet().stream().min(Long::compare).orElse(0L);
|
||||
// Start the async task to compute diff usage data and load labels and icons.
|
||||
new DataProcessManager(
|
||||
context,
|
||||
handler,
|
||||
rawStartTimestamp,
|
||||
asyncResponseDelegate,
|
||||
batteryLevelData.getHourlyBatteryLevelsPerDay(),
|
||||
processedBatteryHistoryMap).start();
|
||||
|
@@ -265,8 +265,9 @@ public final class DataProcessor {
|
||||
@Nullable
|
||||
public static Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>>
|
||||
generateAppUsagePeriodMap(
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
|
||||
final List<AppUsageEvent> appUsageEventList) {
|
||||
final long rawStartTimestamp,
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
|
||||
final List<AppUsageEvent> appUsageEventList) {
|
||||
if (appUsageEventList.isEmpty()) {
|
||||
Log.w(TAG, "appUsageEventList is empty");
|
||||
return null;
|
||||
@@ -288,8 +289,12 @@ public final class DataProcessor {
|
||||
final List<Long> timestamps = hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
|
||||
final long hourlySize = timestamps.size() - 1;
|
||||
for (int hourlyIndex = 0; hourlyIndex < timestamps.size() - 1; hourlyIndex++) {
|
||||
// The start and end timestamps of this slot should be the adjacent timestamps.
|
||||
final long startTimestamp = timestamps.get(hourlyIndex);
|
||||
// The start slot timestape is the near hour timestamp instead of the last full
|
||||
// charge time. So use rawStartTimestamp instead of reading the timestamp from
|
||||
// hourlyBatteryLevelsPerDay here.
|
||||
final long startTimestamp =
|
||||
dailyIndex == 0 && hourlyIndex == 0 && !sDebug
|
||||
? rawStartTimestamp : timestamps.get(hourlyIndex);
|
||||
// The final slot is to show the data from last even hour until now but the
|
||||
// timestamp in hourlyBatteryLevelsPerDay is not the real value. So use current
|
||||
// timestamp instead of reading the timestamp from hourlyBatteryLevelsPerDay here.
|
||||
|
@@ -139,13 +139,13 @@ public final class DatabaseUtils {
|
||||
Context context,
|
||||
final Calendar calendar,
|
||||
final List<Integer> userIds,
|
||||
final long startTimestampOfLevelData) {
|
||||
final long rawStartTimestamp) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final long sixDaysAgoTimestamp = getTimestampSixDaysAgo(calendar);
|
||||
// Query a longer time period and then trim to the original time period in order to make
|
||||
// sure the app usage calculation near the boundaries is correct.
|
||||
final long queryTimestamp =
|
||||
Math.max(startTimestampOfLevelData, sixDaysAgoTimestamp) - USAGE_QUERY_BUFFER_HOURS;
|
||||
Math.max(rawStartTimestamp, sixDaysAgoTimestamp) - USAGE_QUERY_BUFFER_HOURS;
|
||||
Log.d(TAG, "sixDayAgoTimestamp: " + sixDaysAgoTimestamp);
|
||||
final String queryUserIdString = userIds.stream()
|
||||
.map(userId -> String.valueOf(userId))
|
||||
|
@@ -82,8 +82,8 @@ public final class DataProcessManagerTest {
|
||||
doReturn(66).when(mIntent).getIntExtra(eq(BatteryManager.EXTRA_LEVEL), anyInt());
|
||||
|
||||
mDataProcessManager = new DataProcessManager(
|
||||
mContext, /*handler=*/ null, /*callbackFunction=*/ null,
|
||||
/*hourlyBatteryLevelsPerDay=*/ new ArrayList<>(),
|
||||
mContext, /*handler=*/ null, /*rawStartTimestamp=*/ 0L,
|
||||
/*callbackFunction=*/ null, /*hourlyBatteryLevelsPerDay=*/ new ArrayList<>(),
|
||||
/*batteryHistoryMap=*/ new HashMap<>());
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ public final class DataProcessManagerTest {
|
||||
DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
|
||||
|
||||
final DataProcessManager dataProcessManager = new DataProcessManager(
|
||||
mContext, /*handler=*/ null, /*callbackFunction=*/ null,
|
||||
mContext, /*handler=*/ null, /*rawStartTimestamp=*/ 2L, /*callbackFunction=*/ null,
|
||||
hourlyBatteryLevelsPerDay, /*batteryHistoryMap=*/ new HashMap<>());
|
||||
dataProcessManager.start();
|
||||
|
||||
@@ -249,42 +249,6 @@ public final class DataProcessManagerTest {
|
||||
assertThat(mDataProcessManager.getShowScreenOnTime()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStartTimestampOfBatteryLevelData_returnExpectedResult() {
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||
new ArrayList<>();
|
||||
final List<Long> timestamps = new ArrayList<>();
|
||||
timestamps.add(101L);
|
||||
timestamps.add(1001L);
|
||||
final List<Integer> levels = new ArrayList<>();
|
||||
levels.add(1);
|
||||
levels.add(2);
|
||||
hourlyBatteryLevelsPerDay.add(null);
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
|
||||
|
||||
final DataProcessManager dataProcessManager = new DataProcessManager(
|
||||
mContext, /*handler=*/ null, /*callbackFunction=*/ null,
|
||||
hourlyBatteryLevelsPerDay, /*batteryHistoryMap=*/ null);
|
||||
|
||||
assertThat(dataProcessManager.getStartTimestampOfBatteryLevelData()).isEqualTo(101);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStartTimestampOfBatteryLevelData_emptyLevels_returnZero() {
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||
new ArrayList<>();
|
||||
hourlyBatteryLevelsPerDay.add(null);
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
|
||||
|
||||
final DataProcessManager dataProcessManager = new DataProcessManager(
|
||||
mContext, /*handler=*/ null, /*callbackFunction=*/ null,
|
||||
hourlyBatteryLevelsPerDay, /*batteryHistoryMap=*/ null);
|
||||
|
||||
assertThat(dataProcessManager.getStartTimestampOfBatteryLevelData()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBatteryLevelData_emptyHistoryMap_returnNull() {
|
||||
assertThat(DataProcessManager.getBatteryLevelData(
|
||||
|
@@ -250,7 +250,7 @@ public final class DataProcessorTest {
|
||||
|
||||
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> periodMap =
|
||||
DataProcessor.generateAppUsagePeriodMap(
|
||||
hourlyBatteryLevelsPerDay, appUsageEventList);
|
||||
14400000L, hourlyBatteryLevelsPerDay, appUsageEventList);
|
||||
|
||||
assertThat(periodMap).hasSize(3);
|
||||
// Day 1
|
||||
@@ -288,7 +288,7 @@ public final class DataProcessorTest {
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
|
||||
assertThat(DataProcessor.generateAppUsagePeriodMap(
|
||||
hourlyBatteryLevelsPerDay, new ArrayList<>())).isNull();
|
||||
0L, hourlyBatteryLevelsPerDay, new ArrayList<>())).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Reference in New Issue
Block a user