diff --git a/res/layout/battery_chart_graph.xml b/res/layout/battery_chart_graph.xml
index 6187d07b66c..2984f43d5bc 100644
--- a/res/layout/battery_chart_graph.xml
+++ b/res/layout/battery_chart_graph.xml
@@ -26,7 +26,7 @@
android:id="@+id/chart_summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="45dp"
+ android:layout_marginBottom="30dp"
android:textAppearance="?android:attr/textAppearanceSmall"
settings:textColor="?android:attr/textColorSecondary"
android:text="@string/battery_usage_chart_graph_hint" />
@@ -34,9 +34,16 @@
+
diff --git a/res/layout/companion_apps_remove_button_widget.xml b/res/layout/companion_apps_remove_button_widget.xml
new file mode 100644
index 00000000000..a3c229537ac
--- /dev/null
+++ b/res/layout/companion_apps_remove_button_widget.xml
@@ -0,0 +1,24 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/preference_companion_app.xml b/res/layout/preference_companion_app.xml
new file mode 100644
index 00000000000..22712753b2f
--- /dev/null
+++ b/res/layout/preference_companion_app.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 0aaf930e112..7bc760a9c6f 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -205,6 +205,9 @@
21dp
16dp
+
+ 20dp
+
1px
16dp
@@ -441,6 +444,7 @@
24dp
+ 3dp
1dp
4dp
3dp
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 19f892e5525..41f76e8f974 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1815,6 +1815,10 @@
Device\'s Bluetooth address: %1$s
Forget device?
+
+ Remove association
+
+ Disconnect App?
Your phone will no longer be paired with %1$s
@@ -1822,12 +1826,16 @@
Your tablet will no longer be paired with %1$s
Your device will no longer be paired with %1$s
+
+ %1$s app will no longer connect to your %2$s
%1$s will no longer be paired with any device linked to this account
Forget device
+
+ Disconnect app
Connect to\u2026
diff --git a/res/xml/accessibility_button_settings.xml b/res/xml/accessibility_button_settings.xml
index 5e81616bffc..bb16f0397cd 100644
--- a/res/xml/accessibility_button_settings.xml
+++ b/res/xml/accessibility_button_settings.xml
@@ -56,7 +56,6 @@
diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml
index 94052b67213..5084d4d481a 100644
--- a/res/xml/bluetooth_device_details_fragment.xml
+++ b/res/xml/bluetooth_device_details_fragment.xml
@@ -44,6 +44,9 @@
settings:allowDividerBelow="true"
settings:allowDividerAbove="true"/>
+
+
diff --git a/res/xml/date_time_prefs.xml b/res/xml/date_time_prefs.xml
index 47056dfb5a8..27311322e28 100644
--- a/res/xml/date_time_prefs.xml
+++ b/res/xml/date_time_prefs.xml
@@ -38,26 +38,31 @@
android:summary="@string/summary_placeholder"
settings:userRestriction="no_config_date_time"/>
-
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/xml/privacy_dashboard_settings.xml b/res/xml/privacy_dashboard_settings.xml
index fcd0de493f8..a1a608d20ac 100644
--- a/res/xml/privacy_dashboard_settings.xml
+++ b/res/xml/privacy_dashboard_settings.xml
@@ -78,7 +78,7 @@
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 84183154236..031fb8a9d92 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -48,6 +48,7 @@ import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable;
@@ -94,6 +95,7 @@ import android.widget.EditText;
import android.widget.ListView;
import android.widget.TabWidget;
+import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.core.graphics.drawable.IconCompat;
@@ -111,6 +113,7 @@ import com.android.settings.dashboard.profileselector.ProfileFragmentBridge;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.widget.ActionBarShadowController;
+import com.android.settingslib.widget.AdaptiveIcon;
import java.util.Iterator;
import java.util.List;
@@ -967,15 +970,36 @@ public final class Utils extends com.android.settingslib.Utils {
}
/**
- * Sets the preference icon with a drawable that is scaled down to to avoid crashing Settings if
- * it's too big.
+ * Gets the adaptive icon with a drawable that wrapped with an adaptive background using {@code
+ * backgroundColor} if it is not a {@link AdaptiveIconDrawable}
+ *
+ * If the given {@code icon} is too big, it will be auto scaled down to to avoid crashing
+ * Settings.
*/
- public static void setSafeIcon(Preference pref, Drawable icon) {
+ public static Drawable getAdaptiveIcon(Context context, Drawable icon,
+ @ColorInt int backgroundColor) {
+ Drawable adaptiveIcon = getSafeIcon(icon);
+
+ if (!(adaptiveIcon instanceof AdaptiveIconDrawable)) {
+ adaptiveIcon = new AdaptiveIcon(context, adaptiveIcon);
+ ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(backgroundColor);
+ }
+
+ return adaptiveIcon;
+ }
+
+ /**
+ * Gets the icon with a drawable that is scaled down to to avoid crashing Settings if it's too
+ * big and not a {@link VectorDrawable}.
+ */
+ public static Drawable getSafeIcon(Drawable icon) {
Drawable safeIcon = icon;
+
if ((icon != null) && !(icon instanceof VectorDrawable)) {
safeIcon = getSafeDrawable(icon, 500, 500);
}
- pref.setIcon(safeIcon);
+
+ return safeIcon;
}
/**
@@ -985,7 +1009,7 @@ public final class Utils extends com.android.settingslib.Utils {
* @param maxWidth maximum width, in pixels.
* @param maxHeight maximum height, in pixels.
*/
- public static Drawable getSafeDrawable(Drawable original, int maxWidth, int maxHeight) {
+ private static Drawable getSafeDrawable(Drawable original, int maxWidth, int maxHeight) {
final int actualWidth = original.getMinimumWidth();
final int actualHeight = original.getMinimumHeight();
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index af8bf47036f..d4db3953f79 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -28,6 +28,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
@@ -663,7 +664,7 @@ public class AccessibilitySettings extends DashboardFragment {
preference.setKey(key);
preference.setTitle(title);
preference.setSummary(summary);
- Utils.setSafeIcon(preference, icon);
+ preference.setIcon(Utils.getAdaptiveIcon(mContext, icon, Color.WHITE));
preference.setFragment(fragment);
preference.setIconSize(ICON_SIZE_MEDIUM);
preference.setPersistent(false); // Disable SharedPreferences.
diff --git a/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java b/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java
index 47882ded46e..a9d1572b7e5 100644
--- a/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java
+++ b/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java
@@ -124,7 +124,7 @@ public class PasswordsPreferenceController extends BasePreferenceController
serviceInfo,
serviceInfo.applicationInfo,
user);
- Utils.setSafeIcon(pref, icon);
+ pref.setIcon(Utils.getSafeIcon(icon));
pref.setIntent(
new Intent(Intent.ACTION_MAIN)
.setClassName(serviceInfo.packageName, service.getPasswordsActivity()));
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
index 8e8e072a017..fd6f4c243e7 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
@@ -66,7 +66,7 @@ public abstract class DefaultAppPreferenceController extends AbstractPreferenceC
}
if (!TextUtils.isEmpty(defaultAppLabel)) {
preference.setSummary(defaultAppLabel);
- Utils.setSafeIcon(preference, getDefaultAppIcon());
+ preference.setIcon(Utils.getSafeIcon(getDefaultAppIcon()));
} else {
Log.d(TAG, "No default app");
preference.setSummary(R.string.app_list_preference_none);
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java
index 200c0b949ec..0eb823bc4d9 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java
@@ -57,15 +57,15 @@ public class FaceSettingsAttentionPreferenceController extends FaceSettingsPrefe
private final GetFeatureCallback mGetFeatureCallback = new GetFeatureCallback() {
@Override
- public void onCompleted(boolean success, int feature, boolean value) {
- if (feature == FaceManager.FEATURE_REQUIRE_ATTENTION && success) {
- if (!mFaceManager.hasEnrolledTemplates(getUserId())) {
- mPreference.setEnabled(false);
- } else {
- mPreference.setEnabled(true);
- mPreference.setChecked(value);
+ public void onCompleted(boolean success, int[] features, boolean[] featureState) {
+ boolean requireAttentionEnabled = false;
+ for (int i = 0; i < features.length; i++) {
+ if (features[i] == FaceManager.FEATURE_REQUIRE_ATTENTION) {
+ requireAttentionEnabled = featureState[i];
}
}
+ mPreference.setEnabled(success);
+ mPreference.setChecked(requireAttentionEnabled);
}
};
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java
new file mode 100644
index 00000000000..f2a94ba7440
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsController.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static com.android.internal.util.CollectionUtils.filter;
+
+import android.companion.Association;
+import android.companion.CompanionDeviceManager;
+import android.companion.ICompanionDeviceManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import com.google.common.base.Objects;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+
+/**
+ * This class adds Companion Device app rows to launch the app or remove the associations
+ */
+public class BluetoothDetailsCompanionAppsController extends BluetoothDetailsController {
+ public static final String KEY_DEVICE_COMPANION_APPS = "device_companion_apps";
+ private static final String LOG_TAG = "BTCompanionController";
+
+ private CachedBluetoothDevice mCachedDevice;
+
+ @VisibleForTesting
+ PreferenceCategory mProfilesContainer;
+
+ @VisibleForTesting
+ CompanionDeviceManager mCompanionDeviceManager;
+
+ @VisibleForTesting
+ PackageManager mPackageManager;
+
+ public BluetoothDetailsCompanionAppsController(Context context,
+ PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) {
+ super(context, fragment, device, lifecycle);
+ mCachedDevice = device;
+ mCompanionDeviceManager = context.getSystemService(CompanionDeviceManager.class);
+ mPackageManager = context.getPackageManager();
+ lifecycle.addObserver(this);
+ }
+
+ @Override
+ protected void init(PreferenceScreen screen) {
+ mProfilesContainer = screen.findPreference(getPreferenceKey());
+ mProfilesContainer.setLayoutResource(R.layout.preference_companion_app);
+ }
+
+ private List getAssociations(String address) {
+ return filter(
+ mCompanionDeviceManager.getAllAssociations(),
+ a -> Objects.equal(address, a.getDeviceMacAddress()));
+ }
+
+ private static void removePreference(PreferenceCategory container, String packageName) {
+ Preference preference = container.findPreference(packageName);
+ if (preference != null) {
+ container.removePreference(preference);
+ }
+ }
+
+ private void removeAssociationDialog(String packageName, String address,
+ PreferenceCategory container, CharSequence appName, Context context) {
+ DialogInterface.OnClickListener dialogClickListener = (dialog, which) -> {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ removeAssociation(packageName, address, container);
+ }
+ };
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+
+ builder.setPositiveButton(
+ R.string.bluetooth_companion_app_remove_association_confirm_button,
+ dialogClickListener)
+ .setNegativeButton(android.R.string.cancel, dialogClickListener)
+ .setTitle(R.string.bluetooth_companion_app_remove_association_dialog_title)
+ .setMessage(mContext.getString(
+ R.string.bluetooth_companion_app_body, appName, mCachedDevice.getName()))
+ .show();
+ }
+
+ private static void removeAssociation(String packageName, String address,
+ PreferenceCategory container) {
+ try {
+ java.util.Objects.requireNonNull(ICompanionDeviceManager.Stub.asInterface(
+ ServiceManager.getService(
+ Context.COMPANION_DEVICE_SERVICE))).disassociate(
+ address, packageName);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+
+ removePreference(container, packageName);
+ }
+
+ private CharSequence getAppName(String packageName) {
+ CharSequence appName = null;
+ try {
+ appName = mPackageManager.getApplicationLabel(
+ mPackageManager.getApplicationInfo(packageName, 0));
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Package Not Found", e);
+ }
+
+ return appName;
+ }
+
+ private List getPreferencesNeedToShow(String address, PreferenceCategory container) {
+ List preferencesToRemove = new ArrayList<>();
+ Set packages = getAssociations(address)
+ .stream().map(Association::getPackageName)
+ .collect(Collectors.toSet());
+
+ for (int i = 0; i < container.getPreferenceCount(); i++) {
+ String preferenceKey = container.getPreference(i).getKey();
+ if (packages.isEmpty() || !packages.contains(preferenceKey)) {
+ preferencesToRemove.add(preferenceKey);
+ }
+ }
+
+ for (String preferenceName : preferencesToRemove) {
+ removePreference(container, preferenceName);
+ }
+
+ return packages.stream()
+ .filter(p -> container.findPreference(p) == null)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Refreshes the state of the preferences for all the associations, possibly adding or
+ * removing preferences as needed.
+ */
+ @Override
+ protected void refresh() {
+ updatePreferences(mContext, mCachedDevice.getAddress(), mProfilesContainer);
+ }
+
+ /**
+ * Add preferences for each association for the bluetooth device
+ */
+ public void updatePreferences(Context context,
+ String address, PreferenceCategory container) {
+ Set addedPackages = new HashSet<>();
+
+ for (String packageName : getPreferencesNeedToShow(address, container)) {
+ CharSequence appName = getAppName(packageName);
+
+ if (TextUtils.isEmpty(appName) || !addedPackages.add(packageName)) {
+ continue;
+ }
+
+ Drawable removeIcon = context.getResources().getDrawable(R.drawable.ic_clear);
+ CompanionAppWidgetPreference preference = new CompanionAppWidgetPreference(
+ removeIcon,
+ v -> removeAssociationDialog(packageName, address, container, appName, context),
+ context
+ );
+
+ Drawable appIcon;
+
+ try {
+ appIcon = mPackageManager.getApplicationIcon(packageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Icon Not Found", e);
+ continue;
+ }
+ Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
+ preference.setIcon(appIcon);
+ preference.setTitle(appName.toString());
+ preference.setOnPreferenceClickListener(v -> {
+ context.startActivity(intent);
+ return true;
+ });
+
+ preference.setKey(packageName);
+ preference.setVisible(true);
+ container.addPreference(preference);
+ }
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_DEVICE_COMPANION_APPS;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index a8812475ace..4980ba313fb 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -185,6 +185,8 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
lifecycle, mManager));
controllers.add(new BluetoothDetailsButtonsController(context, this, mCachedDevice,
lifecycle));
+ controllers.add(new BluetoothDetailsCompanionAppsController(context, this,
+ mCachedDevice, lifecycle));
controllers.add(new BluetoothDetailsProfilesController(context, this, mManager,
mCachedDevice, lifecycle));
controllers.add(new BluetoothDetailsMacAddressController(context, this, mCachedDevice,
diff --git a/src/com/android/settings/bluetooth/CompanionAppWidgetPreference.java b/src/com/android/settings/bluetooth/CompanionAppWidgetPreference.java
new file mode 100644
index 00000000000..cd0643373d6
--- /dev/null
+++ b/src/com/android/settings/bluetooth/CompanionAppWidgetPreference.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.ImageButton;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+
+/**
+ * A custom preference for companion device apps. Added a button for association removal
+ */
+public class CompanionAppWidgetPreference extends Preference {
+ private Drawable mWidgetIcon;
+ private View.OnClickListener mWidgetListener;
+ private int mImageButtonPadding;
+
+ public CompanionAppWidgetPreference(Drawable widgetIcon, View.OnClickListener widgetListener,
+ Context context) {
+ super(context);
+ mWidgetIcon = widgetIcon;
+ mWidgetListener = widgetListener;
+ mImageButtonPadding = context.getResources().getDimensionPixelSize(
+ R.dimen.bluetooth_companion_app_widget);
+ setWidgetLayoutResource(R.layout.companion_apps_remove_button_widget);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ ImageButton imageButton = (ImageButton) holder.findViewById(R.id.remove_button);
+ imageButton.setPadding(
+ mImageButtonPadding, mImageButtonPadding, mImageButtonPadding, mImageButtonPadding);
+ imageButton.setColorFilter(getContext().getColor(android.R.color.darker_gray));
+ imageButton.setImageDrawable(mWidgetIcon);
+ imageButton.setOnClickListener(mWidgetListener);
+ }
+
+}
diff --git a/src/com/android/settings/dream/CurrentDreamPreferenceController.java b/src/com/android/settings/dream/CurrentDreamPreferenceController.java
index a73898fdc0a..c267921fd2a 100644
--- a/src/com/android/settings/dream/CurrentDreamPreferenceController.java
+++ b/src/com/android/settings/dream/CurrentDreamPreferenceController.java
@@ -88,6 +88,6 @@ public class CurrentDreamPreferenceController extends BasePreferenceController {
}
final GearPreference gearPref = (GearPreference) preference;
gearPref.setIconSize(RestrictedPreference.ICON_SIZE_SMALL);
- Utils.setSafeIcon(gearPref, mBackend.getActiveIcon());
+ gearPref.setIcon(Utils.getSafeIcon(mBackend.getActiveIcon()));
}
}
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index 3ef7c455302..4dbde251276 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -42,6 +42,8 @@ import com.android.settings.applications.appinfo.ButtonActionDialogFragment;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
@@ -61,7 +63,7 @@ import java.util.List;
*/
public class AdvancedPowerUsageDetail extends DashboardFragment implements
ButtonActionDialogFragment.AppButtonsDialogListener,
- RadioButtonPreference.OnClickListener {
+ BatteryTipPreferenceController.BatteryTipListener, RadioButtonPreference.OnClickListener {
public static final String TAG = "AdvancedPowerDetail";
public static final String EXTRA_UID = "extra_uid";
@@ -73,6 +75,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
public static final String EXTRA_POWER_USAGE_PERCENT = "extra_power_usage_percent";
public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount";
+ private static final String KEY_PREF_FOREGROUND = "app_usage_foreground";
+ private static final String KEY_PREF_BACKGROUND = "app_usage_background";
private static final String KEY_PREF_HEADER = "header_view";
private static final String KEY_PREF_UNRESTRICTED = "unrestricted_pref";
private static final String KEY_PREF_OPTIMIZED = "optimized_pref";
@@ -92,7 +96,10 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
BatteryUtils mBatteryUtils;
@VisibleForTesting
BatteryOptimizeUtils mBatteryOptimizeUtils;
-
+ @VisibleForTesting
+ Preference mForegroundPreference;
+ @VisibleForTesting
+ Preference mBackgroundPreference;
@VisibleForTesting
Preference mFooterPreference;
@VisibleForTesting
@@ -101,18 +108,18 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
RadioButtonPreference mOptimizePreference;
@VisibleForTesting
RadioButtonPreference mUnrestrictedPreference;
- private AppButtonsPreferenceController mAppButtonsPreferenceController;
- private UnrestrictedPreferenceController mUnrestrictedPreferenceController;
- private OptimizedPreferenceController mOptimizedPreferenceController;
- private RestrictedPreferenceController mRestrictedPreferenceController;
+ @VisibleForTesting
+ boolean enableTriState = true;
- private String mPackageName;
+ private AppButtonsPreferenceController mAppButtonsPreferenceController;
+ private BackgroundActivityPreferenceController mBackgroundActivityPreferenceController;
// A wrapper class to carry LaunchBatteryDetailPage required arguments.
private static final class LaunchBatteryDetailPageArgs {
private String mUsagePercent;
private String mPackageName;
private String mAppLabel;
+ private String mSlotInformation;
private int mUid;
private int mIconId;
private int mConsumedPower;
@@ -124,18 +131,22 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
/** Launches battery details page for an individual battery consumer. */
public static void startBatteryDetailPage(
Activity caller, InstrumentedPreferenceFragment fragment,
- BatteryDiffEntry diffEntry, String usagePercent) {
+ BatteryDiffEntry diffEntry, String usagePercent,
+ boolean isValidToShowSummary, String slotInformation) {
final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
final LaunchBatteryDetailPageArgs launchArgs = new LaunchBatteryDetailPageArgs();
// configure the launch argument.
launchArgs.mUsagePercent = usagePercent;
launchArgs.mPackageName = diffEntry.getPackageName();
launchArgs.mAppLabel = diffEntry.getAppLabel();
+ launchArgs.mSlotInformation = slotInformation;
launchArgs.mUid = (int) histEntry.mUid;
launchArgs.mIconId = diffEntry.getAppIconId();
launchArgs.mConsumedPower = (int) diffEntry.mConsumePower;
- launchArgs.mForegroundTimeMs = diffEntry.mForegroundUsageTimeInMs;
- launchArgs.mBackgroundTimeMs = diffEntry.mBackgroundUsageTimeInMs;
+ launchArgs.mForegroundTimeMs =
+ isValidToShowSummary ? diffEntry.mForegroundUsageTimeInMs : 0;
+ launchArgs.mBackgroundTimeMs =
+ isValidToShowSummary ? diffEntry.mBackgroundUsageTimeInMs : 0;
launchArgs.mIsUserEntry = histEntry.isUserEntry();
startBatteryDetailPage(caller, fragment, launchArgs);
}
@@ -227,22 +238,17 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- mPackageName = getArguments().getString(EXTRA_PACKAGE_NAME);
- mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE);
- mHeaderPreference = (LayoutPreference) findPreference(KEY_PREF_HEADER);
+ final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME);
+ if (enableTriState) {
+ onCreateForTriState(packageName);
+ } else {
+ mForegroundPreference = findPreference(KEY_PREF_FOREGROUND);
+ mBackgroundPreference = findPreference(KEY_PREF_BACKGROUND);
+ }
+ mHeaderPreference = findPreference(KEY_PREF_HEADER);
- mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED);
- mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED);
- mRestrictedPreference = findPreference(KEY_PREF_RESTRICTED);
- mUnrestrictedPreference.setOnClickListener(this);
- mOptimizePreference.setOnClickListener(this);
- mRestrictedPreference.setOnClickListener(this);
-
- mBatteryOptimizeUtils = new BatteryOptimizeUtils(
- getContext(), getArguments().getInt(EXTRA_UID), mPackageName);
-
- if (mPackageName != null) {
- mAppEntry = mState.getEntry(mPackageName, UserHandle.myUserId());
+ if (packageName != null) {
+ mAppEntry = mState.getEntry(packageName, UserHandle.myUserId());
}
}
@@ -251,7 +257,11 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
super.onResume();
initHeader();
- initPreference();
+ if (enableTriState) {
+ initPreferenceForTriState(getContext());
+ } else {
+ initPreference(getContext());
+ }
}
@VisibleForTesting
@@ -281,17 +291,39 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
controller.setIsInstantApp(AppUtils.isInstant(mAppEntry.info));
}
- final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
- final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
- //TODO(b/178197718) Update layout to support multiple lines
- controller.setSummary(getAppActiveTime(foregroundTimeMs, backgroundTimeMs));
+ if (enableTriState) {
+ final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
+ final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
+ //TODO(b/178197718) Update layout to support multiple lines
+ controller.setSummary(getAppActiveTime(foregroundTimeMs, backgroundTimeMs));
+ }
controller.done(context, true /* rebindActions */);
}
@VisibleForTesting
- void initPreference() {
- final Context context = getContext();
+ void initPreference(Context context) {
+ final Bundle bundle = getArguments();
+ final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
+ final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
+ mForegroundPreference.setSummary(
+ TextUtils.expandTemplate(getText(R.string.battery_used_for),
+ StringUtil.formatElapsedTime(
+ context,
+ foregroundTimeMs,
+ /* withSeconds */ false,
+ /* collapseTimeUnit */ false)));
+ mBackgroundPreference.setSummary(
+ TextUtils.expandTemplate(getText(R.string.battery_active_for),
+ StringUtil.formatElapsedTime(
+ context,
+ backgroundTimeMs,
+ /* withSeconds */ false,
+ /* collapseTimeUnit */ false)));
+ }
+
+ @VisibleForTesting
+ void initPreferenceForTriState(Context context) {
final String stateString;
final String footerString;
//TODO(b/178197718) Update strings
@@ -308,7 +340,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
} else {
//Present default string to normal app.
footerString = context.getString(R.string.manager_battery_usage_footer);
-
}
mFooterPreference.setTitle(Html.fromHtml(footerString, Html.FROM_HTML_MODE_COMPACT));
}
@@ -325,7 +356,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
@Override
protected int getPreferenceScreenResId() {
- return R.xml.power_usage_detail;
+ return enableTriState ? R.xml.power_usage_detail : R.xml.power_usage_detail_legacy;
}
@Override
@@ -336,18 +367,20 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
final String packageName = bundle.getString(EXTRA_PACKAGE_NAME);
mAppButtonsPreferenceController = new AppButtonsPreferenceController(
- (SettingsActivity) getActivity(), this, getSettingsLifecycle(), packageName, mState,
- REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
+ (SettingsActivity) getActivity(), this, getSettingsLifecycle(), packageName,
+ mState, REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
controllers.add(mAppButtonsPreferenceController);
- mUnrestrictedPreferenceController =
- new UnrestrictedPreferenceController(context, uid, packageName);
- mOptimizedPreferenceController =
- new OptimizedPreferenceController(context, uid, packageName);
- mRestrictedPreferenceController =
- new RestrictedPreferenceController(context, uid, packageName);
- controllers.add(mUnrestrictedPreferenceController);
- controllers.add(mOptimizedPreferenceController);
- controllers.add(mRestrictedPreferenceController);
+ if (enableTriState) {
+ controllers.add(new UnrestrictedPreferenceController(context, uid, packageName));
+ controllers.add(new OptimizedPreferenceController(context, uid, packageName));
+ controllers.add(new RestrictedPreferenceController(context, uid, packageName));
+ } else {
+ mBackgroundActivityPreferenceController = new BackgroundActivityPreferenceController(
+ context, this, uid, packageName);
+ controllers.add(mBackgroundActivityPreferenceController);
+ controllers.add(new BatteryOptimizationPreferenceController(
+ (SettingsActivity) getActivity(), this, packageName));
+ }
return controllers;
}
@@ -367,6 +400,12 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
}
}
+ @Override
+ public void onBatteryTipHandled(BatteryTip batteryTip) {
+ mBackgroundActivityPreferenceController.updateSummary(
+ findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
+ }
+
@Override
public void onRadioButtonClicked(RadioButtonPreference selected) {
updatePreferenceState(mUnrestrictedPreference, selected.getKey());
@@ -378,6 +417,19 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
preference.setChecked(selectedKey.equals(preference.getKey()));
}
+ private void onCreateForTriState(String packageName) {
+ mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED);
+ mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED);
+ mRestrictedPreference = findPreference(KEY_PREF_RESTRICTED);
+ mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE);
+ mUnrestrictedPreference.setOnClickListener(this);
+ mOptimizePreference.setOnClickListener(this);
+ mRestrictedPreference.setOnClickListener(this);
+
+ mBatteryOptimizeUtils = new BatteryOptimizeUtils(
+ getContext(), getArguments().getInt(EXTRA_UID), packageName);
+ }
+
//TODO(b/178197718) Update method to support time period
private CharSequence getAppActiveTime(long foregroundTimeMs, long backgroundTimeMs) {
final long totalTimeMs = foregroundTimeMs + backgroundTimeMs;
diff --git a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java
index ff7447725fc..99e6b0db666 100644
--- a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java
@@ -151,21 +151,22 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
final PowerGaugePreference powerPref = (PowerGaugePreference) preference;
final BatteryDiffEntry diffEntry = powerPref.getBatteryDiffEntry();
final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
+ final String packageName = histEntry.mPackageName;
// Checks whether the package is installed or not.
boolean isValidPackage = true;
if (histEntry.isAppEntry()) {
if (mBatteryUtils == null) {
mBatteryUtils = BatteryUtils.getInstance(mPrefContext);
}
- isValidPackage = mBatteryUtils.getPackageUid(histEntry.mPackageName)
+ isValidPackage = mBatteryUtils.getPackageUid(packageName)
!= BatteryUtils.UID_NULL;
}
Log.d(TAG, String.format("handleClick() label=%s key=%s isValid:%b %s",
- diffEntry.getAppLabel(), histEntry.getKey(), isValidPackage,
- histEntry.mPackageName));
+ diffEntry.getAppLabel(), histEntry.getKey(), isValidPackage, packageName));
if (isValidPackage) {
AdvancedPowerUsageDetail.startBatteryDetailPage(
- mActivity, mFragment, diffEntry, powerPref.getPercent());
+ mActivity, mFragment, diffEntry, powerPref.getPercent(),
+ isValidToShowSummary(packageName), getSlotInformation());
return true;
}
return false;
@@ -267,8 +268,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|| (mTrapezoidIndex == trapezoidIndex && !isForce)) {
return false;
}
- Log.d(TAG, String.format("refreshUi: index=%d batteryIndexedMap.size=%d",
- mTrapezoidIndex, mBatteryIndexedMap.size()));
+ Log.d(TAG, String.format("refreshUi: index=%d size=%d isForce:%b",
+ trapezoidIndex, mBatteryIndexedMap.size(), isForce));
mTrapezoidIndex = trapezoidIndex;
mHandler.post(() -> {
@@ -471,11 +472,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs;
final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
// Checks whether the package is allowed to show summary or not.
- for (CharSequence notAllowPackageName : mNotAllowShowSummaryPackages) {
- if (TextUtils.equals(entry.getPackageName(), notAllowPackageName)) {
- preference.setSummary(null);
- return;
- }
+ if (!isValidToShowSummary(entry.getPackageName())) {
+ preference.setSummary(null);
+ return;
}
String usageTimeSummary = null;
// Not shows summary for some system components without usage time.
@@ -515,6 +514,17 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return mPrefContext.getString(resourceId, timeSequence);
}
+ private boolean isValidToShowSummary(String packageName) {
+ if (mNotAllowShowSummaryPackages != null) {
+ for (CharSequence notAllowPackageName : mNotAllowShowSummaryPackages) {
+ if (TextUtils.equals(packageName, notAllowPackageName)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
private static String utcToLocalTime(long[] timestamps) {
final StringBuilder builder = new StringBuilder();
for (int index = 0; index < timestamps.length; index++) {
diff --git a/src/com/android/settings/fuelgauge/BatteryChartView.java b/src/com/android/settings/fuelgauge/BatteryChartView.java
index 366946e12e2..d56cbd45bec 100644
--- a/src/com/android/settings/fuelgauge/BatteryChartView.java
+++ b/src/com/android/settings/fuelgauge/BatteryChartView.java
@@ -22,11 +22,13 @@ import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
+import android.widget.TextView;
import androidx.appcompat.widget.AppCompatImageView;
@@ -38,6 +40,8 @@ import java.util.Locale;
/** A widget component to draw chart graph. */
public class BatteryChartView extends AppCompatImageView implements View.OnClickListener {
private static final String TAG = "BatteryChartView";
+ // For drawing the percentage information.
+ private static final String[] PERCENTAGES = new String[] {"100%", "50%", "0%"};
private static final int DEFAULT_TRAPEZOID_COUNT = 12;
/** Selects all trapezoid shapes. */
public static final int SELECTED_INDEX_ALL = -1;
@@ -58,8 +62,14 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private int mTrapezoidColor;
private int mTrapezoidSolidColor;
private final int mDividerColor = Color.parseColor("#CDCCC5");
+ // For drawing the percentage information.
+ private int mTextPadding;
+ private final Rect mIndent = new Rect();
+ private final Rect[] mPercentageBound =
+ new Rect[] {new Rect(), new Rect(), new Rect()};
private int[] mLevels;
+ private Paint mTextPaint;
private Paint mDividerPaint;
private Paint mTrapezoidPaint;
private TrapezoidSlot[] mTrapezoidSlot;
@@ -128,6 +138,37 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
mOnSelectListener = listener;
}
+ /** Sets the companion {@link TextView} for percentage information. */
+ public void setCompanionTextView(TextView textView) {
+ requestLayout();
+ if (textView != null) {
+ // Pre-draws the view first to load style atttributions into paint.
+ textView.draw(new Canvas());
+ mTextPaint = textView.getPaint();
+ } else {
+ mTextPaint = null;
+ }
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ // Measures text bounds and updates indent configuration.
+ if (mTextPaint != null) {
+ for (int index = 0; index < PERCENTAGES.length; index++) {
+ mTextPaint.getTextBounds(
+ PERCENTAGES[index], 0, PERCENTAGES[index].length(),
+ mPercentageBound[index]);
+ }
+ // Updates the indent configurations.
+ mIndent.top = mPercentageBound[0].height();
+ mIndent.right = mPercentageBound[0].width() + mTextPadding * 2;
+ Log.d(TAG, "setIndent:" + mPercentageBound[0]);
+ } else {
+ mIndent.set(0, 0, 0, 0);
+ }
+ }
+
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
@@ -194,32 +235,56 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
mTrapezoidPaint.setPathEffect(
new CornerPathEffect(
resources.getDimensionPixelSize(R.dimen.chartview_trapezoid_radius)));
+ // Initializes for drawing text information.
+ mTextPadding = resources.getDimensionPixelSize(R.dimen.chartview_text_padding);
}
private void drawHorizontalDividers(Canvas canvas) {
+ final int width = getWidth() - mIndent.right;
+ final int height = getHeight() - mIndent.top - mIndent.bottom;
// Draws the top divider line for 100% curve.
- float offsetY = mDividerWidth * 0.5f;
- canvas.drawLine(0, offsetY, getWidth(), offsetY, mDividerPaint);
+ float offsetY = mIndent.top + mDividerWidth * .5f;
+ canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint);
+ if (mTextPaint != null) {
+ canvas.drawText(
+ PERCENTAGES[0],
+ getWidth() - mPercentageBound[0].width(),
+ offsetY + mPercentageBound[0].height() *.5f , mTextPaint);
+ }
// Draws the center divider line for 50% curve.
final float availableSpace =
- getHeight() - mDividerWidth * 2 - mTrapezoidVOffset - mDividerHeight;
- offsetY = mDividerWidth + availableSpace * 0.5f;
- canvas.drawLine(0, offsetY, getWidth(), offsetY, mDividerPaint);
- // Draws the center divider line for 0% curve.
- offsetY = getHeight() - mDividerHeight - mDividerWidth * 0.5f;
- canvas.drawLine(0, offsetY, getWidth(), offsetY, mDividerPaint);
+ height - mDividerWidth * 2 - mTrapezoidVOffset - mDividerHeight;
+ offsetY = mIndent.top + mDividerWidth + availableSpace * .5f;
+ canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint);
+ if (mTextPaint != null) {
+ canvas.drawText(
+ PERCENTAGES[1],
+ getWidth() - mPercentageBound[1].width(),
+ offsetY + mPercentageBound[1].height() *.5f , mTextPaint);
+ }
+ // Draws the bottom divider line for 0% curve.
+ offsetY = mIndent.top + (height - mDividerHeight - mDividerWidth * .5f);
+ canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint);
+ if (mTextPaint != null) {
+ canvas.drawText(
+ PERCENTAGES[2],
+ getWidth() - mPercentageBound[2].width(),
+ offsetY + mPercentageBound[2].height() *.5f , mTextPaint);
+ }
}
private void drawVerticalDividers(Canvas canvas) {
+ final int width = getWidth() - mIndent.right;
final int dividerCount = mTrapezoidCount + 1;
final float dividerSpace = dividerCount * mDividerWidth;
- final float unitWidth = (getWidth() - dividerSpace) / (float) mTrapezoidCount;
- final float startY = getHeight() - mDividerHeight;
- final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * 0.5f;
+ final float unitWidth = (width - dividerSpace) / (float) mTrapezoidCount;
+ final float bottomY = getHeight() - mIndent.bottom;
+ final float startY = bottomY - mDividerHeight;
+ final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * .5f;
// Draws each vertical dividers.
- float startX = mDividerWidth * 0.5f;
+ float startX = mDividerWidth * .5f;
for (int index = 0; index < dividerCount; index++) {
- canvas.drawLine(startX, startY, startX, getHeight(), mDividerPaint);
+ canvas.drawLine(startX, startY, startX, bottomY, mDividerPaint);
final float nextX = startX + mDividerWidth + unitWidth;
// Updates the trapezoid slots for drawing.
if (index < mTrapezoidSlot.length) {
@@ -236,8 +301,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
return;
}
final float trapezoidBottom =
- getHeight() - mDividerHeight - mDividerWidth - mTrapezoidVOffset;
- final float availableSpace = trapezoidBottom - mDividerWidth;
+ getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth
+ - mTrapezoidVOffset;
+ final float availableSpace = trapezoidBottom - mDividerWidth * .5f - mIndent.top;
final float unitHeight = availableSpace / 100f;
// Draws all trapezoid shapes into the canvas.
final Path trapezoidPath = new Path();
@@ -249,8 +315,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
// Configures the trapezoid paint color.
mTrapezoidPaint.setColor(
mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL
- ? mTrapezoidSolidColor
- : mTrapezoidColor);
+ ? mTrapezoidSolidColor
+ : mTrapezoidColor);
final float leftTop = round(trapezoidBottom - mLevels[index] * unitHeight);
final float rightTop = round(trapezoidBottom - mLevels[index + 1] * unitHeight);
trapezoidPath.reset();
diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java b/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java
index 94e35cb3966..b2818bf601d 100644
--- a/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java
+++ b/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java
@@ -100,6 +100,8 @@ public class BatteryHistoryPreference extends Preference {
}
if (mIsChartGraphEnabled) {
mBatteryChartView = (BatteryChartView) view.findViewById(R.id.battery_chart);
+ mBatteryChartView.setCompanionTextView(
+ (TextView) view.findViewById(R.id.companion_text));
if (mChartPreferenceController != null) {
mChartPreferenceController.setBatteryChartView(mBatteryChartView);
}
diff --git a/src/com/android/settings/fuelgauge/ExpandDividerPreference.java b/src/com/android/settings/fuelgauge/ExpandDividerPreference.java
index 40536eb37c7..b89f2d42acb 100644
--- a/src/com/android/settings/fuelgauge/ExpandDividerPreference.java
+++ b/src/com/android/settings/fuelgauge/ExpandDividerPreference.java
@@ -38,6 +38,7 @@ public class ExpandDividerPreference extends Preference {
private OnExpandListener mOnExpandListener;
private boolean mIsExpanded = false;
+ private String mTitleContent = null;
/** A callback listener for expand state is changed by users. */
public interface OnExpandListener {
@@ -72,6 +73,7 @@ public class ExpandDividerPreference extends Preference {
}
void setTitle(final String titleContent) {
+ mTitleContent = titleContent;
if (mTextView != null) {
mTextView.postDelayed(
() -> mTextView.setText(titleContent), 50);
@@ -95,5 +97,6 @@ public class ExpandDividerPreference extends Preference {
if (mImageView != null) {
mImageView.setImageResource(iconId);
}
+ setTitle(mTitleContent);
}
}
diff --git a/src/com/android/settings/localepicker/LocaleRecyclerView.java b/src/com/android/settings/localepicker/LocaleRecyclerView.java
index e82874dab70..d32a735d48c 100644
--- a/src/com/android/settings/localepicker/LocaleRecyclerView.java
+++ b/src/com/android/settings/localepicker/LocaleRecyclerView.java
@@ -37,7 +37,7 @@ class LocaleRecyclerView extends RecyclerView {
@Override
public boolean onTouchEvent(MotionEvent e) {
- if (e.getAction() == MotionEvent.ACTION_UP) {
+ if (e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_CANCEL) {
LocaleDragAndDropAdapter adapter = (LocaleDragAndDropAdapter) this.getAdapter();
if (adapter != null) {
adapter.doTheUpdate();
diff --git a/src/com/android/settings/widget/RadioButtonPickerFragment.java b/src/com/android/settings/widget/RadioButtonPickerFragment.java
index 0f4fbc3433b..7d765df60fc 100644
--- a/src/com/android/settings/widget/RadioButtonPickerFragment.java
+++ b/src/com/android/settings/widget/RadioButtonPickerFragment.java
@@ -205,7 +205,7 @@ public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFr
public RadioButtonPreference bindPreference(RadioButtonPreference pref,
String key, CandidateInfo info, String defaultKey) {
pref.setTitle(info.loadLabel());
- Utils.setSafeIcon(pref, info.loadIcon());
+ pref.setIcon(Utils.getSafeIcon(info.loadIcon()));
pref.setKey(key);
if (TextUtils.equals(defaultKey, key)) {
pref.setChecked(true);
diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java
index 9f3b1a711d9..1e0670788ad 100644
--- a/src/com/android/settings/wifi/WifiConfigController2.java
+++ b/src/com/android/settings/wifi/WifiConfigController2.java
@@ -1339,12 +1339,13 @@ public class WifiConfigController2 implements TextWatcher,
mIpAddressView = (TextView) mView.findViewById(R.id.ipaddress);
mIpAddressView.addTextChangedListener(this);
mGatewayView = (TextView) mView.findViewById(R.id.gateway);
- mGatewayView.addTextChangedListener(this);
+ mGatewayView.addTextChangedListener(getIpConfigFieldsTextWatcher(mGatewayView));
mNetworkPrefixLengthView = (TextView) mView.findViewById(
R.id.network_prefix_length);
- mNetworkPrefixLengthView.addTextChangedListener(this);
+ mNetworkPrefixLengthView.addTextChangedListener(
+ getIpConfigFieldsTextWatcher(mNetworkPrefixLengthView));
mDns1View = (TextView) mView.findViewById(R.id.dns1);
- mDns1View.addTextChangedListener(this);
+ mDns1View.addTextChangedListener(getIpConfigFieldsTextWatcher(mDns1View));
mDns2View = (TextView) mView.findViewById(R.id.dns2);
mDns2View.addTextChangedListener(this);
}
@@ -1562,6 +1563,47 @@ public class WifiConfigController2 implements TextWatcher,
// work done in afterTextChanged
}
+ /* TODO: Add more test cases for this TextWatcher b/186368002
+ * This TextWatcher is for IP config fields (Gateway/Network Prefix Length/DNS1) to prevent
+ * to rewrite the value in these columns that the user wanted to change after they saved.
+ * When afterTextChanged we will check the text is empty or not then set the Hint for user.
+ */
+ private TextWatcher getIpConfigFieldsTextWatcher(final TextView view) {
+ return new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // work done in afterTextChanged
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // work done in afterTextChanged
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() == 0) {
+ if (view.getId() == R.id.gateway) {
+ mGatewayView.setHint(R.string.wifi_gateway_hint);
+ } else if (view.getId() == R.id.network_prefix_length) {
+ mNetworkPrefixLengthView.setHint(R.string.wifi_network_prefix_length_hint);
+ } else if (view.getId() == R.id.dns1) {
+ mDns1View.setHint(R.string.wifi_dns1_hint);
+ }
+ Button submit = mConfigUi.getSubmitButton();
+ if (submit == null) return;
+
+ submit.setEnabled(false);
+ } else {
+ ThreadUtils.postOnMainThread(() -> {
+ showWarningMessagesIfAppropriate();
+ enableSubmitIfAppropriate();
+ });
+ }
+ }
+ };
+ }
+
@Override
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
if (textView == mPasswordView) {
diff --git a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
index 6659c7ace91..ecaf9ee8fc4 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppConfiguratorActivity.java
@@ -144,31 +144,8 @@ public class WifiDppConfiguratorActivity extends WifiDppBaseActivity implements
}
break;
case Settings.ACTION_PROCESS_WIFI_EASY_CONNECT_URI:
- final Uri uri = intent.getData();
- final String uriString = (uri == null) ? null : uri.toString();
- mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(uriString);
- mWifiDppRemoteBandSupport = intent.getIntArrayExtra(
- Settings.EXTRA_EASY_CONNECT_BAND_LIST); // returns null if none
- final boolean isDppSupported = WifiDppUtils.isWifiDppEnabled(this);
- if (!isDppSupported) {
- Log.e(TAG,
- "ACTION_PROCESS_WIFI_EASY_CONNECT_URI for a device that doesn't "
- + "support Wifi DPP - use WifiManager#isEasyConnectSupported");
- }
- if (mWifiDppQrCode == null) {
- Log.e(TAG, "ACTION_PROCESS_WIFI_EASY_CONNECT_URI with null URI!");
- }
- if (mWifiDppQrCode == null || !isDppSupported) {
- cancelActivity = true;
- } else {
- final WifiNetworkConfig connectedConfig = getConnectedWifiNetworkConfigOrNull();
- if (connectedConfig == null || !connectedConfig.isSupportWifiDpp(this)) {
- showChooseSavedWifiNetworkFragment(/* addToBackStack */ false);
- } else {
- mWifiNetworkConfig = connectedConfig;
- showAddDeviceFragment(/* addToBackStack */ false);
- }
- }
+ WifiDppUtils.showLockScreen(this,
+ () -> handleActionProcessWifiEasyConnectUriIntent(intent));
break;
default:
cancelActivity = true;
@@ -180,6 +157,34 @@ public class WifiDppConfiguratorActivity extends WifiDppBaseActivity implements
}
}
+ private void handleActionProcessWifiEasyConnectUriIntent(Intent intent) {
+ final Uri uri = intent.getData();
+ final String uriString = (uri == null) ? null : uri.toString();
+ mWifiDppQrCode = WifiQrCode.getValidWifiDppQrCodeOrNull(uriString);
+ mWifiDppRemoteBandSupport = intent.getIntArrayExtra(
+ Settings.EXTRA_EASY_CONNECT_BAND_LIST); // returns null if none
+ final boolean isDppSupported = WifiDppUtils.isWifiDppEnabled(this);
+ if (!isDppSupported) {
+ Log.e(TAG,
+ "ACTION_PROCESS_WIFI_EASY_CONNECT_URI for a device that doesn't "
+ + "support Wifi DPP - use WifiManager#isEasyConnectSupported");
+ }
+ if (mWifiDppQrCode == null) {
+ Log.e(TAG, "ACTION_PROCESS_WIFI_EASY_CONNECT_URI with null URI!");
+ }
+ if (mWifiDppQrCode == null || !isDppSupported) {
+ finish();
+ } else {
+ final WifiNetworkConfig connectedConfig = getConnectedWifiNetworkConfigOrNull();
+ if (connectedConfig == null || !connectedConfig.isSupportWifiDpp(this)) {
+ showChooseSavedWifiNetworkFragment(/* addToBackStack */ false);
+ } else {
+ mWifiNetworkConfig = connectedConfig;
+ showAddDeviceFragment(/* addToBackStack */ false);
+ }
+ }
+ }
+
private void showQrCodeScannerFragment() {
WifiDppQrCodeScannerFragment fragment =
(WifiDppQrCodeScannerFragment) mFragmentManager.findFragmentByTag(
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsControllerTest.java
new file mode 100644
index 00000000000..3f49938412b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsCompanionAppsControllerTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.companion.Association;
+import android.companion.CompanionDeviceManager;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothDetailsCompanionAppsControllerTest extends
+ BluetoothDetailsControllerTestBase {
+ private static final String PACKAGE_NAME_ONE = "com.google.android.deskclock";
+ private static final String PACKAGE_NAME_TWO = "com.google.android.calculator";
+ private static final String PACKAGE_NAME_THREE = "com.google.android.GoogleCamera";
+ private static final CharSequence APP_NAME_ONE = "deskclock";
+ private static final CharSequence APP_NAME_TWO = "calculator";
+ private static final CharSequence APP_NAME_THREE = "GoogleCamera";
+
+ @Mock
+ private CompanionDeviceManager mCompanionDeviceManager;
+ @Mock
+ private PackageManager mPackageManager;
+
+ private BluetoothDetailsCompanionAppsController mController;
+ private PreferenceCategory mProfiles;
+ private List mPackages;
+ private List mAppNames;
+ private List mAssociations;
+
+
+ @Override
+ public void setUp() {
+ super.setUp();
+ mPackages = Arrays.asList(PACKAGE_NAME_ONE, PACKAGE_NAME_TWO, PACKAGE_NAME_THREE);
+ mAppNames = Arrays.asList(APP_NAME_ONE, APP_NAME_TWO, APP_NAME_THREE);
+ mProfiles = spy(new PreferenceCategory(mContext));
+ mAssociations = new ArrayList<>();
+ when(mCompanionDeviceManager.getAllAssociations()).thenReturn(mAssociations);
+ when(mProfiles.getPreferenceManager()).thenReturn(mPreferenceManager);
+ setupDevice(mDeviceConfig);
+ mController =
+ new BluetoothDetailsCompanionAppsController(mContext, mFragment, mCachedDevice,
+ mLifecycle);
+ mController.mCompanionDeviceManager = mCompanionDeviceManager;
+ mController.mPackageManager = mPackageManager;
+ mController.mProfilesContainer = mProfiles;
+ mProfiles.setKey(mController.getPreferenceKey());
+ mScreen.addPreference(mProfiles);
+ }
+
+ private void setupFakeLabelAndInfo(String packageName, CharSequence appName) {
+ ApplicationInfo appInfo = mock(ApplicationInfo.class);
+ try {
+ when(mPackageManager.getApplicationInfo(packageName, 0)).thenReturn(appInfo);
+ when(mPackageManager.getApplicationLabel(appInfo)).thenReturn(appName);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void addFakeAssociation(String packageName, CharSequence appName) {
+ setupFakeLabelAndInfo(packageName, appName);
+ Association association = new Association(
+ 0, mCachedDevice.getAddress(), packageName, "", true, System.currentTimeMillis());
+ mAssociations.add(association);
+ showScreen(mController);
+ }
+
+ private Preference getPreference(int index) {
+ PreferenceCategory preferenceCategory = mProfiles.findPreference(
+ mController.getPreferenceKey());
+ return Objects.requireNonNull(preferenceCategory).getPreference(index);
+ }
+
+ private void removeAssociation(String packageName) {
+ mAssociations = mAssociations.stream()
+ .filter(a -> !a.getPackageName().equals(packageName))
+ .collect(Collectors.toList());
+
+ when(mCompanionDeviceManager.getAllAssociations()).thenReturn(mAssociations);
+
+ showScreen(mController);
+ }
+
+ @Test
+ public void addOneAssociation_preferenceShouldBeAdded() {
+ addFakeAssociation(PACKAGE_NAME_ONE, APP_NAME_ONE);
+
+ Preference preferenceOne = getPreference(0);
+
+ assertThat(preferenceOne.getClass()).isEqualTo(CompanionAppWidgetPreference.class);
+ assertThat(preferenceOne.getKey()).isEqualTo(PACKAGE_NAME_ONE);
+ assertThat(preferenceOne.getTitle()).isEqualTo(APP_NAME_ONE.toString());
+ assertThat(mProfiles.getPreferenceCount()).isEqualTo(1);
+
+ removeAssociation(PACKAGE_NAME_ONE);
+
+ assertThat(mProfiles.getPreferenceCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void removeOneAssociation_preferenceShouldBeRemoved() {
+ addFakeAssociation(PACKAGE_NAME_ONE, APP_NAME_ONE);
+
+ removeAssociation(PACKAGE_NAME_ONE);
+
+ assertThat(mProfiles.getPreferenceCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void addMultipleAssociations_preferencesShouldBeAdded() {
+ for (int i = 0; i < mPackages.size(); i++) {
+ addFakeAssociation(mPackages.get(i), mAppNames.get(i));
+
+ Preference preference = getPreference(i);
+
+ assertThat(preference.getClass()).isEqualTo(CompanionAppWidgetPreference.class);
+ assertThat(preference.getKey()).isEqualTo(mPackages.get(i));
+ assertThat(preference.getTitle()).isEqualTo(mAppNames.get(i).toString());
+ assertThat(mProfiles.getPreferenceCount()).isEqualTo(i + 1);
+ }
+ }
+
+ @Test
+ public void removeMultipleAssociations_preferencesShouldBeRemoved() {
+ for (int i = 0; i < mPackages.size(); i++) {
+ addFakeAssociation(mPackages.get(i), mAppNames.get(i).toString());
+ }
+
+ for (int i = 0; i < mPackages.size(); i++) {
+ removeAssociation(mPackages.get(i));
+
+ assertThat(mProfiles.getPreferenceCount()).isEqualTo(mPackages.size() - i - 1);
+
+ if (i == mPackages.size() - 1) {
+ break;
+ }
+
+ assertThat(getPreference(0).getKey()).isEqualTo(mPackages.get(i + 1));
+ assertThat(getPreference(0).getTitle()).isEqualTo(mAppNames.get(i + 1).toString());
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/CompanionAppWidgetPreferenceTest.java b/tests/robotests/src/com/android/settings/bluetooth/CompanionAppWidgetPreferenceTest.java
new file mode 100644
index 00000000000..8ee3ef78d81
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/CompanionAppWidgetPreferenceTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowAlertDialogCompat.class})
+public class CompanionAppWidgetPreferenceTest {
+ private static final String TITLE_ONE = "Test Title 1";
+ private static final String TITLE_TWO = "Test Title 1";
+ private static final String KEY_ONE = "Test Key 1";
+ private static final String KEY_TWO = "Test Key 1";
+
+ private Context mContext;
+ private Drawable mWidgetIconOne;
+ private Drawable mWidgetIconTwo;
+ private Drawable mAppIconOne;
+ private Drawable mAppIconTwo;
+
+ @Mock
+ private View.OnClickListener mWidgetListenerOne;
+ @Mock
+ private View.OnClickListener mWidgetListenerTwo;
+
+ private List mPreferenceContainer;
+
+ @Before
+ public void setUp() {
+ mPreferenceContainer = new ArrayList<>();
+ Context context = spy(RuntimeEnvironment.application.getApplicationContext());
+ mContext = new ContextThemeWrapper(context, R.style.Theme_Settings);
+ mWidgetIconOne = mock(Drawable.class);
+ mAppIconOne = mock(Drawable.class);
+ mWidgetListenerOne = mock(View.OnClickListener.class);
+ mWidgetIconTwo = mock(Drawable.class);
+ mAppIconTwo = mock(Drawable.class);
+ mWidgetListenerTwo = mock(View.OnClickListener.class);
+ }
+
+ private void setUpPreferenceContainer(Drawable widgetIcon, Drawable appIcon,
+ View.OnClickListener listener, String title, String key) {
+ CompanionAppWidgetPreference preference = new CompanionAppWidgetPreference(
+ widgetIcon, listener, mContext);
+ preference.setIcon(appIcon);
+ preference.setTitle(title);
+ preference.setKey(key);
+ mPreferenceContainer.add(preference);
+ }
+
+ @Test
+ public void setUpPreferenceContainer_preferenceShouldBeAdded() {
+ setUpPreferenceContainer(
+ mWidgetIconOne, mAppIconOne, mWidgetListenerOne, TITLE_ONE, KEY_ONE);
+
+ assertThat(mPreferenceContainer.get(0).getIcon()).isEqualTo(mAppIconOne);
+ assertThat(mPreferenceContainer.get(0).getKey()).isEqualTo(KEY_ONE);
+ assertThat(mPreferenceContainer.get(0).getTitle()).isEqualTo(TITLE_ONE);
+
+ setUpPreferenceContainer(
+ mWidgetIconTwo, mAppIconTwo, mWidgetListenerTwo, TITLE_TWO, KEY_TWO);
+
+ assertThat(mPreferenceContainer.get(1).getIcon()).isEqualTo(mAppIconTwo);
+ assertThat(mPreferenceContainer.get(1).getKey()).isEqualTo(KEY_TWO);
+ assertThat(mPreferenceContainer.get(1).getTitle()).isEqualTo(TITLE_TWO);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
index dd10a9e1556..5185593e8b7 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
@@ -47,6 +47,7 @@ import androidx.loader.app.LoaderManager;
import androidx.preference.Preference;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowActivityManager;
@@ -82,6 +83,7 @@ public class AdvancedPowerUsageDetailTest {
private static final String USAGE_PERCENT = "16%";
private static final int ICON_ID = 123;
private static final int UID = 1;
+ private static final int POWER_MAH = 150;
private static final long BACKGROUND_TIME_MS = 100;
private static final long FOREGROUND_ACTIVITY_TIME_MS = 123;
private static final long FOREGROUND_SERVICE_TIME_MS = 444;
@@ -117,6 +119,8 @@ public class AdvancedPowerUsageDetailTest {
@Mock
private BatteryOptimizeUtils mBatteryOptimizeUtils;
private Context mContext;
+ private Preference mForegroundPreference;
+ private Preference mBackgroundPreference;
private Preference mFooterPreference;
private RadioButtonPreference mRestrictedPreference;
private RadioButtonPreference mOptimizePreference;
@@ -166,6 +170,7 @@ public class AdvancedPowerUsageDetailTest {
mFragment.mHeaderPreference = mHeaderPreference;
mFragment.mState = mState;
+ mFragment.enableTriState = true;
mFragment.mBatteryUtils = new BatteryUtils(RuntimeEnvironment.application);
mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils;
mAppEntry.info = mock(ApplicationInfo.class);
@@ -190,10 +195,14 @@ public class AdvancedPowerUsageDetailTest {
nullable(UserHandle.class));
doAnswer(callable).when(mActivity).startActivity(captor.capture());
+ mForegroundPreference = new Preference(mContext);
+ mBackgroundPreference = new Preference(mContext);
mFooterPreference = new Preference(mContext);
mRestrictedPreference = new RadioButtonPreference(mContext);
mOptimizePreference = new RadioButtonPreference(mContext);
mUnrestrictedPreference = new RadioButtonPreference(mContext);
+ mFragment.mForegroundPreference = mForegroundPreference;
+ mFragment.mBackgroundPreference = mBackgroundPreference;
mFragment.mFooterPreference = mFooterPreference;
mFragment.mRestrictedPreference = mRestrictedPreference;
mFragment.mOptimizePreference = mOptimizePreference;
@@ -205,6 +214,17 @@ public class AdvancedPowerUsageDetailTest {
ShadowEntityHeaderController.reset();
}
+ @Test
+ public void testGetPreferenceScreenResId_returnNewLayout() {
+ assertThat(mFragment.getPreferenceScreenResId()).isEqualTo(R.xml.power_usage_detail);
+ }
+
+ @Test
+ public void testGetPreferenceScreenResId_disableTriState_returnLegacyLayout() {
+ mFragment.enableTriState = false;
+ assertThat(mFragment.getPreferenceScreenResId()).isEqualTo(R.xml.power_usage_detail_legacy);
+ }
+
@Test
public void testInitHeader_NoAppEntry_BuildByBundle() {
mFragment.mAppEntry = null;
@@ -455,32 +475,52 @@ public class AdvancedPowerUsageDetailTest {
}
@Test
- public void testInitPreference_isValidPackageName_hasCorrectString() {
+ public void testInitPreference_hasCorrectSummary() {
+ Bundle bundle = new Bundle(4);
+ bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, BACKGROUND_TIME_MS);
+ bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, FOREGROUND_TIME_MS);
+ bundle.putString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT, USAGE_PERCENT);
+ bundle.putInt(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_AMOUNT, POWER_MAH);
+ when(mFragment.getArguments()).thenReturn(bundle);
+
+ doReturn(mContext.getText(R.string.battery_used_for)).when(mFragment).getText(
+ R.string.battery_used_for);
+ doReturn(mContext.getText(R.string.battery_active_for)).when(mFragment).getText(
+ R.string.battery_active_for);
+
+ mFragment.initPreference(mContext);
+
+ assertThat(mForegroundPreference.getSummary().toString()).isEqualTo("Used for 0 min");
+ assertThat(mBackgroundPreference.getSummary().toString()).isEqualTo("Active for 0 min");
+ }
+
+ @Test
+ public void testInitPreferenceForTriState_isValidPackageName_hasCorrectString() {
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(false);
- mFragment.initPreference();
+ mFragment.initPreferenceForTriState(mContext);
assertThat(mFooterPreference.getTitle().toString())
.isEqualTo("This app requires Optimized battery usage.");
}
@Test
- public void testInitPreference_isSystemOrDefaultApp_hasCorrectString() {
+ public void testInitPreferenceForTriState_isSystemOrDefaultApp_hasCorrectString() {
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
- mFragment.initPreference();
+ mFragment.initPreferenceForTriState(mContext);
assertThat(mFooterPreference.getTitle()
.toString()).isEqualTo("This app requires Unrestricted battery usage.");
}
@Test
- public void testInitPreference_hasCorrectString() {
+ public void testInitPreferenceForTriState_hasCorrectString() {
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false);
- mFragment.initPreference();
+ mFragment.initPreferenceForTriState(mContext);
assertThat(mFooterPreference.getTitle().toString())
.isEqualTo("Changing how an app uses your battery can affect its performance.");