Merge "Unify the caption preference class naming"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e85d686..784a94b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4397,14 +4397,6 @@
</receiver>
<receiver
- android:name=".sim.receivers.SuwFinishReceiver"
- android:exported="true">
- <intent-filter>
- <action android:name="com.google.android.setupwizard.SETUP_WIZARD_FINISHED" />
- </intent-filter>
- </receiver>
-
- <receiver
android:name=".sim.receivers.SimCompleteBootReceiver"
android:exported="true">
<intent-filter>
diff --git a/res/layout-land/udfps_enroll_enrolling_land.xml b/res/layout-land/udfps_enroll_enrolling.xml
similarity index 93%
rename from res/layout-land/udfps_enroll_enrolling_land.xml
rename to res/layout-land/udfps_enroll_enrolling.xml
index 776f8a9..f323788 100644
--- a/res/layout-land/udfps_enroll_enrolling_land.xml
+++ b/res/layout-land/udfps_enroll_enrolling.xml
@@ -33,11 +33,15 @@
<!-- Both texts are kept as separate text views so it doesn't jump around in portrait.
See layouts/fingerprint_enroll_enrolling_base.xml. -->
<LinearLayout
+ android:id="@+id/layout_container"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_marginStart="?attr/sudMarginStart"
+ android:layout_marginEnd="@dimen/enroll_margin_end"
android:layout_marginBottom="@dimen/sud_content_frame_padding_bottom"
+ android:paddingStart="@dimen/enroll_padding_start"
+ android:paddingEnd="@dimen/enroll_padding_end"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
diff --git a/res/values-ldltr/dimens.xml b/res/values-ldltr/dimens.xml
new file mode 100755
index 0000000..11d5b33
--- /dev/null
+++ b/res/values-ldltr/dimens.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <!-- Biometrics UDFPS enroll landscape dimensions-->
+ <dimen name="enroll_padding_start">0dp</dimen>
+ <dimen name="enroll_padding_end">0dp</dimen>
+ <dimen name="enroll_margin_end">0dp</dimen>
+
+ <dimen name="rotation_90_enroll_padding_start">-290dp</dimen>
+ <dimen name="rotation_90_enroll_padding_end">108dp</dimen>
+ <dimen name="rotation_90_enroll_margin_end">150dp</dimen>
+</resources>
diff --git a/res/values-ldrtl/dimens.xml b/res/values-ldrtl/dimens.xml
new file mode 100755
index 0000000..cbe7eb5
--- /dev/null
+++ b/res/values-ldrtl/dimens.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <!-- Biometrics UDFPS enroll landscape RTL dimensions-->
+ <dimen name="enroll_padding_start">-440dp</dimen>
+ <dimen name="enroll_padding_end">320dp</dimen>
+ <dimen name="enroll_margin_end">150dp</dimen>
+
+ <dimen name="rotation_90_enroll_padding_start">20dp</dimen>
+ <dimen name="rotation_90_enroll_padding_end">0dp</dimen>
+ <dimen name="rotation_90_enroll_margin_end">20dp</dimen>
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 05d6eb5..33786cc 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -494,6 +494,14 @@
<dimen name="sims_select_margin_bottom">24dp</dimen>
<dimen name="sims_select_margin_top">8dp</dimen>
+ <!-- Biometrics UDFPS enroll default dimensions-->
+ <dimen name="enroll_padding_start">0dp</dimen>
+ <dimen name="enroll_padding_end">0dp</dimen>
+ <dimen name="enroll_margin_end">0dp</dimen>
+ <dimen name="rotation_90_enroll_padding_start">0dp</dimen>
+ <dimen name="rotation_90_enroll_padding_end">0dp</dimen>
+ <dimen name="rotation_90_enroll_margin_end">0dp</dimen>
+
<!-- QR code picture size -->
<dimen name="qrcode_preview_margin">40dp</dimen>
<dimen name="qrcode_preview_radius">30dp</dimen>
diff --git a/src/com/android/settings/UserCredentialsSettings.java b/src/com/android/settings/UserCredentialsSettings.java
index 80b97e4..c45b5ef 100644
--- a/src/com/android/settings/UserCredentialsSettings.java
+++ b/src/com/android/settings/UserCredentialsSettings.java
@@ -48,7 +48,6 @@
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.internal.widget.LockPatternUtils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -297,7 +296,6 @@
private SortedMap<String, Credential> getCredentialsForUid(KeyStore keyStore, int uid) {
try {
final SortedMap<String, Credential> aliasMap = new TreeMap<>();
- boolean isSystem = UserHandle.getAppId(uid) == Process.SYSTEM_UID;
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
@@ -315,19 +313,6 @@
// We don't display any symmetric key entries.
continue;
}
- if (isSystem) {
- // Do not show work profile keys in user credentials
- if (alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT) ||
- alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT)) {
- continue;
- }
- // Do not show synthetic password keys in user credential
- // We should never reach this point because the synthetic password key
- // is symmetric.
- if (alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
- continue;
- }
- }
// At this point we have determined that we have an asymmetric key.
// so we have at least a USER_KEY and USER_CERTIFICATE.
c.storedTypes.add(Credential.Type.USER_KEY);
diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java
index eea1bad..75e4098 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollBase.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java
@@ -181,16 +181,6 @@
getWindow().setStatusBarColor(getBackgroundColor());
}
- @Override
- protected void onStop() {
- super.onStop();
- if (!isChangingConfigurations() && shouldFinishWhenBackgrounded()
- && !BiometricUtils.isAnyMultiBiometricFlow(this)) {
- setResult(RESULT_TIMEOUT);
- finish();
- }
- }
-
protected boolean shouldFinishWhenBackgrounded() {
return !WizardManagerHelper.isAnySetupWizard(getIntent());
}
diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
index acfe5a1..d9f8bd1 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
@@ -242,6 +242,16 @@
}
@Override
+ protected void onStop() {
+ if (!isChangingConfigurations() && shouldFinishWhenBackgrounded()
+ && !BiometricUtils.isAnyMultiBiometricFlow(this)) {
+ setResult(RESULT_TIMEOUT);
+ finish();
+ }
+ super.onStop();
+ }
+
+ @Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_CONFIRMING_CREDENTIALS, mConfirmingCredentials);
diff --git a/src/com/android/settings/biometrics/BiometricHandoffActivity.java b/src/com/android/settings/biometrics/BiometricHandoffActivity.java
index 7f28ced..2b8d89b 100644
--- a/src/com/android/settings/biometrics/BiometricHandoffActivity.java
+++ b/src/com/android/settings/biometrics/BiometricHandoffActivity.java
@@ -49,6 +49,16 @@
mFooterBarMixin.setPrimaryButton(getPrimaryFooterButton());
}
+ @Override
+ protected void onStop() {
+ if (!isChangingConfigurations() && shouldFinishWhenBackgrounded()
+ && !BiometricUtils.isAnyMultiBiometricFlow(this)) {
+ setResult(RESULT_TIMEOUT);
+ finish();
+ }
+ super.onStop();
+ }
+
@NonNull
protected FooterButton getPrimaryFooterButton() {
if (mPrimaryFooterButton == null) {
diff --git a/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java b/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java
index 3a61d5e..6f68f51 100644
--- a/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/BiometricsEnrollEnrolling.java
@@ -62,7 +62,11 @@
@Override
protected void onStop() {
- super.onStop();
+ if (!isChangingConfigurations() && shouldFinishWhenBackgrounded()
+ && !BiometricUtils.isAnyMultiBiometricFlow(this)) {
+ setResult(RESULT_TIMEOUT);
+ finish();
+ }
if (mSidecar != null) {
mSidecar.setListener(null);
@@ -80,6 +84,7 @@
}
finish();
}
+ super.onStop();
}
@Override
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollEducation.java b/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
index d2d356b..cdd99a2 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollEducation.java
@@ -171,6 +171,16 @@
}
@Override
+ protected void onStop() {
+ if (!isChangingConfigurations() && shouldFinishWhenBackgrounded()
+ && !BiometricUtils.isAnyMultiBiometricFlow(this)) {
+ setResult(RESULT_TIMEOUT);
+ finish();
+ }
+ super.onStop();
+ }
+
+ @Override
protected boolean shouldFinishWhenBackgrounded() {
return super.shouldFinishWhenBackgrounded() && !mNextClicked;
}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollFinish.java b/src/com/android/settings/biometrics/face/FaceEnrollFinish.java
index 6e99cdb..1351794 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollFinish.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollFinish.java
@@ -22,6 +22,7 @@
import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollBase;
+import com.android.settings.biometrics.BiometricUtils;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
@@ -49,6 +50,16 @@
}
@Override
+ protected void onStop() {
+ if (!isChangingConfigurations() && shouldFinishWhenBackgrounded()
+ && !BiometricUtils.isAnyMultiBiometricFlow(this)) {
+ setResult(RESULT_TIMEOUT);
+ finish();
+ }
+ super.onStop();
+ }
+
+ @Override
public int getMetricsCategory() {
return SettingsEnums.FACE_ENROLL_FINISHED;
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index ca79a24..5f9a74f 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -47,6 +47,7 @@
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -64,10 +65,12 @@
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.util.WizardManagerHelper;
+import com.google.android.setupdesign.GlifLayout;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
+import java.util.Locale;
/**
* Activity which handles the actual enrolling for fingerprint.
@@ -168,13 +171,37 @@
mAccessibilityManager = getSystemService(AccessibilityManager.class);
mIsAccessibilityEnabled = mAccessibilityManager.isEnabled();
+ final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(
+ Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL);
listenOrientationEvent();
if (mCanAssumeUdfps) {
- if (BiometricUtils.isReverseLandscape(getApplicationContext())) {
- setContentView(R.layout.udfps_enroll_enrolling_land);
- } else {
- setContentView(R.layout.udfps_enroll_enrolling);
+ switch(getApplicationContext().getDisplay().getRotation()) {
+ case Surface.ROTATION_90:
+ final GlifLayout layout = (GlifLayout) getLayoutInflater().inflate(
+ R.layout.udfps_enroll_enrolling, null, false);
+ final LinearLayout layoutContainer = layout.findViewById(
+ R.id.layout_container);
+ final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+
+ lp.setMarginEnd((int) getResources().getDimension(
+ R.dimen.rotation_90_enroll_margin_end));
+ layoutContainer.setPaddingRelative((int) getResources().getDimension(
+ R.dimen.rotation_90_enroll_padding_start), 0, isLayoutRtl
+ ? 0 : (int) getResources().getDimension(
+ R.dimen.rotation_90_enroll_padding_end), 0);
+ layoutContainer.setLayoutParams(lp);
+ setContentView(layout, lp);
+ break;
+
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ case Surface.ROTATION_270:
+ default:
+ setContentView(R.layout.udfps_enroll_enrolling);
+ break;
}
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
} else {
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index 627a514..79a1065 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -249,10 +249,15 @@
@Override
protected void onStop() {
- super.onStop();
if (mAnimation != null) {
mAnimation.pauseAnimation();
}
+ if (!isChangingConfigurations() && shouldFinishWhenBackgrounded()
+ && !BiometricUtils.isAnyMultiBiometricFlow(this)) {
+ setResult(RESULT_TIMEOUT);
+ finish();
+ }
+ super.onStop();
}
@Override
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
index 16773d3..05ca39c 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java
@@ -109,6 +109,16 @@
}
@Override
+ protected void onStop() {
+ if (!isChangingConfigurations() && shouldFinishWhenBackgrounded()
+ && !BiometricUtils.isAnyMultiBiometricFlow(this)) {
+ setResult(RESULT_TIMEOUT);
+ finish();
+ }
+ super.onStop();
+ }
+
+ @Override
protected void onNextButtonClick(View view) {
updateFingerprintSuggestionEnableState();
setResult(RESULT_FINISHED);
diff --git a/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java b/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java
index ea4a2fd..c97d5c9 100644
--- a/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java
+++ b/src/com/android/settings/deviceinfo/StorageWizardFormatProgress.java
@@ -36,6 +36,7 @@
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
+import android.view.WindowManager;
public class StorageWizardFormatProgress extends StorageWizardBase {
private static final String TAG = "StorageWizardFormatProgress";
@@ -54,6 +55,16 @@
return;
}
setContentView(R.layout.storage_wizard_progress);
+
+ // hide the navigation bar for this activity only. So that user can not press back button accidentally.
+ View decorView = getWindow().getDecorView();
+ int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ decorView.setSystemUiVisibility(uiOptions);
+
+ //disable touch in activity so user can not make the hidden navigation bar visible.
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+
setKeepScreenOn(true);
mFormatPrivate = getIntent().getBooleanExtra(EXTRA_FORMAT_PRIVATE, false);
diff --git a/src/com/android/settings/deviceinfo/StorageWizardMigrateProgress.java b/src/com/android/settings/deviceinfo/StorageWizardMigrateProgress.java
index 7e042c1..a4b29af 100644
--- a/src/com/android/settings/deviceinfo/StorageWizardMigrateProgress.java
+++ b/src/com/android/settings/deviceinfo/StorageWizardMigrateProgress.java
@@ -31,6 +31,8 @@
import com.android.settings.R;
+import android.view.WindowManager;
+
public class StorageWizardMigrateProgress extends StorageWizardBase {
private static final String TAG = "StorageWizardMigrateProgress";
@@ -46,7 +48,16 @@
return;
}
setContentView(R.layout.storage_wizard_progress);
+
+ // hide the navigation bar for this activity only. So that user can not press back button accidentally.
+ View decorView = getWindow().getDecorView();
+ int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ decorView.setSystemUiVisibility(uiOptions);
+ //disable touch in activity so user can not make the hidden navigation bar visible.
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+
mMoveId = getIntent().getIntExtra(EXTRA_MOVE_ID, -1);
setIcon(R.drawable.ic_swap_horiz);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java
index 175bba7..2932984 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2.java
@@ -266,8 +266,9 @@
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 @@
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.
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java
index 74f87fd..493891f 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java
@@ -33,23 +33,31 @@
// 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 @@
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 @@
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);
}
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java
index 0995a08..bbe1a8f 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2.java
@@ -47,8 +47,11 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- /**
- * 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 @@
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;
}
- drawAxisLabel(canvas, xOffsets);
+ switch (mViewModel.axisLabelPosition()) {
+ case CENTER_OF_TRAPEZOIDS:
+ drawAxisLabelsCenterOfTrapezoids(canvas, xOffsets, unitWidth);
+ break;
+ case BETWEEN_TRAPEZOIDS:
+ default:
+ drawAxisLabelsBetweenTrapezoids(canvas, xOffsets);
+ break;
+ }
}
}
- 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 @@
}
}
+ 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 @@
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 @@
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 {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
index 5743cac..b5d4dde 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java
@@ -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 @@
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 @@
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);
+ }
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index d8650a6..a004a51 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -18,21 +18,36 @@
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 @@
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 @@
@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,12 +105,34 @@
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.
* The keys of processed history map should contain every hour between the start and end
@@ -187,7 +244,7 @@
@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 @@
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 @@
* 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);
@@ -235,6 +292,40 @@
}
/**
+ * @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 @@
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);
+ });
+ }
+ }
+ }
}
diff --git a/src/com/android/settings/vpn2/AdvancedVpnFeatureProvider.java b/src/com/android/settings/vpn2/AdvancedVpnFeatureProvider.java
index cb56c35..962b6c2 100644
--- a/src/com/android/settings/vpn2/AdvancedVpnFeatureProvider.java
+++ b/src/com/android/settings/vpn2/AdvancedVpnFeatureProvider.java
@@ -47,4 +47,9 @@
* Returns {@code true} advanced vpn is removable.
*/
boolean isAdvancedVpnRemovable();
+
+ /**
+ * Returns {@code true} if the disconnect dialog is enabled when advanced vpn is connected.
+ */
+ boolean isDisconnectDialogEnabled();
}
diff --git a/src/com/android/settings/vpn2/AdvancedVpnFeatureProviderImpl.java b/src/com/android/settings/vpn2/AdvancedVpnFeatureProviderImpl.java
index c5bc69c..b8f58a9 100644
--- a/src/com/android/settings/vpn2/AdvancedVpnFeatureProviderImpl.java
+++ b/src/com/android/settings/vpn2/AdvancedVpnFeatureProviderImpl.java
@@ -46,4 +46,9 @@
public boolean isAdvancedVpnRemovable() {
return true;
}
+
+ @Override
+ public boolean isDisconnectDialogEnabled() {
+ return true;
+ }
}
diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java
index 3b875eb..a91bb6c 100644
--- a/src/com/android/settings/vpn2/VpnSettings.java
+++ b/src/com/android/settings/vpn2/VpnSettings.java
@@ -366,7 +366,7 @@
public void setShownPreferences(final Collection<Preference> updates) {
retainAllPreference(updates);
- final PreferenceGroup vpnGroup = getPreferenceScreen();
+ final PreferenceGroup vpnGroup = mPreferenceScreen;
updatePreferenceGroup(vpnGroup, updates);
// Show all new preferences on the screen
@@ -448,14 +448,16 @@
} else if (preference instanceof AppPreference) {
AppPreference pref = (AppPreference) preference;
boolean connected = (pref.getState() == AppPreference.STATE_CONNECTED);
+ String vpnPackageName = pref.getPackageName();
- if (!connected) {
+ if ((!connected) || (isAdvancedVpn(mFeatureProvider, vpnPackageName, getContext())
+ && !mFeatureProvider.isDisconnectDialogEnabled())) {
try {
UserHandle user = UserHandle.of(pref.getUserId());
- Context userContext = getActivity().createPackageContextAsUser(
- getActivity().getPackageName(), 0 /* flags */, user);
+ Context userContext = getContext().createPackageContextAsUser(
+ getContext().getPackageName(), 0 /* flags */, user);
PackageManager pm = userContext.getPackageManager();
- Intent appIntent = pm.getLaunchIntentForPackage(pref.getPackageName());
+ Intent appIntent = pm.getLaunchIntentForPackage(vpnPackageName);
if (appIntent != null) {
userContext.startActivityAsUser(appIntent, user);
return true;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2Test.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2Test.java
index b8862d4..a5f193b 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2Test.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerV2Test.java
@@ -178,7 +178,8 @@
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 @@
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 @@
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 @@
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 @@
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
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java
index a8b8d10..1747335 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewV2Test.java
@@ -104,7 +104,8 @@
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 @@
.thenReturn(false);
mBatteryChartView.onAttachedToWindow();
+
assertThat(mBatteryChartView.isClickable()).isFalse();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
}
@@ -148,6 +150,7 @@
doReturn(false).when(mMockAccessibilityManager).isEnabled();
mBatteryChartView.onAttachedToWindow();
+
assertThat(mBatteryChartView.isClickable()).isTrue();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
}
@@ -163,6 +166,7 @@
.getEnabledAccessibilityServiceList(anyInt());
mBatteryChartView.onAttachedToWindow();
+
assertThat(mBatteryChartView.isClickable()).isTrue();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
}
@@ -175,6 +179,7 @@
doReturn(true).when(mMockAccessibilityManager).isEnabled();
mBatteryChartView.onAttachedToWindow();
+
assertThat(mBatteryChartView.isClickable()).isFalse();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
}
@@ -188,7 +193,8 @@
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);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
index 0306c4b..1b1e469 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
@@ -19,11 +19,16 @@
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.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 @@
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 @@
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 @@
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 @@
.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 @@
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 @@
.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);
+ }
}
diff --git a/tests/unit/src/com/android/settings/vpn2/VpnSettingsTest.java b/tests/unit/src/com/android/settings/vpn2/VpnSettingsTest.java
index d6ba33a..953a524 100644
--- a/tests/unit/src/com/android/settings/vpn2/VpnSettingsTest.java
+++ b/tests/unit/src/com/android/settings/vpn2/VpnSettingsTest.java
@@ -21,16 +21,21 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Looper;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.ArraySet;
import androidx.preference.Preference;
@@ -48,6 +53,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -58,13 +64,18 @@
@RunWith(AndroidJUnit4.class)
public class VpnSettingsTest {
- private static final String ADVANCED_VPN_GROUP_KEY = "advanced_vpn_group";
- private static final String VPN_GROUP_KEY = "vpn_group";
- private static final String ADVANCED_VPN_GROUP_TITLE = "advanced_vpn_group_title";
- private static final String VPN_GROUP_TITLE = "vpn_group_title";
- private static final String FAKE_PACKAGE_NAME = "com.fake.package.name";
- private static final String ADVANCED_VPN_GROUP_PACKAGE_NAME = "com.advanced.package.name";
private static final int USER_ID_1 = UserHandle.USER_NULL;
+ private static final String VPN_GROUP_KEY = "vpn_group";
+ private static final String VPN_GROUP_TITLE = "vpn_group_title";
+ private static final String VPN_PACKAGE_NAME = "vpn.package.name";
+ private static final String VPN_LAUNCH_INTENT = "vpn.action";
+ private static final String ADVANCED_VPN_GROUP_KEY = "advanced_vpn_group";
+ private static final String ADVANCED_VPN_GROUP_TITLE = "advanced_vpn_group_title";
+ private static final String ADVANCED_VPN_PACKAGE_NAME = "advanced.vpn.package.name";
+ private static final String ADVANCED_VPN_LAUNCH_INTENT = "advanced.vpn.action";
+
+ private final Intent mVpnIntent = new Intent().setAction(VPN_LAUNCH_INTENT);
+ private final Intent mAdvancedVpnIntent = new Intent().setAction(ADVANCED_VPN_LAUNCH_INTENT);
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -108,7 +119,7 @@
when(mFakeFeatureFactory.mAdvancedVpnFeatureProvider.getVpnPreferenceGroupTitle(mContext))
.thenReturn(VPN_GROUP_TITLE);
when(mFakeFeatureFactory.mAdvancedVpnFeatureProvider.getAdvancedVpnPackageName())
- .thenReturn(ADVANCED_VPN_GROUP_PACKAGE_NAME);
+ .thenReturn(ADVANCED_VPN_PACKAGE_NAME);
when(mFakeFeatureFactory.mAdvancedVpnFeatureProvider.isAdvancedVpnSupported(any()))
.thenReturn(true);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
@@ -122,7 +133,7 @@
public void setShownAdvancedPreferences_hasGeneralVpn_returnsVpnCountAs1() {
Set<Preference> updates = new ArraySet<>();
AppPreference pref =
- spy(new AppPreference(mContext, USER_ID_1, FAKE_PACKAGE_NAME));
+ spy(new AppPreference(mContext, USER_ID_1, VPN_PACKAGE_NAME));
updates.add(pref);
mVpnSettings.setShownAdvancedPreferences(updates);
@@ -136,7 +147,7 @@
public void setShownAdvancedPreferences_hasAdvancedVpn_returnsAdvancedVpnCountAs1() {
Set<Preference> updates = new ArraySet<>();
AppPreference pref =
- spy(new AppPreference(mContext, USER_ID_1, ADVANCED_VPN_GROUP_PACKAGE_NAME));
+ spy(new AppPreference(mContext, USER_ID_1, ADVANCED_VPN_PACKAGE_NAME));
updates.add(pref);
mVpnSettings.setShownAdvancedPreferences(updates);
@@ -175,7 +186,7 @@
List<AppOpsManager.OpEntry> opEntries = new ArrayList<>();
List<AppOpsManager.PackageOps> apps = new ArrayList<>();
AppOpsManager.PackageOps packageOps =
- new AppOpsManager.PackageOps(FAKE_PACKAGE_NAME, uid, opEntries);
+ new AppOpsManager.PackageOps(VPN_PACKAGE_NAME, uid, opEntries);
apps.add(packageOps);
when(mAppOpsManager.getPackagesForOps((int[]) any())).thenReturn(apps);
when(mFakeFeatureFactory.mAdvancedVpnFeatureProvider.isAdvancedVpnSupported(any()))
@@ -185,4 +196,90 @@
mFakeFeatureFactory.getAdvancedVpnFeatureProvider(),
mAppOpsManager)).isEmpty();
}
+
+ @Test
+ public void clickVpn_VpnConnected_doesNotStartVpnLaunchIntent()
+ throws PackageManager.NameNotFoundException {
+ Set<Preference> updates = new ArraySet<>();
+ AppPreference pref = spy(new AppPreference(mContext, USER_ID_1, VPN_PACKAGE_NAME));
+ pref.setState(AppPreference.STATE_CONNECTED);
+ updates.add(pref);
+ when(mContext.createPackageContextAsUser(any(), anyInt(), any())).thenReturn(mContext);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.getLaunchIntentForPackage(any())).thenReturn(mVpnIntent);
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ doNothing().when(mContext).startActivityAsUser(captor.capture(), any());
+ mVpnSettings.setShownPreferences(updates);
+
+ mVpnSettings.onPreferenceClick(pref);
+
+ verify(mContext, never()).startActivityAsUser(any(), any());
+ }
+
+ @Test
+ public void clickVpn_VpnDisconnected_startsVpnLaunchIntent()
+ throws PackageManager.NameNotFoundException {
+ Set<Preference> updates = new ArraySet<>();
+ AppPreference pref = spy(new AppPreference(mContext, USER_ID_1, VPN_PACKAGE_NAME));
+ pref.setState(AppPreference.STATE_DISCONNECTED);
+ updates.add(pref);
+ when(mContext.createPackageContextAsUser(any(), anyInt(), any())).thenReturn(mContext);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.getLaunchIntentForPackage(any())).thenReturn(mVpnIntent);
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ doNothing().when(mContext).startActivityAsUser(captor.capture(), any());
+ mVpnSettings.setShownPreferences(updates);
+
+ mVpnSettings.onPreferenceClick(pref);
+
+ verify(mContext).startActivityAsUser(captor.capture(), any());
+ assertThat(TextUtils.equals(captor.getValue().getAction(),
+ VPN_LAUNCH_INTENT)).isTrue();
+ }
+
+ @Test
+ public void clickAdvancedVpn_VpnConnectedDisconnectDialogDisabled_startsAppLaunchIntent()
+ throws PackageManager.NameNotFoundException {
+ Set<Preference> updates = new ArraySet<>();
+ AppPreference pref =
+ spy(new AppPreference(mContext, USER_ID_1, ADVANCED_VPN_PACKAGE_NAME));
+ pref.setState(AppPreference.STATE_CONNECTED);
+ updates.add(pref);
+ when(mFakeFeatureFactory.mAdvancedVpnFeatureProvider.isDisconnectDialogEnabled())
+ .thenReturn(false);
+ when(mContext.createPackageContextAsUser(any(), anyInt(), any())).thenReturn(mContext);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.getLaunchIntentForPackage(any())).thenReturn(mAdvancedVpnIntent);
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ doNothing().when(mContext).startActivityAsUser(captor.capture(), any());
+ mVpnSettings.setShownAdvancedPreferences(updates);
+
+ mVpnSettings.onPreferenceClick(pref);
+
+ verify(mContext).startActivityAsUser(captor.capture(), any());
+ assertThat(TextUtils.equals(captor.getValue().getAction(),
+ ADVANCED_VPN_LAUNCH_INTENT)).isTrue();
+ }
+
+ @Test
+ public void clickAdvancedVpn_VpnConnectedDisconnectDialogEnabled_doesNotStartAppLaunchIntent()
+ throws PackageManager.NameNotFoundException {
+ Set<Preference> updates = new ArraySet<>();
+ AppPreference pref =
+ spy(new AppPreference(mContext, USER_ID_1, ADVANCED_VPN_PACKAGE_NAME));
+ pref.setState(AppPreference.STATE_CONNECTED);
+ updates.add(pref);
+ when(mFakeFeatureFactory.mAdvancedVpnFeatureProvider.isDisconnectDialogEnabled())
+ .thenReturn(true);
+ when(mContext.createPackageContextAsUser(any(), anyInt(), any())).thenReturn(mContext);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.getLaunchIntentForPackage(any())).thenReturn(mAdvancedVpnIntent);
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ doNothing().when(mContext).startActivityAsUser(captor.capture(), any());
+ mVpnSettings.setShownAdvancedPreferences(updates);
+
+ mVpnSettings.onPreferenceClick(pref);
+
+ verify(mContext, never()).startActivityAsUser(any(), any());
+ }
}