From ab00ac091da7ee3925a10f107ce58877044342d2 Mon Sep 17 00:00:00 2001 From: Zaiyue Xue Date: Thu, 24 Aug 2023 12:29:28 +0800 Subject: [PATCH] Enable i18n for battery tips card and fix b/297036263. Bug: 291689623 Bug: 297036263 Fix: 297036263 Test: manual Change-Id: I41aff99c73ace995ef9dfa8f1dc28024cd12d236 Merged-In: I41aff99c73ace995ef9dfa8f1dc28024cd12d236 (cherry picked from commit d3156cc00198f4283ad98d61f010648fb2247494) --- res/values/arrays.xml | 4 +- res/values/strings.xml | 10 +- .../fuelgauge/batteryusage/DatabaseUtils.java | 225 +++++------------- .../batteryusage/DatabaseUtilsTest.java | 16 ++ 4 files changed, 86 insertions(+), 169 deletions(-) diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 552ca3fcfdb..47cf21f210b 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -1415,12 +1415,12 @@ Reduce screen timeout to extend battery life - + @string/battery_tips_card_action_button @string/battery_tips_card_action_button - + @string/battery_tips_card_dismiss_button @string/battery_tips_card_dismiss_button diff --git a/res/values/strings.xml b/res/values/strings.xml index ad23aaf8a54..d4bbdb2c038 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9657,14 +9657,14 @@ Set battery usage for apps - - View Settings + + View Settings - - Got it + + Got it - Is this message helpful? + Is this message helpful? Unrestricted diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java index 0ed90ae8753..e78d25c0191 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java @@ -35,6 +35,7 @@ import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserManager; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -51,10 +52,10 @@ import java.time.Clock; import java.time.Duration; import java.util.ArrayList; import java.util.Calendar; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -66,6 +67,7 @@ public final class DatabaseUtils { /** Clear memory threshold for device booting phase. **/ private static final long CLEAR_MEMORY_THRESHOLD_MS = Duration.ofMinutes(5).toMillis(); private static final long CLEAR_MEMORY_DELAYED_MS = Duration.ofSeconds(2).toMillis(); + private static final long INVALID_TIMESTAMP = 0L; static final int DATA_RETENTION_INTERVAL_DAY = 9; static final String KEY_LAST_LOAD_FULL_CHARGE_TIME = "last_load_full_charge_time"; @@ -158,8 +160,8 @@ public final class DatabaseUtils { .appendQueryParameter( QUERY_KEY_USERID, Long.toString(userId)) .build(); - final long latestTimestamp = - loadAppUsageLatestTimestampFromContentProvider(context, appUsageLatestTimestampUri); + final long latestTimestamp = loadLongFromContentProvider( + context, appUsageLatestTimestampUri, /*defaultValue=*/ INVALID_TIMESTAMP); final String latestTimestampString = utcToLocalTimeForLogging(latestTimestamp); Log.d(TAG, String.format( "getAppUsageStartTimestampOfUser() userId=%d latestTimestamp=%s in %d/ms", @@ -196,8 +198,8 @@ public final class DatabaseUtils { .appendQueryParameter(QUERY_KEY_USERID, queryUserIdString) .build(); - final List appUsageEventList = - loadAppUsageEventsFromContentProvider(context, appUsageEventUri); + final List appUsageEventList = loadListFromContentProvider( + context, appUsageEventUri, ConvertUtils::convertToAppUsageEvent); Log.d(TAG, String.format("getAppUsageEventForUser userId=%s size=%d in %d/ms", queryUserIdString, appUsageEventList.size(), (System.currentTimeMillis() - startTime))); @@ -229,8 +231,8 @@ public final class DatabaseUtils { QUERY_BATTERY_EVENT_TYPE, queryBatteryEventTypesString) .build(); - final List batteryEventList = - loadBatteryEventsFromContentProvider(context, batteryEventUri); + final List batteryEventList = loadListFromContentProvider( + context, batteryEventUri, ConvertUtils::convertToBatteryEvent); Log.d(TAG, String.format("getBatteryEvents size=%d in %d/ms", batteryEventList.size(), (System.currentTimeMillis() - startTime))); return batteryEventList; @@ -257,8 +259,8 @@ public final class DatabaseUtils { QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp)) .build(); - final List batteryUsageSlotList = - loadBatteryUsageSlotsFromContentProvider(context, batteryUsageSlotUri); + final List batteryUsageSlotList = loadListFromContentProvider( + context, batteryUsageSlotUri, ConvertUtils::convertToBatteryUsageSlot); Log.d(TAG, String.format("getBatteryUsageSlots size=%d in %d/ms", batteryUsageSlotList.size(), (System.currentTimeMillis() - startTime))); return batteryUsageSlotList; @@ -274,8 +276,8 @@ public final class DatabaseUtils { .authority(AUTHORITY) .appendPath(LAST_FULL_CHARGE_TIMESTAMP_PATH) .build(); - final long lastFullChargeTime = loadLastFullChargeTimeFromContentProvider( - context, lastFullChargeTimeUri); + final long lastFullChargeTime = loadLongFromContentProvider( + context, lastFullChargeTimeUri, /*defaultValue=*/ INVALID_TIMESTAMP); final String lastFullChargeTimeString = utcToLocalTimeForLogging(lastFullChargeTime); Log.d(TAG, String.format( "getLastFullChargeTime() lastFullChargeTime=%s in %d/ms", @@ -297,8 +299,8 @@ public final class DatabaseUtils { .appendQueryParameter( QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp)) .build(); - final long batteryStateLatestTimestamp = loadBatteryStateLatestTimestampFromContentProvider( - context, batteryStateLatestTimestampUri); + final long batteryStateLatestTimestamp = loadLongFromContentProvider( + context, batteryStateLatestTimestampUri, /*defaultValue=*/ INVALID_TIMESTAMP); final String batteryStateLatestTimestampString = utcToLocalTimeForLogging(batteryStateLatestTimestamp); Log.d(TAG, String.format( @@ -322,8 +324,21 @@ public final class DatabaseUtils { QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp)) .build(); - final Map> resultMap = - loadHistoryMapFromContentProvider(context, batteryStateUri); + final List batteryHistEntryList = loadListFromContentProvider( + context, batteryStateUri, cursor -> new BatteryHistEntry(cursor)); + final Map> resultMap = new ArrayMap(); + for (final BatteryHistEntry entry : batteryHistEntryList) { + final long timestamp = entry.mTimestamp; + final String key = entry.getKey(); + Map batteryHistEntryMap = resultMap.get(timestamp); + // Creates new one if there is no corresponding map. + if (batteryHistEntryMap == null) { + batteryHistEntryMap = new ArrayMap(); + resultMap.put(timestamp, batteryHistEntryMap); + } + batteryHistEntryMap.put(key, entry); + } + if (resultMap == null || resultMap.isEmpty()) { Log.d(TAG, "getBatteryHistoryMap() returns empty or null"); } else { @@ -713,6 +728,39 @@ public final class DatabaseUtils { } } + @VisibleForTesting + static T loadFromContentProvider( + Context context, Uri uri, T defaultValue, Function cursorReader) { + // Transfer work profile to user profile. Please see b/297036263. + context = getParentContext(context); + if (context == null) { + return defaultValue; + } + try (Cursor cursor = sFakeSupplier != null ? sFakeSupplier.get() : + context.getContentResolver().query(uri, null, null, null)) { + return (cursor == null || cursor.getCount() == 0) + ? defaultValue : cursorReader.apply(cursor); + } + } + + private static long loadLongFromContentProvider( + Context context, Uri uri, final long defaultValue) { + return loadFromContentProvider(context, uri, defaultValue, + cursor -> cursor.moveToFirst() ? cursor.getLong(/*columnIndex=*/ 0) : defaultValue); + } + + private static List loadListFromContentProvider( + Context context, Uri uri, Function converter) { + return loadFromContentProvider(context, uri, new ArrayList<>(), + cursor -> { + final List list = new ArrayList<>(); + while (cursor.moveToNext()) { + list.add(converter.apply(cursor)); + } + return list; + }); + } + private static void writeString( Context context, PrintWriter writer, String prefix, String key) { final SharedPreferences sharedPreferences = getSharedPreferences(context); @@ -722,153 +770,6 @@ public final class DatabaseUtils { } } - private static long loadAppUsageLatestTimestampFromContentProvider( - Context context, final Uri appUsageLatestTimestampUri) { - // We have already make sure the context here is with profile parent's user identity. Don't - // need to check whether current user is work profile. - try (Cursor cursor = sFakeSupplier != null - ? sFakeSupplier.get() - : context.getContentResolver().query( - appUsageLatestTimestampUri, null, null, null)) { - if (cursor == null || cursor.getCount() == 0) { - return INVALID_USER_ID; - } - cursor.moveToFirst(); - // There is only one column returned so use the index 0 directly. - final long latestTimestamp = cursor.getLong(/*columnIndex=*/ 0); - // If there is no data for this user, 0 will be returned from the database. - return latestTimestamp == 0 ? INVALID_USER_ID : latestTimestamp; - } - } - - private static List loadAppUsageEventsFromContentProvider( - Context context, Uri appUsageEventUri) { - final List appUsageEventList = new ArrayList<>(); - context = getParentContext(context); - if (context == null) { - return appUsageEventList; - } - try (Cursor cursor = sFakeSupplier != null - ? sFakeSupplier.get() - : context.getContentResolver().query(appUsageEventUri, null, null, null)) { - if (cursor == null || cursor.getCount() == 0) { - return appUsageEventList; - } - // Loads and converts all AppUsageEvent data from cursor. - while (cursor.moveToNext()) { - appUsageEventList.add(ConvertUtils.convertToAppUsageEvent(cursor)); - } - } - return appUsageEventList; - } - - private static List loadBatteryEventsFromContentProvider( - Context context, Uri batteryEventUri) { - final List batteryEventList = new ArrayList<>(); - context = getParentContext(context); - if (context == null) { - return batteryEventList; - } - try (Cursor cursor = sFakeSupplier != null - ? sFakeSupplier.get() - : context.getContentResolver().query(batteryEventUri, null, null, null)) { - if (cursor == null || cursor.getCount() == 0) { - return batteryEventList; - } - // Loads and converts all AppUsageEvent data from cursor. - while (cursor.moveToNext()) { - batteryEventList.add(ConvertUtils.convertToBatteryEvent(cursor)); - } - } - return batteryEventList; - } - - private static List loadBatteryUsageSlotsFromContentProvider( - Context context, Uri batteryUsageSlotUri) { - final List batteryUsageSlotList = new ArrayList<>(); - context = getParentContext(context); - if (context == null) { - return batteryUsageSlotList; - } - try (Cursor cursor = sFakeSupplier != null - ? sFakeSupplier.get() - : context.getContentResolver().query(batteryUsageSlotUri, null, null, null)) { - if (cursor == null || cursor.getCount() == 0) { - return batteryUsageSlotList; - } - // Loads and converts all AppUsageEvent data from cursor. - while (cursor.moveToNext()) { - batteryUsageSlotList.add(ConvertUtils.convertToBatteryUsageSlot(cursor)); - } - } - return batteryUsageSlotList; - } - - private static long loadLastFullChargeTimeFromContentProvider( - Context context, final Uri lastFullChargeTimeUri) { - // We have already make sure the context here is with profile parent's user identity. Don't - // need to check whether current user is work profile. - try (Cursor cursor = sFakeSupplier != null - ? sFakeSupplier.get() - : context.getContentResolver().query( - lastFullChargeTimeUri, null, null, null)) { - if (cursor == null || cursor.getCount() == 0) { - return 0L; - } - cursor.moveToFirst(); - // There is only one column returned so use the index 0 directly. - final long lastFullChargeTime = cursor.getLong(/*columnIndex=*/ 0); - return lastFullChargeTime; - } - } - - private static long loadBatteryStateLatestTimestampFromContentProvider( - Context context, final Uri batteryStateLatestTimestampUri) { - // We have already make sure the context here is with profile parent's user identity. Don't - // need to check whether current user is work profile. - try (Cursor cursor = sFakeSupplier != null - ? sFakeSupplier.get() - : context.getContentResolver().query( - batteryStateLatestTimestampUri, null, null, null)) { - if (cursor == null || cursor.getCount() == 0) { - return 0L; - } - cursor.moveToFirst(); - // There is only one column returned so use the index 0 directly. - final long batteryStateLatestTimestamp = cursor.getLong(/*columnIndex=*/ 0); - return batteryStateLatestTimestamp; - } - } - - private static Map> loadHistoryMapFromContentProvider( - Context context, Uri batteryStateUri) { - context = getParentContext(context); - if (context == null) { - return null; - } - final Map> resultMap = new HashMap(); - try (Cursor cursor = sFakeSupplier != null ? sFakeSupplier.get() : - context.getContentResolver().query(batteryStateUri, null, null, null)) { - if (cursor == null || cursor.getCount() == 0) { - return resultMap; - } - // Loads and converts all BatteryHistEntry data from cursor. - while (cursor.moveToNext()) { - final BatteryHistEntry entry = new BatteryHistEntry(cursor); - final long timestamp = entry.mTimestamp; - final String key = entry.getKey(); - Map batteryHistEntryMap = resultMap.get(timestamp); - // Creates new one if there is no corresponding map. - if (batteryHistEntryMap == null) { - batteryHistEntryMap = new HashMap<>(); - resultMap.put(timestamp, batteryHistEntryMap); - } - batteryHistEntryMap.put(key, entry); - } - } - return resultMap; - } - private static void clearMemory() { if (SystemClock.uptimeMillis() > CLEAR_MEMORY_THRESHOLD_MS) { return; diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java index f72b333b570..12bd4570677 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java @@ -22,6 +22,8 @@ import static android.app.usage.UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVIT import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -36,6 +38,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.database.MatrixCursor; +import android.net.Uri; import android.os.BatteryManager; import android.os.BatteryUsageStats; import android.os.RemoteException; @@ -530,6 +533,19 @@ public final class DatabaseUtilsTest { assertThat(dumpContent.contains("LastUploadFullChargeTime")).isTrue(); } + @Test + public void loadFromContentProvider_workProfile_transferToUserProfile() throws Exception { + // Test to verify b/297036263 + doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); + doReturn(true).when(mUserManager).isManagedProfile(); + doReturn(UserHandle.CURRENT).when(mContext).getUser(); + doReturn(UserHandle.SYSTEM).when(mUserManager).getProfileParent(UserHandle.CURRENT); + + DatabaseUtils.loadFromContentProvider(mContext, Uri.EMPTY, null, cursor -> 1); + + verify(mContext).createPackageContextAsUser(anyString(), anyInt(), any()); + } + private static void verifyBatteryEntryContentValues( double consumedPower, ContentValues values) { final BatteryInformation batteryInformation =