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