diff --git a/res/drawable/extra_dim_banner.gif b/res/drawable/extra_dim_banner.gif new file mode 100644 index 00000000000..a31087cdd6f Binary files /dev/null and b/res/drawable/extra_dim_banner.gif differ diff --git a/res/layout/preview_seek_bar_view_pager.xml b/res/layout/preview_seek_bar_view_pager.xml index 6ddff130451..fa9f550ec1e 100644 --- a/res/layout/preview_seek_bar_view_pager.xml +++ b/res/layout/preview_seek_bar_view_pager.xml @@ -27,7 +27,7 @@ android:layout_width="match_parent" android:layout_height="400dp" android:layout_weight="1" - android:background="?android:attr/colorBackgroundFloating" + android:background="?android:attr/colorBackground" android:contentDescription="@string/preview_pager_content_description" /> + android:fitsSystemWindows="true"> diff --git a/res/values-night/themes.xml b/res/values-night/themes.xml index a4f495a6507..604610d37f0 100644 --- a/res/values-night/themes.xml +++ b/res/values-night/themes.xml @@ -45,8 +45,9 @@ @*android:color/edge_effect_device_default_dark - diff --git a/res/values/strings.xml b/res/values/strings.xml index 2e525a3edbe..2a05a4d2ecb 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5890,6 +5890,21 @@ Since this app is not set to optimize battery, you can\u2019t restrict it.\n\nTo restrict the app, first turn on battery optimization. + + Unrestricted + + Optimized + + Allow battery usage in background without restrictions. May use more battery. + + Optimize based on your usage. Recommended for most apps. + + Restrict battery usage while in background. App may not work as expected. Notifications may be delayed. + + Changing how an app uses your battery can affect its performance. + + This app requires %1$s battery usage. + Screen usage since full charge @@ -6298,6 +6313,8 @@ Since full charge Manage battery usage + + ^1 total • ^2 background for past 24 hr Battery left estimate is based on your device usage @@ -13103,17 +13120,17 @@ Apps - Untrusted device wants to access your messages. Tap for details. + A device wants to access your messages. Tap for details. Allow access to messages? - An untrusted Bluetooth device, %1$s, wants to access your messages.\n\nYou haven\u2019t connected to %2$s before. + A Bluetooth device, %1$s, wants to access your messages.\n\nYou haven\u2019t connected to %2$s before. - Untrusted device wants to access your contacts and call log. Tap for details. + A device wants to access your contacts and call log. Tap for details. Allow access to contacts and call log? - An untrusted Bluetooth device, %1$s, wants to access your contacts and call log. This includes data about incoming and outgoing calls.\n\nYou haven\u2019t connected to %2$s before. + A Bluetooth device, %1$s, wants to access your contacts and call log. This includes data about incoming and outgoing calls.\n\nYou haven\u2019t connected to %2$s before. Brightness @@ -13211,4 +13228,7 @@ All apps + + + Don\u2019t allow diff --git a/res/values/themes.xml b/res/values/themes.xml index d7b3257f753..4df4a78f31e 100644 --- a/res/values/themes.xml +++ b/res/values/themes.xml @@ -74,6 +74,8 @@ @style/Widget.ActionBar.SubSettings @style/ThemeOverlay.SwitchBar.Settings + @android:color/transparent + @android:color/transparent - diff --git a/res/xml/network_provider_settings.xml b/res/xml/network_provider_settings.xml index 30fe6fea3bd..b29755bab82 100644 --- a/res/xml/network_provider_settings.xml +++ b/res/xml/network_provider_settings.xml @@ -52,6 +52,10 @@ android:layout="@layout/preference_category_no_label" settings:controller="com.android.settings.network.NetworkMobileProviderController"/> + + diff --git a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java index ad995565dd9..4bc086ea0e8 100644 --- a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java @@ -20,6 +20,7 @@ import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import android.app.settings.SettingsEnums; +import android.content.ContentResolver; import android.content.Context; import android.hardware.display.ColorDisplayManager; import android.net.Uri; @@ -61,7 +62,10 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // TODO(b/170973645): Add banner + mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(getPrefContext().getPackageName()) + .appendPath(String.valueOf(R.drawable.extra_dim_banner)) + .build(); mComponentName = AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME; mPackageName = getText(R.string.reduce_bright_colors_preference_title); mHtmlDescription = getText(R.string.reduce_bright_colors_preference_subtitle); diff --git a/src/com/android/settings/applications/intentpicker/ProgressDialogFragment.java b/src/com/android/settings/applications/intentpicker/ProgressDialogFragment.java index f99775e27dd..e55d9048738 100644 --- a/src/com/android/settings/applications/intentpicker/ProgressDialogFragment.java +++ b/src/com/android/settings/applications/intentpicker/ProgressDialogFragment.java @@ -44,6 +44,7 @@ import com.android.settingslib.utils.ThreadUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.SortedSet; /** A customized {@link InstrumentedDialogFragment} with a progress bar. */ public class ProgressDialogFragment extends InstrumentedDialogFragment { @@ -146,9 +147,9 @@ public class ProgressDialogFragment extends InstrumentedDialogFragment { int index = 0; mSupportedLinkWrapperList = new ArrayList<>(); for (String host : links) { - final List ownerList = + final SortedSet ownerSet = mDomainVerificationManager.getOwnersForDomain(host); - mSupportedLinkWrapperList.add(new SupportedLinkWrapper(getActivity(), host, ownerList)); + mSupportedLinkWrapperList.add(new SupportedLinkWrapper(getActivity(), host, ownerSet)); index++; // The cancel was clicked while progressing to collect data. if (!mProgressAlertDialog.isShowing()) { @@ -167,7 +168,7 @@ public class ProgressDialogFragment extends InstrumentedDialogFragment { mProgressAlertDialog.getProgressBar().setProgress(progress); } }); - if (ownerList.size() == 0) { + if (ownerSet.size() == 0) { SystemClock.sleep(PROGRESS_BAR_STEPPING_TIME); } } diff --git a/src/com/android/settings/applications/intentpicker/SupportedLinkWrapper.java b/src/com/android/settings/applications/intentpicker/SupportedLinkWrapper.java index 0acc2bc823a..0db4361f1c5 100644 --- a/src/com/android/settings/applications/intentpicker/SupportedLinkWrapper.java +++ b/src/com/android/settings/applications/intentpicker/SupportedLinkWrapper.java @@ -24,6 +24,7 @@ import android.util.Log; import com.android.settings.R; import java.util.List; +import java.util.SortedSet; import java.util.stream.Collectors; /** @@ -34,14 +35,14 @@ public class SupportedLinkWrapper implements Comparable { private static final String TAG = "SupportedLinkWrapper"; private String mHost; - private List mOwnerList; + private SortedSet mOwnerSet; private boolean mIsEnabled; private String mLastOwnerName; private boolean mIsChecked; - public SupportedLinkWrapper(Context context, String host, List ownerList) { + public SupportedLinkWrapper(Context context, String host, SortedSet ownerSet) { mHost = host; - mOwnerList = ownerList; + mOwnerSet = ownerSet; mIsEnabled = true; mLastOwnerName = ""; mIsChecked = false; @@ -49,8 +50,8 @@ public class SupportedLinkWrapper implements Comparable { } private void init(Context context) { - if (mOwnerList.size() > 0) { - final long nonOverirideableNo = mOwnerList.stream() + if (mOwnerSet.size() > 0) { + final long nonOverirideableNo = mOwnerSet.stream() .filter(it -> !it.isOverrideable()) .count(); mIsEnabled = (nonOverirideableNo == 0L); @@ -63,7 +64,7 @@ public class SupportedLinkWrapper implements Comparable { } private String getLastPackageLabel(Context context, boolean isOverrideable) { - final List labelList = mOwnerList.stream() + final List labelList = mOwnerSet.stream() .filter(it -> it.isOverrideable() == isOverrideable) .map(it -> getLabel(context, it.getPackageName())) .filter(label -> label != null) diff --git a/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java index e40e30dd0a0..10acdb545b3 100644 --- a/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java +++ b/src/com/android/settings/bluetooth/BluetoothPermissionActivity.java @@ -16,6 +16,8 @@ package com.android.settings.bluetooth; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; + import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; @@ -34,8 +36,6 @@ import com.android.internal.app.AlertActivity; import com.android.internal.app.AlertController; import com.android.settings.R; -import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; - /** * BluetoothPermissionActivity shows a dialog for accepting incoming * profile connection request from untrusted devices. @@ -138,7 +138,7 @@ public class BluetoothPermissionActivity extends AlertActivity implements } p.mPositiveButtonText = getString(R.string.allow); p.mPositiveButtonListener = this; - p.mNegativeButtonText = getString(R.string.deny); + p.mNegativeButtonText = getString(R.string.request_manage_bluetooth_permission_dont_allow); p.mNegativeButtonListener = this; mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); setupAlert(); diff --git a/src/com/android/settings/fuelgauge/BatteryDiffEntry.java b/src/com/android/settings/fuelgauge/BatteryDiffEntry.java new file mode 100644 index 00000000000..6c885a19e34 --- /dev/null +++ b/src/com/android/settings/fuelgauge/BatteryDiffEntry.java @@ -0,0 +1,77 @@ +/* + * 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.fuelgauge; + +import java.time.Duration; + +/** A container class to carry battery data in a specific time slot. */ +public final class BatteryDiffEntry { + private static final String TAG = "BatteryDiffEntry"; + + public long mForegroundUsageTimeInMs; + public long mBackgroundUsageTimeInMs; + public double mConsumePower; + // A BatteryHistEntry corresponding to this diff usage data. + public final BatteryHistEntry mBatteryHistEntry; + + private double mTotalConsumePower; + private double mPercentOfTotal; + + public BatteryDiffEntry( + long foregroundUsageTimeInMs, + long backgroundUsageTimeInMs, + double consumePower, + BatteryHistEntry batteryHistEntry) { + mForegroundUsageTimeInMs = foregroundUsageTimeInMs; + mBackgroundUsageTimeInMs = backgroundUsageTimeInMs; + mConsumePower = consumePower; + mBatteryHistEntry = batteryHistEntry; + } + + /** Sets the total consumed power in a specific time slot. */ + public void setTotalConsumePower(double totalConsumePower) { + mTotalConsumePower = totalConsumePower; + mPercentOfTotal = totalConsumePower == 0 + ? 0 : (mConsumePower / mTotalConsumePower) * 100.0; + } + + /** Gets the percentage of total consumed power. */ + public double getPercentOfTotal() { + return mPercentOfTotal; + } + + /** Clones a new instance. */ + public BatteryDiffEntry clone() { + return new BatteryDiffEntry( + this.mForegroundUsageTimeInMs, + this.mBackgroundUsageTimeInMs, + this.mConsumePower, + this.mBatteryHistEntry /*same instance*/); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder() + .append("BatteryDiffEntry{") + .append("\n\tname=" + mBatteryHistEntry.mAppLabel) + .append(String.format("\n\tconsume=%.2f%% %f/%f", + mPercentOfTotal, mConsumePower, mTotalConsumePower)) + .append(String.format("\n\tforeground:%d background:%d", + Duration.ofMillis(mForegroundUsageTimeInMs).getSeconds(), + Duration.ofMillis(mBackgroundUsageTimeInMs).getSeconds())) + .append(String.format("\n\tpackage:%s uid:%s", + mBatteryHistEntry.mPackageName, mBatteryHistEntry.mUid)); + return builder.toString(); + } +} diff --git a/src/com/android/settings/fuelgauge/BatteryHistEntry.java b/src/com/android/settings/fuelgauge/BatteryHistEntry.java index fb46140aba6..e8cbce5bfd5 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistEntry.java +++ b/src/com/android/settings/fuelgauge/BatteryHistEntry.java @@ -112,6 +112,11 @@ public final class BatteryHistEntry { return mIsValidEntry; } + /** Gets an identifier to represent this {@link BatteryHistEntry}. */ + public String getKey() { + return mPackageName + "-" + mUserId; + } + @Override public String toString() { final String recordAtDateTime = ConvertUtils.utcToLocalTime(mTimestamp); @@ -135,7 +140,7 @@ public final class BatteryHistEntry { return values.getAsInteger(key); }; mIsValidEntry = false; - return -1; + return 0; } private int getInteger(Cursor cursor, String key) { @@ -144,7 +149,7 @@ public final class BatteryHistEntry { return cursor.getInt(columnIndex); } mIsValidEntry = false; - return -1; + return 0; } private long getLong(ContentValues values, String key) { @@ -152,7 +157,7 @@ public final class BatteryHistEntry { return values.getAsLong(key); } mIsValidEntry = false; - return -1L; + return 0L; } private long getLong(Cursor cursor, String key) { @@ -161,7 +166,7 @@ public final class BatteryHistEntry { return cursor.getLong(columnIndex); } mIsValidEntry = false; - return -1L; + return 0L; } private double getDouble(ContentValues values, String key) { diff --git a/src/com/android/settings/fuelgauge/ConvertUtils.java b/src/com/android/settings/fuelgauge/ConvertUtils.java index 4ddee3252ef..2e943d12c72 100644 --- a/src/com/android/settings/fuelgauge/ConvertUtils.java +++ b/src/com/android/settings/fuelgauge/ConvertUtils.java @@ -26,13 +26,22 @@ import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Set; import java.util.TimeZone; /** A utility class to convert data into another types. */ public final class ConvertUtils { private static final String TAG = "ConvertUtils"; + private static final Map EMPTY_BATTERY_MAP = new HashMap<>(); + private static final BatteryHistEntry EMPTY_BATTERY_HIST_ENTRY = + new BatteryHistEntry(new ContentValues()); /** Invalid system battery consumer drain type. */ public static final int INVALID_DRAIN_TYPE = -1; @@ -126,4 +135,170 @@ public final class ConvertUtils { } return sSimpleDateFormat.format(new Date(timestamp)); } + + /** Gets indexed battery usage data for each corresponding time slot. */ + public static Map> getIndexedUsageMap( + final int timeSlotSize, + final long[] batteryHistoryKeys, + final Map> batteryHistoryMap) { + final Map> resultMap = new HashMap<>(); + // Generates a temporary map to calculate diff usage data, which converts the inputted + // List into Map with the key comes from + // the BatteryHistEntry.getKey() method. + final Map> newBatteryHistoryMap = new HashMap<>(); + for (int index = 0; index < batteryHistoryKeys.length; index++) { + final Long timestamp = Long.valueOf(batteryHistoryKeys[index]); + final List entries = batteryHistoryMap.get(timestamp); + if (entries == null || entries.isEmpty()) { + continue; + } + final Map slotBatteryHistDataMap = new HashMap<>(); + for (BatteryHistEntry entry : entries) { + // Excludes auto-generated fake BatteryHistEntry data, + // which is used to record battery level and status purpose only. + if (!FAKE_PACKAGE_NAME.equals(entry.mPackageName)) { + slotBatteryHistDataMap.put(entry.getKey(), entry); + } + } + newBatteryHistoryMap.put(timestamp, slotBatteryHistDataMap); + } + + // 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. + final int timestampStride = 2; + for (int index = 0; index < timeSlotSize; index++) { + final Long currentTimestamp = + Long.valueOf(batteryHistoryKeys[index * timestampStride]); + final Long nextTimestamp = + Long.valueOf(batteryHistoryKeys[index * timestampStride + 1]); + final Long nextTwoTimestamp = + Long.valueOf(batteryHistoryKeys[index * timestampStride + 2]); + + // Fetches BatteryHistEntry data from corresponding time slot. + final Map currentBatteryHistMap = + newBatteryHistoryMap.getOrDefault(currentTimestamp, EMPTY_BATTERY_MAP); + final Map nextBatteryHistMap = + newBatteryHistoryMap.getOrDefault(nextTimestamp, EMPTY_BATTERY_MAP); + final Map nextTwoBatteryHistMap = + newBatteryHistoryMap.getOrDefault(nextTwoTimestamp, EMPTY_BATTERY_MAP); + + // Collects all keys in these three time slot records as population. + final Set allBatteryHistEntryKeys = new HashSet<>(); + allBatteryHistEntryKeys.addAll(currentBatteryHistMap.keySet()); + allBatteryHistEntryKeys.addAll(nextBatteryHistMap.keySet()); + allBatteryHistEntryKeys.addAll(nextTwoBatteryHistMap.keySet()); + + double totalConsumePower = 0.0; + final List batteryDiffEntryList = new ArrayList<>(); + // Adds a specific time slot BatteryDiffEntry list into result map. + resultMap.put(Integer.valueOf(index), batteryDiffEntryList); + + // 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. + final long foregroundUsageTimeInMs = + getDiffValue( + currentEntry.mForegroundUsageTimeInMs, + nextEntry.mForegroundUsageTimeInMs, + nextTwoEntry.mForegroundUsageTimeInMs); + final long backgroundUsageTimeInMs = + getDiffValue( + currentEntry.mBackgroundUsageTimeInMs, + nextEntry.mBackgroundUsageTimeInMs, + nextTwoEntry.mBackgroundUsageTimeInMs); + final double consumePower = + getDiffValue( + currentEntry.mConsumePower, + nextEntry.mConsumePower, + nextTwoEntry.mConsumePower); + totalConsumePower += consumePower; + + // 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; + } + batteryDiffEntryList.add( + new BatteryDiffEntry( + foregroundUsageTimeInMs, + backgroundUsageTimeInMs, + consumePower, + selectedBatteryEntry)); + } + // Sets total consume power data into all BatteryDiffEntry in the same slot. + for (BatteryDiffEntry diffEntry : batteryDiffEntryList) { + diffEntry.setTotalConsumePower(totalConsumePower); + } + } + insert24HoursData(BatteryChartView.SELECTED_INDEX_ALL, resultMap); + return resultMap; + } + + private static void insert24HoursData( + final int desiredIndex, + final Map> indexedUsageMap) { + final Map resultMap = new HashMap<>(); + double totalConsumePower = 0.0; + // Loops for all BatteryDiffEntry and aggregate them together. + for (List entryList : indexedUsageMap.values()) { + for (BatteryDiffEntry entry : entryList) { + final String key = entry.mBatteryHistEntry.getKey(); + final BatteryDiffEntry oldBatteryDiffEntry = resultMap.get(key); + // Creates new BatteryDiffEntry if we don't have it. + if (oldBatteryDiffEntry == null) { + resultMap.put(key, entry.clone()); + } else { + // Sums up some fields data into the existing one. + oldBatteryDiffEntry.mForegroundUsageTimeInMs += + entry.mForegroundUsageTimeInMs; + oldBatteryDiffEntry.mBackgroundUsageTimeInMs += + entry.mBackgroundUsageTimeInMs; + oldBatteryDiffEntry.mConsumePower += entry.mConsumePower; + } + totalConsumePower += entry.mConsumePower; + } + } + final List resultList = new ArrayList<>(resultMap.values()); + // Sets total 24 hours consume power data into all BatteryDiffEntry. + for (BatteryDiffEntry entry : resultList) { + entry.setTotalConsumePower(totalConsumePower); + } + indexedUsageMap.put(Integer.valueOf(desiredIndex), resultList); + } + + 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); + } + + private static BatteryHistEntry selectBatteryHistEntry( + BatteryHistEntry entry1, + BatteryHistEntry entry2, + BatteryHistEntry entry3) { + if (entry1 != null && entry1 != EMPTY_BATTERY_HIST_ENTRY) { + return entry1; + } else if (entry2 != null && entry2 != EMPTY_BATTERY_HIST_ENTRY) { + return entry2; + } else { + return entry3 != null && entry3 != EMPTY_BATTERY_HIST_ENTRY + ? entry3 : null; + } + } } diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java index 3b849be8638..5aee0291e4d 100644 --- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java +++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java @@ -75,11 +75,11 @@ public abstract class BatteryTip implements Comparable, Parcelable { static final SparseIntArray TIP_ORDER; static { TIP_ORDER = new SparseIntArray(); - TIP_ORDER.append(TipType.BATTERY_DEFENDER, 0); - TIP_ORDER.append(TipType.APP_RESTRICTION, 1); - TIP_ORDER.append(TipType.BATTERY_SAVER, 2); - TIP_ORDER.append(TipType.HIGH_DEVICE_USAGE, 3); - TIP_ORDER.append(TipType.LOW_BATTERY, 4); + TIP_ORDER.append(TipType.BATTERY_SAVER, 0); + TIP_ORDER.append(TipType.LOW_BATTERY, 1); + TIP_ORDER.append(TipType.BATTERY_DEFENDER, 2); + TIP_ORDER.append(TipType.APP_RESTRICTION, 3); + TIP_ORDER.append(TipType.HIGH_DEVICE_USAGE, 4); TIP_ORDER.append(TipType.SUMMARY, 5); TIP_ORDER.append(TipType.SMART_BATTERY_MANAGER, 6); TIP_ORDER.append(TipType.REDUCED_BATTERY, 7); diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java index 1d8ee7a59b8..c368fa3f66a 100644 --- a/src/com/android/settings/network/NetworkProviderSettings.java +++ b/src/com/android/settings/network/NetworkProviderSettings.java @@ -50,6 +50,7 @@ import android.widget.Toast; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; @@ -80,6 +81,7 @@ import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.connectivity.ConnectivitySubsystemsRecoveryManager; import com.android.settingslib.search.Indexable; import com.android.settingslib.search.SearchIndexable; +import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.widget.LayoutPreference; import com.android.settingslib.wifi.LongPressWifiEntryPreference; import com.android.settingslib.wifi.WifiSavedConfigUtils; @@ -100,7 +102,7 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment implements Indexable, WifiPickerTracker.WifiPickerTrackerCallback, WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener, ConnectivitySubsystemsRecoveryManager.RecoveryStatusCallback, - AirplaneModeEnabler.OnAirplaneModeChangedListener { + AirplaneModeEnabler.OnAirplaneModeChangedListener, InternetUpdater.InternetChangeListener { public static final String ACTION_NETWORK_PROVIDER_SETTINGS = "android.settings.NETWORK_PROVIDER_SETTINGS"; @@ -123,7 +125,10 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment private static final String PREF_KEY_AIRPLANE_MODE_MSG = "airplane_mode_message"; private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list"; // TODO(b/70983952): Rename these to use WifiEntry instead of AccessPoint. - private static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point"; + @VisibleForTesting + static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point"; + @VisibleForTesting + static final String PREF_KEY_FIRST_ACCESS_POINTS = "first_access_points"; private static final String PREF_KEY_ACCESS_POINTS = "access_points"; private static final String PREF_KEY_CONFIGURE_WIFI_SETTINGS = "configure_wifi_settings"; private static final String PREF_KEY_SAVED_NETWORKS = "saved_networks"; @@ -192,12 +197,17 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment @VisibleForTesting WifiPickerTracker mWifiPickerTracker; private WifiPickerTrackerHelper mWifiPickerTrackerHelper; + @VisibleForTesting + InternetUpdater mInternetUpdater; private WifiDialog2 mDialog; private View mProgressHeader; - private PreferenceCategory mConnectedWifiEntryPreferenceCategory; + @VisibleForTesting + PreferenceCategory mConnectedWifiEntryPreferenceCategory; + @VisibleForTesting + PreferenceCategory mFirstWifiEntryPreferenceCategory; private PreferenceCategory mWifiEntryPreferenceCategory; @VisibleForTesting AddWifiNetworkPreference mAddWifiNetworkPreference; @@ -262,6 +272,7 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment mAirplaneModeMsgPreference = findPreference(PREF_KEY_AIRPLANE_MODE_MSG); updateAirplaneModeMsgPreference(mAirplaneModeEnabler.isAirplaneModeOn() /* visible */); mConnectedWifiEntryPreferenceCategory = findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS); + mFirstWifiEntryPreferenceCategory = findPreference(PREF_KEY_FIRST_ACCESS_POINTS); mWifiEntryPreferenceCategory = findPreference(PREF_KEY_ACCESS_POINTS); mConfigureWifiSettingsPreference = findPreference(PREF_KEY_CONFIGURE_WIFI_SETTINGS); mSavedNetworksPreference = findPreference(PREF_KEY_SAVED_NETWORKS); @@ -318,6 +329,7 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment mWifiPickerTrackerHelper = new WifiPickerTrackerHelper(getSettingsLifecycle(), getContext(), this); mWifiPickerTracker = mWifiPickerTrackerHelper.getWifiPickerTracker(); + mInternetUpdater = new InternetUpdater(getContext(), getSettingsLifecycle(), this); final Activity activity = getActivity(); @@ -635,6 +647,13 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment } } + @Override + public void onInternetTypeChanged(@InternetUpdater.InternetType int internetType) { + ThreadUtils.postOnMainThread(() -> { + onWifiStateChanged(); + }); + } + /** Called when the state of Wifi has changed. */ @Override public void onWifiStateChanged() { @@ -738,17 +757,18 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment mWifiEntryPreferenceCategory.setVisible(true); final WifiEntry connectedEntry = mWifiPickerTracker.getConnectedWifiEntry(); - mConnectedWifiEntryPreferenceCategory.setVisible(connectedEntry != null); + PreferenceCategory connectedWifiPreferenceCategory = getConnectedWifiPreferenceCategory(); + connectedWifiPreferenceCategory.setVisible(connectedEntry != null); if (connectedEntry != null) { final LongPressWifiEntryPreference connectedPref = - mConnectedWifiEntryPreferenceCategory.findPreference(connectedEntry.getKey()); + connectedWifiPreferenceCategory.findPreference(connectedEntry.getKey()); if (connectedPref == null || connectedPref.getWifiEntry() != connectedEntry) { - mConnectedWifiEntryPreferenceCategory.removeAll(); + connectedWifiPreferenceCategory.removeAll(); final ConnectedWifiEntryPreference pref = - new ConnectedWifiEntryPreference(getPrefContext(), connectedEntry, this); + createConnectedWifiEntryPreference(connectedEntry); pref.setKey(connectedEntry.getKey()); pref.refresh(); - mConnectedWifiEntryPreferenceCategory.addPreference(pref); + connectedWifiPreferenceCategory.addPreference(pref); pref.setOnPreferenceClickListener(preference -> { if (connectedEntry.canSignIn()) { connectedEntry.signIn(null /* callback */); @@ -763,11 +783,11 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment if (mClickedConnect) { mClickedConnect = false; - scrollToPreference(mConnectedWifiEntryPreferenceCategory); + scrollToPreference(connectedWifiPreferenceCategory); } } } else { - mConnectedWifiEntryPreferenceCategory.removeAll(); + connectedWifiPreferenceCategory.removeAll(); } int index = 0; @@ -821,6 +841,27 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment setAdditionalSettingsSummaries(); } + @VisibleForTesting + PreferenceCategory getConnectedWifiPreferenceCategory() { + if (mInternetUpdater.getInternetType() == InternetUpdater.INTERNET_WIFI) { + mFirstWifiEntryPreferenceCategory.setVisible(false); + mFirstWifiEntryPreferenceCategory.removeAll(); + return mConnectedWifiEntryPreferenceCategory; + } + + mConnectedWifiEntryPreferenceCategory.setVisible(false); + mConnectedWifiEntryPreferenceCategory.removeAll(); + return mFirstWifiEntryPreferenceCategory; + } + + @VisibleForTesting + ConnectedWifiEntryPreference createConnectedWifiEntryPreference(WifiEntry wifiEntry) { + if (mInternetUpdater.getInternetType() == InternetUpdater.INTERNET_WIFI) { + return new ConnectedWifiEntryPreference(getPrefContext(), wifiEntry, this); + } + return new FirstWifiEntryPreference(getPrefContext(), wifiEntry, this); + } + private void launchNetworkDetailsFragment(LongPressWifiEntryPreference pref) { final WifiEntry wifiEntry = pref.getWifiEntry(); final Context context = getContext(); @@ -1217,4 +1258,22 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment public void onAirplaneModeChanged(boolean isAirplaneModeOn) { updateAirplaneModeMsgPreference(isAirplaneModeOn /* visible */); } + + /** + * A Wi-Fi preference for the connected Wi-Fi network without internet access. + * + * Override the icon color attribute by {@link ConnectedWifiEntryPreference#getIconColorAttr()} + * and show the icon color to android.R.attr.colorControlNormal for the preference. + */ + public class FirstWifiEntryPreference extends ConnectedWifiEntryPreference { + public FirstWifiEntryPreference(Context context, WifiEntry wifiEntry, + Fragment fragment) { + super(context, wifiEntry, fragment); + } + + @Override + protected int getIconColorAttr() { + return android.R.attr.colorControlNormal; + } + } } diff --git a/src/com/android/settings/vpn2/ConfigDialog.java b/src/com/android/settings/vpn2/ConfigDialog.java index 24fb576a818..a88be039312 100644 --- a/src/com/android/settings/vpn2/ConfigDialog.java +++ b/src/com/android/settings/vpn2/ConfigDialog.java @@ -153,7 +153,7 @@ class ConfigDialog extends AlertDialog implements TextWatcher, mL2tpSecret.setTextAppearance(android.R.style.TextAppearance_DeviceDefault_Medium); mIpsecIdentifier.setText(mProfile.ipsecIdentifier); mIpsecSecret.setText(mProfile.ipsecSecret); - AndroidKeystoreAliasLoader androidKeystoreAliasLoader = + final AndroidKeystoreAliasLoader androidKeystoreAliasLoader = new AndroidKeystoreAliasLoader(null); loadCertificates(mIpsecUserCert, androidKeystoreAliasLoader.getKeyCertAliases(), 0, mProfile.ipsecUserCert); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryDiffEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryDiffEntryTest.java new file mode 100644 index 00000000000..07e00fc9ed5 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryDiffEntryTest.java @@ -0,0 +1,52 @@ +/* + * 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.fuelgauge; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public final class BatteryDiffEntryTest { + + @Test + public void testSetTotalConsumePower_returnExpectedResult() { + final BatteryDiffEntry entry = + new BatteryDiffEntry( + /*foregroundUsageTimeInMs=*/ 10001L, + /*backgroundUsageTimeInMs=*/ 20002L, + /*consumePower=*/ 22.0, + /*batteryHistEntry=*/ null); + entry.setTotalConsumePower(100.0); + + assertThat(entry.getPercentOfTotal()).isEqualTo(22.0); + } + + @Test + public void testSetTotalConsumePower_setZeroValue_returnsZeroValue() { + final BatteryDiffEntry entry = + new BatteryDiffEntry( + /*foregroundUsageTimeInMs=*/ 10001L, + /*backgroundUsageTimeInMs=*/ 20002L, + /*consumePower=*/ 22.0, + /*batteryHistEntry=*/ null); + entry.setTotalConsumePower(0); + + assertThat(entry.getPercentOfTotal()).isEqualTo(0); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistEntryTest.java index b0fb3d14bd9..d97b0d22baf 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistEntryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistEntryTest.java @@ -161,5 +161,7 @@ public final class BatteryHistEntryTest { .isEqualTo(BatteryManager.BATTERY_STATUS_FULL); assertThat(entry.mBatteryHealth) .isEqualTo(BatteryManager.BATTERY_HEALTH_COLD); + assertThat(entry.getKey()) + .isEqualTo("com.google.android.settings.battery-" + entry.mUserId); } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java index 6a5ea617b18..681fce69086 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java @@ -37,6 +37,11 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.TimeZone; @RunWith(RobolectricTestRunner.class) @@ -165,4 +170,84 @@ public final class ConvertUtilsTest { assertThat(ConvertUtils.getConsumerType(mockBatteryConsumer)) .isEqualTo(ConvertUtils.CONSUMER_TYPE_UNKNOWN); } + + @Test + public void testGetIndexedUsageMap_returnsExpectedResult() { + // Creates the fake testing data. + final int timeSlotSize = 2; + final long[] batteryHistoryKeys = new long[] {101L, 102L, 103L, 104L, 105L}; + final Map> batteryHistoryMap = new HashMap<>(); + batteryHistoryMap.put( + Long.valueOf(batteryHistoryKeys[0]), + Arrays.asList( + createBatteryHistEntry( + "package1", "label1", 5.0, 1L, 10L, 20L))); + batteryHistoryMap.put( + Long.valueOf(batteryHistoryKeys[1]), new ArrayList()); + batteryHistoryMap.put( + Long.valueOf(batteryHistoryKeys[2]), + Arrays.asList( + createBatteryHistEntry( + "package2", "label2", 10.0, 2L, 15L, 25L))); + batteryHistoryMap.put( + Long.valueOf(batteryHistoryKeys[3]), + Arrays.asList( + createBatteryHistEntry( + "package2", "label2", 15.0, 2L, 25L, 35L), + createBatteryHistEntry( + "package3", "label3", 5.0, 2L, 5L, 5L))); + batteryHistoryMap.put( + Long.valueOf(batteryHistoryKeys[4]), + Arrays.asList( + createBatteryHistEntry( + "package2", "label2", 30.0, 2L, 30L, 40L), + createBatteryHistEntry( + "package2", "label2", 75.0, 3L, 40L, 50L), + createBatteryHistEntry( + "package3", "label3", 5.0, 2L, 5L, 5L))); + + final Map> resultMap = + ConvertUtils.getIndexedUsageMap( + timeSlotSize, batteryHistoryKeys, batteryHistoryMap); + + assertThat(resultMap).hasSize(3); + // Verifies the first timestamp result. + List entryList = resultMap.get(Integer.valueOf(0)); + assertThat(entryList).hasSize(1); + assertBatteryDiffEntry(entryList.get(0), 100, 15L, 25L); + // Verifies the second timestamp result. + entryList = resultMap.get(Integer.valueOf(1)); + assertThat(entryList).hasSize(3); + assertBatteryDiffEntry(entryList.get(0), 5, 5L, 5L); + assertBatteryDiffEntry(entryList.get(1), 75, 40L, 50L); + assertBatteryDiffEntry(entryList.get(2), 20, 15L, 15L); + // Verifies the last 24 hours aggregate result. + entryList = resultMap.get(Integer.valueOf(-1)); + assertThat(entryList).hasSize(3); + assertBatteryDiffEntry(entryList.get(0), 4, 5L, 5L); + assertBatteryDiffEntry(entryList.get(1), 68, 40L, 50L); + assertBatteryDiffEntry(entryList.get(2), 27, 30L, 40L); + } + + private static BatteryHistEntry createBatteryHistEntry( + String packageName, String appLabel, double consumePower, + long userId, long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) { + // Only insert required fields. + final ContentValues values = new ContentValues(); + values.put("packageName", packageName); + values.put("appLabel", appLabel); + values.put("userId", userId); + values.put("consumePower", consumePower); + values.put("foregroundUsageTimeInMs", foregroundUsageTimeInMs); + values.put("backgroundUsageTimeInMs", backgroundUsageTimeInMs); + return new BatteryHistEntry(values); + } + + private static void assertBatteryDiffEntry( + BatteryDiffEntry entry, int percentOfTotal, + long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) { + assertThat((int) entry.getPercentOfTotal()).isEqualTo(percentOfTotal); + assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs); + assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java index 5d7b082bd4a..95280b669bc 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java @@ -50,10 +50,10 @@ import java.util.List; public class BatteryTipLoaderTest { private static final int[] TIP_ORDER = { - BatteryTip.TipType.BATTERY_DEFENDER, BatteryTip.TipType.BATTERY_SAVER, - BatteryTip.TipType.HIGH_DEVICE_USAGE, BatteryTip.TipType.LOW_BATTERY, + BatteryTip.TipType.BATTERY_DEFENDER, + BatteryTip.TipType.HIGH_DEVICE_USAGE, BatteryTip.TipType.SMART_BATTERY_MANAGER}; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private BatteryUsageStats mBatteryUsageStats; diff --git a/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java b/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java index c1377ff2aa9..f02a8066311 100644 --- a/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java +++ b/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java @@ -51,6 +51,7 @@ import android.view.View; import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import androidx.recyclerview.widget.RecyclerView; @@ -61,6 +62,7 @@ import com.android.settings.datausage.DataUsagePreference; import com.android.settings.testutils.shadow.ShadowDataUsageUtils; import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settings.wifi.AddWifiNetworkPreference; +import com.android.settings.wifi.ConnectedWifiEntryPreference; import com.android.settings.wifi.WifiConfigController2; import com.android.settings.wifi.WifiDialog2; import com.android.settingslib.connectivity.ConnectivitySubsystemsRecoveryManager; @@ -109,6 +111,12 @@ public class NetworkProviderSettingsTest { private LayoutPreference mResetInternetPreference; @Mock private MenuItem mMenuItem; + @Mock + InternetUpdater mInternetUpdater; + @Mock + PreferenceCategory mConnectedWifiEntryPreferenceCategory; + @Mock + PreferenceCategory mFirstWifiEntryPreferenceCategory; @Before public void setUp() { @@ -132,6 +140,15 @@ public class NetworkProviderSettingsTest { mNetworkProviderSettings.mResetInternetPreference = mResetInternetPreference; mNetworkProviderSettings.mAirplaneModeMsgPreference = mAirplaneModeMsgPreference; mNetworkProviderSettings.mAirplaneModeEnabler = mAirplaneModeEnabler; + mNetworkProviderSettings.mInternetUpdater = mInternetUpdater; + doReturn(NetworkProviderSettings.PREF_KEY_CONNECTED_ACCESS_POINTS) + .when(mConnectedWifiEntryPreferenceCategory).getKey(); + mNetworkProviderSettings.mConnectedWifiEntryPreferenceCategory = + mConnectedWifiEntryPreferenceCategory; + doReturn(NetworkProviderSettings.PREF_KEY_FIRST_ACCESS_POINTS) + .when(mFirstWifiEntryPreferenceCategory).getKey(); + mNetworkProviderSettings.mFirstWifiEntryPreferenceCategory = + mFirstWifiEntryPreferenceCategory; FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL, false); } @@ -452,11 +469,48 @@ public class NetworkProviderSettingsTest { verify(mAirplaneModeMsgPreference).setVisible(true); } - @Test public void onAirplaneModeChanged_apmIsOff_hideApmMsg() { mNetworkProviderSettings.onAirplaneModeChanged(false); verify(mAirplaneModeMsgPreference).setVisible(false); } + + @Test + public void getConnectedWifiPreferenceCategory_internetWiFi_getConnectedAccessPoints() { + doReturn(InternetUpdater.INTERNET_WIFI).when(mInternetUpdater).getInternetType(); + + final PreferenceCategory pc = mNetworkProviderSettings.getConnectedWifiPreferenceCategory(); + + assertThat(pc.getKey()).isEqualTo(NetworkProviderSettings.PREF_KEY_CONNECTED_ACCESS_POINTS); + } + + @Test + public void getConnectedWifiPreferenceCategory_internetCellular_getFirstAccessPoints() { + doReturn(InternetUpdater.INTERNET_CELLULAR).when(mInternetUpdater).getInternetType(); + + final PreferenceCategory pc = mNetworkProviderSettings.getConnectedWifiPreferenceCategory(); + + assertThat(pc.getKey()).isEqualTo(NetworkProviderSettings.PREF_KEY_FIRST_ACCESS_POINTS); + } + + @Test + public void createConnectedWifiEntryPreference_internetWiFi_createConnectedPreference() { + final WifiEntry wifiEntry = mock(WifiEntry.class); + doReturn(InternetUpdater.INTERNET_WIFI).when(mInternetUpdater).getInternetType(); + + final Preference p = mNetworkProviderSettings.createConnectedWifiEntryPreference(wifiEntry); + + assertThat(p instanceof ConnectedWifiEntryPreference).isTrue(); + } + + @Test + public void createConnectedWifiEntryPreference_internetCellular_createFirstWifiPreference() { + final WifiEntry wifiEntry = mock(WifiEntry.class); + doReturn(InternetUpdater.INTERNET_CELLULAR).when(mInternetUpdater).getInternetType(); + + final Preference p = mNetworkProviderSettings.createConnectedWifiEntryPreference(wifiEntry); + + assertThat(p instanceof NetworkProviderSettings.FirstWifiEntryPreference).isTrue(); + } }