diff --git a/res/layout/battery_chart_graph.xml b/res/layout/battery_chart_graph.xml index f116c8ee313..9e816ed9ba1 100644 --- a/res/layout/battery_chart_graph.xml +++ b/res/layout/battery_chart_graph.xml @@ -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 @@ 6dp 1dp 4dp + 4dp + 4dp + 12dp + 2dp + 12dp + 182dp 5dp 1dp 2dp diff --git a/res/values/strings.xml b/res/values/strings.xml index 48caa5e1348..aef86a6f072 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9934,7 +9934,7 @@ Gesture navigation - 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. + 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. 3-button navigation @@ -12144,7 +12144,9 @@ Aspect ratio - Choose an aspect ratio to view this app if it hasn\'t been designed to fit your %1$s + Try a new aspect ratio to view this app if it hasn\'t been designed to fit your %1$s + + Try a new aspect ratio to view this app if it hasn\'t been designed to fit your %1$s. Some apps may not be optimized for certain aspect ratios. Suggested apps @@ -12164,7 +12166,7 @@ 4:3 - The app will restart when you change aspect ratio. You may lose unsaved changes. + The app will restart when you change aspect ratio. You may lose unsaved changes. Some apps may not be optimized for certain aspect ratios. diff --git a/res/xml/apps.xml b/res/xml/apps.xml index 651ed9bc98d..db46a1a7265 100644 --- a/res/xml/apps.xml +++ b/res/xml/apps.xml @@ -79,18 +79,6 @@ android:key="dashboard_tile_placeholder" android:order="10"/> - - - - - + + + + + + + diff --git a/res/xml/reset_dashboard_fragment.xml b/res/xml/reset_dashboard_fragment.xml index 3bd7a136760..08852c92c86 100644 --- a/res/xml/reset_dashboard_fragment.xml +++ b/res/xml/reset_dashboard_fragment.xml @@ -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" /> + + diff --git a/src/com/android/settings/MainClear.java b/src/com/android/settings/MainClear.java index f706c785401..8a441e2c2ce 100644 --- a/src/com/android/settings/MainClear.java +++ b/src/com/android/settings/MainClear.java @@ -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()) diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java index 30eabfabb3f..4253ca6db55 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java @@ -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 */ diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java index 127178a4993..5931e206b1d 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java @@ -80,11 +80,6 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider return false; } - @Override - public boolean isBatteryTipsFeedbackEnabled() { - return false; - } - @Override public double getBatteryUsageListScreenOnTimeThresholdInMs() { return 0; diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java index 1720f0d62b1..1893096dbf4 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java @@ -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> 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 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> 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 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) { diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java index 086f56c03fe..bb468fe35d6 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java @@ -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) { diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java index f58d2415e19..bf8a771a117 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java @@ -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 levels, @NonNull List 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); diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java index 53ebbd90595..09d66c704dc 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelData.java @@ -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 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; } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java index ea5534d6a60..e98077c699c 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreference.java @@ -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); } } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java index 80b26950383..400e70ad5dd 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsController.java @@ -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 getInfo(PowerAnomalyEvent powerAnomalyEvent, Function warningBannerInfoSupplier, Function 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); } } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java index ed5f182fd90..952b83f440f 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java @@ -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) { diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java index ec0d01a4a85..a1987c9b8ad 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java +++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java @@ -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( diff --git a/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobManager.java b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobManager.java index 8c0e66c78d8..43cd69dc942 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobManager.java +++ b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobManager.java @@ -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); } diff --git a/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java index 2371a19f85c..dccca43e680 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java +++ b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java @@ -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; } diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java index e4f8b39540d..4e8e396528e 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java +++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java @@ -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 mBatteryLevelData; + private Map> 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 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 createPreferenceControllers(Context context) { final List 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 batteryDiffDataMap) { - if (mBatteryLevelData != null && mBatteryChartPreferenceController != null) { - Map> 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 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 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> 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); } }; } diff --git a/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto b/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto index e3b604ba0a7..5bc1a3e330e 100644 --- a/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto +++ b/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto @@ -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; } diff --git a/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto b/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto index 644ab9eba54..99df215f5aa 100644 --- a/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto +++ b/src/com/android/settings/fuelgauge/protos/power_anomaly_event.proto @@ -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; } diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt index c58f2d4c3c4..188459994b2 100644 --- a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt +++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt @@ -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 diff --git a/src/com/android/settings/system/FactoryResetDemoUserPreferenceController.java b/src/com/android/settings/system/FactoryResetDemoUserPreferenceController.java new file mode 100644 index 00000000000..f6a9b3198a3 --- /dev/null +++ b/src/com/android/settings/system/FactoryResetDemoUserPreferenceController.java @@ -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; + } +} diff --git a/src/com/android/settings/system/FactoryResetPreferenceController.java b/src/com/android/settings/system/FactoryResetPreferenceController.java index a307171d122..6e010c1fbc2 100644 --- a/src/com/android/settings/system/FactoryResetPreferenceController.java +++ b/src/com/android/settings/system/FactoryResetPreferenceController.java @@ -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; diff --git a/src/com/android/settings/system/ResetDashboardFragment.java b/src/com/android/settings/system/ResetDashboardFragment.java index aea92aafc90..662edc53b20 100644 --- a/src/com/android/settings/system/ResetDashboardFragment.java +++ b/src/com/android/settings/system/ResetDashboardFragment.java @@ -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; } diff --git a/src/com/android/settings/system/ResetPreferenceController.java b/src/com/android/settings/system/ResetPreferenceController.java index 0740ac9dae0..35f1ff7a9f2 100644 --- a/src/com/android/settings/system/ResetPreferenceController.java +++ b/src/com/android/settings/system/ResetPreferenceController.java @@ -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 diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java index bf4e8938f88..a0b449a3ba4 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImplTest.java @@ -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); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java index 786a529814d..cd4e599546d 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java @@ -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> 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( diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelDataTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelDataTest.java index 13d60bb6c12..7dc4eabf6f4 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelDataTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryLevelDataTest.java @@ -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)); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java index ac67dfdf86b..630ff45bf2f 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsCardPreferenceTest.java @@ -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 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"); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java index ac9de1fa336..913c00a1fb6 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryTipsControllerTest.java @@ -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"); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java new file mode 100644 index 00000000000..953c2d4ef39 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java @@ -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)); + } +} diff --git a/tests/robotests/src/com/android/settings/system/FactoryResetDemoUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/system/FactoryResetDemoUserPreferenceControllerTest.java new file mode 100644 index 00000000000..0c92b057f92 --- /dev/null +++ b/tests/robotests/src/com/android/settings/system/FactoryResetDemoUserPreferenceControllerTest.java @@ -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); + } +} diff --git a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java index f2a932ee0fe..6e6fad83a27 100644 --- a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java @@ -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 diff --git a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java index 3297d1ef4de..10355605747 100644 --- a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java @@ -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") diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt index c314655e0d2..044968d75e3 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProviderTest.kt @@ -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() }