Snap for 10804332 from fa0371ae8f
to udc-qpr1-release
Change-Id: I5e355ce0be54f4b9021d48d2372e37ec2b5d9618
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:text="@string/battery_usage_chart_graph_hint_last_full_charge" />
|
||||
@@ -40,7 +41,7 @@
|
||||
<com.android.settings.fuelgauge.batteryusage.BatteryChartView
|
||||
android:id="@+id/daily_battery_chart"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="170dp"
|
||||
android:layout_height="@dimen/chartview_layout_height"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:visibility="gone"
|
||||
android:contentDescription="@string/daily_battery_usage_chart"
|
||||
@@ -50,7 +51,7 @@
|
||||
<com.android.settings.fuelgauge.batteryusage.BatteryChartView
|
||||
android:id="@+id/hourly_battery_chart"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="170dp"
|
||||
android:layout_height="@dimen/chartview_layout_height"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:visibility="visible"
|
||||
android:contentDescription="@string/hourly_battery_usage_chart"
|
||||
|
@@ -369,6 +369,12 @@
|
||||
<dimen name="chartview_text_padding">6dp</dimen>
|
||||
<dimen name="chartview_divider_width">1dp</dimen>
|
||||
<dimen name="chartview_divider_height">4dp</dimen>
|
||||
<dimen name="chartview_transom_width">4dp</dimen>
|
||||
<dimen name="chartview_transom_radius">4dp</dimen>
|
||||
<dimen name="chartview_transom_icon_size">12dp</dimen>
|
||||
<dimen name="chartview_transom_padding_top">2dp</dimen>
|
||||
<dimen name="chartview_transom_layout_height">12dp</dimen>
|
||||
<dimen name="chartview_layout_height">182dp</dimen>
|
||||
<dimen name="chartview_trapezoid_radius">5dp</dimen>
|
||||
<dimen name="chartview_trapezoid_margin_start">1dp</dimen>
|
||||
<dimen name="chartview_trapezoid_margin_bottom">2dp</dimen>
|
||||
|
@@ -9934,7 +9934,7 @@
|
||||
<!-- Title text for edge to edge navigation [CHAR LIMIT=60] -->
|
||||
<string name="edge_to_edge_navigation_title">Gesture navigation</string>
|
||||
<!-- Summary text for edge to edge navigation [CHAR LIMIT=NONE] -->
|
||||
<string name="edge_to_edge_navigation_summary">To go Home, swipe up from the bottom of the screen. To switch apps, swipe up from the bottom, hold, then release. To go back, swipe from either the left or right edge.</string>
|
||||
<string name="edge_to_edge_navigation_summary">To go home, swipe up from the bottom of the screen. To switch apps, swipe up from the bottom, hold, then release. To go back, swipe from either the left or right edge.</string>
|
||||
|
||||
<!-- Title text for 3-button navigation [CHAR LIMIT=60] -->
|
||||
<string name="legacy_navigation_title">3-button navigation</string>
|
||||
@@ -12144,7 +12144,9 @@
|
||||
<!-- [CHAR LIMIT=60] Aspect ratio title setting to choose app aspect ratio -->
|
||||
<string name="aspect_ratio_title">Aspect ratio</string>
|
||||
<!-- [CHAR LIMIT=NONE] Aspect ratio setting summary to choose aspect ratio for apps unoptimized for device -->
|
||||
<string name="aspect_ratio_summary">Choose an aspect ratio to view this app if it hasn\'t been designed to fit your <xliff:g id="device_name">%1$s</xliff:g></string>
|
||||
<string name="aspect_ratio_summary">Try a new aspect ratio to view this app if it hasn\'t been designed to fit your <xliff:g id="device_name">%1$s</xliff:g></string>
|
||||
<!-- [CHAR LIMIT=NONE] Aspect ratio setting main summary on page to choose aspect ratio for apps unoptimized for device -->
|
||||
<string name="aspect_ratio_main_summary">Try a new aspect ratio to view this app if it hasn\'t been designed to fit your <xliff:g id="device_name">%1$s</xliff:g>. Some apps may not be optimized for certain aspect ratios.</string>
|
||||
<!-- [CHAR LIMIT=NONE] Aspect ratio suggested apps filter label -->
|
||||
<string name="user_aspect_ratio_suggested_apps_label">Suggested apps</string>
|
||||
<!-- [CHAR LIMIT=14] Filter label for apps that have user aspect ratio changed -->
|
||||
@@ -12164,7 +12166,7 @@
|
||||
<!-- [CHAR LIMIT=NONE] 4:3 aspect ratio entry -->
|
||||
<string name="user_aspect_ratio_4_3">4:3</string>
|
||||
<!-- [CHAR LIMIT=NONE] Warning description for app info aspect ratio page -->
|
||||
<string name="app_aspect_ratio_footer">The app will restart when you change aspect ratio. You may lose unsaved changes.</string>
|
||||
<string name="app_aspect_ratio_footer">The app will restart when you change aspect ratio. You may lose unsaved changes. Some apps may not be optimized for certain aspect ratios.</string>
|
||||
|
||||
|
||||
<!-- Accessibility label for fingerprint sensor [CHAR LIMIT=NONE] -->
|
||||
|
@@ -79,18 +79,6 @@
|
||||
android:key="dashboard_tile_placeholder"
|
||||
android:order="10"/>
|
||||
|
||||
<Preference
|
||||
android:key="aspect_ratio_apps"
|
||||
android:title="@string/aspect_ratio_title"
|
||||
android:summary="@string/summary_placeholder"
|
||||
android:order="14"
|
||||
settings:controller="com.android.settings.applications.appcompat.UserAspectRatioAppsPreferenceController"
|
||||
android:fragment="com.android.settings.applications.manageapplications.ManageApplications">
|
||||
<extra android:name="classname"
|
||||
android:value="com.android.settings.Settings$UserAspectRatioAppListActivity"/>
|
||||
<intent android:action="android.settings.MANAGE_USER_ASPECT_RATIO_SETTINGS"/>
|
||||
</Preference>
|
||||
|
||||
<Preference
|
||||
android:key="hibernated_apps"
|
||||
android:title="@string/unused_apps"
|
||||
@@ -119,4 +107,23 @@
|
||||
android:title="@string/special_access"
|
||||
android:order="20"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="advanced_category"
|
||||
android:title="@string/advanced_apps"
|
||||
android:order="21"
|
||||
android:visibility="gone"
|
||||
settings:searchable="false"/>
|
||||
|
||||
<Preference
|
||||
android:key="aspect_ratio_apps"
|
||||
android:title="@string/aspect_ratio_title"
|
||||
android:summary="@string/summary_placeholder"
|
||||
android:order="22"
|
||||
settings:controller="com.android.settings.applications.appcompat.UserAspectRatioAppsPreferenceController"
|
||||
android:fragment="com.android.settings.applications.manageapplications.ManageApplications">
|
||||
<extra android:name="classname"
|
||||
android:value="com.android.settings.Settings$UserAspectRatioAppListActivity"/>
|
||||
<intent android:action="android.settings.MANAGE_USER_ASPECT_RATIO_SETTINGS"/>
|
||||
</Preference>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
@@ -57,5 +57,13 @@
|
||||
settings:keywords="@string/keywords_factory_data_reset"
|
||||
settings:userRestriction="no_factory_reset"
|
||||
settings:useAdminDisabledSummary="true"
|
||||
settings:controller="com.android.settings.system.FactoryResetPreferenceController"
|
||||
android:fragment="com.android.settings.MainClear" />
|
||||
|
||||
<Preference
|
||||
android:key="factory_reset_demo_user"
|
||||
android:title="@string/main_clear_title"
|
||||
settings:keywords="@string/keywords_factory_data_reset"
|
||||
settings:controller="com.android.settings.system.FactoryResetDemoUserPreferenceController"
|
||||
android:fragment="com.android.settings.MainClear" />
|
||||
</PreferenceScreen>
|
||||
|
@@ -569,7 +569,7 @@ public class MainClear extends InstrumentedFragment implements OnGlobalLayoutLis
|
||||
UserHandle.myUserId());
|
||||
if (disallow && !Utils.isDemoUser(context)) {
|
||||
return inflater.inflate(R.layout.main_clear_disallowed_screen, null);
|
||||
} else if (admin != null) {
|
||||
} else if (admin != null && !Utils.isDemoUser(context)) {
|
||||
new ActionDisabledByAdminDialogHelper(getActivity())
|
||||
.prepareDialogBuilder(UserManager.DISALLOW_FACTORY_RESET, admin)
|
||||
.setOnDismissListener(__ -> getActivity().finish())
|
||||
|
@@ -43,11 +43,6 @@ public interface PowerUsageFeatureProvider {
|
||||
*/
|
||||
boolean isBatteryTipsEnabled();
|
||||
|
||||
/**
|
||||
* Check whether the feedback card is enabled in the battery tips card
|
||||
*/
|
||||
boolean isBatteryTipsFeedbackEnabled();
|
||||
|
||||
/**
|
||||
* Returns a threshold (in milliseconds) for the minimal screen on time in battery usage list
|
||||
*/
|
||||
|
@@ -80,11 +80,6 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatteryTipsFeedbackEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBatteryUsageListScreenOnTimeThresholdInMs() {
|
||||
return 0;
|
||||
|
@@ -36,7 +36,6 @@ import androidx.preference.PreferenceScreen;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
@@ -44,7 +43,6 @@ import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnCreate;
|
||||
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
||||
import com.android.settingslib.core.lifecycle.events.OnPause;
|
||||
import com.android.settingslib.core.lifecycle.events.OnResume;
|
||||
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
||||
|
||||
@@ -52,17 +50,12 @@ import com.google.common.base.Objects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
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;
|
||||
|
||||
/** Controls the update for chart graph and the list items. */
|
||||
public class BatteryChartPreferenceController extends AbstractPreferenceController
|
||||
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy, OnPause,
|
||||
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy,
|
||||
OnSaveInstanceState, OnResume {
|
||||
private static final String TAG = "BatteryChartPreferenceController";
|
||||
private static final String PREFERENCE_KEY = "battery_chart";
|
||||
@@ -74,53 +67,17 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
private static final String KEY_DAILY_CHART_INDEX = "daily_chart_index";
|
||||
private static final String KEY_HOURLY_CHART_INDEX = "hourly_chart_index";
|
||||
|
||||
/**
|
||||
* A callback listener for battery usage is updated.
|
||||
* This happens when battery usage data is ready or the selected index is changed.
|
||||
*/
|
||||
public interface OnBatteryUsageUpdatedListener {
|
||||
/**
|
||||
* The callback function for battery usage is updated.
|
||||
* @param slotUsageData The battery usage diff data for the selected slot. This is used in
|
||||
* the app list.
|
||||
* @param slotTimestamp The selected slot timestamp information. This is used in the battery
|
||||
* usage breakdown category.
|
||||
* @param isAllUsageDataEmpty Whether all the battery usage data is null or empty. This is
|
||||
* used when showing the footer.
|
||||
*/
|
||||
void onBatteryUsageUpdated(
|
||||
BatteryDiffData slotUsageData, String slotTimestamp, boolean isAllUsageDataEmpty);
|
||||
/** A callback listener for the selected index is updated. */
|
||||
interface OnSelectedIndexUpdatedListener {
|
||||
/** The callback function for the selected index is updated. */
|
||||
void onSelectedIndexUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback listener for the device screen on time is updated.
|
||||
* This happens when screen on time data is ready or the selected index is changed.
|
||||
*/
|
||||
public interface OnScreenOnTimeUpdatedListener {
|
||||
/**
|
||||
* The callback function for the device screen on time is updated.
|
||||
* @param screenOnTime The selected slot device screen on time.
|
||||
* @param slotTimestamp The selected slot timestamp information.
|
||||
*/
|
||||
void onScreenOnTimeUpdated(Long screenOnTime, String slotTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback listener for the battery tips card is updated.
|
||||
* This happens when battery tips card is ready.
|
||||
*/
|
||||
public interface OnBatteryTipsUpdatedListener {
|
||||
/**
|
||||
* The callback function for the battery tips card is updated.
|
||||
* @param powerAnomalyEvent the power anomaly event with highest score
|
||||
*/
|
||||
void onBatteryTipsUpdated(PowerAnomalyEvent powerAnomalyEvent);
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
Context mPrefContext;
|
||||
@VisibleForTesting
|
||||
TextView mChartSummaryTextView;
|
||||
@VisibleForTesting
|
||||
BatteryChartView mDailyChartView;
|
||||
@VisibleForTesting
|
||||
BatteryChartView mHourlyChartView;
|
||||
@@ -129,27 +86,23 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
@VisibleForTesting
|
||||
int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
@VisibleForTesting
|
||||
Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
|
||||
int mDailyHighlightSlotIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||
@VisibleForTesting
|
||||
int mHourlyHighlightSlotIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||
|
||||
private boolean mIs24HourFormat;
|
||||
private View mBatteryChartViewGroup;
|
||||
private TextView mChartSummaryTextView;
|
||||
private BatteryChartViewModel mDailyViewModel;
|
||||
private List<BatteryChartViewModel> mHourlyViewModels;
|
||||
private OnBatteryUsageUpdatedListener mOnBatteryUsageUpdatedListener;
|
||||
private OnScreenOnTimeUpdatedListener mOnScreenOnTimeUpdatedListener;
|
||||
private OnBatteryTipsUpdatedListener mOnBatteryTipsUpdatedListener;
|
||||
private AtomicBoolean mIsAppResume = new AtomicBoolean(false);
|
||||
private OnSelectedIndexUpdatedListener mOnSelectedIndexUpdatedListener;
|
||||
|
||||
private final SettingsActivity mActivity;
|
||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
private final PowerUsageFeatureProvider mPowerUsageFeatureProvider;
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private final AnimatorListenerAdapter mHourlyChartFadeInAdapter =
|
||||
createHourlyChartAnimatorListenerAdapter(/*visible=*/ true);
|
||||
private final AnimatorListenerAdapter mHourlyChartFadeOutAdapter =
|
||||
createHourlyChartAnimatorListenerAdapter(/*visible=*/ false);
|
||||
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
@VisibleForTesting
|
||||
final DailyChartLabelTextGenerator mDailyChartLabelTextGenerator =
|
||||
@@ -165,8 +118,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
mIs24HourFormat = DateFormat.is24HourFormat(context);
|
||||
mMetricsFeatureProvider =
|
||||
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
||||
mPowerUsageFeatureProvider =
|
||||
FeatureFactory.getFactory(mContext).getPowerUsageFeatureProvider(context);
|
||||
if (lifecycle != null) {
|
||||
lifecycle.addObserver(this);
|
||||
}
|
||||
@@ -184,15 +135,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d",
|
||||
mDailyChartIndex, mHourlyChartIndex));
|
||||
}
|
||||
@Override
|
||||
public void onPause() {
|
||||
mIsAppResume.compareAndSet(/* expect= */ true, /* update= */ false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
mIsAppResume.compareAndSet(/* expect= */ false, /* update= */ true);
|
||||
mIs24HourFormat = DateFormat.is24HourFormat(mContext);
|
||||
mMetricsFeatureProvider.action(mPrefContext, SettingsEnums.OPEN_BATTERY_USAGE);
|
||||
}
|
||||
@@ -232,16 +177,16 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
return PREFERENCE_KEY;
|
||||
}
|
||||
|
||||
void setOnBatteryUsageUpdatedListener(OnBatteryUsageUpdatedListener listener) {
|
||||
mOnBatteryUsageUpdatedListener = listener;
|
||||
int getDailyChartIndex() {
|
||||
return mDailyChartIndex;
|
||||
}
|
||||
|
||||
void setOnScreenOnTimeUpdatedListener(OnScreenOnTimeUpdatedListener listener) {
|
||||
mOnScreenOnTimeUpdatedListener = listener;
|
||||
int getHourlyChartIndex() {
|
||||
return mHourlyChartIndex;
|
||||
}
|
||||
|
||||
void setOnBatteryTipsUpdatedListener(OnBatteryTipsUpdatedListener listener) {
|
||||
mOnBatteryTipsUpdatedListener = listener;
|
||||
void setOnSelectedIndexUpdatedListener(OnSelectedIndexUpdatedListener listener) {
|
||||
mOnSelectedIndexUpdatedListener = listener;
|
||||
}
|
||||
|
||||
void onBatteryLevelDataUpdate(final BatteryLevelData batteryLevelData) {
|
||||
@@ -276,13 +221,37 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
refreshUi();
|
||||
}
|
||||
|
||||
void onBatteryUsageMapUpdate(Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
|
||||
Log.d(TAG, "onBatteryUsageMapUpdate: " + batteryUsageMap);
|
||||
mBatteryUsageMap = batteryUsageMap;
|
||||
logScreenUsageTime();
|
||||
void onHighlightSlotIndexUpdate(int dailyHighlightSlotIndex, int hourlyHighlightSlotIndex) {
|
||||
if (mDailyHighlightSlotIndex == dailyHighlightSlotIndex
|
||||
&& mHourlyHighlightSlotIndex == hourlyHighlightSlotIndex) {
|
||||
return;
|
||||
}
|
||||
mDailyHighlightSlotIndex = dailyHighlightSlotIndex;
|
||||
mHourlyHighlightSlotIndex = hourlyHighlightSlotIndex;
|
||||
refreshUi();
|
||||
}
|
||||
|
||||
void selectHighlightSlotIndex() {
|
||||
if (mDailyHighlightSlotIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID
|
||||
|| mHourlyHighlightSlotIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID) {
|
||||
return;
|
||||
}
|
||||
if (mDailyHighlightSlotIndex == mDailyChartIndex
|
||||
&& mHourlyHighlightSlotIndex == mHourlyChartIndex) {
|
||||
return;
|
||||
}
|
||||
mDailyChartIndex = mDailyHighlightSlotIndex;
|
||||
mHourlyChartIndex = mHourlyHighlightSlotIndex;
|
||||
Log.d(TAG, String.format("onDailyChartSelect:%d, onHourlyChartSelect:%d",
|
||||
mDailyChartIndex, mHourlyChartIndex));
|
||||
refreshUi();
|
||||
mHandler.post(() -> mDailyChartView.announceForAccessibility(
|
||||
getAccessibilityAnnounceMessage()));
|
||||
if (mOnSelectedIndexUpdatedListener != null) {
|
||||
mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
void setBatteryChartView(@NonNull final BatteryChartView dailyChartView,
|
||||
@NonNull final BatteryChartView hourlyChartView) {
|
||||
final View parentView = (View) dailyChartView.getParent();
|
||||
@@ -319,6 +288,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
? SettingsEnums.ACTION_BATTERY_USAGE_DAILY_SHOW_ALL
|
||||
: SettingsEnums.ACTION_BATTERY_USAGE_DAILY_TIME_SLOT,
|
||||
mDailyChartIndex);
|
||||
if (mOnSelectedIndexUpdatedListener != null) {
|
||||
mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
|
||||
}
|
||||
});
|
||||
mHourlyChartView = hourlyChartView;
|
||||
mHourlyChartView.setOnSelectListener(trapezoidIndex -> {
|
||||
@@ -340,105 +312,37 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL
|
||||
: SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT,
|
||||
mHourlyChartIndex);
|
||||
if (mOnSelectedIndexUpdatedListener != null) {
|
||||
mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
|
||||
}
|
||||
});
|
||||
refreshUi();
|
||||
}
|
||||
|
||||
// Show empty hourly chart view only if there is no valid battery usage data.
|
||||
void showEmptyChart() {
|
||||
setChartSummaryVisible(true);
|
||||
mDailyChartView.setVisibility(View.GONE);
|
||||
mHourlyChartView.setVisibility(View.VISIBLE);
|
||||
mHourlyChartView.setViewModel(null);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean refreshUi() {
|
||||
void refreshUi() {
|
||||
if (mDailyChartView == null || mHourlyChartView == null) {
|
||||
// Chart views are not initialized.
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// When mDailyViewModel or mHourlyViewModels is null, there is no battery level data.
|
||||
// This is mainly in 2 cases:
|
||||
// 1) battery data is within 2 hours
|
||||
// 2) no battery data in the latest 7 days (power off >= 7 days)
|
||||
final boolean refreshUiResult = mDailyViewModel == null || mHourlyViewModels == null
|
||||
? refreshUiWithNoLevelDataCase()
|
||||
: refreshUiWithLevelDataCase();
|
||||
|
||||
if (!refreshUiResult) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mOnBatteryUsageUpdatedListener != null && mBatteryUsageMap != null
|
||||
&& mBatteryUsageMap.get(mDailyChartIndex) != null) {
|
||||
final BatteryDiffData slotUsageData =
|
||||
mBatteryUsageMap.get(mDailyChartIndex).get(mHourlyChartIndex);
|
||||
if (slotUsageData != null) {
|
||||
mOnScreenOnTimeUpdatedListener.onScreenOnTimeUpdated(
|
||||
slotUsageData.getScreenOnTime(),
|
||||
getSlotInformation());
|
||||
}
|
||||
mOnBatteryUsageUpdatedListener.onBatteryUsageUpdated(
|
||||
slotUsageData, getSlotInformation(), isBatteryUsageMapNullOrEmpty());
|
||||
|
||||
Log.d(TAG, "isBatteryTipsEnabled = "
|
||||
+ mPowerUsageFeatureProvider.isBatteryTipsEnabled());
|
||||
if (mOnBatteryTipsUpdatedListener != null) {
|
||||
mExecutor.execute(() -> {
|
||||
final PowerAnomalyEventList anomalyEventList = mPowerUsageFeatureProvider
|
||||
.detectSettingsAnomaly(mContext, /* displayDrain= */ 0);
|
||||
Log.d(TAG, "anomalyEventList = " + anomalyEventList);
|
||||
final PowerAnomalyEvent displayEvent =
|
||||
getHighestScoreAnomalyEvent(anomalyEventList);
|
||||
mHandler.post(() -> {
|
||||
if (mIsAppResume.get()) {
|
||||
mOnBatteryTipsUpdatedListener
|
||||
.onBatteryTipsUpdated(displayEvent);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
PowerAnomalyEvent getHighestScoreAnomalyEvent(PowerAnomalyEventList anomalyEventList) {
|
||||
if (anomalyEventList == null || anomalyEventList.getPowerAnomalyEventsCount() == 0) {
|
||||
return null;
|
||||
}
|
||||
final Set<String> 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() {
|
||||
setChartSummaryVisible(false);
|
||||
if (mBatteryUsageMap == null) {
|
||||
// There is no battery level data and battery usage data is not ready, wait for data
|
||||
// ready to refresh UI. Show nothing temporarily.
|
||||
if (mDailyViewModel == null || mHourlyViewModels == null) {
|
||||
setChartSummaryVisible(false);
|
||||
mDailyChartView.setVisibility(View.GONE);
|
||||
mHourlyChartView.setVisibility(View.GONE);
|
||||
mDailyChartView.setViewModel(null);
|
||||
mHourlyChartView.setViewModel(null);
|
||||
return false;
|
||||
} else if (mBatteryUsageMap
|
||||
.get(BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||
.get(BatteryChartViewModel.SELECTED_INDEX_ALL) == null) {
|
||||
// There is no battery level data and battery usage data, show an empty hourly chart
|
||||
// view.
|
||||
mDailyChartView.setVisibility(View.GONE);
|
||||
mHourlyChartView.setVisibility(View.VISIBLE);
|
||||
mHourlyChartView.setViewModel(null);
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean refreshUiWithLevelDataCase() {
|
||||
setChartSummaryVisible(true);
|
||||
// Gets valid battery level data.
|
||||
if (isBatteryLevelDataInOneDay()) {
|
||||
@@ -451,6 +355,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
}
|
||||
mDailyViewModel.setSelectedIndex(mDailyChartIndex);
|
||||
mDailyViewModel.setHighlightSlotIndex(mDailyHighlightSlotIndex);
|
||||
mDailyChartView.setViewModel(mDailyViewModel);
|
||||
}
|
||||
|
||||
@@ -465,17 +370,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
}
|
||||
hourlyViewModel.setSelectedIndex(mHourlyChartIndex);
|
||||
hourlyViewModel.setHighlightSlotIndex((mDailyChartIndex == mDailyHighlightSlotIndex)
|
||||
? mHourlyHighlightSlotIndex
|
||||
: BatteryChartViewModel.SELECTED_INDEX_INVALID);
|
||||
mHourlyChartView.setViewModel(hourlyViewModel);
|
||||
}
|
||||
|
||||
if (mBatteryUsageMap == null) {
|
||||
// Battery usage data is not ready, wait for data ready to refresh UI.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
String getSlotInformation() {
|
||||
if (mDailyViewModel == null || mHourlyViewModels == null) {
|
||||
// No data
|
||||
@@ -566,44 +467,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
};
|
||||
}
|
||||
|
||||
private void logScreenUsageTime() {
|
||||
if (mBatteryUsageMap == null) {
|
||||
return;
|
||||
}
|
||||
final BatteryDiffData allBatteryDiffData = mBatteryUsageMap.get(
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL).get(
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL);
|
||||
if (allBatteryDiffData == null) {
|
||||
return;
|
||||
}
|
||||
mMetricsFeatureProvider.action(
|
||||
mPrefContext,
|
||||
SettingsEnums.ACTION_BATTERY_USAGE_SCREEN_ON_TIME,
|
||||
(int) allBatteryDiffData.getScreenOnTime());
|
||||
mMetricsFeatureProvider.action(
|
||||
mPrefContext,
|
||||
SettingsEnums.ACTION_BATTERY_USAGE_FOREGROUND_USAGE_TIME,
|
||||
(int) getTotalForegroundUsageTime());
|
||||
}
|
||||
|
||||
private long getTotalForegroundUsageTime() {
|
||||
if (mBatteryUsageMap == null) {
|
||||
return 0;
|
||||
}
|
||||
final BatteryDiffData totalBatteryUsageDiffData =
|
||||
mBatteryUsageMap
|
||||
.get(BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||
.get(BatteryChartViewModel.SELECTED_INDEX_ALL);
|
||||
if (totalBatteryUsageDiffData == null) {
|
||||
return 0;
|
||||
}
|
||||
long totalValue = 0;
|
||||
for (final BatteryDiffEntry entry : totalBatteryUsageDiffData.getAppDiffEntryList()) {
|
||||
totalValue += entry.mForegroundUsageTimeInMs;
|
||||
}
|
||||
return totalValue;
|
||||
}
|
||||
|
||||
private boolean isBatteryLevelDataInOneDay() {
|
||||
return mHourlyViewModels != null && mHourlyViewModels.size() == 1;
|
||||
}
|
||||
@@ -614,19 +477,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
&& mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
}
|
||||
|
||||
private boolean isBatteryUsageMapNullOrEmpty() {
|
||||
if (mBatteryUsageMap == null) {
|
||||
return true;
|
||||
}
|
||||
BatteryDiffData allBatteryDiffData = mBatteryUsageMap
|
||||
.get(BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||
.get(BatteryChartViewModel.SELECTED_INDEX_ALL);
|
||||
// If all data is null or empty, each slot must be null or empty.
|
||||
return allBatteryDiffData == null
|
||||
|| (allBatteryDiffData.getAppDiffEntryList().isEmpty()
|
||||
&& allBatteryDiffData.getSystemDiffEntryList().isEmpty());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static int getTotalHours(final BatteryLevelData batteryLevelData) {
|
||||
if (batteryLevelData == null) {
|
||||
|
@@ -31,6 +31,7 @@ import android.graphics.CornerPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.util.ArraySet;
|
||||
import android.util.AttributeSet;
|
||||
@@ -90,6 +91,15 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
private int mTrapezoidHoverColor;
|
||||
private int mDefaultTextColor;
|
||||
private int mTextPadding;
|
||||
private int mTransomIconSize;
|
||||
private int mTransomTop;
|
||||
private int mTransomViewHeight;
|
||||
private int mTransomLineDefaultColor;
|
||||
private int mTransomLineSelectedColor;
|
||||
private float mTransomPadding;
|
||||
private Drawable mTransomIcon;
|
||||
private Paint mTransomLinePaint;
|
||||
private Paint mTransomSelectedSlotPaint;
|
||||
private Paint mDividerPaint;
|
||||
private Paint mTrapezoidPaint;
|
||||
private Paint mTextPaint;
|
||||
@@ -123,8 +133,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, String.format("setViewModel(): size: %d, selectedIndex: %d.",
|
||||
viewModel.size(), viewModel.selectedIndex()));
|
||||
Log.d(TAG, String.format(
|
||||
"setViewModel(): size: %d, selectedIndex: %d, getHighlightSlotIndex: %d",
|
||||
viewModel.size(), viewModel.selectedIndex(), viewModel.getHighlightSlotIndex()));
|
||||
mViewModel = viewModel;
|
||||
initializeAxisLabelsBounds();
|
||||
initializeTrapezoidSlots(viewModel.size() - 1);
|
||||
@@ -162,7 +173,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
mPercentageBounds[index]);
|
||||
}
|
||||
// Updates the indent configurations.
|
||||
mIndent.top = mPercentageBounds[0].height();
|
||||
mIndent.top = mPercentageBounds[0].height() + mTransomViewHeight;
|
||||
final int textWidth = mPercentageBounds[0].width() + mTextPadding;
|
||||
if (isRTL()) {
|
||||
mIndent.left = textWidth;
|
||||
@@ -196,6 +207,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
}
|
||||
drawVerticalDividers(canvas);
|
||||
drawTrapezoids(canvas);
|
||||
drawTransomLine(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -340,6 +352,40 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
resources.getDimensionPixelSize(R.dimen.chartview_trapezoid_radius)));
|
||||
// Initializes for drawing text information.
|
||||
mTextPadding = resources.getDimensionPixelSize(R.dimen.chartview_text_padding);
|
||||
// Initializes the padding top for drawing text information.
|
||||
mTransomViewHeight = resources.getDimensionPixelSize(
|
||||
R.dimen.chartview_transom_layout_height);
|
||||
}
|
||||
|
||||
private void initializeTransomPaint() {
|
||||
if (mTransomLinePaint != null && mTransomSelectedSlotPaint != null
|
||||
&& mTransomIcon != null) {
|
||||
return;
|
||||
}
|
||||
// Initializes the transom line paint.
|
||||
final Resources resources = getContext().getResources();
|
||||
final int transomLineWidth = resources.getDimensionPixelSize(
|
||||
R.dimen.chartview_transom_width);
|
||||
final int transomRadius = resources.getDimensionPixelSize(R.dimen.chartview_transom_radius);
|
||||
mTransomPadding = transomRadius * .5f;
|
||||
mTransomTop = resources.getDimensionPixelSize(R.dimen.chartview_transom_padding_top);
|
||||
mTransomLineDefaultColor = Utils.getDisabled(mContext, DIVIDER_COLOR);
|
||||
mTransomLineSelectedColor = resources.getColor(
|
||||
R.color.color_battery_anomaly_yellow_selector);
|
||||
final int slotHighlightColor = Utils.getDisabled(mContext, mTransomLineSelectedColor);
|
||||
mTransomIconSize = resources.getDimensionPixelSize(R.dimen.chartview_transom_icon_size);
|
||||
mTransomLinePaint = new Paint();
|
||||
mTransomLinePaint.setAntiAlias(true);
|
||||
mTransomLinePaint.setStyle(Paint.Style.STROKE);
|
||||
mTransomLinePaint.setStrokeWidth(transomLineWidth);
|
||||
mTransomLinePaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
mTransomLinePaint.setPathEffect(new CornerPathEffect(transomRadius));
|
||||
mTransomSelectedSlotPaint = new Paint();
|
||||
mTransomSelectedSlotPaint.setAntiAlias(true);
|
||||
mTransomSelectedSlotPaint.setColor(slotHighlightColor);
|
||||
mTransomSelectedSlotPaint.setStyle(Paint.Style.FILL);
|
||||
// Get the companion icon beside transom line
|
||||
mTransomIcon = getResources().getDrawable(R.drawable.ic_battery_tips_warning_icon);
|
||||
}
|
||||
|
||||
private void drawHorizontalDividers(Canvas canvas) {
|
||||
@@ -592,6 +638,50 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isHighlightSlotValid() {
|
||||
return mViewModel != null && mViewModel.getHighlightSlotIndex()
|
||||
!= BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||
}
|
||||
|
||||
private void drawTransomLine(Canvas canvas) {
|
||||
if (!isHighlightSlotValid()) {
|
||||
return;
|
||||
}
|
||||
initializeTransomPaint();
|
||||
// Draw the whole transom line and a warning icon
|
||||
mTransomLinePaint.setColor(mTransomLineDefaultColor);
|
||||
final int width = getWidth() - abs(mIndent.width());
|
||||
final float transomOffset = mTrapezoidHOffset + mDividerWidth * .5f + mTransomPadding;
|
||||
final float trapezoidBottom = getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth
|
||||
- mTrapezoidVOffset;
|
||||
canvas.drawLine(mIndent.left + transomOffset, mTransomTop,
|
||||
mIndent.left + width - transomOffset, mTransomTop,
|
||||
mTransomLinePaint);
|
||||
drawTransomIcon(canvas);
|
||||
// Draw selected segment of transom line and a highlight slot
|
||||
mTransomLinePaint.setColor(mTransomLineSelectedColor);
|
||||
final int index = mViewModel.getHighlightSlotIndex();
|
||||
final float startX = mTrapezoidSlots[index].mLeft;
|
||||
final float endX = mTrapezoidSlots[index].mRight;
|
||||
canvas.drawLine(startX + mTransomPadding, mTransomTop,
|
||||
endX - mTransomPadding, mTransomTop,
|
||||
mTransomLinePaint);
|
||||
canvas.drawRect(startX, mTransomTop, endX, trapezoidBottom,
|
||||
mTransomSelectedSlotPaint);
|
||||
}
|
||||
|
||||
private void drawTransomIcon(Canvas canvas) {
|
||||
if (mTransomIcon == null) {
|
||||
return;
|
||||
}
|
||||
final int left = isRTL()
|
||||
? mIndent.left - mTextPadding - mTransomIconSize
|
||||
: getWidth() - abs(mIndent.width()) + mTextPadding;
|
||||
mTransomIcon.setBounds(left, mTransomTop - mTransomIconSize / 2,
|
||||
left + mTransomIconSize, mTransomTop + mTransomIconSize / 2);
|
||||
mTransomIcon.draw(canvas);
|
||||
}
|
||||
|
||||
// Searches the corresponding trapezoid index from x location.
|
||||
private int getTrapezoidIndex(float x) {
|
||||
if (mTrapezoidSlots == null) {
|
||||
|
@@ -55,6 +55,7 @@ class BatteryChartViewModel {
|
||||
private final String[] mFullTexts;
|
||||
|
||||
private int mSelectedIndex = SELECTED_INDEX_ALL;
|
||||
private int mHighlightSlotIndex = SELECTED_INDEX_INVALID;
|
||||
|
||||
BatteryChartViewModel(@NonNull List<Integer> levels, @NonNull List<Long> timestamps,
|
||||
@NonNull AxisLabelPosition axisLabelPosition,
|
||||
@@ -106,6 +107,14 @@ class BatteryChartViewModel {
|
||||
mSelectedIndex = index;
|
||||
}
|
||||
|
||||
public int getHighlightSlotIndex() {
|
||||
return mHighlightSlotIndex;
|
||||
}
|
||||
|
||||
public void setHighlightSlotIndex(int index) {
|
||||
mHighlightSlotIndex = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mLevels, mTimestamps, mSelectedIndex, mAxisLabelPosition);
|
||||
|
@@ -20,6 +20,7 @@ import static com.android.settingslib.fuelgauge.BatteryStatus.BATTERY_LEVEL_UNKN
|
||||
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -69,6 +70,16 @@ public final class BatteryLevelData {
|
||||
return String.format(Locale.ENGLISH, "timestamps: %s; levels: %s",
|
||||
Objects.toString(mTimestamps), Objects.toString(mLevels));
|
||||
}
|
||||
|
||||
private int getIndexByTimestamps(long startTimestamp, long endTimestamp) {
|
||||
for (int index = 0; index < mTimestamps.size() - 1; index++) {
|
||||
if (mTimestamps.get(index) <= startTimestamp
|
||||
&& endTimestamp <= mTimestamps.get(index + 1)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,6 +111,18 @@ public final class BatteryLevelData {
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets daily and hourly index between start and end timestamps. */
|
||||
public Pair<Integer, Integer> getIndexByTimestamps(long startTimestamp, long endTimestamp) {
|
||||
final int dailyHighlightIndex =
|
||||
mDailyBatteryLevels.getIndexByTimestamps(startTimestamp, endTimestamp);
|
||||
final int hourlyHighlightIndex =
|
||||
(dailyHighlightIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID)
|
||||
? BatteryChartViewModel.SELECTED_INDEX_INVALID
|
||||
: mHourlyBatteryLevelsPerDay.get(dailyHighlightIndex)
|
||||
.getIndexByTimestamps(startTimestamp, endTimestamp);
|
||||
return Pair.create(dailyHighlightIndex, hourlyHighlightIndex);
|
||||
}
|
||||
|
||||
public PeriodBatteryLevelData getDailyBatteryLevels() {
|
||||
return mDailyBatteryLevels;
|
||||
}
|
||||
|
@@ -16,13 +16,10 @@
|
||||
|
||||
package com.android.settings.fuelgauge.batteryusage;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
@@ -32,9 +29,6 @@ import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
|
||||
@@ -47,11 +41,17 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
|
||||
|
||||
private static final String TAG = "BatteryTipsCardPreference";
|
||||
|
||||
private final PowerUsageFeatureProvider mPowerUsageFeatureProvider;
|
||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
interface OnConfirmListener {
|
||||
void onConfirm();
|
||||
}
|
||||
|
||||
private String mAnomalyEventId;
|
||||
private PowerAnomalyKey mPowerAnomalyKey;
|
||||
interface OnRejectListener {
|
||||
void onReject();
|
||||
}
|
||||
|
||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
private OnConfirmListener mOnConfirmListener;
|
||||
private OnRejectListener mOnRejectListener;
|
||||
private int mIconResourceId = 0;
|
||||
private int mMainButtonStrokeColorResourceId = 0;
|
||||
|
||||
@@ -59,21 +59,21 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
|
||||
CharSequence mMainButtonLabel;
|
||||
@VisibleForTesting
|
||||
CharSequence mDismissButtonLabel;
|
||||
@VisibleForTesting
|
||||
String mDestinationComponentName;
|
||||
@VisibleForTesting
|
||||
String mPreferenceHighlightKey;
|
||||
@VisibleForTesting
|
||||
Integer mSourceMetricsCategory;
|
||||
|
||||
public BatteryTipsCardPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setLayoutResource(R.layout.battery_tips_card);
|
||||
setSelectable(false);
|
||||
final FeatureFactory featureFactory = FeatureFactory.getFactory(context);
|
||||
mPowerUsageFeatureProvider = featureFactory.getPowerUsageFeatureProvider(context);
|
||||
mMetricsFeatureProvider = featureFactory.getMetricsFeatureProvider();
|
||||
mPowerAnomalyKey = null;
|
||||
}
|
||||
|
||||
public void setOnConfirmListener(OnConfirmListener listener) {
|
||||
mOnConfirmListener = listener;
|
||||
}
|
||||
|
||||
public void setOnRejectListener(OnRejectListener listener) {
|
||||
mOnRejectListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,13 +96,6 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the anomaly event id which is used in metrics.
|
||||
*/
|
||||
public void setAnomalyEventId(final String anomalyEventId) {
|
||||
mAnomalyEventId = anomalyEventId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the label of main button in tips card.
|
||||
*/
|
||||
@@ -123,50 +116,18 @@ 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.
|
||||
*/
|
||||
public void setMainButtonLauncherInfo(final String destinationClassName,
|
||||
final Integer sourceMetricsCategory, final String highlightKey) {
|
||||
mDestinationComponentName = destinationClassName;
|
||||
mSourceMetricsCategory = sourceMetricsCategory;
|
||||
mPreferenceHighlightKey = highlightKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
final int viewId = view.getId();
|
||||
if (viewId == R.id.main_button || viewId == R.id.tips_card) {
|
||||
if (TextUtils.isEmpty(mDestinationComponentName)) {
|
||||
return;
|
||||
}
|
||||
Bundle arguments = Bundle.EMPTY;
|
||||
if (!TextUtils.isEmpty(mPreferenceHighlightKey)) {
|
||||
arguments = new Bundle(1);
|
||||
arguments.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY,
|
||||
mPreferenceHighlightKey);
|
||||
}
|
||||
new SubSettingLauncher(getContext())
|
||||
.setDestination(mDestinationComponentName)
|
||||
.setSourceMetricsCategory(mSourceMetricsCategory)
|
||||
.setArguments(arguments)
|
||||
.launch();
|
||||
setVisible(false);
|
||||
mMetricsFeatureProvider.action(
|
||||
getContext(), SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, mAnomalyEventId);
|
||||
if (mOnConfirmListener != null) {
|
||||
mOnConfirmListener.onConfirm();
|
||||
}
|
||||
} else if (viewId == R.id.dismiss_button) {
|
||||
setVisible(false);
|
||||
mMetricsFeatureProvider.action(
|
||||
getContext(), SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, mAnomalyEventId);
|
||||
if (mPowerAnomalyKey != null) {
|
||||
DatabaseUtils.setDismissedPowerAnomalyKeys(getContext(), mPowerAnomalyKey.name());
|
||||
if (mOnRejectListener != null) {
|
||||
mOnRejectListener.onReject();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,17 +152,5 @@ public class BatteryTipsCardPreference extends Preference implements View.OnClic
|
||||
if (mIconResourceId != 0) {
|
||||
((ImageView) view.findViewById(R.id.icon)).setImageResource(mIconResourceId);
|
||||
}
|
||||
|
||||
if (!mPowerUsageFeatureProvider.isBatteryTipsFeedbackEnabled()) {
|
||||
return;
|
||||
}
|
||||
view.findViewById(R.id.tips_card)
|
||||
.setBackgroundResource(R.drawable.battery_tips_half_rounded_top_bg);
|
||||
view.findViewById(R.id.feedback_card).setVisibility(View.VISIBLE);
|
||||
|
||||
ImageButton thumbUpButton = (ImageButton) view.findViewById(R.id.thumb_up);
|
||||
thumbUpButton.setOnClickListener(this);
|
||||
ImageButton thumbDownButton = (ImageButton) view.findViewById(R.id.thumb_down);
|
||||
thumbDownButton.setOnClickListener(this);
|
||||
}
|
||||
}
|
||||
|
@@ -18,14 +18,16 @@ package com.android.settings.fuelgauge.batteryusage;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
|
||||
@@ -38,23 +40,32 @@ public class BatteryTipsController extends BasePreferenceController {
|
||||
private static final String ROOT_PREFERENCE_KEY = "battery_tips_category";
|
||||
private static final String CARD_PREFERENCE_KEY = "battery_tips_card";
|
||||
|
||||
private final PowerUsageFeatureProvider mPowerUsageFeatureProvider;
|
||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
|
||||
/** A callback listener for the battery tips is confirmed. */
|
||||
interface OnAnomalyConfirmListener {
|
||||
/** The callback function for the battery tips is confirmed. */
|
||||
void onAnomalyConfirm();
|
||||
}
|
||||
|
||||
/** A callback listener for the battery tips is rejected. */
|
||||
interface OnAnomalyRejectListener {
|
||||
/** The callback function for the battery tips is rejected. */
|
||||
void onAnomalyReject();
|
||||
}
|
||||
|
||||
private OnAnomalyConfirmListener mOnAnomalyConfirmListener;
|
||||
private OnAnomalyRejectListener mOnAnomalyRejectListener;
|
||||
|
||||
@VisibleForTesting
|
||||
BatteryTipsCardPreference mCardPreference;
|
||||
|
||||
public BatteryTipsController(Context context) {
|
||||
super(context, ROOT_PREFERENCE_KEY);
|
||||
final FeatureFactory featureFactory = FeatureFactory.getFactory(context);
|
||||
mPowerUsageFeatureProvider = featureFactory.getPowerUsageFeatureProvider(context);
|
||||
mMetricsFeatureProvider = featureFactory.getMetricsFeatureProvider();
|
||||
}
|
||||
|
||||
private boolean isTipsCardVisible() {
|
||||
return mPowerUsageFeatureProvider.isBatteryTipsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
@@ -66,6 +77,14 @@ public class BatteryTipsController extends BasePreferenceController {
|
||||
mCardPreference = screen.findPreference(CARD_PREFERENCE_KEY);
|
||||
}
|
||||
|
||||
void setOnAnomalyConfirmListener(OnAnomalyConfirmListener listener) {
|
||||
mOnAnomalyConfirmListener = listener;
|
||||
}
|
||||
|
||||
void setOnAnomalyRejectListener(OnAnomalyRejectListener listener) {
|
||||
mOnAnomalyRejectListener = listener;
|
||||
}
|
||||
|
||||
private <T> T getInfo(PowerAnomalyEvent powerAnomalyEvent,
|
||||
Function<WarningBannerInfo, T> warningBannerInfoSupplier,
|
||||
Function<WarningItemInfo, T> warningItemInfoSupplier) {
|
||||
@@ -102,12 +121,22 @@ public class BatteryTipsController extends BasePreferenceController {
|
||||
: getStringFromResource(resourceId, resourceIndex);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void handleBatteryTipsCardUpdated(PowerAnomalyEvent powerAnomalyEvent) {
|
||||
if (!isTipsCardVisible()) {
|
||||
mCardPreference.setVisible(false);
|
||||
return;
|
||||
/** Generate a key string of current anomaly to record as dismissed in sharedPreferences. */
|
||||
public static String getDismissRecordKey(PowerAnomalyEvent event) {
|
||||
if (!event.hasKey()) {
|
||||
return null;
|
||||
}
|
||||
switch (event.getKey()){
|
||||
case KEY_APP:
|
||||
return event.hasWarningItemInfo()
|
||||
&& event.getWarningItemInfo().hasDismissRecordKey()
|
||||
? event.getWarningItemInfo().getDismissRecordKey() : null;
|
||||
default:
|
||||
return event.getKey().name();
|
||||
}
|
||||
}
|
||||
|
||||
void handleBatteryTipsCardUpdated(PowerAnomalyEvent powerAnomalyEvent) {
|
||||
if (powerAnomalyEvent == null) {
|
||||
mCardPreference.setVisible(false);
|
||||
return;
|
||||
@@ -121,44 +150,76 @@ public class BatteryTipsController extends BasePreferenceController {
|
||||
R.array.battery_tips_card_colors, cardStyleId, "color");
|
||||
|
||||
// Get card preference strings and navigate fragment info
|
||||
final String eventId = powerAnomalyEvent.hasEventId()
|
||||
? powerAnomalyEvent.getEventId() : null;
|
||||
final PowerAnomalyKey powerAnomalyKey = powerAnomalyEvent.hasKey()
|
||||
? powerAnomalyEvent.getKey() : null;
|
||||
final int resourceIndex = powerAnomalyKey != null ? powerAnomalyKey.getNumber() : -1;
|
||||
|
||||
String titleString = getString(powerAnomalyEvent, WarningBannerInfo::getTitleString,
|
||||
final String titleString = getString(powerAnomalyEvent, WarningBannerInfo::getTitleString,
|
||||
WarningItemInfo::getTitleString, R.array.power_anomaly_titles, resourceIndex);
|
||||
if (titleString.isEmpty()) {
|
||||
mCardPreference.setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
String mainBtnString = getString(powerAnomalyEvent,
|
||||
final String mainBtnString = getString(powerAnomalyEvent,
|
||||
WarningBannerInfo::getMainButtonString, WarningItemInfo::getMainButtonString,
|
||||
R.array.power_anomaly_main_btn_strings, resourceIndex);
|
||||
String dismissBtnString = getString(powerAnomalyEvent,
|
||||
final String dismissBtnString = getString(powerAnomalyEvent,
|
||||
WarningBannerInfo::getCancelButtonString, WarningItemInfo::getCancelButtonString,
|
||||
R.array.power_anomaly_dismiss_btn_strings, resourceIndex);
|
||||
|
||||
String destinationClassName = getInfo(powerAnomalyEvent,
|
||||
final String destinationClassName = getInfo(powerAnomalyEvent,
|
||||
WarningBannerInfo::getMainButtonDestination, null);
|
||||
Integer sourceMetricsCategory = getInfo(powerAnomalyEvent,
|
||||
final Integer sourceMetricsCategory = getInfo(powerAnomalyEvent,
|
||||
WarningBannerInfo::getMainButtonSourceMetricsCategory, null);
|
||||
String preferenceHighlightKey = getInfo(powerAnomalyEvent,
|
||||
final String preferenceHighlightKey = getInfo(powerAnomalyEvent,
|
||||
WarningBannerInfo::getMainButtonSourceHighlightKey, null);
|
||||
|
||||
// Update card preference and main button fragment launcher
|
||||
mCardPreference.setAnomalyEventId(powerAnomalyEvent.getEventId());
|
||||
mCardPreference.setPowerAnomalyKey(powerAnomalyKey);
|
||||
mCardPreference.setTitle(titleString);
|
||||
mCardPreference.setIconResourceId(iconResId);
|
||||
mCardPreference.setMainButtonStrokeColorResourceId(colorResId);
|
||||
mCardPreference.setMainButtonLabel(mainBtnString);
|
||||
mCardPreference.setDismissButtonLabel(dismissBtnString);
|
||||
mCardPreference.setMainButtonLauncherInfo(
|
||||
destinationClassName, sourceMetricsCategory, preferenceHighlightKey);
|
||||
mCardPreference.setVisible(true);
|
||||
|
||||
mMetricsFeatureProvider.action(mContext,
|
||||
SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, powerAnomalyEvent.getEventId());
|
||||
// Set battery tips card listener
|
||||
mCardPreference.setOnConfirmListener(() -> {
|
||||
if (mOnAnomalyConfirmListener != null) {
|
||||
mOnAnomalyConfirmListener.onAnomalyConfirm();
|
||||
} else if (!TextUtils.isEmpty(destinationClassName)) {
|
||||
// Navigate to sub setting page
|
||||
Bundle arguments = Bundle.EMPTY;
|
||||
if (!TextUtils.isEmpty(preferenceHighlightKey)) {
|
||||
arguments = new Bundle(1);
|
||||
arguments.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY,
|
||||
preferenceHighlightKey);
|
||||
}
|
||||
new SubSettingLauncher(mContext)
|
||||
.setDestination(destinationClassName)
|
||||
.setSourceMetricsCategory(sourceMetricsCategory)
|
||||
.setArguments(arguments)
|
||||
.launch();
|
||||
}
|
||||
mMetricsFeatureProvider.action(
|
||||
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, eventId);
|
||||
});
|
||||
mCardPreference.setOnRejectListener(() -> {
|
||||
if (mOnAnomalyRejectListener != null) {
|
||||
mOnAnomalyRejectListener.onAnomalyReject();
|
||||
}
|
||||
// For anomaly events with same record key, dismissed until next time full charged.
|
||||
final String dismissRecordKey = getDismissRecordKey(powerAnomalyEvent);
|
||||
if (!TextUtils.isEmpty(dismissRecordKey)) {
|
||||
DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, dismissRecordKey);
|
||||
}
|
||||
mMetricsFeatureProvider.action(
|
||||
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, eventId);
|
||||
});
|
||||
|
||||
mCardPreference.setVisible(true);
|
||||
mMetricsFeatureProvider.action(
|
||||
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, eventId);
|
||||
}
|
||||
}
|
||||
|
@@ -120,6 +120,7 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
mFetchBatteryUsageData = true;
|
||||
BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ true);
|
||||
BootBroadcastReceiver.invokeJobRecheck(context);
|
||||
}
|
||||
|
||||
private void sendBatteryEventData(Context context, BatteryEventType batteryEventType) {
|
||||
|
@@ -473,6 +473,9 @@ public final class ConvertUtils {
|
||||
.setConsumePower(batteryDiffEntry.mConsumePower)
|
||||
.setForegroundUsageConsumePower(batteryDiffEntry.mForegroundUsageConsumePower)
|
||||
.setBackgroundUsageConsumePower(batteryDiffEntry.mBackgroundUsageConsumePower)
|
||||
.setForegroundServiceUsageConsumePower(
|
||||
batteryDiffEntry.mForegroundServiceUsageConsumePower)
|
||||
.setCachedUsageConsumePower(batteryDiffEntry.mCachedUsageConsumePower)
|
||||
.setForegroundUsageTime(batteryDiffEntry.mForegroundUsageTimeInMs)
|
||||
.setBackgroundUsageTime(batteryDiffEntry.mBackgroundUsageTimeInMs)
|
||||
.setScreenOnTime(batteryDiffEntry.mScreenOnTimeInMs);
|
||||
@@ -525,9 +528,9 @@ public final class ConvertUtils {
|
||||
batteryUsageDiff.getScreenOnTime(),
|
||||
batteryUsageDiff.getConsumePower(),
|
||||
batteryUsageDiff.getForegroundUsageConsumePower(),
|
||||
/*foregroundServiceUsageConsumePower=*/ 0,
|
||||
batteryUsageDiff.getForegroundServiceUsageConsumePower(),
|
||||
batteryUsageDiff.getBackgroundUsageConsumePower(),
|
||||
/*cachedUsageConsumePower=*/ 0);
|
||||
batteryUsageDiff.getCachedUsageConsumePower());
|
||||
}
|
||||
|
||||
static BatteryDiffData convertToBatteryDiffData(
|
||||
|
@@ -68,6 +68,8 @@ public final class PeriodicJobManager {
|
||||
/** Schedules the next alarm job if it is available. */
|
||||
public void refreshJob(final boolean fromBoot) {
|
||||
if (mAlarmManager == null) {
|
||||
BatteryUsageLogUtils.writeLog(mContext, Action.SCHEDULE_JOB,
|
||||
"cannot schedule next alarm job due to AlarmManager is null");
|
||||
Log.e(TAG, "cannot schedule next alarm job");
|
||||
return;
|
||||
}
|
||||
@@ -80,8 +82,8 @@ public final class PeriodicJobManager {
|
||||
AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
|
||||
|
||||
final String utcToLocalTime = ConvertUtils.utcToLocalTimeForLogging(triggerAtMillis);
|
||||
BatteryUsageLogUtils.writeLog(
|
||||
mContext, Action.SCHEDULE_JOB, "triggerTime=" + utcToLocalTime);
|
||||
BatteryUsageLogUtils.writeLog(mContext, Action.SCHEDULE_JOB,
|
||||
String.format("triggerTime=%s, fromBoot=%b", utcToLocalTime, fromBoot));
|
||||
Log.d(TAG, "schedule next alarm job at " + utcToLocalTime);
|
||||
}
|
||||
|
||||
|
@@ -33,12 +33,23 @@ public final class PeriodicJobReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
try {
|
||||
loadDataAndRefreshJob(context, intent);
|
||||
} catch (Exception e) {
|
||||
BatteryUsageLogUtils.writeLog(context, Action.SCHEDULE_JOB,
|
||||
String.format("loadDataAndRefreshJob() failed: %s", e));
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadDataAndRefreshJob(Context context, Intent intent) {
|
||||
final String action = intent == null ? "" : intent.getAction();
|
||||
if (!ACTION_PERIODIC_JOB_UPDATE.equals(action)) {
|
||||
Log.w(TAG, "receive unexpected action=" + action);
|
||||
return;
|
||||
}
|
||||
if (DatabaseUtils.isWorkProfile(context)) {
|
||||
BatteryUsageLogUtils.writeLog(context, Action.SCHEDULE_JOB,
|
||||
"do not refresh job for work profile");
|
||||
Log.w(TAG, "do not refresh job for work profile action=" + action);
|
||||
return;
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
@@ -44,9 +45,13 @@ import com.android.settingslib.utils.AsyncLoaderCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/** Advanced power usage. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
@@ -63,9 +68,9 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
||||
|
||||
private boolean mIsChartDataLoaded = false;
|
||||
private long mResumeTimestamp;
|
||||
private BatteryChartPreferenceController mBatteryChartPreferenceController;
|
||||
private Optional<BatteryLevelData> mBatteryLevelData;
|
||||
private Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
|
||||
|
||||
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private final ContentObserver mBatteryObserver =
|
||||
new ContentObserver(mHandler) {
|
||||
@@ -78,6 +83,19 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
||||
}
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
BatteryTipsController mBatteryTipsController;
|
||||
@VisibleForTesting
|
||||
BatteryChartPreferenceController mBatteryChartPreferenceController;
|
||||
@VisibleForTesting
|
||||
ScreenOnTimeController mScreenOnTimeController;
|
||||
@VisibleForTesting
|
||||
BatteryUsageBreakdownController mBatteryUsageBreakdownController;
|
||||
@VisibleForTesting
|
||||
PowerAnomalyEvent mPowerAnomalyEvent;
|
||||
@VisibleForTesting
|
||||
Optional<BatteryLevelData> mBatteryLevelData;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
@@ -92,6 +110,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
||||
if (getActivity().isChangingConfigurations()) {
|
||||
BatteryEntry.clearUidCache();
|
||||
}
|
||||
mExecutor.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -114,7 +133,6 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
||||
super.onPause();
|
||||
// Resets the flag to reload usage data in onResume() callback.
|
||||
mIsChartDataLoaded = false;
|
||||
mBatteryLevelData = null;
|
||||
final Uri uri = DatabaseUtils.BATTERY_CONTENT_URI;
|
||||
if (uri != null) {
|
||||
getContext().getContentResolver().unregisterContentObserver(mBatteryObserver);
|
||||
@@ -135,33 +153,25 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
||||
@Override
|
||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||
mBatteryTipsController = new BatteryTipsController(context);
|
||||
mBatteryChartPreferenceController =
|
||||
new BatteryChartPreferenceController(
|
||||
context, getSettingsLifecycle(), (SettingsActivity) getActivity());
|
||||
ScreenOnTimeController screenOnTimeController = new ScreenOnTimeController(context);
|
||||
BatteryUsageBreakdownController batteryUsageBreakdownController =
|
||||
mScreenOnTimeController = new ScreenOnTimeController(context);
|
||||
mBatteryUsageBreakdownController =
|
||||
new BatteryUsageBreakdownController(
|
||||
context, getSettingsLifecycle(), (SettingsActivity) getActivity(), this);
|
||||
|
||||
mBatteryChartPreferenceController.setOnScreenOnTimeUpdatedListener(
|
||||
screenOnTimeController::handleSceenOnTimeUpdated);
|
||||
mBatteryChartPreferenceController.setOnBatteryUsageUpdatedListener(
|
||||
batteryUsageBreakdownController::handleBatteryUsageUpdated);
|
||||
|
||||
controllers.add(mBatteryTipsController);
|
||||
controllers.add(mBatteryChartPreferenceController);
|
||||
controllers.add(screenOnTimeController);
|
||||
controllers.add(batteryUsageBreakdownController);
|
||||
controllers.add(mScreenOnTimeController);
|
||||
controllers.add(mBatteryUsageBreakdownController);
|
||||
setBatteryChartPreferenceController();
|
||||
mBatteryChartPreferenceController.setOnSelectedIndexUpdatedListener(
|
||||
this::onSelectedSlotDataUpdated);
|
||||
|
||||
final PowerUsageFeatureProvider powerUsageFeatureProvider =
|
||||
FeatureFactory.getFactory(context).getPowerUsageFeatureProvider(context);
|
||||
if (powerUsageFeatureProvider.isBatteryTipsEnabled()) {
|
||||
BatteryTipsController batteryTipsController = new BatteryTipsController(context);
|
||||
mBatteryChartPreferenceController.setOnBatteryTipsUpdatedListener(
|
||||
batteryTipsController::handleBatteryTipsCardUpdated);
|
||||
controllers.add(batteryTipsController);
|
||||
}
|
||||
|
||||
// Force UI refresh if battery usage data was loaded before UI initialization.
|
||||
onSelectedSlotDataUpdated();
|
||||
return controllers;
|
||||
}
|
||||
|
||||
@@ -176,12 +186,18 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
||||
bundle.putInt(KEY_REFRESH_TYPE, refreshType);
|
||||
if (!mIsChartDataLoaded) {
|
||||
mIsChartDataLoaded = true;
|
||||
mBatteryLevelData = null;
|
||||
mBatteryUsageMap = null;
|
||||
mPowerAnomalyEvent = null;
|
||||
restartLoader(LoaderIndex.BATTERY_LEVEL_DATA_LOADER, bundle,
|
||||
mBatteryLevelDataLoaderCallbacks);
|
||||
}
|
||||
}
|
||||
|
||||
private void onBatteryLevelDataUpdate(BatteryLevelData batteryLevelData) {
|
||||
if (!isResumed()) {
|
||||
return;
|
||||
}
|
||||
mBatteryLevelData = Optional.ofNullable(batteryLevelData);
|
||||
if (mBatteryChartPreferenceController != null) {
|
||||
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(batteryLevelData);
|
||||
@@ -191,23 +207,164 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
||||
}
|
||||
|
||||
private void onBatteryDiffDataMapUpdate(Map<Long, BatteryDiffData> batteryDiffDataMap) {
|
||||
if (mBatteryLevelData != null && mBatteryChartPreferenceController != null) {
|
||||
Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
|
||||
DataProcessor.generateBatteryUsageMap(
|
||||
getContext(), batteryDiffDataMap, mBatteryLevelData.orElse(null));
|
||||
DataProcessor.loadLabelAndIcon(batteryUsageMap);
|
||||
mBatteryChartPreferenceController.onBatteryUsageMapUpdate(batteryUsageMap);
|
||||
if (!isResumed() || mBatteryLevelData == null) {
|
||||
return;
|
||||
}
|
||||
mBatteryUsageMap = DataProcessor.generateBatteryUsageMap(
|
||||
getContext(), batteryDiffDataMap, mBatteryLevelData.orElse(null));
|
||||
Log.d(TAG, "onBatteryDiffDataMapUpdate: " + mBatteryUsageMap);
|
||||
DataProcessor.loadLabelAndIcon(mBatteryUsageMap);
|
||||
onSelectedSlotDataUpdated();
|
||||
detectAnomaly();
|
||||
logScreenUsageTime();
|
||||
if (mBatteryChartPreferenceController != null
|
||||
&& mBatteryLevelData.isEmpty() && isBatteryUsageMapNullOrEmpty()) {
|
||||
// No available battery usage and battery level data.
|
||||
mBatteryChartPreferenceController.showEmptyChart();
|
||||
}
|
||||
}
|
||||
|
||||
private void onSelectedSlotDataUpdated() {
|
||||
if (mBatteryChartPreferenceController == null
|
||||
|| mScreenOnTimeController == null
|
||||
|| mBatteryUsageBreakdownController == null
|
||||
|| mBatteryUsageMap == null) {
|
||||
return;
|
||||
}
|
||||
final int dailyIndex = mBatteryChartPreferenceController.getDailyChartIndex();
|
||||
final int hourlyIndex = mBatteryChartPreferenceController.getHourlyChartIndex();
|
||||
final String slotInformation = mBatteryChartPreferenceController.getSlotInformation();
|
||||
final BatteryDiffData slotUsageData = mBatteryUsageMap.get(dailyIndex).get(hourlyIndex);
|
||||
if (slotUsageData != null) {
|
||||
mScreenOnTimeController.handleSceenOnTimeUpdated(
|
||||
slotUsageData.getScreenOnTime(), slotInformation);
|
||||
}
|
||||
mBatteryUsageBreakdownController.handleBatteryUsageUpdated(
|
||||
slotUsageData, slotInformation, isBatteryUsageMapNullOrEmpty());
|
||||
Log.d(TAG, String.format("Battery usage list shows in %d millis",
|
||||
System.currentTimeMillis() - mResumeTimestamp));
|
||||
}
|
||||
|
||||
private void detectAnomaly() {
|
||||
mExecutor.execute(() -> {
|
||||
final PowerUsageFeatureProvider powerUsageFeatureProvider =
|
||||
FeatureFactory.getFactory(getContext())
|
||||
.getPowerUsageFeatureProvider(getContext());
|
||||
final PowerAnomalyEventList anomalyEventList =
|
||||
powerUsageFeatureProvider.detectSettingsAnomaly(
|
||||
getContext(), /* displayDrain= */ 0);
|
||||
mHandler.post(() -> onAnomalyDetected(anomalyEventList));
|
||||
});
|
||||
}
|
||||
|
||||
private void onAnomalyDetected(PowerAnomalyEventList anomalyEventList) {
|
||||
if (!isResumed() || anomalyEventList == null) {
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "anomalyEventList = " + anomalyEventList);
|
||||
final PowerAnomalyEvent displayEvent =
|
||||
getHighestScoreAnomalyEvent(getContext(), anomalyEventList);
|
||||
onDisplayAnomalyEventUpdated(displayEvent);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void onDisplayAnomalyEventUpdated(PowerAnomalyEvent event) {
|
||||
mPowerAnomalyEvent = event;
|
||||
if (mBatteryTipsController == null
|
||||
|| mBatteryChartPreferenceController == null
|
||||
|| mBatteryUsageBreakdownController == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update battery tips card preference & behaviour
|
||||
mBatteryTipsController.setOnAnomalyConfirmListener(null);
|
||||
mBatteryTipsController.setOnAnomalyRejectListener(null);
|
||||
mBatteryTipsController.handleBatteryTipsCardUpdated(mPowerAnomalyEvent);
|
||||
|
||||
// Update highlight slot effect in battery chart view
|
||||
Pair<Integer, Integer> highlightSlotIndexPair = Pair.create(
|
||||
BatteryChartViewModel.SELECTED_INDEX_INVALID,
|
||||
BatteryChartViewModel.SELECTED_INDEX_INVALID);
|
||||
if (mPowerAnomalyEvent != null && mPowerAnomalyEvent.hasWarningItemInfo()) {
|
||||
final WarningItemInfo warningItemInfo = mPowerAnomalyEvent.getWarningItemInfo();
|
||||
final Long startTimestamp = warningItemInfo.hasStartTimestamp()
|
||||
? warningItemInfo.getStartTimestamp() : null;
|
||||
final Long endTimestamp = warningItemInfo.hasEndTimestamp()
|
||||
? warningItemInfo.getEndTimestamp() : null;
|
||||
if (startTimestamp != null && endTimestamp != null) {
|
||||
highlightSlotIndexPair = mBatteryLevelData.map(levelData ->
|
||||
levelData.getIndexByTimestamps(startTimestamp, endTimestamp))
|
||||
.orElse(highlightSlotIndexPair);
|
||||
mBatteryTipsController.setOnAnomalyConfirmListener(
|
||||
mBatteryChartPreferenceController::selectHighlightSlotIndex);
|
||||
mBatteryTipsController.setOnAnomalyRejectListener(
|
||||
() -> onDisplayAnomalyEventUpdated(null));
|
||||
}
|
||||
}
|
||||
mBatteryChartPreferenceController.onHighlightSlotIndexUpdate(
|
||||
highlightSlotIndexPair.first, highlightSlotIndexPair.second);
|
||||
}
|
||||
|
||||
private void setBatteryChartPreferenceController() {
|
||||
if (mHistPref != null && mBatteryChartPreferenceController != null) {
|
||||
mHistPref.setChartPreferenceController(mBatteryChartPreferenceController);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBatteryUsageMapNullOrEmpty() {
|
||||
final BatteryDiffData allBatteryDiffData = getAllBatteryDiffData(mBatteryUsageMap);
|
||||
// If all data is null or empty, each slot must be null or empty.
|
||||
return allBatteryDiffData == null
|
||||
|| (allBatteryDiffData.getAppDiffEntryList().isEmpty()
|
||||
&& allBatteryDiffData.getSystemDiffEntryList().isEmpty());
|
||||
}
|
||||
|
||||
private void logScreenUsageTime() {
|
||||
final BatteryDiffData allBatteryDiffData = getAllBatteryDiffData(mBatteryUsageMap);
|
||||
if (allBatteryDiffData == null) {
|
||||
return;
|
||||
}
|
||||
long totalForegroundUsageTime = 0;
|
||||
for (final BatteryDiffEntry entry : allBatteryDiffData.getAppDiffEntryList()) {
|
||||
totalForegroundUsageTime += entry.mForegroundUsageTimeInMs;
|
||||
}
|
||||
mMetricsFeatureProvider.action(
|
||||
getContext(),
|
||||
SettingsEnums.ACTION_BATTERY_USAGE_SCREEN_ON_TIME,
|
||||
(int) allBatteryDiffData.getScreenOnTime());
|
||||
mMetricsFeatureProvider.action(
|
||||
getContext(),
|
||||
SettingsEnums.ACTION_BATTERY_USAGE_FOREGROUND_USAGE_TIME,
|
||||
(int) totalForegroundUsageTime);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static PowerAnomalyEvent getHighestScoreAnomalyEvent(
|
||||
Context context, PowerAnomalyEventList anomalyEventList) {
|
||||
if (anomalyEventList == null || anomalyEventList.getPowerAnomalyEventsCount() == 0) {
|
||||
return null;
|
||||
}
|
||||
final Set<String> dismissedPowerAnomalyKeys =
|
||||
DatabaseUtils.getDismissedPowerAnomalyKeys(context);
|
||||
Log.d(TAG, "dismissedPowerAnomalyKeys = " + dismissedPowerAnomalyKeys);
|
||||
|
||||
final PowerAnomalyEvent highestScoreEvent = anomalyEventList.getPowerAnomalyEventsList()
|
||||
.stream()
|
||||
.filter(event -> !dismissedPowerAnomalyKeys.contains(
|
||||
BatteryTipsController.getDismissRecordKey(event)))
|
||||
.max(Comparator.comparing(PowerAnomalyEvent::getScore))
|
||||
.orElse(null);
|
||||
Log.d(TAG, "highestScoreAnomalyEvent = " + highestScoreEvent);
|
||||
return highestScoreEvent;
|
||||
}
|
||||
|
||||
private static BatteryDiffData getAllBatteryDiffData(
|
||||
Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
|
||||
return batteryUsageMap == null ? null : batteryUsageMap
|
||||
.get(BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||
.get(BatteryChartViewModel.SELECTED_INDEX_ALL);
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
@@ -228,6 +385,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
||||
controllers.add(new BatteryUsageBreakdownController(
|
||||
context, null /* lifecycle */, null /* activity */,
|
||||
null /* fragment */));
|
||||
controllers.add(new BatteryTipsController(context));
|
||||
return controllers;
|
||||
}
|
||||
};
|
||||
@@ -244,7 +402,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
|
||||
public BatteryLevelData loadInBackground() {
|
||||
return DataProcessManager.getBatteryLevelData(
|
||||
getContext(), mHandler, /*isFromPeriodJob=*/ false,
|
||||
map -> PowerUsageAdvanced.this.onBatteryDiffDataMapUpdate(map));
|
||||
PowerUsageAdvanced.this::onBatteryDiffDataMapUpdate);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -26,7 +26,9 @@ message BatteryUsageDiff {
|
||||
optional double consume_power = 9;
|
||||
optional double foreground_usage_consume_power = 10;
|
||||
optional double background_usage_consume_power = 11;
|
||||
optional int64 foreground_usage_time = 12;
|
||||
optional int64 background_usage_time = 13;
|
||||
optional int64 screen_on_time = 14;
|
||||
optional double foreground_service_usage_consume_power = 12;
|
||||
optional double cached_usage_consume_power = 13;
|
||||
optional int64 foreground_usage_time = 14;
|
||||
optional int64 background_usage_time = 15;
|
||||
optional int64 screen_on_time = 16;
|
||||
}
|
||||
|
@@ -60,4 +60,6 @@ message WarningItemInfo {
|
||||
optional string description_string = 5;
|
||||
optional string main_button_string = 6;
|
||||
optional string cancel_button_string = 7;
|
||||
optional string dismiss_record_key = 8;
|
||||
optional string item_key = 9;
|
||||
}
|
||||
|
@@ -110,7 +110,7 @@ fun UserAspectRatioAppList(
|
||||
appList = appList,
|
||||
header = {
|
||||
Box(Modifier.padding(SettingsDimension.itemPadding)) {
|
||||
SettingsBody(UserAspectRatioAppsPageProvider.getSummary())
|
||||
SettingsBody(stringResource(R.string.aspect_ratio_main_summary, Build.MODEL))
|
||||
}
|
||||
Illustration(object : IllustrationModel {
|
||||
override val resId = R.raw.user_aspect_ratio_education
|
||||
|
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
package com.android.settings.system;
|
||||
|
||||
import android.content.Context;
|
||||
import com.android.settings.Utils;
|
||||
|
||||
public class FactoryResetDemoUserPreferenceController extends FactoryResetPreferenceController {
|
||||
|
||||
public FactoryResetDemoUserPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
/** Hide demo user specific "Factory reset" settings for non demo users. */
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return Utils.isDemoUser(mContext) ? AVAILABLE : DISABLED_FOR_USER;
|
||||
}
|
||||
}
|
@@ -24,35 +24,26 @@ import androidx.preference.Preference;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Settings;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
public class FactoryResetPreferenceController extends AbstractPreferenceController
|
||||
implements PreferenceControllerMixin {
|
||||
/** Key of the "Factory reset" preference in {@link R.xml.reset_dashboard_fragment}. */
|
||||
private static final String KEY_FACTORY_RESET = "factory_reset";
|
||||
public class FactoryResetPreferenceController extends BasePreferenceController {
|
||||
|
||||
private final UserManager mUm;
|
||||
|
||||
public FactoryResetPreferenceController(Context context) {
|
||||
super(context);
|
||||
public FactoryResetPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
}
|
||||
|
||||
/** Hide "Factory reset" settings for secondary users, except demo users. */
|
||||
/** Hide "Factory reset" settings for secondary users. */
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return mUm.isAdminUser() || Utils.isDemoUser(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return KEY_FACTORY_RESET;
|
||||
public int getAvailabilityStatus() {
|
||||
return mUm.isAdminUser() ? AVAILABLE : DISABLED_FOR_USER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||
if (KEY_FACTORY_RESET.equals(preference.getKey())) {
|
||||
if (mPreferenceKey.equals(preference.getKey())) {
|
||||
final Intent intent = new Intent(mContext, Settings.FactoryResetActivity.class);
|
||||
mContext.startActivity(intent);
|
||||
return true;
|
||||
|
@@ -78,7 +78,6 @@ public class ResetDashboardFragment extends DashboardFragment {
|
||||
if (SubscriptionUtil.isSimHardwareVisible(context)) {
|
||||
controllers.add(new NetworkResetPreferenceController(context));
|
||||
}
|
||||
controllers.add(new FactoryResetPreferenceController(context));
|
||||
controllers.add(new ResetAppPrefPreferenceController(context, lifecycle));
|
||||
return controllers;
|
||||
}
|
||||
|
@@ -26,13 +26,11 @@ public class ResetPreferenceController extends BasePreferenceController {
|
||||
|
||||
private final UserManager mUm;
|
||||
private final NetworkResetPreferenceController mNetworkReset;
|
||||
private final FactoryResetPreferenceController mFactpruReset;
|
||||
|
||||
public ResetPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
mNetworkReset = new NetworkResetPreferenceController(context);
|
||||
mFactpruReset = new FactoryResetPreferenceController(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -72,10 +72,6 @@ public class PowerUsageFeatureProviderImplTest {
|
||||
assertThat(mPowerFeatureProvider.isBatteryTipsEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsBatteryTipsFeedbackEnabled_returnFalse() {
|
||||
assertThat(mPowerFeatureProvider.isBatteryTipsFeedbackEnabled()).isFalse();
|
||||
}
|
||||
@Test
|
||||
public void testGetBatteryUsageListConsumePowerThreshold_return0() {
|
||||
assertThat(mPowerFeatureProvider.getBatteryUsageListConsumePowerThreshold()).isEqualTo(0.0);
|
||||
|
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.atLeast;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -44,9 +45,9 @@ import android.util.ArrayMap;
|
||||
import android.view.View;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.testutils.BatteryTestUtils;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -72,6 +73,8 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
@Mock
|
||||
private SettingsActivity mSettingsActivity;
|
||||
@Mock
|
||||
private TextView mChartSummaryTextView;
|
||||
@Mock
|
||||
private BatteryChartView mDailyChartView;
|
||||
@Mock
|
||||
private BatteryChartView mHourlyChartView;
|
||||
@@ -112,6 +115,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
setupHourlyChartViewAnimationMock();
|
||||
mBatteryChartPreferenceController = createController();
|
||||
mBatteryChartPreferenceController.mPrefContext = mContext;
|
||||
mBatteryChartPreferenceController.mChartSummaryTextView = mChartSummaryTextView;
|
||||
mBatteryChartPreferenceController.mDailyChartView = mDailyChartView;
|
||||
mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView;
|
||||
BatteryDiffEntry.clearCache();
|
||||
@@ -180,7 +184,6 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
mBatteryChartPreferenceController.mDailyChartLabelTextGenerator);
|
||||
|
||||
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
|
||||
mBatteryChartPreferenceController.onBatteryUsageMapUpdate(getEmptyBatteryUsageMap());
|
||||
|
||||
verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
|
||||
verify(mViewPropertyAnimator, atLeastOnce()).alpha(0f);
|
||||
@@ -275,29 +278,78 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshUi_normalCase_returnTrue() {
|
||||
public void onBatteryLevelDataUpdate_oneDay_showHourlyChartOnly() {
|
||||
doReturn(View.GONE).when(mHourlyChartView).getVisibility();
|
||||
|
||||
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
|
||||
mBatteryChartPreferenceController.onBatteryUsageMapUpdate(getEmptyBatteryUsageMap());
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
|
||||
|
||||
verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
|
||||
verify(mDailyChartView).setVisibility(View.GONE);
|
||||
verify(mHourlyChartView).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshUi_batteryIndexedMapIsNull_returnTrue() {
|
||||
public void onBatteryLevelDataUpdate_selectAllForMultipleDays_showDailyChartOnly() {
|
||||
doReturn(View.GONE).when(mHourlyChartView).getVisibility();
|
||||
|
||||
mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL;
|
||||
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
|
||||
|
||||
verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
|
||||
verify(mDailyChartView).setVisibility(View.VISIBLE);
|
||||
verify(mHourlyChartView, never()).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBatteryLevelDataUpdate_selectOneDayForMultipleDays_showBothCharts() {
|
||||
doReturn(View.GONE).when(mHourlyChartView).getVisibility();
|
||||
|
||||
mBatteryChartPreferenceController.mDailyChartIndex = 0;
|
||||
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
|
||||
|
||||
verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
|
||||
verify(mDailyChartView).setVisibility(View.VISIBLE);
|
||||
verify(mHourlyChartView).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBatteryLevelDataUpdate_batteryLevelDataIsNull_showNoChart() {
|
||||
doReturn(View.GONE).when(mHourlyChartView).getVisibility();
|
||||
|
||||
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(null);
|
||||
mBatteryChartPreferenceController.onBatteryUsageMapUpdate(getEmptyBatteryUsageMap());
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
|
||||
|
||||
verify(mChartSummaryTextView).setVisibility(View.GONE);
|
||||
verify(mDailyChartView).setVisibility(View.GONE);
|
||||
verify(mHourlyChartView).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void showEmptyChart_normalCase_showEmptyChart() {
|
||||
doReturn(View.GONE).when(mHourlyChartView).getVisibility();
|
||||
|
||||
mBatteryChartPreferenceController.showEmptyChart();
|
||||
|
||||
verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
|
||||
verify(mDailyChartView).setVisibility(View.GONE);
|
||||
verify(mHourlyChartView).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshUi_dailyChartViewIsNull_ignoreRefresh() {
|
||||
mBatteryChartPreferenceController.mDailyChartView = null;
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
||||
|
||||
mBatteryChartPreferenceController.refreshUi();
|
||||
|
||||
verify(mChartSummaryTextView, never()).setVisibility(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshUi_hourlyChartViewIsNull_ignoreRefresh() {
|
||||
mBatteryChartPreferenceController.mHourlyChartView = null;
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
||||
|
||||
mBatteryChartPreferenceController.refreshUi();
|
||||
|
||||
verify(mChartSummaryTextView, never()).setVisibility(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -408,57 +460,6 @@ 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;
|
||||
@@ -481,11 +482,6 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
return new BatteryLevelData(batteryLevelMap);
|
||||
}
|
||||
|
||||
private static Map<Integer, Map<Integer, BatteryDiffData>> getEmptyBatteryUsageMap() {
|
||||
return Map.of(SELECTED_INDEX_ALL, Map.of(SELECTED_INDEX_ALL, new BatteryDiffData(
|
||||
null, 0, 0, 0, 0, 0, List.of(), List.of(), Set.of(), Set.of(), false)));
|
||||
}
|
||||
|
||||
private BatteryChartPreferenceController createController() {
|
||||
final BatteryChartPreferenceController controller =
|
||||
new BatteryChartPreferenceController(
|
||||
|
@@ -18,6 +18,10 @@ package com.android.settings.fuelgauge.batteryusage;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.settings.testutils.BatteryTestUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -211,4 +215,29 @@ public class BatteryLevelDataTest {
|
||||
assertThat(result.getHourlyBatteryLevelsPerDay().get(0).getLevels())
|
||||
.isEqualTo(List.of(100, 98));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIndexByTimestamps_returnExpectedResult() {
|
||||
final BatteryLevelData batteryLevelData =
|
||||
new BatteryLevelData(Map.of(
|
||||
1694354400000L, 1, // 2023-09-10 22:00:00
|
||||
1694361600000L, 2, // 2023-09-11 00:00:00
|
||||
1694368800000L, 3)); // 2023-09-11 02:00:00
|
||||
final PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent();
|
||||
|
||||
assertThat(batteryLevelData.getIndexByTimestamps(0L, 0L))
|
||||
.isEqualTo(Pair.create(BatteryChartViewModel.SELECTED_INDEX_INVALID,
|
||||
BatteryChartViewModel.SELECTED_INDEX_INVALID));
|
||||
assertThat(batteryLevelData.getIndexByTimestamps(1694361600000L + 1L, 1694368800000L + 1L))
|
||||
.isEqualTo(Pair.create(BatteryChartViewModel.SELECTED_INDEX_INVALID,
|
||||
BatteryChartViewModel.SELECTED_INDEX_INVALID));
|
||||
assertThat(batteryLevelData.getIndexByTimestamps(1694361600000L, 1694368800000L))
|
||||
.isEqualTo(Pair.create(1, 0));
|
||||
assertThat(batteryLevelData.getIndexByTimestamps(1694361600000L + 1L, 1694368800000L - 1L))
|
||||
.isEqualTo(Pair.create(1, 0));
|
||||
assertThat(batteryLevelData.getIndexByTimestamps(
|
||||
event.getWarningItemInfo().getStartTimestamp(),
|
||||
event.getWarningItemInfo().getEndTimestamp()))
|
||||
.isEqualTo(Pair.create(1, 0));
|
||||
}
|
||||
}
|
||||
|
@@ -19,7 +19,9 @@ package com.android.settings.fuelgauge.batteryusage;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -45,16 +47,24 @@ import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public final class BatteryTipsCardPreferenceTest {
|
||||
|
||||
private Context mContext;
|
||||
private FakeFeatureFactory mFeatureFactory;
|
||||
private BatteryTipsCardPreference mBatteryTipsCardPreference;
|
||||
private PowerUsageAdvanced mPowerUsageAdvanced;
|
||||
private BatteryTipsController mBatteryTipsController;
|
||||
|
||||
@Mock
|
||||
private View mFakeView;
|
||||
@Mock
|
||||
private BatteryChartPreferenceController mBatteryChartPreferenceController;
|
||||
@Mock
|
||||
private BatteryUsageBreakdownController mBatteryUsageBreakdownController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -64,6 +74,14 @@ public final class BatteryTipsCardPreferenceTest {
|
||||
mBatteryTipsCardPreference = new BatteryTipsCardPreference(mContext, /*attrs=*/ null);
|
||||
mBatteryTipsController = new BatteryTipsController(mContext);
|
||||
mBatteryTipsController.mCardPreference = mBatteryTipsCardPreference;
|
||||
mPowerUsageAdvanced = new PowerUsageAdvanced();
|
||||
mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController;
|
||||
mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController;
|
||||
mPowerUsageAdvanced.mBatteryUsageBreakdownController = mBatteryUsageBreakdownController;
|
||||
mPowerUsageAdvanced.mBatteryLevelData = Optional.of(new BatteryLevelData(Map.of(
|
||||
1694354400000L, 1, // 2023-09-10 22:00:00
|
||||
1694361600000L, 2, // 2023-09-11 00:00:00
|
||||
1694368800000L, 3))); // 2023-09-11 02:00:00
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -71,8 +89,9 @@ public final class BatteryTipsCardPreferenceTest {
|
||||
assertThat(mBatteryTipsCardPreference.getLayoutResource()).isEqualTo(
|
||||
R.layout.battery_tips_card);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClick_mainBtn_getAdaptiveBrightnessLauncher() {
|
||||
public void onClick_mainBtnOfSettingsAnomaly_getAdaptiveBrightnessLauncher() {
|
||||
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
|
||||
PowerAnomalyEvent adaptiveBrightnessAnomaly =
|
||||
BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent();
|
||||
@@ -80,10 +99,10 @@ public final class BatteryTipsCardPreferenceTest {
|
||||
when(mFakeView.getId()).thenReturn(R.id.main_button);
|
||||
doNothing().when(mContext).startActivity(captor.capture());
|
||||
|
||||
mBatteryTipsController.handleBatteryTipsCardUpdated(adaptiveBrightnessAnomaly);
|
||||
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(adaptiveBrightnessAnomaly);
|
||||
mBatteryTipsCardPreference.onClick(mFakeView);
|
||||
|
||||
assertThat(mBatteryTipsCardPreference.isVisible()).isEqualTo(false);
|
||||
assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
|
||||
verify(mContext).startActivity(any(Intent.class));
|
||||
final Intent intent = captor.getValue();
|
||||
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||
@@ -96,21 +115,53 @@ public final class BatteryTipsCardPreferenceTest {
|
||||
|
||||
@Test
|
||||
public void onClick_dismissBtn_cardDismissAndLogged() {
|
||||
PowerAnomalyEvent screenTimeoutAnomaly =
|
||||
final PowerAnomalyEvent screenTimeoutAnomaly =
|
||||
BatteryTestUtils.createScreenTimeoutAnomalyEvent();
|
||||
DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
|
||||
when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
|
||||
when(mFakeView.getId()).thenReturn(R.id.dismiss_button);
|
||||
|
||||
mBatteryTipsController.handleBatteryTipsCardUpdated(screenTimeoutAnomaly);
|
||||
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(screenTimeoutAnomaly);
|
||||
mBatteryTipsCardPreference.onClick(mFakeView);
|
||||
|
||||
assertThat(mBatteryTipsCardPreference.isVisible()).isEqualTo(false);
|
||||
assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext).size())
|
||||
.isEqualTo(1);
|
||||
assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
|
||||
assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext)).hasSize(1);
|
||||
assertThat(DatabaseUtils.getDismissedPowerAnomalyKeys(mContext))
|
||||
.contains(PowerAnomalyKey.KEY_SCREEN_TIMEOUT.name());
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, "ScreenTimeoutAnomaly");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClick_mainBtnOfAppsAnomaly_selectHighlightSlot() {
|
||||
final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent();
|
||||
when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
|
||||
when(mFakeView.getId()).thenReturn(R.id.main_button);
|
||||
|
||||
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly);
|
||||
mBatteryTipsCardPreference.onClick(mFakeView);
|
||||
|
||||
assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
|
||||
verify(mContext, never()).startActivity(any(Intent.class));
|
||||
verify(mBatteryChartPreferenceController).selectHighlightSlotIndex();
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_ACCEPT, "AppAnomaly");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClick_dismissBtnOfAppsAnomaly_removeHighlightSlotIndex() {
|
||||
final PowerAnomalyEvent appsAnomaly = BatteryTestUtils.createAppAnomalyEvent();
|
||||
when(mFeatureFactory.powerUsageFeatureProvider.isBatteryTipsEnabled()).thenReturn(true);
|
||||
when(mFakeView.getId()).thenReturn(R.id.dismiss_button);
|
||||
|
||||
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(appsAnomaly);
|
||||
mBatteryTipsCardPreference.onClick(mFakeView);
|
||||
|
||||
assertThat(mBatteryTipsCardPreference.isVisible()).isFalse();
|
||||
verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(
|
||||
eq(BatteryChartViewModel.SELECTED_INDEX_INVALID),
|
||||
eq(BatteryChartViewModel.SELECTED_INDEX_INVALID));
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_DISMISS, "AppAnomaly");
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.settings.fuelgauge.batteryusage;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -73,6 +75,19 @@ public final class BatteryTipsControllerTest {
|
||||
verify(mBatteryTipsCardPreference).setVisible(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDismissRecordKey_returnExpectedResult() {
|
||||
assertThat(BatteryTipsController.getDismissRecordKey(
|
||||
BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent()))
|
||||
.isEqualTo("KEY_BRIGHTNESS");
|
||||
assertThat(BatteryTipsController.getDismissRecordKey(
|
||||
BatteryTestUtils.createScreenTimeoutAnomalyEvent()))
|
||||
.isEqualTo("KEY_SCREEN_TIMEOUT");
|
||||
assertThat(BatteryTipsController.getDismissRecordKey(
|
||||
BatteryTestUtils.createAppAnomalyEvent()))
|
||||
.isEqualTo("KEY_APP_1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleBatteryTipsCardUpdated_adaptiveBrightnessAnomaly_showAnomaly() {
|
||||
PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent();
|
||||
@@ -80,7 +95,6 @@ public final class BatteryTipsControllerTest {
|
||||
|
||||
mBatteryTipsController.handleBatteryTipsCardUpdated(event);
|
||||
|
||||
verify(mBatteryTipsCardPreference).setAnomalyEventId("BrightnessAnomaly");
|
||||
// Check pre-defined string
|
||||
verify(mBatteryTipsCardPreference).setTitle(
|
||||
"Turn on adaptive brightness to extend battery life");
|
||||
@@ -90,9 +104,6 @@ public final class BatteryTipsControllerTest {
|
||||
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
|
||||
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
|
||||
// Check proto info
|
||||
verify(mBatteryTipsCardPreference).setMainButtonLauncherInfo(
|
||||
"com.android.settings.DisplaySettings",
|
||||
46, "auto_brightness_entry");
|
||||
verify(mBatteryTipsCardPreference).setVisible(true);
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "BrightnessAnomaly");
|
||||
@@ -105,16 +116,12 @@ public final class BatteryTipsControllerTest {
|
||||
|
||||
mBatteryTipsController.handleBatteryTipsCardUpdated(event);
|
||||
|
||||
verify(mBatteryTipsCardPreference).setAnomalyEventId("ScreenTimeoutAnomaly");
|
||||
verify(mBatteryTipsCardPreference).setTitle("Reduce screen timeout to extend battery life");
|
||||
verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
|
||||
verify(mBatteryTipsCardPreference).setMainButtonStrokeColorResourceId(
|
||||
R.color.color_accent_selector);
|
||||
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
|
||||
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
|
||||
verify(mBatteryTipsCardPreference).setMainButtonLauncherInfo(
|
||||
"com.android.settings.display.ScreenTimeoutSettings",
|
||||
1852, "60000");
|
||||
verify(mBatteryTipsCardPreference).setVisible(true);
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "ScreenTimeoutAnomaly");
|
||||
@@ -134,16 +141,12 @@ public final class BatteryTipsControllerTest {
|
||||
|
||||
mBatteryTipsController.handleBatteryTipsCardUpdated(event);
|
||||
|
||||
verify(mBatteryTipsCardPreference).setAnomalyEventId("ScreenTimeoutAnomaly");
|
||||
verify(mBatteryTipsCardPreference).setTitle(testTitle);
|
||||
verify(mBatteryTipsCardPreference).setIconResourceId(R.drawable.ic_battery_tips_lightbulb);
|
||||
verify(mBatteryTipsCardPreference).setMainButtonStrokeColorResourceId(
|
||||
R.color.color_accent_selector);
|
||||
verify(mBatteryTipsCardPreference).setMainButtonLabel("View Settings");
|
||||
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
|
||||
verify(mBatteryTipsCardPreference).setMainButtonLauncherInfo(
|
||||
"com.android.settings.display.ScreenTimeoutSettings",
|
||||
1852, "60000");
|
||||
verify(mBatteryTipsCardPreference).setVisible(true);
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "ScreenTimeoutAnomaly");
|
||||
@@ -156,7 +159,6 @@ public final class BatteryTipsControllerTest {
|
||||
|
||||
mBatteryTipsController.handleBatteryTipsCardUpdated(event);
|
||||
|
||||
verify(mBatteryTipsCardPreference).setAnomalyEventId("AppAnomaly");
|
||||
verify(mBatteryTipsCardPreference).setTitle(
|
||||
"Chrome used more battery than usual in foreground");
|
||||
verify(mBatteryTipsCardPreference).setIconResourceId(
|
||||
@@ -165,8 +167,6 @@ public final class BatteryTipsControllerTest {
|
||||
R.color.color_battery_anomaly_yellow_selector);
|
||||
verify(mBatteryTipsCardPreference).setMainButtonLabel("Check");
|
||||
verify(mBatteryTipsCardPreference).setDismissButtonLabel("Got it");
|
||||
verify(mBatteryTipsCardPreference).setMainButtonLauncherInfo(
|
||||
null, null, null);
|
||||
verify(mBatteryTipsCardPreference).setVisible(true);
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext, SettingsEnums.ACTION_BATTERY_TIPS_CARD_SHOW, "AppAnomaly");
|
||||
|
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.settings.fuelgauge.batteryusage;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.ArgumentMatchers.notNull;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.settings.testutils.BatteryTestUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowDashboardFragment;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowDashboardFragment.class)
|
||||
public final class PowerUsageAdvancedTest {
|
||||
|
||||
private Context mContext;
|
||||
private PowerUsageAdvanced mPowerUsageAdvanced;
|
||||
|
||||
@Mock
|
||||
private BatteryTipsController mBatteryTipsController;
|
||||
@Mock
|
||||
private BatteryChartPreferenceController mBatteryChartPreferenceController;
|
||||
@Mock
|
||||
private ScreenOnTimeController mScreenOnTimeController;
|
||||
@Mock
|
||||
private BatteryUsageBreakdownController mBatteryUsageBreakdownController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
|
||||
mPowerUsageAdvanced = new PowerUsageAdvanced();
|
||||
mPowerUsageAdvanced.mBatteryTipsController = mBatteryTipsController;
|
||||
mPowerUsageAdvanced.mBatteryChartPreferenceController = mBatteryChartPreferenceController;
|
||||
mPowerUsageAdvanced.mScreenOnTimeController = mScreenOnTimeController;
|
||||
mPowerUsageAdvanced.mBatteryUsageBreakdownController = mBatteryUsageBreakdownController;
|
||||
mPowerUsageAdvanced.mBatteryLevelData = Optional.of(new BatteryLevelData(Map.of(
|
||||
1694354400000L, 1, // 2023-09-10 22:00:00
|
||||
1694361600000L, 2, // 2023-09-11 00:00:00
|
||||
1694368800000L, 3))); // 2023-09-11 02:00:00
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighestScoreAnomalyEvent_withEmptyOrNullList_getNull() {
|
||||
assertThat(PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, null)).isNull();
|
||||
assertThat(PowerUsageAdvanced.getHighestScoreAnomalyEvent(
|
||||
mContext, BatteryTestUtils.createEmptyPowerAnomalyEventList())).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighestScoreAnomalyEvent_withoutDismissed_getHighestScoreEvent() {
|
||||
final PowerAnomalyEventList powerAnomalyEventList =
|
||||
BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
|
||||
|
||||
final PowerAnomalyEvent highestScoreEvent =
|
||||
PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList);
|
||||
|
||||
assertThat(highestScoreEvent)
|
||||
.isEqualTo(BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighestScoreAnomalyEvent_withBrightnessDismissed_getScreenTimeout() {
|
||||
final PowerAnomalyEventList powerAnomalyEventList =
|
||||
BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
|
||||
DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
|
||||
DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, PowerAnomalyKey.KEY_BRIGHTNESS.name());
|
||||
|
||||
final PowerAnomalyEvent highestScoreEvent =
|
||||
PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList);
|
||||
|
||||
assertThat(highestScoreEvent)
|
||||
.isEqualTo(BatteryTestUtils.createScreenTimeoutAnomalyEvent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighestScoreAnomalyEvent_withAllDismissed_getNull() {
|
||||
final PowerAnomalyEventList powerAnomalyEventList =
|
||||
BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
|
||||
DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
|
||||
for (PowerAnomalyKey key : PowerAnomalyKey.values()) {
|
||||
DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, key.name());
|
||||
}
|
||||
|
||||
final PowerAnomalyEvent highestScoreEvent =
|
||||
PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList);
|
||||
|
||||
assertThat(highestScoreEvent).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDisplayAnomalyEventUpdated_withSettingsAnomalyEvent_skipHighlightSlotEffect() {
|
||||
final PowerAnomalyEvent event = BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent();
|
||||
|
||||
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event);
|
||||
|
||||
assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isEqualTo(event);
|
||||
verify(mBatteryTipsController).handleBatteryTipsCardUpdated(eq(event));
|
||||
verify(mPowerUsageAdvanced.mBatteryTipsController).setOnAnomalyConfirmListener(isNull());
|
||||
verify(mPowerUsageAdvanced.mBatteryTipsController).setOnAnomalyRejectListener(isNull());
|
||||
verify(mPowerUsageAdvanced.mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(
|
||||
eq(BatteryChartViewModel.SELECTED_INDEX_INVALID),
|
||||
eq(BatteryChartViewModel.SELECTED_INDEX_INVALID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDisplayAnomalyEventUpdated_withAppAnomalyEvent_setHighlightSlotEffect() {
|
||||
final PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent();
|
||||
|
||||
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event);
|
||||
|
||||
assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isEqualTo(event);
|
||||
verify(mBatteryTipsController).handleBatteryTipsCardUpdated(eq(event));
|
||||
verify(mBatteryTipsController).setOnAnomalyConfirmListener(isNull());
|
||||
verify(mBatteryTipsController).setOnAnomalyRejectListener(isNull());
|
||||
|
||||
assertThat(event.getWarningItemInfo().hasStartTimestamp()).isTrue();
|
||||
assertThat(event.getWarningItemInfo().hasEndTimestamp()).isTrue();
|
||||
assertThat(mPowerUsageAdvanced.mBatteryLevelData.get().getIndexByTimestamps(
|
||||
event.getWarningItemInfo().getStartTimestamp(),
|
||||
event.getWarningItemInfo().getEndTimestamp()
|
||||
)).isEqualTo(Pair.create(1, 0));
|
||||
verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(eq(1), eq(0));
|
||||
verify(mBatteryTipsController).setOnAnomalyConfirmListener(notNull());
|
||||
verify(mBatteryTipsController).setOnAnomalyRejectListener(notNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDisplayAnomalyEventUpdated_withNull_removeHighlightSlotEffect() {
|
||||
final PowerAnomalyEvent event = BatteryTestUtils.createAppAnomalyEvent();
|
||||
|
||||
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(event);
|
||||
mPowerUsageAdvanced.onDisplayAnomalyEventUpdated(null);
|
||||
|
||||
assertThat(mPowerUsageAdvanced.mPowerAnomalyEvent).isNull();
|
||||
verify(mBatteryTipsController, times(2))
|
||||
.setOnAnomalyConfirmListener(isNull());
|
||||
verify(mBatteryTipsController, times(2))
|
||||
.setOnAnomalyRejectListener(isNull());
|
||||
verify(mBatteryTipsController).setOnAnomalyConfirmListener(notNull());
|
||||
verify(mBatteryTipsController).setOnAnomalyRejectListener(notNull());
|
||||
|
||||
verify(mBatteryChartPreferenceController)
|
||||
.onHighlightSlotIndexUpdate(eq(1), eq(0));
|
||||
verify(mBatteryChartPreferenceController).onHighlightSlotIndexUpdate(
|
||||
eq(BatteryChartViewModel.SELECTED_INDEX_INVALID),
|
||||
eq(BatteryChartViewModel.SELECTED_INDEX_INVALID));
|
||||
}
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
package com.android.settings.system;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
import com.android.settings.testutils.shadow.ShadowUtils;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowUserManager.class)
|
||||
public class FactoryResetDemoUserPreferenceControllerTest {
|
||||
|
||||
private static final String FACTORY_RESET_DEMO_USER_KEY = "factory_reset_demo_user";
|
||||
|
||||
private ShadowUserManager mShadowUserManager;
|
||||
|
||||
private Context mContext;
|
||||
private FactoryResetDemoUserPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mShadowUserManager = ShadowUserManager.getShadow();
|
||||
|
||||
mController = new FactoryResetDemoUserPreferenceController(
|
||||
mContext, FACTORY_RESET_DEMO_USER_KEY);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowUtils.reset();
|
||||
mShadowUserManager.setIsAdminUser(false);
|
||||
mShadowUserManager.setIsDemoUser(false);
|
||||
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_DEMO_MODE, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_systemUser() {
|
||||
mShadowUserManager.setIsAdminUser(true);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_nonSystemUser() {
|
||||
mShadowUserManager.setIsAdminUser(false);
|
||||
mShadowUserManager.setIsDemoUser(false);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_demoUser() {
|
||||
mShadowUserManager.setIsAdminUser(false);
|
||||
|
||||
// Place the device in demo mode.
|
||||
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_DEMO_MODE, 1);
|
||||
|
||||
// Indicate the user is a demo user.
|
||||
mShadowUserManager.addUser(UserHandle.myUserId(), "test", UserInfo.FLAG_DEMO);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPreferenceKey() {
|
||||
assertThat(mController.getPreferenceKey()).isEqualTo(FACTORY_RESET_DEMO_USER_KEY);
|
||||
}
|
||||
}
|
@@ -49,7 +49,7 @@ public class FactoryResetPreferenceControllerTest {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mShadowUserManager = ShadowUserManager.getShadow();
|
||||
|
||||
mController = new FactoryResetPreferenceController(mContext);
|
||||
mController = new FactoryResetPreferenceController(mContext, FACTORY_RESET_KEY);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -85,7 +85,7 @@ public class FactoryResetPreferenceControllerTest {
|
||||
// Indicate the user is a demo user.
|
||||
mShadowUserManager.addUser(UserHandle.myUserId(), "test", UserInfo.FLAG_DEMO);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -283,6 +283,9 @@ public class BatteryTestUtils {
|
||||
.setKey(PowerAnomalyKey.KEY_APP)
|
||||
.setScore(2.0f)
|
||||
.setWarningItemInfo(WarningItemInfo.newBuilder()
|
||||
.setDismissRecordKey("KEY_APP_1")
|
||||
.setStartTimestamp(1694361600000L) // 2023-09-11 00:00:00
|
||||
.setEndTimestamp(1694368800000L) // 2023-09-11 02:00:00
|
||||
.setTitleString("Chrome used more battery than usual in foreground")
|
||||
.setMainButtonString("Check")
|
||||
.setCancelButtonString("Got it")
|
||||
|
@@ -67,7 +67,8 @@ class UserAspectRatioAppsPageProviderTest {
|
||||
@Test
|
||||
fun injectEntry_summary() {
|
||||
setInjectEntry()
|
||||
composeTestRule.onNodeWithText(context.getString(R.string.aspect_ratio_summary, Build.MODEL))
|
||||
composeTestRule
|
||||
.onNodeWithText(context.getString(R.string.aspect_ratio_summary, Build.MODEL))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user