Snap for 8891587 from 0dc8d58de5 to udc-release
Change-Id: I2666daf4de325aa4ba22ecc3b9f1a65f5532761f
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
|
||||
<com.android.settings.accessibility.LocalePreference
|
||||
android:key="captioning_locale"
|
||||
android:summary="%s"
|
||||
android:title="@string/captioning_locale"
|
||||
settings:controller="com.android.settings.accessibility.CaptionLocalePreferenceController"/>
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ public class CaptionCustomController extends BasePreferenceController
|
||||
private Preference mCustom;
|
||||
private final CaptionHelper mCaptionHelper;
|
||||
private final ContentResolver mContentResolver;
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
@VisibleForTesting
|
||||
AccessibilitySettingsContentObserver mSettingsContentObserver;
|
||||
@VisibleForTesting
|
||||
@@ -54,7 +53,8 @@ public class CaptionCustomController extends BasePreferenceController
|
||||
super(context, preferenceKey);
|
||||
mCaptionHelper = new CaptionHelper(context);
|
||||
mContentResolver = context.getContentResolver();
|
||||
mSettingsContentObserver = new AccessibilitySettingsContentObserver(mHandler);
|
||||
mSettingsContentObserver = new AccessibilitySettingsContentObserver(
|
||||
new Handler(Looper.getMainLooper()));
|
||||
mSettingsContentObserver.registerKeysToObserverCallback(CAPTIONING_FEATURE_KEYS,
|
||||
key -> refreshShowingCustom());
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ public class CaptionFontSizeController extends BasePreferenceController
|
||||
|
||||
private final CaptioningManager mCaptioningManager;
|
||||
private final CaptionHelper mCaptionHelper;
|
||||
private ListPreference mPreference;
|
||||
|
||||
public CaptionFontSizeController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
@@ -49,19 +48,19 @@ public class CaptionFontSizeController extends BasePreferenceController
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
|
||||
final ListPreference listPreference = screen.findPreference(getPreferenceKey());
|
||||
final float fontSize = mCaptioningManager.getFontScale();
|
||||
mPreference.setValue(Float.toString(fontSize));
|
||||
listPreference.setValue(Float.toString(fontSize));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final ListPreference listPreference = (ListPreference) preference;
|
||||
final ContentResolver cr = mContext.getContentResolver();
|
||||
Settings.Secure.putFloat(
|
||||
cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE,
|
||||
Float.parseFloat((String) newValue));
|
||||
mPreference.setValue((String) newValue);
|
||||
listPreference.setValue((String) newValue);
|
||||
mCaptionHelper.setEnabled(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -188,17 +188,27 @@ public class CaptionHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the caption raw user style.
|
||||
* Sets the captioning raw user style.
|
||||
*
|
||||
* @param type The caption raw user style
|
||||
* @param type The captioning raw user style
|
||||
*/
|
||||
public void setRawUserStyle(int type) {
|
||||
Settings.Secure.putInt(mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET, type);
|
||||
}
|
||||
|
||||
/** Returns the caption raw user style.*/
|
||||
/** Returns the captioning raw preset number.*/
|
||||
public int getRawUserStyle() {
|
||||
return mCaptioningManager.getRawUserStyle();
|
||||
}
|
||||
|
||||
/** Returns the captioning visual properties.*/
|
||||
public CaptionStyle getUserStyle() {
|
||||
return mCaptioningManager.getUserStyle();
|
||||
}
|
||||
|
||||
/** Returns the captioning locale language.*/
|
||||
public Locale getLocale() {
|
||||
return mCaptioningManager.getLocale();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ public class CaptionLocalePreferenceController extends BasePreferenceController
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
private final CaptioningManager mCaptioningManager;
|
||||
private LocalePreference mPreference;
|
||||
|
||||
public CaptionLocalePreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
@@ -45,22 +44,17 @@ public class CaptionLocalePreferenceController extends BasePreferenceController
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
final LocalePreference localePreference = screen.findPreference(getPreferenceKey());
|
||||
final String rawLocale = mCaptioningManager.getRawLocale();
|
||||
mPreference.setValue(rawLocale == null ? "" : rawLocale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return mPreference.getEntry();
|
||||
localePreference.setValue(rawLocale == null ? "" : rawLocale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final LocalePreference localePreference = (LocalePreference) preference;
|
||||
Settings.Secure.putString(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_CAPTIONING_LOCALE, (String) newValue);
|
||||
mPreference.setValue((String) newValue);
|
||||
mPreference.setSummary(mPreference.getEntry());
|
||||
localePreference.setValue((String) newValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.CaptioningManager;
|
||||
import android.view.accessibility.CaptioningManager.CaptionStyle;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
@@ -57,16 +57,11 @@ public class CaptionPreviewPreferenceController extends BasePreferenceController
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
@VisibleForTesting
|
||||
AccessibilitySettingsContentObserver mSettingsContentObserver;
|
||||
private CaptioningManager mCaptioningManager;
|
||||
private CaptionHelper mCaptionHelper;
|
||||
private LayoutPreference mPreference;
|
||||
private SubtitleView mPreviewText;
|
||||
private View mPreviewWindow;
|
||||
private View mPreviewViewport;
|
||||
|
||||
public CaptionPreviewPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mCaptioningManager = context.getSystemService(CaptioningManager.class);
|
||||
mCaptionHelper = new CaptionHelper(context);
|
||||
mSettingsContentObserver = new AccessibilitySettingsContentObserver(mHandler);
|
||||
mSettingsContentObserver.registerKeysToObserverCallback(CAPTIONING_FEATURE_KEYS,
|
||||
@@ -92,16 +87,14 @@ public class CaptionPreviewPreferenceController extends BasePreferenceController
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
mPreviewText = mPreference.findViewById(R.id.preview_text);
|
||||
mPreviewWindow = mPreference.findViewById(R.id.preview_window);
|
||||
mPreviewViewport = mPreference.findViewById(R.id.preview_viewport);
|
||||
mPreviewViewport.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
|
||||
final View previewViewport = mPreference.findViewById(R.id.preview_viewport);
|
||||
previewViewport.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
|
||||
@Override
|
||||
public void onLayoutChange(View v, int left, int top, int right,
|
||||
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
|
||||
if ((oldRight - oldLeft) != (right - left)) {
|
||||
// Remove the listener once the callback is triggered.
|
||||
mPreviewViewport.removeOnLayoutChangeListener(this);
|
||||
previewViewport.removeOnLayoutChangeListener(this);
|
||||
mHandler.post(() -> refreshPreviewText());
|
||||
}
|
||||
}
|
||||
@@ -109,26 +102,28 @@ public class CaptionPreviewPreferenceController extends BasePreferenceController
|
||||
}
|
||||
|
||||
private void refreshPreviewText() {
|
||||
if (mPreviewText != null) {
|
||||
final int styleId = mCaptioningManager.getRawUserStyle();
|
||||
mCaptionHelper.applyCaptionProperties(mPreviewText, mPreviewViewport, styleId);
|
||||
final SubtitleView previewText = mPreference.findViewById(R.id.preview_text);
|
||||
if (previewText != null) {
|
||||
final View previewViewport = mPreference.findViewById(R.id.preview_viewport);
|
||||
final int styleId = mCaptionHelper.getRawUserStyle();
|
||||
mCaptionHelper.applyCaptionProperties(previewText, previewViewport, styleId);
|
||||
|
||||
final Locale locale = mCaptioningManager.getLocale();
|
||||
final Locale locale = mCaptionHelper.getLocale();
|
||||
if (locale != null) {
|
||||
final CharSequence localizedText = AccessibilityUtils.getTextForLocale(
|
||||
mContext, locale, R.string.captioning_preview_text);
|
||||
mPreviewText.setText(localizedText);
|
||||
previewText.setText(localizedText);
|
||||
} else {
|
||||
mPreviewText.setText(R.string.captioning_preview_text);
|
||||
previewText.setText(R.string.captioning_preview_text);
|
||||
}
|
||||
|
||||
final CaptioningManager.CaptionStyle style = mCaptioningManager.getUserStyle();
|
||||
final View previewWindow = mPreference.findViewById(R.id.preview_window);
|
||||
final CaptionStyle style = mCaptionHelper.getUserStyle();
|
||||
if (style.hasWindowColor()) {
|
||||
mPreviewWindow.setBackgroundColor(style.windowColor);
|
||||
previewWindow.setBackgroundColor(style.windowColor);
|
||||
} else {
|
||||
final CaptioningManager.CaptionStyle defStyle =
|
||||
CaptioningManager.CaptionStyle.DEFAULT;
|
||||
mPreviewWindow.setBackgroundColor(defStyle.windowColor);
|
||||
final CaptionStyle defStyle = CaptionStyle.DEFAULT;
|
||||
previewWindow.setBackgroundColor(defStyle.windowColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,9 +57,9 @@ public class CaptionTogglePreferenceController extends TogglePreferenceControlle
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
|
||||
SettingsMainSwitchPreference pref = screen.findPreference(getPreferenceKey());
|
||||
pref.addOnSwitchChangeListener(this);
|
||||
pref.setChecked(isChecked());
|
||||
SettingsMainSwitchPreference preference = screen.findPreference(getPreferenceKey());
|
||||
preference.addOnSwitchChangeListener(this);
|
||||
preference.setChecked(isChecked());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,7 +32,6 @@ public class CaptionTypefaceController extends BasePreferenceController
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
private final CaptionHelper mCaptionHelper;
|
||||
private ListPreference mPreference;
|
||||
|
||||
public CaptionTypefaceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
@@ -47,20 +46,20 @@ public class CaptionTypefaceController extends BasePreferenceController
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
|
||||
final ListPreference listPreference = screen.findPreference(getPreferenceKey());
|
||||
final ContentResolver cr = mContext.getContentResolver();
|
||||
final CaptionStyle attrs = CaptionStyle.getCustomStyle(cr);
|
||||
final String rawTypeface = attrs.mRawTypeface;
|
||||
mPreference.setValue(rawTypeface == null ? "" : rawTypeface);
|
||||
listPreference.setValue(rawTypeface == null ? "" : rawTypeface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final ListPreference listPreference = (ListPreference) preference;
|
||||
final ContentResolver cr = mContext.getContentResolver();
|
||||
Settings.Secure.putString(
|
||||
cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE, (String) newValue);
|
||||
mPreference.setValue((String) newValue);
|
||||
listPreference.setValue((String) newValue);
|
||||
mCaptionHelper.setEnabled(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -266,8 +266,9 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
||||
Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null"
|
||||
: ("size=" + batteryHistoryMap.size())));
|
||||
// TODO: implement the callback function.
|
||||
final BatteryLevelData batteryLevelData =
|
||||
DataProcessor.getBatteryLevelData(mContext, batteryHistoryMap);
|
||||
DataProcessor.getBatteryLevelData(mContext, mHandler, batteryHistoryMap, null);
|
||||
Log.d(TAG, "getBatteryLevelData: " + batteryLevelData);
|
||||
if (batteryLevelData == null) {
|
||||
mDailyViewModel = null;
|
||||
@@ -279,15 +280,17 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
||||
batteryLevelData.getDailyBatteryLevels().getLevels(),
|
||||
generateTimestampDayOfWeekTexts(
|
||||
mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps()),
|
||||
mDailyChartIndex);
|
||||
mDailyChartIndex,
|
||||
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS);
|
||||
mHourlyViewModels = new ArrayList<>();
|
||||
for (BatteryLevelData.PeriodBatteryLevelData perDayData :
|
||||
for (BatteryLevelData.PeriodBatteryLevelData hourlyBatteryLevelsPerDay :
|
||||
batteryLevelData.getHourlyBatteryLevelsPerDay()) {
|
||||
mHourlyViewModels.add(new BatteryChartViewModel(
|
||||
perDayData.getLevels(),
|
||||
hourlyBatteryLevelsPerDay.getLevels(),
|
||||
generateTimestampHourTexts(
|
||||
mContext, perDayData.getTimestamps()),
|
||||
mHourlyChartIndex));
|
||||
mContext, hourlyBatteryLevelsPerDay.getTimestamps()),
|
||||
mHourlyChartIndex,
|
||||
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||
}
|
||||
refreshUi();
|
||||
// TODO: Loads item icon and label and build mBatteryIndexedMap.
|
||||
|
||||
@@ -33,23 +33,31 @@ class BatteryChartViewModel {
|
||||
// We need at least 2 levels to draw a trapezoid.
|
||||
private static final int MIN_LEVELS_DATA_SIZE = 2;
|
||||
|
||||
enum AxisLabelPosition {
|
||||
BETWEEN_TRAPEZOIDS,
|
||||
CENTER_OF_TRAPEZOIDS,
|
||||
}
|
||||
|
||||
private final List<Integer> mLevels;
|
||||
private final List<String> mTexts;
|
||||
private final AxisLabelPosition mAxisLabelPosition;
|
||||
private int mSelectedIndex;
|
||||
|
||||
BatteryChartViewModel(
|
||||
@NonNull List<Integer> levels, @NonNull List<String> texts, int selectedIndex) {
|
||||
@NonNull List<Integer> levels, @NonNull List<String> texts, int selectedIndex,
|
||||
@NonNull AxisLabelPosition axisLabelPosition) {
|
||||
Preconditions.checkArgument(
|
||||
levels.size() == texts.size()
|
||||
&& levels.size() >= MIN_LEVELS_DATA_SIZE
|
||||
&& selectedIndex >= SELECTED_INDEX_ALL
|
||||
&& selectedIndex < levels.size(),
|
||||
String.format(Locale.getDefault(), "Invalid BatteryChartViewModel"
|
||||
String.format(Locale.ENGLISH, "Invalid BatteryChartViewModel"
|
||||
+ " levels.size: %d\ntexts.size: %d\nselectedIndex: %d.",
|
||||
levels.size(), texts.size(), selectedIndex));
|
||||
mLevels = levels;
|
||||
mTexts = texts;
|
||||
mSelectedIndex = selectedIndex;
|
||||
mAxisLabelPosition = axisLabelPosition;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
@@ -72,9 +80,13 @@ class BatteryChartViewModel {
|
||||
mSelectedIndex = index;
|
||||
}
|
||||
|
||||
public AxisLabelPosition axisLabelPosition() {
|
||||
return mAxisLabelPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mLevels, mTexts, mSelectedIndex);
|
||||
return Objects.hash(mLevels, mTexts, mSelectedIndex, mAxisLabelPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,12 +99,15 @@ class BatteryChartViewModel {
|
||||
final BatteryChartViewModel batteryChartViewModel = (BatteryChartViewModel) other;
|
||||
return Objects.equals(mLevels, batteryChartViewModel.mLevels)
|
||||
&& Objects.equals(mTexts, batteryChartViewModel.mTexts)
|
||||
&& mSelectedIndex == batteryChartViewModel.mSelectedIndex;
|
||||
&& mSelectedIndex == batteryChartViewModel.mSelectedIndex
|
||||
&& mAxisLabelPosition == batteryChartViewModel.mAxisLabelPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(Locale.getDefault(), "levels: %s\ntexts: %s\nselectedIndex: %d",
|
||||
Objects.toString(mLevels), Objects.toString(mTexts), mSelectedIndex);
|
||||
return String.format(Locale.ENGLISH,
|
||||
"levels: %s\ntexts: %s\nselectedIndex: %d, axisLabelPosition: %s",
|
||||
Objects.toString(mLevels), Objects.toString(mTexts), mSelectedIndex,
|
||||
mAxisLabelPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,11 @@ import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.Utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/** A widget component to draw chart graph. */
|
||||
public class BatteryChartViewV2 extends AppCompatImageView implements View.OnClickListener,
|
||||
@@ -57,10 +60,10 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
private static final List<String> ACCESSIBILITY_SERVICE_NAMES =
|
||||
Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService");
|
||||
|
||||
private static final int DEFAULT_AXIS_LABEL_COUNT = 4;
|
||||
private static final int AXIS_LABEL_GAPS_COUNT = DEFAULT_AXIS_LABEL_COUNT - 1;
|
||||
private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5");
|
||||
private static final long UPDATE_STATE_DELAYED_TIME = 500L;
|
||||
private static final Map<Integer, Integer[]> MODEL_SIZE_TO_LABEL_INDEXES_MAP =
|
||||
buildModelSizeToLabelIndexesMap();
|
||||
|
||||
/** A callback listener for selected group index is updated. */
|
||||
public interface OnSelectListener {
|
||||
@@ -76,11 +79,10 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
private float mTrapezoidHOffset;
|
||||
private boolean mIsSlotsClickabled;
|
||||
private String[] mPercentages = getPercentages();
|
||||
private Integer[] mLabelsIndexes;
|
||||
|
||||
@VisibleForTesting
|
||||
int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||
@VisibleForTesting
|
||||
String[] mAxisLabels;
|
||||
|
||||
// Colors for drawing the trapezoid shape and dividers.
|
||||
private int mTrapezoidColor;
|
||||
@@ -92,8 +94,7 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
private final Rect[] mPercentageBounds =
|
||||
new Rect[]{new Rect(), new Rect(), new Rect()};
|
||||
// For drawing the axis label information.
|
||||
private final Rect[] mAxisLabelsBounds =
|
||||
new Rect[]{new Rect(), new Rect(), new Rect(), new Rect()};
|
||||
private final Rect[] mAxisLabelsBounds = initializeAxisLabelsBounds();
|
||||
|
||||
@VisibleForTesting
|
||||
Handler mHandler = new Handler();
|
||||
@@ -137,9 +138,8 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
Log.d(TAG, String.format("setViewModel(): size: %d, selectedIndex: %d.",
|
||||
viewModel.size(), viewModel.selectedIndex()));
|
||||
mViewModel = viewModel;
|
||||
|
||||
mLabelsIndexes = MODEL_SIZE_TO_LABEL_INDEXES_MAP.get(mViewModel.size());
|
||||
initializeTrapezoidSlots(viewModel.size() - 1);
|
||||
initializeAxisLabels(viewModel.texts());
|
||||
setClickable(hasAnyValidTrapezoid(viewModel));
|
||||
requestLayout();
|
||||
}
|
||||
@@ -176,12 +176,11 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
mIndent.top = mPercentageBounds[0].height();
|
||||
mIndent.right = mPercentageBounds[0].width() + mTextPadding;
|
||||
|
||||
if (mAxisLabels != null) {
|
||||
if (mViewModel != null) {
|
||||
int maxHeight = 0;
|
||||
for (int index = 0; index < DEFAULT_AXIS_LABEL_COUNT; index++) {
|
||||
mTextPaint.getTextBounds(
|
||||
mAxisLabels[index], 0, mAxisLabels[index].length(),
|
||||
mAxisLabelsBounds[index]);
|
||||
for (int index = 0; index < mLabelsIndexes.length; index++) {
|
||||
final String text = getAxisLabelText(index);
|
||||
mTextPaint.getTextBounds(text, 0, text.length(), mAxisLabelsBounds[index]);
|
||||
maxHeight = Math.max(maxHeight, mAxisLabelsBounds[index].height());
|
||||
}
|
||||
mIndent.bottom = maxHeight + round(mTextPadding * 1.5f);
|
||||
@@ -333,21 +332,6 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the displayed X-axis labels list selected from the model all texts list.
|
||||
*/
|
||||
private void initializeAxisLabels(@NonNull List<String> allTexts) {
|
||||
if (mAxisLabels == null) {
|
||||
mAxisLabels = new String[DEFAULT_AXIS_LABEL_COUNT];
|
||||
}
|
||||
// Current logic is always showing {@code AXIS_LABEL_GAPS_COUNT} labels.
|
||||
// TODO: Support different count of labels for different levels sizes.
|
||||
final int step = (allTexts.size() - 1) / AXIS_LABEL_GAPS_COUNT;
|
||||
for (int index = 0; index < DEFAULT_AXIS_LABEL_COUNT; index++) {
|
||||
mAxisLabels[index] = allTexts.get(index * step);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeColors(Context context) {
|
||||
setBackgroundColor(Color.TRANSPARENT);
|
||||
mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context);
|
||||
@@ -432,37 +416,43 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
startX = nextX;
|
||||
}
|
||||
// Draws the axis label slot information.
|
||||
if (mAxisLabels != null) {
|
||||
final float[] xOffsets = new float[DEFAULT_AXIS_LABEL_COUNT];
|
||||
if (mViewModel != null) {
|
||||
final float[] xOffsets = new float[mLabelsIndexes.length];
|
||||
final float baselineX = mDividerWidth * .5f;
|
||||
final float offsetX = mDividerWidth + unitWidth;
|
||||
// TODO: Support different count of labels for different levels sizes.
|
||||
final int slotBarOffset = (/*total 12 bars*/ 12) / AXIS_LABEL_GAPS_COUNT;
|
||||
for (int index = 0; index < DEFAULT_AXIS_LABEL_COUNT; index++) {
|
||||
xOffsets[index] = baselineX + index * offsetX * slotBarOffset;
|
||||
for (int index = 0; index < mLabelsIndexes.length; index++) {
|
||||
xOffsets[index] = baselineX + mLabelsIndexes[index] * offsetX;
|
||||
}
|
||||
switch (mViewModel.axisLabelPosition()) {
|
||||
case CENTER_OF_TRAPEZOIDS:
|
||||
drawAxisLabelsCenterOfTrapezoids(canvas, xOffsets, unitWidth);
|
||||
break;
|
||||
case BETWEEN_TRAPEZOIDS:
|
||||
default:
|
||||
drawAxisLabelsBetweenTrapezoids(canvas, xOffsets);
|
||||
break;
|
||||
}
|
||||
drawAxisLabel(canvas, xOffsets);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawAxisLabel(Canvas canvas, float[] xOffsets) {
|
||||
private void drawAxisLabelsBetweenTrapezoids(Canvas canvas, float[] xOffsets) {
|
||||
// Draws the 1st axis label info.
|
||||
canvas.drawText(
|
||||
mAxisLabels[0], xOffsets[0] - mAxisLabelsBounds[0].left, getAxisLabelY(0),
|
||||
getAxisLabelText(0), xOffsets[0] - mAxisLabelsBounds[0].left, getAxisLabelY(0),
|
||||
mTextPaint);
|
||||
final int latestIndex = DEFAULT_AXIS_LABEL_COUNT - 1;
|
||||
final int latestIndex = mLabelsIndexes.length - 1;
|
||||
// Draws the last axis label info.
|
||||
canvas.drawText(
|
||||
mAxisLabels[latestIndex],
|
||||
getAxisLabelText(latestIndex),
|
||||
xOffsets[latestIndex]
|
||||
- mAxisLabelsBounds[latestIndex].width()
|
||||
- mAxisLabelsBounds[latestIndex].left,
|
||||
getAxisLabelY(latestIndex),
|
||||
mTextPaint);
|
||||
// Draws the rest of axis label info since it is located in the center.
|
||||
for (int index = 1; index <= DEFAULT_AXIS_LABEL_COUNT - 2; index++) {
|
||||
for (int index = 1; index <= mLabelsIndexes.length - 2; index++) {
|
||||
canvas.drawText(
|
||||
mAxisLabels[index],
|
||||
getAxisLabelText(index),
|
||||
xOffsets[index]
|
||||
- (mAxisLabelsBounds[index].width() - mAxisLabelsBounds[index].left)
|
||||
* .5f,
|
||||
@@ -471,6 +461,18 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
}
|
||||
}
|
||||
|
||||
private void drawAxisLabelsCenterOfTrapezoids(
|
||||
Canvas canvas, float[] xOffsets, float unitWidth) {
|
||||
for (int index = 0; index < mLabelsIndexes.length - 1; index++) {
|
||||
canvas.drawText(
|
||||
getAxisLabelText(index),
|
||||
xOffsets[index] + (unitWidth - (mAxisLabelsBounds[index].width()
|
||||
- mAxisLabelsBounds[index].left)) * .5f,
|
||||
getAxisLabelY(index),
|
||||
mTextPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private int getAxisLabelY(int index) {
|
||||
return getHeight()
|
||||
- mAxisLabelsBounds[index].height()
|
||||
@@ -554,6 +556,10 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
return BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||
}
|
||||
|
||||
private String getAxisLabelText(int labelIndex) {
|
||||
return mViewModel.texts().get(mLabelsIndexes[labelIndex]);
|
||||
}
|
||||
|
||||
private static boolean isTrapezoidValid(
|
||||
@NonNull BatteryChartViewModel viewModel, int trapezoidIndex) {
|
||||
return viewModel.levels().get(trapezoidIndex) != null
|
||||
@@ -607,6 +613,33 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Map<Integer, Integer[]> buildModelSizeToLabelIndexesMap() {
|
||||
final Map<Integer, Integer[]> result = new HashMap<>();
|
||||
result.put(2, new Integer[]{0, 1});
|
||||
result.put(3, new Integer[]{0, 1, 2});
|
||||
result.put(4, new Integer[]{0, 1, 2, 3});
|
||||
result.put(5, new Integer[]{0, 1, 2, 3, 4});
|
||||
result.put(6, new Integer[]{0, 1, 2, 3, 4, 5});
|
||||
result.put(7, new Integer[]{0, 1, 2, 3, 4, 5, 6});
|
||||
result.put(8, new Integer[]{0, 1, 2, 3, 4, 5, 6, 7});
|
||||
result.put(9, new Integer[]{0, 2, 4, 6, 8});
|
||||
result.put(10, new Integer[]{0, 3, 6, 9});
|
||||
result.put(11, new Integer[]{0, 5, 10});
|
||||
result.put(12, new Integer[]{0, 4, 7, 11});
|
||||
result.put(13, new Integer[]{0, 4, 8, 12});
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Rect[] initializeAxisLabelsBounds() {
|
||||
final int maxLabelsLength = MODEL_SIZE_TO_LABEL_INDEXES_MAP.values().stream().max(
|
||||
Comparator.comparingInt(indexes -> indexes.length)).get().length;
|
||||
final Rect[] bounds = new Rect[maxLabelsLength];
|
||||
for (int i = 0; i < maxLabelsLength; i++) {
|
||||
bounds[i] = new Rect();
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
// A container class for each trapezoid left and right location.
|
||||
@VisibleForTesting
|
||||
static final class TrapezoidSlot {
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package com.android.settings.fuelgauge.batteryusage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** Wraps the battery usage diff data for each entry used for battery usage app list. */
|
||||
@@ -24,10 +26,24 @@ public class BatteryDiffData {
|
||||
private final List<BatteryDiffEntry> mAppEntries;
|
||||
private final List<BatteryDiffEntry> mSystemEntries;
|
||||
|
||||
/** Constructor for the diff entries which already have totalConsumePower value. */
|
||||
public BatteryDiffData(
|
||||
List<BatteryDiffEntry> appDiffEntries, List<BatteryDiffEntry> systemDiffEntries) {
|
||||
mAppEntries = appDiffEntries == null ? new ArrayList<>() : appDiffEntries;
|
||||
mSystemEntries = systemDiffEntries == null ? new ArrayList<>() : systemDiffEntries;
|
||||
@NonNull List<BatteryDiffEntry> appDiffEntries,
|
||||
@NonNull List<BatteryDiffEntry> systemDiffEntries) {
|
||||
mAppEntries = appDiffEntries;
|
||||
mSystemEntries = systemDiffEntries;
|
||||
sortEntries();
|
||||
}
|
||||
|
||||
/** Constructor for the diff entries which have not set totalConsumePower value. */
|
||||
public BatteryDiffData(
|
||||
@NonNull List<BatteryDiffEntry> appDiffEntries,
|
||||
@NonNull List<BatteryDiffEntry> systemDiffEntries,
|
||||
final double totalConsumePower) {
|
||||
mAppEntries = appDiffEntries;
|
||||
mSystemEntries = systemDiffEntries;
|
||||
setTotalConsumePowerForAllEntries(totalConsumePower);
|
||||
sortEntries();
|
||||
}
|
||||
|
||||
public List<BatteryDiffEntry> getAppDiffEntryList() {
|
||||
@@ -38,9 +54,15 @@ public class BatteryDiffData {
|
||||
return mSystemEntries;
|
||||
}
|
||||
|
||||
/** Sets total consume power for each entry. */
|
||||
public void setTotalConsumePowerForAllEntries(double totalConsumePower) {
|
||||
// Sets total consume power for each entry.
|
||||
private void setTotalConsumePowerForAllEntries(final double totalConsumePower) {
|
||||
mAppEntries.forEach(diffEntry -> diffEntry.setTotalConsumePower(totalConsumePower));
|
||||
mSystemEntries.forEach(diffEntry -> diffEntry.setTotalConsumePower(totalConsumePower));
|
||||
}
|
||||
|
||||
// Sorts entries based on consumed percentage.
|
||||
private void sortEntries() {
|
||||
Collections.sort(mAppEntries, BatteryDiffEntry.COMPARATOR);
|
||||
Collections.sort(mSystemEntries, BatteryDiffEntry.COMPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,36 @@ package com.android.settings.fuelgauge.batteryusage;
|
||||
|
||||
import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTime;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.fuelgauge.BatteryUtils;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.fuelgauge.BatteryStatus;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A utility class to process data loaded from database and make the data easy to use for battery
|
||||
@@ -43,10 +58,27 @@ public final class DataProcessor {
|
||||
private static final String TAG = "DataProcessor";
|
||||
private static final int MIN_DAILY_DATA_SIZE = 2;
|
||||
private static final int MIN_TIMESTAMP_DATA_SIZE = 2;
|
||||
// Maximum total time value for each hourly slot cumulative data at most 2 hours.
|
||||
private static final float TOTAL_HOURLY_TIME_THRESHOLD = DateUtils.HOUR_IN_MILLIS * 2;
|
||||
private static final Map<String, BatteryHistEntry> EMPTY_BATTERY_MAP = new HashMap<>();
|
||||
private static final BatteryHistEntry EMPTY_BATTERY_HIST_ENTRY =
|
||||
new BatteryHistEntry(new ContentValues());
|
||||
|
||||
@VisibleForTesting
|
||||
static final double PERCENTAGE_OF_TOTAL_THRESHOLD = 1f;
|
||||
@VisibleForTesting
|
||||
static final int SELECTED_INDEX_ALL = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
|
||||
/** A fake package name to represent no BatteryEntry data. */
|
||||
public static final String FAKE_PACKAGE_NAME = "fake_package";
|
||||
|
||||
/** A callback listener when battery usage loading async task is executed. */
|
||||
public interface UsageMapAsyncResponse {
|
||||
/** The callback function when batteryUsageMap is loaded. */
|
||||
void onBatteryUsageMapLoaded(
|
||||
Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap);
|
||||
}
|
||||
|
||||
private DataProcessor() {
|
||||
}
|
||||
|
||||
@@ -58,11 +90,14 @@ public final class DataProcessor {
|
||||
@Nullable
|
||||
public static BatteryLevelData getBatteryLevelData(
|
||||
Context context,
|
||||
@Nullable final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
||||
@Nullable Handler handler,
|
||||
@Nullable final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
|
||||
final UsageMapAsyncResponse asyncResponseDelegate) {
|
||||
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
|
||||
Log.d(TAG, "getBatteryLevelData() returns null");
|
||||
return null;
|
||||
}
|
||||
handler = handler != null ? handler : new Handler(Looper.getMainLooper());
|
||||
// Process raw history map data into hourly timestamps.
|
||||
final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap =
|
||||
getHistoryMapWithExpectedTimestamps(context, batteryHistoryMap);
|
||||
@@ -70,11 +105,33 @@ public final class DataProcessor {
|
||||
final BatteryLevelData batteryLevelData =
|
||||
getLevelDataThroughProcessedHistoryMap(context, processedBatteryHistoryMap);
|
||||
|
||||
//TODO: Add the async task to compute diff usage data and load labels and icons.
|
||||
// Start the async task to compute diff usage data and load labels and icons.
|
||||
if (batteryLevelData != null) {
|
||||
new ComputeUsageMapAndLoadItemsTask(
|
||||
context,
|
||||
handler,
|
||||
asyncResponseDelegate,
|
||||
batteryLevelData.getHourlyBatteryLevelsPerDay(),
|
||||
processedBatteryHistoryMap).execute();
|
||||
}
|
||||
|
||||
return batteryLevelData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns whether the target is in the CharSequence array.
|
||||
*/
|
||||
public static boolean contains(String target, CharSequence[] packageNames) {
|
||||
if (target != null && packageNames != null) {
|
||||
for (CharSequence packageName : packageNames) {
|
||||
if (TextUtils.equals(target, packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the processed history map which has interpolated to every hour data.
|
||||
* The start and end timestamp must be the even hours.
|
||||
@@ -187,7 +244,7 @@ public final class DataProcessor {
|
||||
@VisibleForTesting
|
||||
static boolean isFromFullCharge(@Nullable final Map<String, BatteryHistEntry> entryList) {
|
||||
if (entryList == null) {
|
||||
Log.d(TAG, "entryList is nul in isFromFullCharge()");
|
||||
Log.d(TAG, "entryList is null in isFromFullCharge()");
|
||||
return false;
|
||||
}
|
||||
final List<String> entryKeys = new ArrayList<>(entryList.keySet());
|
||||
@@ -205,14 +262,14 @@ public final class DataProcessor {
|
||||
static long[] findNearestTimestamp(final List<Long> timestamps, final long target) {
|
||||
final long[] results = new long[] {Long.MIN_VALUE, Long.MAX_VALUE};
|
||||
// Searches the nearest lower and upper timestamp value.
|
||||
for (long timestamp : timestamps) {
|
||||
timestamps.forEach(timestamp -> {
|
||||
if (timestamp <= target && timestamp > results[0]) {
|
||||
results[0] = timestamp;
|
||||
}
|
||||
if (timestamp >= target && timestamp < results[1]) {
|
||||
results[1] = timestamp;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Uses zero value to represent invalid searching result.
|
||||
results[0] = results[0] == Long.MIN_VALUE ? 0 : results[0];
|
||||
results[1] = results[1] == Long.MAX_VALUE ? 0 : results[1];
|
||||
@@ -224,7 +281,7 @@ public final class DataProcessor {
|
||||
* timezone.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static long getTimestampOfNextDay(long timestamp) {
|
||||
static long getTimestampOfNextDay(long timestamp) {
|
||||
final Calendar nextDayCalendar = Calendar.getInstance();
|
||||
nextDayCalendar.setTimeInMillis(timestamp);
|
||||
nextDayCalendar.add(Calendar.DAY_OF_YEAR, 1);
|
||||
@@ -234,6 +291,40 @@ public final class DataProcessor {
|
||||
return nextDayCalendar.getTimeInMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the indexed battery usage data for each corresponding time slot.
|
||||
*
|
||||
* There could be 2 cases of the returned value:
|
||||
* 1) null: empty or invalid data.
|
||||
* 2) non-null: must be a 2d map and composed by 3 parts:
|
||||
* 1 - [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]
|
||||
* 2 - [0][SELECTED_INDEX_ALL] ~ [maxDailyIndex][SELECTED_INDEX_ALL]
|
||||
* 3 - [0][0] ~ [maxDailyIndex][maxHourlyIndex]
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
static Map<Integer, Map<Integer, BatteryDiffData>> getBatteryUsageMap(
|
||||
final Context context,
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
||||
if (batteryHistoryMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap = new HashMap<>();
|
||||
// Insert diff data from [0][0] to [maxDailyIndex][maxHourlyIndex].
|
||||
insertHourlyUsageDiffData(
|
||||
context, hourlyBatteryLevelsPerDay, batteryHistoryMap, resultMap);
|
||||
// Insert diff data from [0][SELECTED_INDEX_ALL] to [maxDailyIndex][SELECTED_INDEX_ALL].
|
||||
insertDailyUsageDiffData(hourlyBatteryLevelsPerDay, resultMap);
|
||||
// Insert diff data [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL].
|
||||
insertAllUsageDiffData(resultMap);
|
||||
purgeLowPercentageAndFakeData(context, resultMap);
|
||||
if (!isUsageMapValid(resultMap, hourlyBatteryLevelsPerDay)) {
|
||||
return null;
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolates history map based on expected timestamp slots and processes the corner case when
|
||||
* the expected start timestamp is earlier than what we have.
|
||||
@@ -458,11 +549,454 @@ public final class DataProcessor {
|
||||
return Math.round(batteryLevelCounter / entryMap.size());
|
||||
}
|
||||
|
||||
private static void log(Context context, String content, long timestamp,
|
||||
BatteryHistEntry entry) {
|
||||
private static void insertHourlyUsageDiffData(
|
||||
Context context,
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap) {
|
||||
final int currentUserId = context.getUserId();
|
||||
final UserHandle userHandle =
|
||||
Utils.getManagedProfile(context.getSystemService(UserManager.class));
|
||||
final int workProfileUserId =
|
||||
userHandle != null ? userHandle.getIdentifier() : Integer.MIN_VALUE;
|
||||
// Each time slot usage diff data =
|
||||
// Math.abs(timestamp[i+2] data - timestamp[i+1] data) +
|
||||
// Math.abs(timestamp[i+1] data - timestamp[i] data);
|
||||
// since we want to aggregate every two hours data into a single time slot.
|
||||
for (int dailyIndex = 0; dailyIndex < hourlyBatteryLevelsPerDay.size(); dailyIndex++) {
|
||||
final Map<Integer, BatteryDiffData> dailyDiffMap = new HashMap<>();
|
||||
resultMap.put(dailyIndex, dailyDiffMap);
|
||||
if (hourlyBatteryLevelsPerDay.get(dailyIndex) == null) {
|
||||
continue;
|
||||
}
|
||||
final List<Long> timestamps = hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
|
||||
for (int hourlyIndex = 0; hourlyIndex < timestamps.size() - 1; hourlyIndex++) {
|
||||
final BatteryDiffData hourlyBatteryDiffData =
|
||||
insertHourlyUsageDiffDataPerSlot(
|
||||
context,
|
||||
currentUserId,
|
||||
workProfileUserId,
|
||||
hourlyIndex,
|
||||
timestamps,
|
||||
batteryHistoryMap);
|
||||
dailyDiffMap.put(hourlyIndex, hourlyBatteryDiffData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void insertDailyUsageDiffData(
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap) {
|
||||
for (int index = 0; index < hourlyBatteryLevelsPerDay.size(); index++) {
|
||||
Map<Integer, BatteryDiffData> dailyUsageMap = resultMap.get(index);
|
||||
if (dailyUsageMap == null) {
|
||||
dailyUsageMap = new HashMap<>();
|
||||
resultMap.put(index, dailyUsageMap);
|
||||
}
|
||||
dailyUsageMap.put(
|
||||
SELECTED_INDEX_ALL,
|
||||
getAccumulatedUsageDiffData(dailyUsageMap.values()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void insertAllUsageDiffData(
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap) {
|
||||
final List<BatteryDiffData> diffDataList = new ArrayList<>();
|
||||
resultMap.keySet().forEach(
|
||||
key -> diffDataList.add(resultMap.get(key).get(SELECTED_INDEX_ALL)));
|
||||
final Map<Integer, BatteryDiffData> allUsageMap = new HashMap<>();
|
||||
allUsageMap.put(SELECTED_INDEX_ALL, getAccumulatedUsageDiffData(diffDataList));
|
||||
resultMap.put(SELECTED_INDEX_ALL, allUsageMap);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static BatteryDiffData insertHourlyUsageDiffDataPerSlot(
|
||||
Context context,
|
||||
final int currentUserId,
|
||||
final int workProfileUserId,
|
||||
final int currentIndex,
|
||||
final List<Long> timestamps,
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
||||
final List<BatteryDiffEntry> appEntries = new ArrayList<>();
|
||||
final List<BatteryDiffEntry> systemEntries = new ArrayList<>();
|
||||
|
||||
final Long currentTimestamp = timestamps.get(currentIndex);
|
||||
final Long nextTimestamp = currentTimestamp + DateUtils.HOUR_IN_MILLIS;
|
||||
final Long nextTwoTimestamp = nextTimestamp + DateUtils.HOUR_IN_MILLIS;
|
||||
// Fetches BatteryHistEntry data from corresponding time slot.
|
||||
final Map<String, BatteryHistEntry> currentBatteryHistMap =
|
||||
batteryHistoryMap.getOrDefault(currentTimestamp, EMPTY_BATTERY_MAP);
|
||||
final Map<String, BatteryHistEntry> nextBatteryHistMap =
|
||||
batteryHistoryMap.getOrDefault(nextTimestamp, EMPTY_BATTERY_MAP);
|
||||
final Map<String, BatteryHistEntry> nextTwoBatteryHistMap =
|
||||
batteryHistoryMap.getOrDefault(nextTwoTimestamp, EMPTY_BATTERY_MAP);
|
||||
// We should not get the empty list since we have at least one fake data to record
|
||||
// the battery level and status in each time slot, the empty list is used to
|
||||
// represent there is no enough data to apply interpolation arithmetic.
|
||||
if (currentBatteryHistMap.isEmpty()
|
||||
|| nextBatteryHistMap.isEmpty()
|
||||
|| nextTwoBatteryHistMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Collects all keys in these three time slot records as all populations.
|
||||
final Set<String> allBatteryHistEntryKeys = new ArraySet<>();
|
||||
allBatteryHistEntryKeys.addAll(currentBatteryHistMap.keySet());
|
||||
allBatteryHistEntryKeys.addAll(nextBatteryHistMap.keySet());
|
||||
allBatteryHistEntryKeys.addAll(nextTwoBatteryHistMap.keySet());
|
||||
|
||||
double totalConsumePower = 0.0;
|
||||
double consumePowerFromOtherUsers = 0f;
|
||||
// Calculates all packages diff usage data in a specific time slot.
|
||||
for (String key : allBatteryHistEntryKeys) {
|
||||
final BatteryHistEntry currentEntry =
|
||||
currentBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY);
|
||||
final BatteryHistEntry nextEntry =
|
||||
nextBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY);
|
||||
final BatteryHistEntry nextTwoEntry =
|
||||
nextTwoBatteryHistMap.getOrDefault(key, EMPTY_BATTERY_HIST_ENTRY);
|
||||
// Cumulative values is a specific time slot for a specific app.
|
||||
long foregroundUsageTimeInMs =
|
||||
getDiffValue(
|
||||
currentEntry.mForegroundUsageTimeInMs,
|
||||
nextEntry.mForegroundUsageTimeInMs,
|
||||
nextTwoEntry.mForegroundUsageTimeInMs);
|
||||
long backgroundUsageTimeInMs =
|
||||
getDiffValue(
|
||||
currentEntry.mBackgroundUsageTimeInMs,
|
||||
nextEntry.mBackgroundUsageTimeInMs,
|
||||
nextTwoEntry.mBackgroundUsageTimeInMs);
|
||||
double consumePower =
|
||||
getDiffValue(
|
||||
currentEntry.mConsumePower,
|
||||
nextEntry.mConsumePower,
|
||||
nextTwoEntry.mConsumePower);
|
||||
// Excludes entry since we don't have enough data to calculate.
|
||||
if (foregroundUsageTimeInMs == 0
|
||||
&& backgroundUsageTimeInMs == 0
|
||||
&& consumePower == 0) {
|
||||
continue;
|
||||
}
|
||||
final BatteryHistEntry selectedBatteryEntry =
|
||||
selectBatteryHistEntry(currentEntry, nextEntry, nextTwoEntry);
|
||||
if (selectedBatteryEntry == null) {
|
||||
continue;
|
||||
}
|
||||
// Forces refine the cumulative value since it may introduce deviation error since we
|
||||
// will apply the interpolation arithmetic.
|
||||
final float totalUsageTimeInMs =
|
||||
foregroundUsageTimeInMs + backgroundUsageTimeInMs;
|
||||
if (totalUsageTimeInMs > TOTAL_HOURLY_TIME_THRESHOLD) {
|
||||
final float ratio = TOTAL_HOURLY_TIME_THRESHOLD / totalUsageTimeInMs;
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, String.format("abnormal usage time %d|%d for:\n%s",
|
||||
Duration.ofMillis(foregroundUsageTimeInMs).getSeconds(),
|
||||
Duration.ofMillis(backgroundUsageTimeInMs).getSeconds(),
|
||||
currentEntry));
|
||||
}
|
||||
foregroundUsageTimeInMs =
|
||||
Math.round(foregroundUsageTimeInMs * ratio);
|
||||
backgroundUsageTimeInMs =
|
||||
Math.round(backgroundUsageTimeInMs * ratio);
|
||||
consumePower = consumePower * ratio;
|
||||
}
|
||||
totalConsumePower += consumePower;
|
||||
|
||||
final boolean isFromOtherUsers = isConsumedFromOtherUsers(
|
||||
currentUserId, workProfileUserId, selectedBatteryEntry);
|
||||
if (isFromOtherUsers) {
|
||||
consumePowerFromOtherUsers += consumePower;
|
||||
} else {
|
||||
final BatteryDiffEntry currentBatteryDiffEntry = new BatteryDiffEntry(
|
||||
context,
|
||||
foregroundUsageTimeInMs,
|
||||
backgroundUsageTimeInMs,
|
||||
consumePower,
|
||||
selectedBatteryEntry);
|
||||
if (currentBatteryDiffEntry.isSystemEntry()) {
|
||||
systemEntries.add(currentBatteryDiffEntry);
|
||||
} else {
|
||||
appEntries.add(currentBatteryDiffEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (consumePowerFromOtherUsers != 0) {
|
||||
systemEntries.add(createOtherUsersEntry(context, consumePowerFromOtherUsers));
|
||||
}
|
||||
|
||||
// If there is no data, return null instead of empty item.
|
||||
if (appEntries.isEmpty() && systemEntries.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final BatteryDiffData resultDiffData =
|
||||
new BatteryDiffData(appEntries, systemEntries, totalConsumePower);
|
||||
return resultDiffData;
|
||||
}
|
||||
|
||||
private static boolean isConsumedFromOtherUsers(
|
||||
final int currentUserId,
|
||||
final int workProfileUserId,
|
||||
final BatteryHistEntry batteryHistEntry) {
|
||||
return batteryHistEntry.mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY
|
||||
&& batteryHistEntry.mUserId != currentUserId
|
||||
&& batteryHistEntry.mUserId != workProfileUserId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static BatteryDiffData getAccumulatedUsageDiffData(
|
||||
final Collection<BatteryDiffData> diffEntryListData) {
|
||||
double totalConsumePower = 0f;
|
||||
final Map<String, BatteryDiffEntry> diffEntryMap = new HashMap<>();
|
||||
final List<BatteryDiffEntry> appEntries = new ArrayList<>();
|
||||
final List<BatteryDiffEntry> systemEntries = new ArrayList<>();
|
||||
|
||||
for (BatteryDiffData diffEntryList : diffEntryListData) {
|
||||
if (diffEntryList == null) {
|
||||
continue;
|
||||
}
|
||||
for (BatteryDiffEntry entry : diffEntryList.getAppDiffEntryList()) {
|
||||
computeUsageDiffDataPerEntry(entry, diffEntryMap);
|
||||
totalConsumePower += entry.mConsumePower;
|
||||
}
|
||||
for (BatteryDiffEntry entry : diffEntryList.getSystemDiffEntryList()) {
|
||||
computeUsageDiffDataPerEntry(entry, diffEntryMap);
|
||||
totalConsumePower += entry.mConsumePower;
|
||||
}
|
||||
}
|
||||
|
||||
final Collection<BatteryDiffEntry> diffEntryList = diffEntryMap.values();
|
||||
for (BatteryDiffEntry entry : diffEntryList) {
|
||||
// Sets total daily consume power data into all BatteryDiffEntry.
|
||||
entry.setTotalConsumePower(totalConsumePower);
|
||||
if (entry.isSystemEntry()) {
|
||||
systemEntries.add(entry);
|
||||
} else {
|
||||
appEntries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return diffEntryList.isEmpty() ? null : new BatteryDiffData(appEntries, systemEntries);
|
||||
}
|
||||
|
||||
private static void computeUsageDiffDataPerEntry(
|
||||
final BatteryDiffEntry entry,
|
||||
final Map<String, BatteryDiffEntry> diffEntryMap) {
|
||||
final String key = entry.mBatteryHistEntry.getKey();
|
||||
final BatteryDiffEntry oldBatteryDiffEntry = diffEntryMap.get(key);
|
||||
// Creates new BatteryDiffEntry if we don't have it.
|
||||
if (oldBatteryDiffEntry == null) {
|
||||
diffEntryMap.put(key, entry.clone());
|
||||
} else {
|
||||
// Sums up some field data into the existing one.
|
||||
oldBatteryDiffEntry.mForegroundUsageTimeInMs +=
|
||||
entry.mForegroundUsageTimeInMs;
|
||||
oldBatteryDiffEntry.mBackgroundUsageTimeInMs +=
|
||||
entry.mBackgroundUsageTimeInMs;
|
||||
oldBatteryDiffEntry.mConsumePower += entry.mConsumePower;
|
||||
}
|
||||
}
|
||||
|
||||
// Removes low percentage data and fake usage data, which will be zero value.
|
||||
private static void purgeLowPercentageAndFakeData(
|
||||
final Context context,
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap) {
|
||||
final Set<CharSequence> backgroundUsageTimeHideList =
|
||||
FeatureFactory.getFactory(context)
|
||||
.getPowerUsageFeatureProvider(context)
|
||||
.getHideBackgroundUsageTimeSet(context);
|
||||
final CharSequence[] notAllowShowEntryPackages =
|
||||
FeatureFactory.getFactory(context)
|
||||
.getPowerUsageFeatureProvider(context)
|
||||
.getHideApplicationEntries(context);
|
||||
resultMap.keySet().forEach(dailyKey -> {
|
||||
final Map<Integer, BatteryDiffData> dailyUsageMap = resultMap.get(dailyKey);
|
||||
dailyUsageMap.values().forEach(diffEntryLists -> {
|
||||
if (diffEntryLists == null) {
|
||||
return;
|
||||
}
|
||||
purgeLowPercentageAndFakeData(
|
||||
diffEntryLists.getAppDiffEntryList(), backgroundUsageTimeHideList,
|
||||
notAllowShowEntryPackages);
|
||||
purgeLowPercentageAndFakeData(
|
||||
diffEntryLists.getSystemDiffEntryList(), backgroundUsageTimeHideList,
|
||||
notAllowShowEntryPackages);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static void purgeLowPercentageAndFakeData(
|
||||
final List<BatteryDiffEntry> entries,
|
||||
final Set<CharSequence> backgroundUsageTimeHideList,
|
||||
final CharSequence[] notAllowShowEntryPackages) {
|
||||
final Iterator<BatteryDiffEntry> iterator = entries.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final BatteryDiffEntry entry = iterator.next();
|
||||
final String packageName = entry.getPackageName();
|
||||
if (entry.getPercentOfTotal() < PERCENTAGE_OF_TOTAL_THRESHOLD
|
||||
|| FAKE_PACKAGE_NAME.equals(packageName)
|
||||
|| contains(packageName, notAllowShowEntryPackages)) {
|
||||
iterator.remove();
|
||||
}
|
||||
if (packageName != null
|
||||
&& !backgroundUsageTimeHideList.isEmpty()
|
||||
&& contains(packageName, backgroundUsageTimeHideList)) {
|
||||
entry.mBackgroundUsageTimeInMs = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isUsageMapValid(
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap,
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay) {
|
||||
if (batteryUsageMap.get(SELECTED_INDEX_ALL) == null
|
||||
|| batteryUsageMap.get(SELECTED_INDEX_ALL).get(SELECTED_INDEX_ALL) == null) {
|
||||
Log.e(TAG, "no [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL] in batteryUsageMap");
|
||||
return false;
|
||||
}
|
||||
for (int dailyIndex = 0; dailyIndex < hourlyBatteryLevelsPerDay.size(); dailyIndex++) {
|
||||
if (batteryUsageMap.get(dailyIndex) == null
|
||||
|| !batteryUsageMap.get(dailyIndex).containsKey(SELECTED_INDEX_ALL)) {
|
||||
Log.e(TAG, "no [" + dailyIndex + "][SELECTED_INDEX_ALL] in batteryUsageMap, "
|
||||
+ "daily size is: " + hourlyBatteryLevelsPerDay.size());
|
||||
return false;
|
||||
}
|
||||
if (hourlyBatteryLevelsPerDay.get(dailyIndex) == null) {
|
||||
continue;
|
||||
}
|
||||
final List<Long> timestamps = hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
|
||||
// Length of hourly usage map should be the length of hourly level data - 1.
|
||||
for (int hourlyIndex = 0; hourlyIndex < timestamps.size() - 1; hourlyIndex++) {
|
||||
if (!batteryUsageMap.get(dailyIndex).containsKey(hourlyIndex)) {
|
||||
Log.e(TAG, "no [" + dailyIndex + "][" + hourlyIndex + "] in batteryUsageMap, "
|
||||
+ "hourly size is: " + (timestamps.size() - 1));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean contains(String target, Set<CharSequence> packageNames) {
|
||||
if (target != null && packageNames != null) {
|
||||
for (CharSequence packageName : packageNames) {
|
||||
if (TextUtils.equals(target, packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static long getDiffValue(long v1, long v2, long v3) {
|
||||
return (v2 > v1 ? v2 - v1 : 0) + (v3 > v2 ? v3 - v2 : 0);
|
||||
}
|
||||
|
||||
private static double getDiffValue(double v1, double v2, double v3) {
|
||||
return (v2 > v1 ? v2 - v1 : 0) + (v3 > v2 ? v3 - v2 : 0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static BatteryHistEntry selectBatteryHistEntry(
|
||||
final BatteryHistEntry... batteryHistEntries) {
|
||||
for (BatteryHistEntry entry : batteryHistEntries) {
|
||||
if (entry != null && entry != EMPTY_BATTERY_HIST_ENTRY) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static BatteryDiffEntry createOtherUsersEntry(
|
||||
Context context, final double consumePower) {
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(BatteryHistEntry.KEY_UID, BatteryUtils.UID_OTHER_USERS);
|
||||
values.put(BatteryHistEntry.KEY_USER_ID, BatteryUtils.UID_OTHER_USERS);
|
||||
values.put(BatteryHistEntry.KEY_CONSUMER_TYPE, ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
|
||||
// We will show the percentage for the "other users" item only, the aggregated
|
||||
// running time information is useless for users to identify individual apps.
|
||||
final BatteryDiffEntry batteryDiffEntry = new BatteryDiffEntry(
|
||||
context,
|
||||
/*foregroundUsageTimeInMs=*/ 0,
|
||||
/*backgroundUsageTimeInMs=*/ 0,
|
||||
consumePower,
|
||||
new BatteryHistEntry(values));
|
||||
return batteryDiffEntry;
|
||||
}
|
||||
|
||||
private static void log(Context context, final String content, final long timestamp,
|
||||
final BatteryHistEntry entry) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format(entry != null ? "%s %s:\n%s" : "%s %s:%s",
|
||||
utcToLocalTime(context, timestamp), content, entry));
|
||||
}
|
||||
}
|
||||
|
||||
// Compute diff map and loads all items (icon and label) in the background.
|
||||
private static final class ComputeUsageMapAndLoadItemsTask
|
||||
extends AsyncTask<Void, Void, Map<Integer, Map<Integer, BatteryDiffData>>> {
|
||||
|
||||
private Context mApplicationContext;
|
||||
private Handler mHandler;
|
||||
private UsageMapAsyncResponse mAsyncResponseDelegate;
|
||||
private List<BatteryLevelData.PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
|
||||
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
|
||||
|
||||
private ComputeUsageMapAndLoadItemsTask(
|
||||
Context context,
|
||||
Handler handler,
|
||||
final UsageMapAsyncResponse asyncResponseDelegate,
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
||||
mApplicationContext = context.getApplicationContext();
|
||||
mHandler = handler;
|
||||
mAsyncResponseDelegate = asyncResponseDelegate;
|
||||
mHourlyBatteryLevelsPerDay = hourlyBatteryLevelsPerDay;
|
||||
mBatteryHistoryMap = batteryHistoryMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<Integer, Map<Integer, BatteryDiffData>> doInBackground(Void... voids) {
|
||||
if (mApplicationContext == null
|
||||
|| mHandler == null
|
||||
|| mAsyncResponseDelegate == null
|
||||
|| mBatteryHistoryMap == null
|
||||
|| mHourlyBatteryLevelsPerDay == null) {
|
||||
Log.e(TAG, "invalid input for ComputeUsageMapAndLoadItemsTask()");
|
||||
return null;
|
||||
}
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
|
||||
getBatteryUsageMap(
|
||||
mApplicationContext, mHourlyBatteryLevelsPerDay, mBatteryHistoryMap);
|
||||
if (batteryUsageMap != null) {
|
||||
// Pre-loads each BatteryDiffEntry relative icon and label for all slots.
|
||||
final BatteryDiffData batteryUsageMapForAll =
|
||||
batteryUsageMap.get(SELECTED_INDEX_ALL).get(SELECTED_INDEX_ALL);
|
||||
if (batteryUsageMapForAll != null) {
|
||||
batteryUsageMapForAll.getAppDiffEntryList().forEach(
|
||||
entry -> entry.loadLabelAndIcon());
|
||||
batteryUsageMapForAll.getSystemDiffEntryList().forEach(
|
||||
entry -> entry.loadLabelAndIcon());
|
||||
}
|
||||
}
|
||||
Log.d(TAG, String.format("execute ComputeUsageMapAndLoadItemsTask in %d/ms",
|
||||
(System.currentTimeMillis() - startTime)));
|
||||
return batteryUsageMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
|
||||
mApplicationContext = null;
|
||||
mHourlyBatteryLevelsPerDay = null;
|
||||
mBatteryHistoryMap = null;
|
||||
// Post results back to main thread to refresh UI.
|
||||
if (mHandler != null && mAsyncResponseDelegate != null) {
|
||||
mHandler.post(() -> {
|
||||
mAsyncResponseDelegate.onBatteryUsageMapLoaded(batteryUsageMap);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,21 +64,21 @@ public class CaptionLocalePreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_byDefault_shouldReturnDefault() {
|
||||
public void displayPreference_byDefault_shouldReturnDefault() {
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
assertThat(mController.getSummary().toString()).isEqualTo(
|
||||
assertThat(mPreference.getEntry().toString()).isEqualTo(
|
||||
mContext.getResources().getString(R.string.locale_default));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_byArabicLocale_shouldReturnArabic() {
|
||||
public void displayPreference_byArabicLocale_shouldReturnArabic() {
|
||||
Settings.Secure.putString(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_CAPTIONING_LOCALE, "af_ZA");
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
assertThat(mController.getSummary().toString()).isEqualTo("Afrikaans");
|
||||
assertThat(mPreference.getEntry().toString()).isEqualTo("Afrikaans");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -87,6 +87,6 @@ public class CaptionLocalePreferenceControllerTest {
|
||||
|
||||
mController.onPreferenceChange(mPreference, "af_ZA");
|
||||
|
||||
assertThat(mPreference.getSummary().toString()).isEqualTo("Afrikaans");
|
||||
assertThat(mPreference.getEntry().toString()).isEqualTo("Afrikaans");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ public class CaptionPreviewPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPause_unregisterContentObserver() {
|
||||
public void onStop_unregisterContentObserver() {
|
||||
mController.onStop();
|
||||
|
||||
verify(mContentResolver).unregisterContentObserver(mController.mSettingsContentObserver);
|
||||
|
||||
@@ -178,7 +178,8 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
|
||||
List.of(100, 97, 95),
|
||||
List.of("8 am", "10 am", "12 pm"),
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL));
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL,
|
||||
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -193,7 +194,8 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
verify(mDailyChartView).setViewModel(new BatteryChartViewModel(
|
||||
List.of(100, 83, 59, 41),
|
||||
List.of("SAT", "SUN", "MON", "MON"),
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL));
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL,
|
||||
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS));
|
||||
|
||||
reset(mDailyChartView);
|
||||
reset(mHourlyChartView);
|
||||
@@ -204,12 +206,14 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
verify(mDailyChartView).setViewModel(new BatteryChartViewModel(
|
||||
List.of(100, 83, 59, 41),
|
||||
List.of("SAT", "SUN", "MON", "MON"),
|
||||
0));
|
||||
0,
|
||||
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS));
|
||||
verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
|
||||
List.of(100, 97, 95, 93, 91, 89, 87, 85, 83),
|
||||
List.of("8 am", "10 am", "12 pm", "2 pm", "4 pm", "6 pm", "8 pm", "10 pm",
|
||||
"12 am"),
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL));
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL,
|
||||
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||
|
||||
reset(mDailyChartView);
|
||||
reset(mHourlyChartView);
|
||||
@@ -221,12 +225,14 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
verify(mDailyChartView).setViewModel(new BatteryChartViewModel(
|
||||
List.of(100, 83, 59, 41),
|
||||
List.of("SAT", "SUN", "MON", "MON"),
|
||||
1));
|
||||
1,
|
||||
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS));
|
||||
verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
|
||||
List.of(83, 81, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59),
|
||||
List.of("12 am", "2 am", "4 am", "6 am", "8 am", "10 am", "12 pm", "2 pm",
|
||||
"4 pm", "6 pm", "8 pm", "10 pm", "12 am"),
|
||||
6));
|
||||
6,
|
||||
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||
|
||||
reset(mDailyChartView);
|
||||
reset(mHourlyChartView);
|
||||
@@ -239,12 +245,14 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
verify(mDailyChartView).setViewModel(new BatteryChartViewModel(
|
||||
List.of(100, 83, 59, 41),
|
||||
List.of("SAT", "SUN", "MON", "MON"),
|
||||
2));
|
||||
2,
|
||||
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS));
|
||||
verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
|
||||
List.of(59, 57, 55, 53, 51, 49, 47, 45, 43, 41),
|
||||
List.of("12 am", "2 am", "4 am", "6 am", "8 am", "10 am", "12 pm", "2 pm",
|
||||
"4 pm", "6 pm"),
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL));
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL,
|
||||
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -104,7 +104,8 @@ public final class BatteryChartViewV2Test {
|
||||
final int originalSelectedIndex = 2;
|
||||
mBatteryChartView.setViewModel(
|
||||
new BatteryChartViewModel(List.of(90, 80, 70, 60), List.of("", "", "", ""),
|
||||
originalSelectedIndex));
|
||||
originalSelectedIndex,
|
||||
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||
for (int i = 0; i < mBatteryChartView.mTrapezoidSlots.length; i++) {
|
||||
mBatteryChartView.mTrapezoidSlots[i] = new BatteryChartViewV2.TrapezoidSlot();
|
||||
mBatteryChartView.mTrapezoidSlots[i].mLeft = i;
|
||||
@@ -136,6 +137,7 @@ public final class BatteryChartViewV2Test {
|
||||
.thenReturn(false);
|
||||
|
||||
mBatteryChartView.onAttachedToWindow();
|
||||
|
||||
assertThat(mBatteryChartView.isClickable()).isFalse();
|
||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
|
||||
}
|
||||
@@ -148,6 +150,7 @@ public final class BatteryChartViewV2Test {
|
||||
doReturn(false).when(mMockAccessibilityManager).isEnabled();
|
||||
|
||||
mBatteryChartView.onAttachedToWindow();
|
||||
|
||||
assertThat(mBatteryChartView.isClickable()).isTrue();
|
||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
|
||||
}
|
||||
@@ -163,6 +166,7 @@ public final class BatteryChartViewV2Test {
|
||||
.getEnabledAccessibilityServiceList(anyInt());
|
||||
|
||||
mBatteryChartView.onAttachedToWindow();
|
||||
|
||||
assertThat(mBatteryChartView.isClickable()).isTrue();
|
||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
|
||||
}
|
||||
@@ -175,6 +179,7 @@ public final class BatteryChartViewV2Test {
|
||||
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
||||
|
||||
mBatteryChartView.onAttachedToWindow();
|
||||
|
||||
assertThat(mBatteryChartView.isClickable()).isFalse();
|
||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
|
||||
}
|
||||
@@ -188,7 +193,8 @@ public final class BatteryChartViewV2Test {
|
||||
texts.add("");
|
||||
}
|
||||
mBatteryChartView.setViewModel(new BatteryChartViewModel(
|
||||
levels, texts, BatteryChartViewModel.SELECTED_INDEX_ALL));
|
||||
levels, texts, BatteryChartViewModel.SELECTED_INDEX_ALL,
|
||||
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||
mBatteryChartView.setClickableForce(true);
|
||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||
.thenReturn(true);
|
||||
|
||||
@@ -19,11 +19,16 @@ package com.android.settings.fuelgauge.batteryusage;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import com.android.settings.fuelgauge.BatteryUtils;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -35,6 +40,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
@@ -45,18 +51,30 @@ public class DataProcessorTest {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private FakeFeatureFactory mFeatureFactory;
|
||||
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
|
||||
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBatteryLevelData_emptyHistoryMap_returnNull() {
|
||||
assertThat(DataProcessor.getBatteryLevelData(mContext, null)).isNull();
|
||||
assertThat(DataProcessor.getBatteryLevelData(mContext, new HashMap<>())).isNull();
|
||||
assertThat(DataProcessor.getBatteryLevelData(
|
||||
mContext,
|
||||
/*handler=*/ null,
|
||||
/*batteryHistoryMap=*/ null,
|
||||
/*asyncResponseDelegate=*/ null))
|
||||
.isNull();
|
||||
assertThat(DataProcessor.getBatteryLevelData(
|
||||
mContext, /*handler=*/ null, new HashMap<>(), /*asyncResponseDelegate=*/ null))
|
||||
.isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -67,7 +85,9 @@ public class DataProcessorTest {
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
|
||||
createHistoryMap(timestamps, levels);
|
||||
|
||||
assertThat(DataProcessor.getBatteryLevelData(mContext, batteryHistoryMap)).isNull();
|
||||
assertThat(DataProcessor.getBatteryLevelData(
|
||||
mContext, /*handler=*/ null, batteryHistoryMap, /*asyncResponseDelegate=*/ null))
|
||||
.isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -79,7 +99,11 @@ public class DataProcessorTest {
|
||||
createHistoryMap(timestamps, levels);
|
||||
|
||||
final BatteryLevelData resultData =
|
||||
DataProcessor.getBatteryLevelData(mContext, batteryHistoryMap);
|
||||
DataProcessor.getBatteryLevelData(
|
||||
mContext,
|
||||
/*handler=*/ null,
|
||||
batteryHistoryMap,
|
||||
/*asyncResponseDelegate=*/ null);
|
||||
|
||||
final List<Long> expectedDailyTimestamps = List.of(timestamps[0], timestamps[2]);
|
||||
final List<Integer> expectedDailyLevels = List.of(levels[0], levels[2]);
|
||||
@@ -377,6 +401,450 @@ public class DataProcessorTest {
|
||||
.isEqualTo(1640966400000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBatteryUsageMap_emptyHistoryMap_returnNull() {
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||
new ArrayList<>();
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
|
||||
|
||||
assertThat(DataProcessor.getBatteryUsageMap(
|
||||
mContext, hourlyBatteryLevelsPerDay, new HashMap<>())).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBatteryUsageMap_emptyHourlyData_returnNull() {
|
||||
final long[] timestamps = {1000000L, 2000000L};
|
||||
final int[] levels = {100, 99};
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
|
||||
createHistoryMap(timestamps, levels);
|
||||
|
||||
assertThat(DataProcessor.getBatteryUsageMap(
|
||||
mContext, new ArrayList<>(), batteryHistoryMap)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBatteryUsageMap_returnsExpectedResult() {
|
||||
final long[] batteryHistoryKeys = new long[]{
|
||||
1641045600000L, // 2022-01-01 22:00:00
|
||||
1641049200000L, // 2022-01-01 23:00:00
|
||||
1641052800000L, // 2022-01-02 00:00:00
|
||||
1641056400000L, // 2022-01-02 01:00:00
|
||||
1641060000000L, // 2022-01-02 02:00:00
|
||||
};
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||
final int currentUserId = mContext.getUserId();
|
||||
final BatteryHistEntry fakeEntry = createBatteryHistEntry(
|
||||
ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", /*consumePower=*/ 0, /*uid=*/ 0L,
|
||||
currentUserId, ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
|
||||
/*foregroundUsageTimeInMs=*/ 0L, /*backgroundUsageTimeInMs=*/ 0L);
|
||||
// Adds the index = 0 data.
|
||||
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
|
||||
BatteryHistEntry entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 5.0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
|
||||
/*backgroundUsageTimeInMs=*/ 20L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entryMap.put(fakeEntry.getKey(), fakeEntry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
|
||||
// Adds the index = 1 data.
|
||||
entryMap = new HashMap<>();
|
||||
entryMap.put(fakeEntry.getKey(), fakeEntry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
|
||||
// Adds the index = 2 data.
|
||||
entryMap = new HashMap<>();
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 20.0, /*uid=*/ 2L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 15L,
|
||||
25L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entryMap.put(fakeEntry.getKey(), fakeEntry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
|
||||
// Adds the index = 3 data.
|
||||
entryMap = new HashMap<>();
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 40.0, /*uid=*/ 2L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 25L,
|
||||
/*backgroundUsageTimeInMs=*/ 35L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 10.0, /*uid=*/ 3L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*foregroundUsageTimeInMs=*/ 40L,
|
||||
/*backgroundUsageTimeInMs=*/ 50L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package3", "label3", /*consumePower=*/ 15.0, /*uid=*/ 4L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 5L,
|
||||
/*backgroundUsageTimeInMs=*/ 5L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entryMap.put(fakeEntry.getKey(), fakeEntry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[3], entryMap);
|
||||
// Adds the index = 4 data.
|
||||
entryMap = new HashMap<>();
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 40.0, /*uid=*/ 2L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 30L,
|
||||
/*backgroundUsageTimeInMs=*/ 40L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 20.0, /*uid=*/ 3L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*foregroundUsageTimeInMs=*/ 50L,
|
||||
/*backgroundUsageTimeInMs=*/ 60L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package3", "label3", /*consumePower=*/ 40.0, /*uid=*/ 4L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 5L,
|
||||
/*backgroundUsageTimeInMs=*/ 5L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entryMap.put(fakeEntry.getKey(), fakeEntry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[4], entryMap);
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||
new ArrayList<>();
|
||||
// Adds the day 1 data.
|
||||
List<Long> timestamps =
|
||||
List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
|
||||
final List<Integer> levels = List.of(100, 100);
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
|
||||
// Adds the day 2 data.
|
||||
timestamps = List.of(batteryHistoryKeys[2], batteryHistoryKeys[4]);
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
|
||||
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
|
||||
DataProcessor.getBatteryUsageMap(
|
||||
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
|
||||
|
||||
BatteryDiffData resultDiffData =
|
||||
resultMap
|
||||
.get(DataProcessor.SELECTED_INDEX_ALL)
|
||||
.get(DataProcessor.SELECTED_INDEX_ALL);
|
||||
assertBatteryDiffEntry(
|
||||
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 2L,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 40.0,
|
||||
/*foregroundUsageTimeInMs=*/ 30, /*backgroundUsageTimeInMs=*/ 40);
|
||||
assertBatteryDiffEntry(
|
||||
resultDiffData.getAppDiffEntryList().get(1), currentUserId, /*uid=*/ 4L,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 40.0,
|
||||
/*foregroundUsageTimeInMs=*/ 5, /*backgroundUsageTimeInMs=*/ 5);
|
||||
assertBatteryDiffEntry(
|
||||
resultDiffData.getSystemDiffEntryList().get(0), currentUserId, /*uid=*/ 3L,
|
||||
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*consumePercentage=*/ 20.0,
|
||||
/*foregroundUsageTimeInMs=*/ 50, /*backgroundUsageTimeInMs=*/ 60);
|
||||
resultDiffData = resultMap.get(0).get(DataProcessor.SELECTED_INDEX_ALL);
|
||||
assertBatteryDiffEntry(
|
||||
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 2L,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 100.0,
|
||||
/*foregroundUsageTimeInMs=*/ 15, /*backgroundUsageTimeInMs=*/ 25);
|
||||
resultDiffData = resultMap.get(1).get(DataProcessor.SELECTED_INDEX_ALL);
|
||||
assertBatteryDiffEntry(
|
||||
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 4L,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 50.0,
|
||||
/*foregroundUsageTimeInMs=*/ 5, /*backgroundUsageTimeInMs=*/ 5);
|
||||
assertBatteryDiffEntry(
|
||||
resultDiffData.getAppDiffEntryList().get(1), currentUserId, /*uid=*/ 2L,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 25.0,
|
||||
/*foregroundUsageTimeInMs=*/ 15, /*backgroundUsageTimeInMs=*/ 15);
|
||||
assertBatteryDiffEntry(
|
||||
resultDiffData.getSystemDiffEntryList().get(0), currentUserId, /*uid=*/ 3L,
|
||||
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*consumePercentage=*/ 25.0,
|
||||
/*foregroundUsageTimeInMs=*/ 50, /*backgroundUsageTimeInMs=*/ 60);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBatteryUsageMap_multipleUsers_returnsExpectedResult() {
|
||||
final long[] batteryHistoryKeys = new long[]{
|
||||
1641052800000L, // 2022-01-02 00:00:00
|
||||
1641056400000L, // 2022-01-02 01:00:00
|
||||
1641060000000L // 2022-01-02 02:00:00
|
||||
};
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||
final int currentUserId = mContext.getUserId();
|
||||
// Adds the index = 0 data.
|
||||
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
|
||||
BatteryHistEntry entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 5.0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
|
||||
/*backgroundUsageTimeInMs=*/ 20L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 10.0, /*uid=*/ 2L, currentUserId + 1,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
|
||||
/*backgroundUsageTimeInMs=*/ 20L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 5.0, /*uid=*/ 3L, currentUserId + 2,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
|
||||
/*backgroundUsageTimeInMs=*/ 30L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
|
||||
// Adds the index = 1 data.
|
||||
entryMap = new HashMap<>();
|
||||
entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 15.0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
|
||||
/*backgroundUsageTimeInMs=*/ 30L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 30.0, /*uid=*/ 2L, currentUserId + 1,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
|
||||
/*backgroundUsageTimeInMs=*/ 20L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 15.0, /*uid=*/ 3L, currentUserId + 2,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 30L,
|
||||
/*backgroundUsageTimeInMs=*/ 30L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
|
||||
// Adds the index = 2 data.
|
||||
entryMap = new HashMap<>();
|
||||
entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 25.0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
|
||||
/*backgroundUsageTimeInMs=*/ 30L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 50.0, /*uid=*/ 2L, currentUserId + 1,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
|
||||
/*backgroundUsageTimeInMs=*/ 20L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 25.0, /*uid=*/ 3L, currentUserId + 2,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 30L,
|
||||
/*backgroundUsageTimeInMs=*/ 30L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||
new ArrayList<>();
|
||||
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
|
||||
final List<Integer> levels = List.of(100, 100);
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
|
||||
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
|
||||
DataProcessor.getBatteryUsageMap(
|
||||
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
|
||||
|
||||
final BatteryDiffData resultDiffData =
|
||||
resultMap
|
||||
.get(DataProcessor.SELECTED_INDEX_ALL)
|
||||
.get(DataProcessor.SELECTED_INDEX_ALL);
|
||||
assertBatteryDiffEntry(
|
||||
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 1L,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 25.0,
|
||||
/*foregroundUsageTimeInMs=*/ 10, /*backgroundUsageTimeInMs=*/ 10);
|
||||
assertBatteryDiffEntry(
|
||||
resultDiffData.getSystemDiffEntryList().get(0), BatteryUtils.UID_OTHER_USERS,
|
||||
/*uid=*/ BatteryUtils.UID_OTHER_USERS, ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
|
||||
/*consumePercentage=*/ 75.0, /*foregroundUsageTimeInMs=*/ 0,
|
||||
/*backgroundUsageTimeInMs=*/ 0);
|
||||
assertThat(resultMap.get(0).get(0)).isNotNull();
|
||||
assertThat(resultMap.get(0).get(DataProcessor.SELECTED_INDEX_ALL)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBatteryUsageMap_usageTimeExceed_returnsExpectedResult() {
|
||||
final long[] batteryHistoryKeys = new long[]{
|
||||
1641052800000L, // 2022-01-02 00:00:00
|
||||
1641056400000L, // 2022-01-02 01:00:00
|
||||
1641060000000L // 2022-01-02 02:00:00
|
||||
};
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||
final int currentUserId = mContext.getUserId();
|
||||
// Adds the index = 0 data.
|
||||
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
|
||||
BatteryHistEntry entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
|
||||
/*backgroundUsageTimeInMs=*/ 0L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
|
||||
// Adds the index = 1 data.
|
||||
entryMap = new HashMap<>();
|
||||
entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
|
||||
/*backgroundUsageTimeInMs=*/ 0L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
|
||||
// Adds the index = 2 data.
|
||||
entryMap = new HashMap<>();
|
||||
entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 500.0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 3600000L,
|
||||
/*backgroundUsageTimeInMs=*/ 7200000L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||
new ArrayList<>();
|
||||
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
|
||||
final List<Integer> levels = List.of(100, 100);
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
|
||||
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
|
||||
DataProcessor.getBatteryUsageMap(
|
||||
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
|
||||
|
||||
final BatteryDiffData resultDiffData =
|
||||
resultMap
|
||||
.get(DataProcessor.SELECTED_INDEX_ALL)
|
||||
.get(DataProcessor.SELECTED_INDEX_ALL);
|
||||
// Verifies the clipped usage time.
|
||||
final float ratio = (float) (7200) / (float) (3600 + 7200);
|
||||
final BatteryDiffEntry resultEntry = resultDiffData.getAppDiffEntryList().get(0);
|
||||
assertThat(resultEntry.mForegroundUsageTimeInMs)
|
||||
.isEqualTo(Math.round(entry.mForegroundUsageTimeInMs * ratio));
|
||||
assertThat(resultEntry.mBackgroundUsageTimeInMs)
|
||||
.isEqualTo(Math.round(entry.mBackgroundUsageTimeInMs * ratio));
|
||||
assertThat(resultEntry.mConsumePower)
|
||||
.isEqualTo(entry.mConsumePower * ratio);
|
||||
assertThat(resultMap.get(0).get(0)).isNotNull();
|
||||
assertThat(resultMap.get(0).get(DataProcessor.SELECTED_INDEX_ALL)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBatteryUsageMap_hideApplicationEntries_returnsExpectedResult() {
|
||||
final long[] batteryHistoryKeys = new long[]{
|
||||
1641052800000L, // 2022-01-02 00:00:00
|
||||
1641056400000L, // 2022-01-02 01:00:00
|
||||
1641060000000L // 2022-01-02 02:00:00
|
||||
};
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||
final int currentUserId = mContext.getUserId();
|
||||
// Adds the index = 0 data.
|
||||
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
|
||||
BatteryHistEntry entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
|
||||
/*backgroundUsageTimeInMs=*/ 0L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
|
||||
/*backgroundUsageTimeInMs=*/ 0L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
|
||||
// Adds the index = 1 data.
|
||||
entryMap = new HashMap<>();
|
||||
entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
|
||||
/*backgroundUsageTimeInMs=*/ 0L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
|
||||
/*backgroundUsageTimeInMs=*/ 0L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
|
||||
// Adds the index = 2 data.
|
||||
entryMap = new HashMap<>();
|
||||
entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 10.0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
|
||||
/*backgroundUsageTimeInMs=*/ 20L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 10.0, /*uid=*/ 2L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
|
||||
/*backgroundUsageTimeInMs=*/ 20L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||
new ArrayList<>();
|
||||
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
|
||||
final List<Integer> levels = List.of(100, 100);
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
|
||||
when(mPowerUsageFeatureProvider.getHideApplicationEntries(mContext))
|
||||
.thenReturn(new CharSequence[]{"package1"});
|
||||
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
|
||||
DataProcessor.getBatteryUsageMap(
|
||||
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
|
||||
|
||||
final BatteryDiffData resultDiffData =
|
||||
resultMap
|
||||
.get(DataProcessor.SELECTED_INDEX_ALL)
|
||||
.get(DataProcessor.SELECTED_INDEX_ALL);
|
||||
assertBatteryDiffEntry(
|
||||
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 2L,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 50.0,
|
||||
/*foregroundUsageTimeInMs=*/ 10, /*backgroundUsageTimeInMs=*/ 20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBatteryUsageMap_hideBackgroundUsageTime_returnsExpectedResult() {
|
||||
final long[] batteryHistoryKeys = new long[]{
|
||||
1641052800000L, // 2022-01-02 00:00:00
|
||||
1641056400000L, // 2022-01-02 01:00:00
|
||||
1641060000000L // 2022-01-02 02:00:00
|
||||
};
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||
final int currentUserId = mContext.getUserId();
|
||||
// Adds the index = 0 data.
|
||||
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
|
||||
BatteryHistEntry entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
|
||||
/*backgroundUsageTimeInMs=*/ 0L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
|
||||
/*backgroundUsageTimeInMs=*/ 0L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
|
||||
// Adds the index = 1 data.
|
||||
entryMap = new HashMap<>();
|
||||
entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
|
||||
/*backgroundUsageTimeInMs=*/ 0L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
|
||||
/*backgroundUsageTimeInMs=*/ 0L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
|
||||
// Adds the index = 2 data.
|
||||
entryMap = new HashMap<>();
|
||||
entry = createBatteryHistEntry(
|
||||
"package1", "label1", /*consumePower=*/ 10.0, /*uid=*/ 1L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
|
||||
/*backgroundUsageTimeInMs=*/ 20L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
entry = createBatteryHistEntry(
|
||||
"package2", "label2", /*consumePower=*/ 10.0, /*uid=*/ 2L, currentUserId,
|
||||
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
|
||||
/*backgroundUsageTimeInMs=*/ 20L);
|
||||
entryMap.put(entry.getKey(), entry);
|
||||
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
|
||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
|
||||
new ArrayList<>();
|
||||
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
|
||||
final List<Integer> levels = List.of(100, 100);
|
||||
hourlyBatteryLevelsPerDay.add(
|
||||
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
|
||||
when(mPowerUsageFeatureProvider.getHideBackgroundUsageTimeSet(mContext))
|
||||
.thenReturn(new HashSet(Arrays.asList((CharSequence) "package2")));
|
||||
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
|
||||
DataProcessor.getBatteryUsageMap(
|
||||
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
|
||||
|
||||
final BatteryDiffData resultDiffData =
|
||||
resultMap
|
||||
.get(DataProcessor.SELECTED_INDEX_ALL)
|
||||
.get(DataProcessor.SELECTED_INDEX_ALL);
|
||||
BatteryDiffEntry resultEntry = resultDiffData.getAppDiffEntryList().get(0);
|
||||
assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(20);
|
||||
resultEntry = resultDiffData.getAppDiffEntryList().get(1);
|
||||
assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(0);
|
||||
}
|
||||
|
||||
private static Map<Long, Map<String, BatteryHistEntry>> createHistoryMap(
|
||||
final long[] timestamps, final int[] levels) {
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||
@@ -391,6 +859,23 @@ public class DataProcessorTest {
|
||||
return batteryHistoryMap;
|
||||
}
|
||||
|
||||
private static BatteryHistEntry createBatteryHistEntry(
|
||||
final String packageName, final String appLabel, final double consumePower,
|
||||
final long uid, final long userId, final int consumerType,
|
||||
final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs) {
|
||||
// Only insert required fields.
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(BatteryHistEntry.KEY_PACKAGE_NAME, packageName);
|
||||
values.put(BatteryHistEntry.KEY_APP_LABEL, appLabel);
|
||||
values.put(BatteryHistEntry.KEY_UID, uid);
|
||||
values.put(BatteryHistEntry.KEY_USER_ID, userId);
|
||||
values.put(BatteryHistEntry.KEY_CONSUMER_TYPE, consumerType);
|
||||
values.put(BatteryHistEntry.KEY_CONSUME_POWER, consumePower);
|
||||
values.put(BatteryHistEntry.KEY_FOREGROUND_USAGE_TIME, foregroundUsageTimeInMs);
|
||||
values.put(BatteryHistEntry.KEY_BACKGROUND_USAGE_TIME, backgroundUsageTimeInMs);
|
||||
return new BatteryHistEntry(values);
|
||||
}
|
||||
|
||||
private static void verifyExpectedBatteryLevelData(
|
||||
final BatteryLevelData resultData,
|
||||
final List<Long> expectedDailyTimestamps,
|
||||
@@ -451,4 +936,15 @@ public class DataProcessorTest {
|
||||
.isEqualTo(expectedEnd.getTimeInMillis());
|
||||
}
|
||||
|
||||
private static void assertBatteryDiffEntry(
|
||||
final BatteryDiffEntry entry, final long userId, final long uid,
|
||||
final int consumerType, final double consumePercentage,
|
||||
final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs) {
|
||||
assertThat(entry.mBatteryHistEntry.mUserId).isEqualTo(userId);
|
||||
assertThat(entry.mBatteryHistEntry.mUid).isEqualTo(uid);
|
||||
assertThat(entry.mBatteryHistEntry.mConsumerType).isEqualTo(consumerType);
|
||||
assertThat(entry.getPercentOfTotal()).isEqualTo(consumePercentage);
|
||||
assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs);
|
||||
assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user