From bcf0a411a19957d4214c7d0c332dd1db188e335b Mon Sep 17 00:00:00 2001 From: ykhung Date: Sun, 18 Apr 2021 23:01:27 +0800 Subject: [PATCH] Add app usage preferencee item into the history preference Bug: 177406865 Test: make SettingsRoboTests Test: make SettingsGoogleRoboTests Change-Id: I7d9a14eca0b8d3b054e040cb13135ef15390e512 --- .../BatteryChartPreferenceController.java | 109 +++++++++++++++++- .../settings/fuelgauge/BatteryDiffEntry.java | 2 +- .../settings/fuelgauge/BatteryHistEntry.java | 2 +- .../fuelgauge/PowerUsageAdvanced.java | 8 +- .../BatteryChartPreferenceControllerTest.java | 89 +++++++++++++- 5 files changed, 201 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java index cc7f84553d7..eca2bf3d34e 100644 --- a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java @@ -18,6 +18,10 @@ package com.android.settings.fuelgauge; import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.text.TextUtils; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -38,6 +42,7 @@ import com.android.settingslib.core.lifecycle.events.OnPause; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -63,6 +68,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll private final String mPreferenceKey; private final SettingsActivity mActivity; private final InstrumentedPreferenceFragment mFragment; + private final Handler mHandler = new Handler(Looper.getMainLooper()); + + // Preference cache to avoid create new instance each time. + @VisibleForTesting + final Map mPreferenceCache = new HashMap<>(); public BatteryChartPreferenceController( Context context, String preferenceKey, @@ -86,6 +96,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll if (mActivity.isChangingConfigurations()) { BatteryDiffEntry.clearCache(); } + mHandler.removeCallbacksAndMessages(/*token=*/ null); + mPreferenceCache.clear(); } @Override @@ -93,6 +105,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll super.displayPreference(screen); mPrefContext = screen.getContext(); mAppListPrefGroup = screen.findPreference(mPreferenceKey); + mAppListPrefGroup.setOrderingAsAdded(false); } @Override @@ -116,7 +129,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll refreshUi(trapezoidIndex, /*isForce=*/ false); } - void setBatteryHistoryMap(Map> batteryHistoryMap) { + void setBatteryHistoryMap( + final Map> batteryHistoryMap) { + mHandler.post(() -> setBatteryHistoryMapInner(batteryHistoryMap)); + } + + private void setBatteryHistoryMapInner( + final Map> batteryHistoryMap) { // Resets all battery history data relative variables. if (batteryHistoryMap == null) { mBatteryIndexedMap = null; @@ -163,7 +182,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll Arrays.toString(mBatteryHistoryLevels))); } - void setBatteryChartView(BatteryChartView batteryChartView) { + void setBatteryChartView(final BatteryChartView batteryChartView) { + mHandler.post(() -> setBatteryChartViewInner(batteryChartView)); + } + + private void setBatteryChartViewInner(final BatteryChartView batteryChartView) { mBatteryChartView = batteryChartView; mBatteryChartView.setOnSelectListener(this); forceRefreshUi(); @@ -174,6 +197,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll mTrapezoidIndex == BatteryChartView.SELECTED_INDEX_INVALID ? BatteryChartView.SELECTED_INDEX_ALL : mTrapezoidIndex; + if (mBatteryChartView != null) { + mBatteryChartView.setLevels(mBatteryHistoryLevels); + } refreshUi(refreshIndex, /*isForce=*/ true); } @@ -185,12 +211,89 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll || (mTrapezoidIndex == trapezoidIndex && !isForce)) { return false; } - mTrapezoidIndex = trapezoidIndex; Log.d(TAG, String.format("refreshUi: index=%d batteryIndexedMap.size=%d", mTrapezoidIndex, mBatteryIndexedMap.size())); + + mTrapezoidIndex = trapezoidIndex; + mHandler.post(() -> { + removeAndCacheAllPrefs(); + addAllPreferences(); + }); return true; } + private void addAllPreferences() { + final List entries = + mBatteryIndexedMap.get(Integer.valueOf(mTrapezoidIndex)); + if (entries == null) { + Log.w(TAG, "cannot find BatteryDiffEntry for:" + mTrapezoidIndex); + return; + } + // Separates data into two groups and sort them individually. + final List appEntries = new ArrayList<>(); + final List systemEntries = new ArrayList<>(); + entries.forEach(entry -> { + if (entry.isSystemEntry()) { + systemEntries.add(entry); + } else { + appEntries.add(entry); + } + }); + Collections.sort(appEntries, BatteryDiffEntry.COMPARATOR); + Collections.sort(systemEntries, BatteryDiffEntry.COMPARATOR); + Log.d(TAG, String.format("addAllPreferences() app=%d system=%d", + appEntries.size(), systemEntries.size())); + addPreferenceToScreen(appEntries); + addPreferenceToScreen(systemEntries); + } + + @VisibleForTesting + void addPreferenceToScreen(List entries) { + if (mAppListPrefGroup == null || entries.isEmpty()) { + return; + } + int prefIndex = mAppListPrefGroup.getPreferenceCount(); + for (BatteryDiffEntry entry : entries) { + final String appLabel = entry.getAppLabel(); + final Drawable appIcon = entry.getAppIcon(); + if (TextUtils.isEmpty(appLabel) || appIcon == null) { + Log.w(TAG, "cannot find app resource:" + entry.mBatteryHistEntry); + continue; + } + final String prefKey = entry.mBatteryHistEntry.getKey(); + PowerGaugePreference pref = + (PowerGaugePreference) mPreferenceCache.get(prefKey); + // Creates new innstance if cached preference is not found. + if (pref == null) { + pref = new PowerGaugePreference(mPrefContext); + pref.setKey(prefKey); + mPreferenceCache.put(prefKey, pref); + } + pref.setIcon(appIcon); + pref.setTitle(appLabel); + pref.setOrder(prefIndex); + pref.setPercent(entry.getPercentOfTotal()); + mAppListPrefGroup.addPreference(pref); + prefIndex++; + } + } + + private void removeAndCacheAllPrefs() { + if (mAppListPrefGroup == null + || mAppListPrefGroup.getPreferenceCount() == 0) { + return; + } + final int prefsCount = mAppListPrefGroup.getPreferenceCount(); + for (int index = 0; index < prefsCount; index++) { + final Preference pref = mAppListPrefGroup.getPreference(index); + if (TextUtils.isEmpty(pref.getKey())) { + continue; + } + mPreferenceCache.put(pref.getKey(), pref); + } + mAppListPrefGroup.removeAll(); + } + private static String utcToLocalTime(long[] timestamps) { final StringBuilder builder = new StringBuilder(); for (int index = 0; index < timestamps.length; index++) { diff --git a/src/com/android/settings/fuelgauge/BatteryDiffEntry.java b/src/com/android/settings/fuelgauge/BatteryDiffEntry.java index 7c27d7de3a0..652b54c8b2d 100644 --- a/src/com/android/settings/fuelgauge/BatteryDiffEntry.java +++ b/src/com/android/settings/fuelgauge/BatteryDiffEntry.java @@ -34,7 +34,7 @@ import java.util.Locale; import java.util.Map; /** A container class to carry battery data in a specific time slot. */ -public final class BatteryDiffEntry { +public class BatteryDiffEntry { private static final String TAG = "BatteryDiffEntry"; static Locale sCurrentLocale = null; diff --git a/src/com/android/settings/fuelgauge/BatteryHistEntry.java b/src/com/android/settings/fuelgauge/BatteryHistEntry.java index 9611de18ec0..41fd55fcffb 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistEntry.java +++ b/src/com/android/settings/fuelgauge/BatteryHistEntry.java @@ -21,7 +21,7 @@ import java.time.Duration; import java.util.TimeZone; /** A container class to carry data from {@link ContentValues}. */ -public final class BatteryHistEntry { +public class BatteryHistEntry { private static final String TAG = "BatteryHistEntry"; /** Keys for accessing {@link ContentValues} or {@link Cursor}. */ diff --git a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java index 96e5011b7c7..99a72abf013 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java +++ b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java @@ -57,6 +57,7 @@ public class PowerUsageAdvanced extends PowerUsageBase { final BatteryHistoryLoaderCallbacks mBatteryHistoryLoaderCallbacks = new BatteryHistoryLoaderCallbacks(); + private boolean mIsChartDataLoaded = false; private boolean mIsChartGraphEnabled = false; private PowerUsageFeatureProvider mPowerUsageFeatureProvider; private BatteryChartPreferenceController mBatteryChartPreferenceController; @@ -145,10 +146,11 @@ public class PowerUsageAdvanced extends PowerUsageBase { final Bundle bundle = new Bundle(); bundle.putInt(KEY_REFRESH_TYPE, refreshType); // Uses customized battery history loader if chart design is enabled. - if (mIsChartGraphEnabled) { + if (mIsChartGraphEnabled && !mIsChartDataLoaded) { + mIsChartDataLoaded = true; getLoaderManager().restartLoader(LOADER_BATTERY_USAGE_STATS, bundle, - mBatteryHistoryLoaderCallbacks); - } else { + mBatteryHistoryLoaderCallbacks); + } else if (!mIsChartGraphEnabled) { super.restartBatteryStatsLoader(refreshType); } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java index 7d649608861..66ef2d207f9 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java @@ -18,14 +18,18 @@ package com.android.settings.fuelgauge; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.content.ContentValues; import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; import androidx.preference.PreferenceGroup; @@ -55,9 +59,13 @@ public final class BatteryChartPreferenceControllerTest { @Mock private SettingsActivity mSettingsActivity; @Mock private PreferenceGroup mAppListGroup; @Mock private PackageManager mPackageManager; + @Mock private Drawable mDrawable; + @Mock private BatteryHistEntry mBatteryHistEntry; @Mock private BatteryChartView mBatteryChartView; + @Mock private PowerGaugePreference mPowerGaugePreference; private Context mContext; + private BatteryDiffEntry mBatteryDiffEntry; private BatteryChartPreferenceController mBatteryChartPreferenceController; @Before @@ -71,6 +79,13 @@ public final class BatteryChartPreferenceControllerTest { mBatteryChartPreferenceController.mPrefContext = mContext; mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup; mBatteryChartPreferenceController.mBatteryChartView = mBatteryChartView; + mBatteryDiffEntry = new BatteryDiffEntry( + mContext, + /*foregroundUsageTimeInMs=*/ 1, + /*backgroundUsageTimeInMs=*/ 2, + /*consumePower=*/ 3, + mBatteryHistEntry); + mBatteryDiffEntry = spy(mBatteryDiffEntry); // Adds fake testing data. BatteryDiffEntry.sResourceCache.put( "fakeBatteryDiffEntryKey", @@ -99,6 +114,19 @@ public final class BatteryChartPreferenceControllerTest { assertThat(BatteryDiffEntry.sResourceCache).isNotEmpty(); } + @Test + public void testOnDestroy_clearPreferenceCache() { + final String prefKey = "preference fake key"; + // Ensures the testing environment is correct. + mBatteryChartPreferenceController.mPreferenceCache.put( + prefKey, mPowerGaugePreference); + assertThat(mBatteryChartPreferenceController.mPreferenceCache).hasSize(1); + + mBatteryChartPreferenceController.onDestroy(); + // Verifies the result after onDestroy. + assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty(); + } + @Test public void testSetBatteryHistoryMap_createExpectedKeysAndLevels() { mBatteryChartPreferenceController.setBatteryHistoryMap( @@ -176,11 +204,70 @@ public final class BatteryChartPreferenceControllerTest { mBatteryChartPreferenceController.setBatteryHistoryMap( createBatteryHistoryMap(/*size=*/ 25)); - assertThat(mBatteryChartPreferenceController.mTrapezoidIndex) .isEqualTo(BatteryChartView.SELECTED_INDEX_ALL); } + @Test + public void testRemoveAndCacheAllPrefs_emptyContent_ignoreRemoveAll() { + final int trapezoidIndex = 1; + doReturn(0).when(mAppListGroup).getPreferenceCount(); + + mBatteryChartPreferenceController.refreshUi( + trapezoidIndex, /*isForce=*/ true); + verify(mAppListGroup, never()).removeAll(); + } + + @Test + public void testRemoveAndCacheAllPrefs_buildCacheAndRemoveAllPreference() { + final int trapezoidIndex = 1; + final String prefKey = "preference fake key"; + doReturn(1).when(mAppListGroup).getPreferenceCount(); + doReturn(mPowerGaugePreference).when(mAppListGroup).getPreference(0); + doReturn(prefKey).when(mPowerGaugePreference).getKey(); + // Ensures the testing data is correct. + assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty(); + + mBatteryChartPreferenceController.refreshUi( + trapezoidIndex, /*isForce=*/ true); + + assertThat(mBatteryChartPreferenceController.mPreferenceCache.get(prefKey)) + .isEqualTo(mPowerGaugePreference); + verify(mAppListGroup).removeAll(); + } + + @Test + public void testAddPreferenceToScreen_emptyContent_ignoreAddPreference() { + mBatteryChartPreferenceController.addPreferenceToScreen( + new ArrayList()); + verify(mAppListGroup, never()).addPreference(any()); + } + + @Test + public void testAddPreferenceToScreen_addPreferenceIntoScreen() { + final String prefKey = "preference fake key"; + final String appLabel = "fake app label"; + doReturn(1).when(mAppListGroup).getPreferenceCount(); + doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); + doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel(); + doReturn(prefKey).when(mBatteryHistEntry).getKey(); + + mBatteryChartPreferenceController.addPreferenceToScreen( + Arrays.asList(mBatteryDiffEntry)); + + // Verifies the preference cache. + final PowerGaugePreference pref = + (PowerGaugePreference) mBatteryChartPreferenceController.mPreferenceCache + .get(prefKey); + assertThat(pref).isNotNull(); + // Verifies the added preference configuration. + verify(mAppListGroup).addPreference(pref); + assertThat(pref.getKey()).isEqualTo(prefKey); + assertThat(pref.getTitle()).isEqualTo(appLabel); + assertThat(pref.getIcon()).isEqualTo(mDrawable); + assertThat(pref.getOrder()).isEqualTo(1); + } + private Map> createBatteryHistoryMap(int size) { final Map> batteryHistoryMap = new HashMap<>(); for (int index = 0; index < size; index++) {