diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java index ea56c0991a4..fc0137b4f28 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java @@ -52,10 +52,10 @@ import com.google.common.base.Objects; import java.util.ArrayList; import java.util.Calendar; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; @@ -397,12 +397,23 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll return true; } - private PowerAnomalyEvent getHighestScoreAnomalyEvent(PowerAnomalyEventList anomalyEventList) { + @VisibleForTesting + PowerAnomalyEvent getHighestScoreAnomalyEvent(PowerAnomalyEventList anomalyEventList) { if (anomalyEventList == null || anomalyEventList.getPowerAnomalyEventsCount() == 0) { return null; } - return Collections.max(anomalyEventList.getPowerAnomalyEventsList(), - Comparator.comparing(PowerAnomalyEvent::getScore)); + final Set dismissedPowerAnomalyKeys = + DatabaseUtils.getDismissedPowerAnomalyKeys(mContext); + Log.d(TAG, "dismissedPowerAnomalyKeys = " + dismissedPowerAnomalyKeys); + + final PowerAnomalyEvent highestScoreEvent = anomalyEventList.getPowerAnomalyEventsList() + .stream() + .filter(event -> event.hasKey() + && !dismissedPowerAnomalyKeys.contains(event.getKey().name())) + .max(Comparator.comparing(PowerAnomalyEvent::getScore)) + .orElse(null); + Log.d(TAG, "highestScoreAnomalyEvent = " + highestScoreEvent); + return highestScoreEvent; } private boolean refreshUiWithNoLevelDataCase() { diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java index 9657101b59a..2d530bd3731 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java @@ -50,6 +50,7 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic private final MetricsFeatureProvider mMetricsFeatureProvider; private String mAnomalyEventId; + private PowerAnomalyKey mPowerAnomalyKey; @VisibleForTesting CharSequence mMainButtonLabel; @@ -69,6 +70,7 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic final FeatureFactory featureFactory = FeatureFactory.getFeatureFactory(); mPowerUsageFeatureProvider = featureFactory.getPowerUsageFeatureProvider(); mMetricsFeatureProvider = featureFactory.getMetricsFeatureProvider(); + mPowerAnomalyKey = null; } /** @@ -98,6 +100,13 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic } } + /** + * Sets the power anomaly key of battery tips card. + */ + public void setPowerAnomalyKey(final PowerAnomalyKey powerAnomalyKey) { + mPowerAnomalyKey = powerAnomalyKey; + } + /** * Sets the info of target fragment launched by main button. */ @@ -133,6 +142,9 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic setVisible(false); mMetricsFeatureProvider.action( getContext(), SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, mAnomalyEventId); + if (mPowerAnomalyKey != null) { + DatabaseUtils.setDismissedPowerAnomalyKeys(getContext(), mPowerAnomalyKey.name()); + } } } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java index 0a9347c53b7..96c1330363d 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java @@ -108,8 +108,9 @@ public class BatteryTipsController extends BasePreferenceController { } // Get card preference strings and navigate fragment info - final int resourceIndex = powerAnomalyEvent.hasKey() - ? powerAnomalyEvent.getKey().getNumber() : -1; + final PowerAnomalyKey powerAnomalyKey = powerAnomalyEvent.hasKey() + ? powerAnomalyEvent.getKey() : null; + final int resourceIndex = powerAnomalyKey != null ? powerAnomalyKey.getNumber() : -1; String titleString = getString(powerAnomalyEvent, WarningBannerInfo::getTitleString, WarningItemInfo::getTitleString, R.array.power_anomaly_titles, resourceIndex); @@ -134,6 +135,7 @@ public class BatteryTipsController extends BasePreferenceController { // Updated card preference and main button fragment launcher mCardPreference.setAnomalyEventId(powerAnomalyEvent.getEventId()); + mCardPreference.setPowerAnomalyKey(powerAnomalyKey); mCardPreference.setTitle(titleString); mCardPreference.setMainButtonLabel(mainBtnString); mCardPreference.setDismissButtonLabel(dismissBtnString); diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java index 48a39f4dd2b..ece996014c3 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java @@ -74,6 +74,7 @@ public final class BatteryUsageDataLoader { context, DatabaseUtils.KEY_LAST_LOAD_FULL_CHARGE_TIME); DatabaseUtils.sendBatteryEventData(context, ConvertUtils.convertToBatteryEvent( currentTime, BatteryEventType.FULL_CHARGED, 100)); + DatabaseUtils.removeDismissedPowerAnomalyKeys(context); } // Uploads the BatteryEntry data into database. diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java index 465afbe8c59..0ed90ae8753 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.ArraySet; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -53,6 +54,7 @@ import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -69,6 +71,7 @@ public final class DatabaseUtils { static final String KEY_LAST_LOAD_FULL_CHARGE_TIME = "last_load_full_charge_time"; static final String KEY_LAST_UPLOAD_FULL_CHARGE_TIME = "last_upload_full_charge_time"; static final String KEY_LAST_USAGE_SOURCE = "last_usage_source"; + static final String KEY_DISMISSED_POWER_ANOMALY_KEYS = "dismissed_power_anomaly_keys"; /** An authority name of the battery content provider. */ public static final String AUTHORITY = "com.android.settings.battery.usage.provider"; @@ -636,6 +639,8 @@ public final class DatabaseUtils { KEY_LAST_LOAD_FULL_CHARGE_TIME); writeString(context, writer, "LastUploadFullChargeTime", KEY_LAST_UPLOAD_FULL_CHARGE_TIME); + writeString(context, writer, "DismissedPowerAnomalyKeys", + KEY_DISMISSED_POWER_ANOMALY_KEYS); } static SharedPreferences getSharedPreferences(Context context) { @@ -674,6 +679,32 @@ public final class DatabaseUtils { return usageSource; } + static void removeDismissedPowerAnomalyKeys(Context context) { + final SharedPreferences sharedPreferences = getSharedPreferences(context); + if (sharedPreferences != null + && sharedPreferences.contains(KEY_DISMISSED_POWER_ANOMALY_KEYS)) { + sharedPreferences.edit().remove(KEY_DISMISSED_POWER_ANOMALY_KEYS).apply(); + } + } + + static Set getDismissedPowerAnomalyKeys(Context context) { + final SharedPreferences sharedPreferences = getSharedPreferences(context); + return sharedPreferences != null + ? sharedPreferences.getStringSet(KEY_DISMISSED_POWER_ANOMALY_KEYS, new ArraySet<>()) + : new ArraySet<>(); + } + + static void setDismissedPowerAnomalyKeys(Context context, String dismissedPowerAnomalyKey) { + final SharedPreferences sharedPreferences = getSharedPreferences(context); + if (sharedPreferences != null) { + final Set dismissedPowerAnomalyKeys = getDismissedPowerAnomalyKeys(context); + dismissedPowerAnomalyKeys.add(dismissedPowerAnomalyKey); + sharedPreferences.edit() + .putStringSet(KEY_DISMISSED_POWER_ANOMALY_KEYS, dismissedPowerAnomalyKeys) + .apply(); + } + } + static void recordDateTime(Context context, String preferenceKey) { final SharedPreferences sharedPreferences = getSharedPreferences(context); if (sharedPreferences != null) { 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 a54d4c174d7..786a529814d 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java @@ -46,6 +46,7 @@ import android.view.ViewPropertyAnimator; import android.widget.LinearLayout; import com.android.settings.SettingsActivity; +import com.android.settings.testutils.BatteryTestUtils; import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; @@ -407,6 +408,57 @@ public final class BatteryChartPreferenceControllerTest { assertThat(totalHour).isEqualTo(59); } + @Test + public void getHighestScoreAnomalyEvent_withEmptyOrNullList_getNull() { + assertThat(mBatteryChartPreferenceController.getHighestScoreAnomalyEvent(null)) + .isEqualTo(null); + assertThat(mBatteryChartPreferenceController.getHighestScoreAnomalyEvent( + BatteryTestUtils.createEmptyPowerAnomalyEventList())) + .isEqualTo(null); + } + + @Test + public void getHighestScoreAnomalyEvent_withoutDismissed_getHighestScoreEvent() { + final PowerAnomalyEventList eventList = + BatteryTestUtils.createNonEmptyPowerAnomalyEventList(); + + final PowerAnomalyEvent highestScoreEvent = + mBatteryChartPreferenceController.getHighestScoreAnomalyEvent(eventList); + + assertThat(highestScoreEvent) + .isEqualTo(BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent()); + } + + @Test + public void getHighestScoreAnomalyEvent_withBrightnessDismissed_getScreenTimeout() { + final PowerAnomalyEventList eventList = + BatteryTestUtils.createNonEmptyPowerAnomalyEventList(); + DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext); + DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, PowerAnomalyKey.KEY_BRIGHTNESS.name()); + + final PowerAnomalyEvent highestScoreEvent = + mBatteryChartPreferenceController.getHighestScoreAnomalyEvent(eventList); + + assertThat(highestScoreEvent) + .isEqualTo(BatteryTestUtils.createScreenTimeoutAnomalyEvent()); + } + + @Test + public void getHighestScoreAnomalyEvent_withAllDismissed_getNull() { + final PowerAnomalyEventList eventList = + BatteryTestUtils.createNonEmptyPowerAnomalyEventList(); + DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext); + for (PowerAnomalyKey key : PowerAnomalyKey.values()) { + DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, key.name()); + } + + final PowerAnomalyEvent highestScoreEvent = + mBatteryChartPreferenceController.getHighestScoreAnomalyEvent(eventList); + + assertThat(highestScoreEvent).isEqualTo(null); + } + + private static Long generateTimestamp(int index) { // "2021-04-23 07:00:00 UTC" + index hours return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS; diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java index 1fe6f4a90c8..ac67dfdf86b 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java @@ -83,6 +83,7 @@ public final class BatteryTipsCardPreferenceTest { mBatteryTipsController.handleBatteryTipsCardUpdated(adaptiveBrightnessAnomaly); mBatteryTipsCardPreference.onClick(mFakeView); + assertThat(mBatteryTipsCardPreference.isVisible()).isEqualTo(false); verify(mContext).startActivity(any(Intent.class)); final Intent intent = captor.getValue(); assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) @@ -94,15 +95,21 @@ public final class BatteryTipsCardPreferenceTest { } @Test - public void onClick_dismissBtn_metricsLogged() { + public void onClick_dismissBtn_cardDismissAndLogged() { PowerAnomalyEvent screenTimeoutAnomaly = BatteryTestUtils.createScreenTimeoutAnomalyEvent(); + DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext); when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true); when(mFakeView.getId()).thenReturn(R.id.dismiss_button); mBatteryTipsController.handleBatteryTipsCardUpdated(screenTimeoutAnomaly); mBatteryTipsCardPreference.onClick(mFakeView); + assertThat(mBatteryTipsCardPreference.isVisible()).isEqualTo(false); + assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext).size()) + .isEqualTo(1); + assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext)) + .contains(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name()); verify(mFeatureFactory.metricsFeatureProvider).action( mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, "ScreenTimeoutAnomaly"); } diff --git a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java index bf49c36dddd..136431dfa19 100644 --- a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java @@ -208,12 +208,21 @@ public class BatteryTestUtils { return PowerAnomalyEventList.getDefaultInstance(); } + /** Create an non-empty power anomaly event list proto. */ + public static PowerAnomalyEventList createNonEmptyPowerAnomalyEventList() { + return PowerAnomalyEventList.newBuilder() + .addPowerAnomalyEvents(0, createAdaptiveBrightnessAnomalyEvent()) + .addPowerAnomalyEvents(1, createScreenTimeoutAnomalyEvent()) + .build(); + } + /** Create a power anomaly event proto of adaptive brightness. */ public static PowerAnomalyEvent createAdaptiveBrightnessAnomalyEvent() { return PowerAnomalyEvent.newBuilder() .setEventId("BrightnessAnomaly") .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER) .setKey(PowerAnomalyKey.KEY_BRIGHTNESS) + .setScore(1.2f) .setWarningBannerInfo(WarningBannerInfo.newBuilder() .setMainButtonDestination(DisplaySettings.class.getName()) .setMainButtonSourceMetricsCategory(SettingsEnums.DISPLAY) @@ -228,6 +237,7 @@ public class BatteryTestUtils { .setEventId("ScreenTimeoutAnomaly") .setType(PowerAnomalyType.TYPE_SETTINGS_BANNER) .setKey(PowerAnomalyKey.KEY_SCREEN_TIMEOUT) + .setScore(1.1f) .setWarningBannerInfo(WarningBannerInfo.newBuilder() .setMainButtonDestination(ScreenTimeoutSettings.class.getName()) .setMainButtonSourceMetricsCategory(SettingsEnums.SCREEN_TIMEOUT)