resolutions =
- pm.queryIntentActivities(requestAccountConfirmation, 0);
- if (resolutions != null && resolutions.size() > 0) {
+ final ResolveInfo resolution = pm.resolveActivity(requestAccountConfirmation, 0);
+ if (resolution != null
+ && resolution.activityInfo != null
+ && packageName.equals(resolution.activityInfo.packageName)) {
+ // Note that we need to check the packagename to make sure that an Activity resolver
+ // wasn't returned.
getActivity().startActivityForResult(
requestAccountConfirmation, CREDENTIAL_CONFIRM_REQUEST);
return true;
@@ -214,6 +221,8 @@ public class MasterClear extends InstrumentedPreferenceFragment {
mInitiateButton.setOnClickListener(mInitiateListener);
mExternalStorageContainer = mContentView.findViewById(R.id.erase_external_container);
mExternalStorage = (CheckBox) mContentView.findViewById(R.id.erase_external);
+ mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container);
+ mEsimStorage = (CheckBox) mContentView.findViewById(R.id.erase_esim);
mScrollView = (ScrollView) mContentView.findViewById(R.id.master_clear_scrollview);
/*
@@ -248,11 +257,25 @@ public class MasterClear extends InstrumentedPreferenceFragment {
}
if (showWipeEuicc()) {
- final View esimAlsoErased = mContentView.findViewById(R.id.also_erases_esim);
- esimAlsoErased.setVisibility(View.VISIBLE);
+ if (showWipeEuiccCheckbox()) {
+ TextView title = mContentView.findViewById(R.id.erase_esim_title);
+ title.setText(R.string.erase_esim_storage);
+ mEsimStorageContainer.setVisibility(View.VISIBLE);
+ mEsimStorageContainer.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mEsimStorage.toggle();
+ }
+ });
+ } else {
+ final View esimAlsoErased = mContentView.findViewById(R.id.also_erases_esim);
+ esimAlsoErased.setVisibility(View.VISIBLE);
- final View noCancelMobilePlan = mContentView.findViewById(R.id.no_cancel_mobile_plan);
- noCancelMobilePlan.setVisibility(View.VISIBLE);
+ final View noCancelMobilePlan = mContentView.findViewById(
+ R.id.no_cancel_mobile_plan);
+ noCancelMobilePlan.setVisibility(View.VISIBLE);
+ mEsimStorage.setChecked(true /* checked */);
+ }
}
final UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
@@ -293,6 +316,12 @@ public class MasterClear extends InstrumentedPreferenceFragment {
return Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) != 0;
}
+ @VisibleForTesting
+ boolean showWipeEuiccCheckbox() {
+ return SystemProperties
+ .getBoolean(KEY_SHOW_ESIM_RESET_CHECKBOX, false /* def */);
+ }
+
@VisibleForTesting
protected boolean isEuiccEnabled(Context context) {
EuiccManager euiccManager = (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
diff --git a/src/com/android/settings/ResetNetworkConfirm.java b/src/com/android/settings/ResetNetworkConfirm.java
index 53d9386643a..bc0fa774a75 100644
--- a/src/com/android/settings/ResetNetworkConfirm.java
+++ b/src/com/android/settings/ResetNetworkConfirm.java
@@ -81,8 +81,7 @@ public class ResetNetworkConfirm extends InstrumentedPreferenceFragment {
@Override
protected Boolean doInBackground(Void... params) {
- return mRecoverySystem.wipeEuiccData(
- mContext, true /* isWipeEuicc */, mPackageName);
+ return mRecoverySystem.wipeEuiccData(mContext, mPackageName);
}
@Override
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 5e815bc2c27..d13a62dcbb3 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -178,5 +178,10 @@ public class Settings extends SettingsActivity {
public static class StorageDashboardActivity extends SettingsActivity {}
public static class UserAndAccountDashboardActivity extends SettingsActivity {}
public static class SystemDashboardActivity extends SettingsActivity {}
+ public static class AdvancedConnectedDeviceActivity extends SettingsActivity {
+ public static final boolean isEnabled() {
+ return FeatureFlagUtils.isEnabled(null /* context */, CONNECTED_DEVICE_V2);
+ }
+ }
}
diff --git a/src/com/android/settings/ShowAdminSupportDetailsDialog.java b/src/com/android/settings/ShowAdminSupportDetailsDialog.java
index c1cd6f567fb..321f93d10c3 100644
--- a/src/com/android/settings/ShowAdminSupportDetailsDialog.java
+++ b/src/com/android/settings/ShowAdminSupportDetailsDialog.java
@@ -150,6 +150,9 @@ public class ShowAdminSupportDetailsDialog extends Activity
case DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE:
titleView.setText(R.string.disabled_by_policy_title_screen_capture);
break;
+ case DevicePolicyManager.POLICY_MANDATORY_BACKUPS:
+ titleView.setText(R.string.disabled_by_policy_title_turn_off_backups);
+ break;
default:
// Use general text if no specialized title applies
titleView.setText(R.string.disabled_by_policy_title);
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index ad951216781..ae10ffee520 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -514,7 +514,8 @@ public final class Utils extends com.android.settingslib.Utils {
Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
CharSequence title, boolean isShortcut, int metricsCategory) {
startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
- titleResPackageName, titleResId, title, isShortcut, metricsCategory, 0);
+ titleResPackageName, titleResId, title, isShortcut, metricsCategory,
+ Intent.FLAG_ACTIVITY_NEW_TASK);
}
diff --git a/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java b/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java
index 8cd4444ed4c..1c2a0af8a74 100644
--- a/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java
+++ b/src/com/android/settings/applications/AppStateDirectoryAccessBridge.java
@@ -33,11 +33,15 @@ import com.android.settingslib.applications.ApplicationsState.AppFilter;
import java.util.Set;
-// TODO(b/63720392): add unit tests
+// TODO(b/72055774): add unit tests
public class AppStateDirectoryAccessBridge extends AppStateBaseBridge {
private static final String TAG = "DirectoryAccessBridge";
+ // TODO(b/72055774): set to false once feature is ready (or use Log.isLoggable)
+ static final boolean DEBUG = true;
+ static final boolean VERBOSE = true;
+
public AppStateDirectoryAccessBridge(ApplicationsState appState, Callback callback) {
super(appState, callback);
}
@@ -59,27 +63,34 @@ public class AppStateDirectoryAccessBridge extends AppStateBaseBridge {
@Override
public void init(Context context) {
- try (Cursor cursor = context.getContentResolver().query(
- new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(AUTHORITY).appendPath(TABLE_PACKAGES).appendPath("*")
- .build(), TABLE_PACKAGES_COLUMNS, null, null)) {
+ mPackages = null;
+ final Uri providerUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(AUTHORITY).appendPath(TABLE_PACKAGES).appendPath("*")
+ .build();
+ try (Cursor cursor = context.getContentResolver().query(providerUri,
+ TABLE_PACKAGES_COLUMNS, null, null)) {
if (cursor == null) {
- Log.w(TAG, "didn't get cursor");
+ Log.w(TAG, "Didn't get cursor for " + providerUri);
return;
}
final int count = cursor.getCount();
if (count == 0) {
- Log.d(TAG, "no packages");
+ if (DEBUG) {
+ Log.d(TAG, "No packages anymore (was " + mPackages + ")");
+ }
return;
}
mPackages = new ArraySet<>(count);
while (cursor.moveToNext()) {
mPackages.add(cursor.getString(TABLE_PACKAGES_COL_PACKAGE));
}
- Log.v(TAG, "init(): " + mPackages);
+ if (DEBUG) {
+ Log.d(TAG, "init(): " + mPackages);
+ }
}
}
+
@Override
public boolean filterApp(AppEntry info) {
return mPackages != null && mPackages.contains(info.info.packageName);
diff --git a/src/com/android/settings/applications/DirectoryAccessDetails.java b/src/com/android/settings/applications/DirectoryAccessDetails.java
index 1f7a81a9336..43422d0f90b 100644
--- a/src/com/android/settings/applications/DirectoryAccessDetails.java
+++ b/src/com/android/settings/applications/DirectoryAccessDetails.java
@@ -17,84 +17,236 @@
package com.android.settings.applications;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.AUTHORITY;
+import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_DIRECTORY;
+import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_GRANTED;
+import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_PACKAGE;
+import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_VOLUME_UUID;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COLUMNS;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_DIRECTORY;
-import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_GRANTED;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_PACKAGE;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_VOLUME_UUID;
+import static com.android.settings.applications.AppStateDirectoryAccessBridge.DEBUG;
+import static com.android.settings.applications.AppStateDirectoryAccessBridge.VERBOSE;
+
+import android.annotation.Nullable;
+import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
import android.support.v7.preference.Preference.OnPreferenceClickListener;
+import android.text.TextUtils;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IconDrawableFactory;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settings.widget.EntityHeaderController.ActionType;
+import com.android.settingslib.applications.AppUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/**
* Detailed settings for an app's directory access permissions (A.K.A Scoped Directory Access).
+ *
+ * Currently, it shows the entry for which the user denied access with the "Do not ask again"
+ * flag checked on: the user than can use the settings toggle to reset that deniel.
+ *
+ *
This fragments dynamically lists all such permissions, starting with one preference per
+ * directory in the primary storage, then adding additional entries for the external volumes (one
+ * entry for the whole volume).
*/
-// TODO(b/63720392): explain its layout
-// TODO(b/63720392): add unit tests
-public class DirectoryAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
- OnPreferenceClickListener {
- private static final String MY_TAG = "DirectoryAccessDetails";
+// TODO(b/72055774): add unit tests
+public class DirectoryAccessDetails extends AppInfoBase {
+
+ @SuppressWarnings("hiding")
+ private static final String TAG = "DirectoryAccessDetails";
+
+ private boolean mCreated;
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ if (mCreated) {
+ Log.w(TAG, "onActivityCreated(): ignoring duplicate call");
+ return;
+ }
+ mCreated = true;
+ if (mPackageInfo == null) {
+ Log.w(TAG, "onActivityCreated(): no package info");
+ return;
+ }
+ final Activity activity = getActivity();
+ final Preference pref = EntityHeaderController
+ .newInstance(activity, this, /* header= */ null )
+ .setRecyclerView(getListView(), getLifecycle())
+ .setIcon(IconDrawableFactory.newInstance(getPrefContext())
+ .getBadgedIcon(mPackageInfo.applicationInfo))
+ .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm))
+ .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
+ .setPackageName(mPackageName)
+ .setUid(mPackageInfo.applicationInfo.uid)
+ .setHasAppInfoLink(false)
+ .setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE)
+ .done(activity, getPrefContext());
+ getPreferenceScreen().addPreference(pref);
+ }
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (true) {
- // TODO(b/63720392): temporarily hack so the screen doesn't crash..
- addPreferencesFromResource(R.xml.app_ops_permissions_details);
- // ... we need to dynamically create the preferences by calling the provider instead:
- try (Cursor cursor = getContentResolver().query(
- new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(AUTHORITY).appendPath(TABLE_PERMISSIONS).appendPath("*")
- .build(),
- TABLE_PERMISSIONS_COLUMNS, null, null)) {
- if (cursor == null) {
- Log.w(TAG, "didn't get cursor");
- return;
+ final Context context = getPrefContext();
+ addPreferencesFromResource(R.xml.directory_access_details);
+ final PreferenceScreen prefsGroup = getPreferenceScreen();
+
+ // Set external directory UUIDs.
+ ArraySet externalDirectoryUuids = null;
+
+ final Uri providerUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(AUTHORITY).appendPath(TABLE_PERMISSIONS).appendPath("*")
+ .build();
+ // Query provider for entries.
+ try (Cursor cursor = context.getContentResolver().query(providerUri,
+ TABLE_PERMISSIONS_COLUMNS, null, new String[] { mPackageName }, null)) {
+ if (cursor == null) {
+ Log.w(TAG, "Didn't get cursor for " + mPackageName);
+ return;
+ }
+ final int count = cursor.getCount();
+ if (count == 0) {
+ if (DEBUG) {
+ Log.d(TAG, "No permissions for " + mPackageName);
}
- final int count = cursor.getCount();
- if (count == 0) {
- Log.d(TAG, "no permissions");
- return;
+ // TODO(b/72055774): display empty message
+ return;
+ }
+
+ while (cursor.moveToNext()) {
+ final String pkg = cursor.getString(TABLE_PERMISSIONS_COL_PACKAGE);
+ final String uuid = cursor.getString(TABLE_PERMISSIONS_COL_VOLUME_UUID);
+ final String dir = cursor.getString(TABLE_PERMISSIONS_COL_DIRECTORY);
+ if (VERBOSE) {
+ Log.v(TAG, "Pkg:" + pkg + " uuid: " + uuid + " dir: " + dir);
}
- while (cursor.moveToNext()) {
- final String pkg = cursor.getString(TABLE_PERMISSIONS_COL_PACKAGE);
- final String uuid = cursor.getString(TABLE_PERMISSIONS_COL_VOLUME_UUID);
- final String dir = cursor.getString(TABLE_PERMISSIONS_COL_DIRECTORY);
- final boolean granted = cursor.getInt(TABLE_PERMISSIONS_COL_GRANTED) == 1;
- Log.v(MY_TAG, "pkg:" + pkg + " uuid: " + uuid + " dir: " + dir + "> "
- + granted);
+
+ if (!mPackageName.equals(pkg)) {
+ // Sanity check, shouldn't happen
+ Log.w(TAG, "Ignoring " + uuid + "/" + dir + " due to package mismatch: "
+ + "expected " + mPackageName + ", got " + pkg);
+ continue;
+ }
+
+ if (uuid == null) {
+ // Primary storage entry: add right away
+ prefsGroup.addPreference(
+ newPreference(context, dir, providerUri, /* uuid= */ null, dir));
+ } else {
+ // External volume entry: save it for later.
+ if (externalDirectoryUuids == null) {
+ externalDirectoryUuids = new ArraySet<>(1);
+ }
+ externalDirectoryUuids.add(uuid);
}
}
}
+
+ // Add entries from external volumes
+ if (externalDirectoryUuids != null) {
+ if (VERBOSE) {
+ Log.v(TAG, "adding external directories: " + externalDirectoryUuids);
+ }
+
+ // Query StorageManager to get the user-friendly volume names.
+ final StorageManager sm = context.getSystemService(StorageManager.class);
+ final List volumes = sm.getVolumes();
+ if (volumes.isEmpty()) {
+ Log.w(TAG, "StorageManager returned no secondary volumes");
+ return;
+ }
+ final Map volumeNames = new HashMap<>(volumes.size());
+ for (VolumeInfo volume : volumes) {
+ final String uuid = volume.getFsUuid();
+ if (uuid == null) continue; // Primary storage; not used.
+
+ String name = sm.getBestVolumeDescription(volume);
+ if (name == null) {
+ Log.w(TAG, "No description for " + volume + "; using uuid instead: " + uuid);
+ name = uuid;
+ }
+ volumeNames.put(uuid, name);
+ }
+ if (VERBOSE) {
+ Log.v(TAG, "UUID -> name mapping: " + volumeNames);
+ }
+
+ externalDirectoryUuids.forEach((uuid) ->{
+ final String name = volumeNames.get(uuid);
+ // TODO(b/72055774): add separator
+ prefsGroup.addPreference(
+ newPreference(context, name, providerUri, uuid, /* dir= */ null));
+ });
+ }
+ return;
+ }
+
+
+ private SwitchPreference newPreference(Context context, String title, Uri providerUri,
+ String uuid, String dir) {
+ final SwitchPreference pref = new SwitchPreference(context);
+ pref.setKey(String.format("%s:%s", uuid, dir));
+ pref.setTitle(title);
+ pref.setChecked(false);
+ pref.setOnPreferenceChangeListener((unused, value) -> {
+ resetDoNotAskAgain(context, value, providerUri, uuid, dir);
+ return true;
+ });
+ return pref;
+ }
+
+ private void resetDoNotAskAgain(Context context, Object value, Uri providerUri,
+ @Nullable String uuid, @Nullable String directory) {
+ if (!Boolean.class.isInstance(value)) {
+ // Sanity check
+ Log.wtf(TAG, "Invalid value from switch: " + value);
+ return;
+ }
+ final boolean newValue = ((Boolean) value).booleanValue();
+ if (DEBUG) {
+ Log.d(TAG, "Asking " + providerUri + " to update " + uuid + "/" + directory + " to "
+ + newValue);
+ }
+ final ContentValues values = new ContentValues(1);
+ values.put(COL_GRANTED, newValue);
+ final int updated = context.getContentResolver().update(providerUri, values,
+ null, new String[] { mPackageName, uuid, directory });
+ if (DEBUG) {
+ Log.d(TAG, "Updated " + updated + " entries for " + uuid + "/" + directory);
+ }
}
- @Override
- public boolean onPreferenceClick(Preference preference) {
- // TODO(b/63720392): implement or remove listener
- return false;
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- // TODO(b/63720392): implement or remove listener
- return false;
- }
-
@Override
protected boolean refreshUi() {
- // TODO(b/63720392): implement
return true;
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
index 127730ba39a..2862083115d 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
@@ -27,14 +27,19 @@ import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.widget.GearPreference;
+import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HeadsetProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
+
+import android.util.Log;
/**
* Update the bluetooth devices. It gets bluetooth event from {@link LocalBluetoothManager} using
@@ -45,6 +50,7 @@ import java.util.Map;
* whether the {@link CachedBluetoothDevice} is relevant.
*/
public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
+ private static final String TAG = "BluetoothDeviceUpdater";
private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
"persist.bluetooth.showdeviceswithoutnames";
@@ -55,6 +61,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
private final boolean mShowDeviceWithoutNames;
private DashboardFragment mFragment;
+ private Preference.OnPreferenceClickListener mDevicePreferenceClickListener = null;
@VisibleForTesting
final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> {
@@ -73,6 +80,38 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
};
+ private class PreferenceClickListener implements
+ Preference.OnPreferenceClickListener {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ final CachedBluetoothDevice device =
+ ((BluetoothDevicePreference) preference).getBluetoothDevice();
+ if (device == null) {
+ return false;
+ }
+
+ // Set the device as active per profile only if the device supports that profile
+ // TODO: The active device selector location might change in the future
+ Log.i(TAG, "OnPreferenceClickListener: device=" + device);
+ boolean result = false;
+ A2dpProfile a2dpProfile = mLocalManager.getProfileManager().getA2dpProfile();
+ if ((a2dpProfile != null) && device.isConnectedProfile(a2dpProfile)) {
+ if (a2dpProfile.setActiveDevice(device.getDevice())) {
+ Log.i(TAG, "OnPreferenceClickListener: A2DP active device=" + device);
+ result = true;
+ }
+ }
+ HeadsetProfile headsetProfile = mLocalManager.getProfileManager().getHeadsetProfile();
+ if ((headsetProfile != null) && device.isConnectedProfile(headsetProfile)) {
+ if (headsetProfile.setActiveDevice(device.getDevice())) {
+ Log.i(TAG, "OnPreferenceClickListener: Headset active device=" + device);
+ result = true;
+ }
+ }
+ return result;
+ }
+ }
+
public BluetoothDeviceUpdater(DashboardFragment fragment,
DevicePreferenceCallback devicePreferenceCallback) {
this(fragment, devicePreferenceCallback, Utils.getLocalBtManager(fragment.getContext()));
@@ -87,6 +126,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
mPreferenceMap = new HashMap<>();
mLocalManager = localManager;
+ mDevicePreferenceClickListener = new PreferenceClickListener();
}
/**
@@ -141,6 +181,18 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
@Override
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {}
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+ Collection cachedDevices =
+ mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
+ // TODO: The state update of the Cached Bluetooth Devices should be
+ // moved to the device manager: b/72316092
+ for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) {
+ boolean isActive = Objects.equals(cachedBluetoothDevice, activeDevice);
+ cachedBluetoothDevice.setActiveDevice(isActive, bluetoothProfile);
+ }
+ }
+
/**
* Set the context to generate the {@link Preference}, so it could get the correct theme.
*/
@@ -176,6 +228,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
new BluetoothDevicePreference(mPrefContext, cachedDevice,
mShowDeviceWithoutNames);
btPreference.setOnGearClickListener(mDeviceProfilesListener);
+ btPreference.setOnPreferenceClickListener(mDevicePreferenceClickListener);
mPreferenceMap.put(device, btPreference);
mDevicePreferenceCallback.onDeviceAdded(btPreference);
}
diff --git a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
index 662cd701de2..43d25e7b49d 100644
--- a/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothSummaryUpdater.java
@@ -76,6 +76,10 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
}
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+ }
+
@Override
public void register(boolean listening) {
if (mBluetoothAdapter == null) {
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
index 0a90edc1dbf..207a4b0643e 100644
--- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
+++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
@@ -277,6 +277,9 @@ public abstract class DeviceListPreferenceFragment extends
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
+ @Override
+ public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { }
+
/**
* Return the key of the {@link PreferenceGroup} that contains the bluetooth devices
*/
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 0a4b1f2d095..026cc2bd777 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -57,6 +57,7 @@ import com.android.settings.applications.assist.ManageAssist;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
import com.android.settings.bluetooth.BluetoothSettings;
+import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
import com.android.settings.datausage.DataPlanUsageSummary;
@@ -254,7 +255,8 @@ public class SettingsGateway {
LockscreenDashboardFragment.class.getName(),
BluetoothDeviceDetailsFragment.class.getName(),
DataUsageList.class.getName(),
- DirectoryAccessDetails.class.getName()
+ DirectoryAccessDetails.class.getName(),
+ AdvancedConnectedDeviceDashboardFragment.class.getName()
};
public static final String[] SETTINGS_FOR_RESTRICTED = {
diff --git a/src/com/android/settings/datetime/timezone/ZonePicker.java b/src/com/android/settings/datetime/timezone/ZonePicker.java
index eafbaa29bf2..d0d17202595 100644
--- a/src/com/android/settings/datetime/timezone/ZonePicker.java
+++ b/src/com/android/settings/datetime/timezone/ZonePicker.java
@@ -31,6 +31,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
import android.widget.Spinner;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
@@ -56,11 +57,14 @@ public class ZonePicker extends InstrumentedFragment
private List mRegions;
private Map> mZoneInfos;
private List mFixedOffsetTimeZones;
- private TimeZoneAdapter mTimeZoneAdapter;
private String mSelectedTimeZone;
private boolean mSelectByRegion;
private DataLoader mDataLoader;
+ private TimeZoneAdapter mTimeZoneAdapter;
+
private RecyclerView mRecyclerView;
+ private LinearLayout mRegionSpinnerLayout;
+ private Spinner mRegionSpinner;
@Override
public int getMetricsCategory() {
@@ -88,15 +92,17 @@ public class ZonePicker extends InstrumentedFragment
final ArrayAdapter regionAdapter = new ArrayAdapter<>(getContext(),
R.layout.filter_spinner_item, mRegions);
regionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- final Spinner spinner = view.findViewById(R.id.tz_region_spinner);
- spinner.setAdapter(regionAdapter);
- spinner.setOnItemSelectedListener(this);
- setupForCurrentTimeZone(spinner);
+
+ mRegionSpinnerLayout = view.findViewById(R.id.tz_region_spinner_layout);
+ mRegionSpinner = view.findViewById(R.id.tz_region_spinner);
+ mRegionSpinner.setAdapter(regionAdapter);
+ mRegionSpinner.setOnItemSelectedListener(this);
+ setupForCurrentTimeZone();
setHasOptionsMenu(true);
return view;
}
- private void setupForCurrentTimeZone(Spinner spinner) {
+ private void setupForCurrentTimeZone() {
final String localeRegionId = mLocale.getCountry().toUpperCase(Locale.ROOT);
final String currentTimeZone = TimeZone.getDefault().getID();
boolean fixedOffset = currentTimeZone.startsWith("Etc/GMT") ||
@@ -105,12 +111,12 @@ public class ZonePicker extends InstrumentedFragment
for (int regionIndex = 0; regionIndex < mRegions.size(); regionIndex++) {
final RegionInfo region = mRegions.get(regionIndex);
if (localeRegionId.equals(region.getId())) {
- spinner.setSelection(regionIndex);
+ mRegionSpinner.setSelection(regionIndex);
}
if (!fixedOffset) {
for (String timeZoneId: region.getTimeZoneIds()) {
if (TextUtils.equals(timeZoneId, mSelectedTimeZone)) {
- spinner.setSelection(regionIndex);
+ mRegionSpinner.setSelection(regionIndex);
return;
}
}
@@ -179,16 +185,15 @@ public class ZonePicker extends InstrumentedFragment
private void setSelectByRegion(boolean selectByRegion) {
mSelectByRegion = selectByRegion;
- getView().findViewById(R.id.tz_region_spinner_layout).setVisibility(
+ mRegionSpinnerLayout.setVisibility(
mSelectByRegion ? View.VISIBLE : View.GONE);
List tzInfos;
if (selectByRegion) {
- Spinner regionSpinner = getView().findViewById(R.id.tz_region_spinner);
- int selectedRegion = regionSpinner.getSelectedItemPosition();
+ int selectedRegion = mRegionSpinner.getSelectedItemPosition();
if (selectedRegion == -1) {
// Arbitrarily pick the first item if no region was selected above.
selectedRegion = 0;
- regionSpinner.setSelection(selectedRegion);
+ mRegionSpinner.setSelection(selectedRegion);
}
tzInfos = getTimeZoneInfos(mRegions.get(selectedRegion));
} else {
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index 819846adea0..de027a3a199 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -374,7 +374,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
@Override
public void onLimitBackgroundActivity() {
- mBackgroundActivityPreferenceController.setUnchecked(
+ mBackgroundActivityPreferenceController.setRestricted(
findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
}
}
diff --git a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
index 8286774226a..01e41825074 100644
--- a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
@@ -43,12 +43,12 @@ import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
* Controller to control whether an app can run in the background
*/
public class BackgroundActivityPreferenceController extends AbstractPreferenceController
- implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+ implements PreferenceControllerMixin {
private static final String TAG = "BgActivityPrefContr";
- private static final String KEY_BACKGROUND_ACTIVITY = "background_activity";
+ @VisibleForTesting
+ static final String KEY_BACKGROUND_ACTIVITY = "background_activity";
- private final PackageManager mPackageManager;
private final AppOpsManager mAppOpsManager;
private final UserManager mUserManager;
private final int mUid;
@@ -70,7 +70,6 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
int uid, String packageName, PowerWhitelistBackend backend) {
super(context);
mPowerWhitelistBackend = backend;
- mPackageManager = context.getPackageManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mDpm = new DevicePolicyManagerWrapper(
(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE));
@@ -86,11 +85,6 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
final int mode = mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
final boolean whitelisted = mPowerWhitelistBackend.isWhitelisted(mTargetPackage);
- // Set checked or not before we may set it disabled
- if (mode != AppOpsManager.MODE_ERRORED) {
- final boolean checked = whitelisted || mode != AppOpsManager.MODE_IGNORED;
- ((SwitchPreference) preference).setChecked(checked);
- }
if (whitelisted || mode == AppOpsManager.MODE_ERRORED
|| Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mTargetPackage)) {
preference.setEnabled(false);
@@ -109,9 +103,8 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
* Called from the warning dialog, if the user decides to go ahead and disable background
* activity for this package
*/
- public void setUnchecked(Preference preference) {
+ public void setRestricted(Preference preference) {
mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_IGNORED);
- ((SwitchPreference) preference).setChecked(false);
updateSummary(preference);
}
@@ -121,17 +114,21 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
}
@Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final boolean switchOn = (Boolean) newValue;
- if (!switchOn) {
- final WarningDialogFragment dialogFragment = new WarningDialogFragment();
- dialogFragment.setTargetFragment(mFragment, 0);
- dialogFragment.show(mFragment.getFragmentManager(), TAG);
- return false;
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (KEY_BACKGROUND_ACTIVITY.equals(preference.getKey())) {
+ final int mode = mAppOpsManager
+ .checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
+ final boolean restricted = mode == AppOpsManager.MODE_IGNORED;
+ if (!restricted) {
+ showDialog();
+ return false;
+ }
+ mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_ALLOWED);
+ updateSummary(preference);
+ return true;
}
- mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_ALLOWED);
- updateSummary(preference);
- return true;
+
+ return false;
}
@VisibleForTesting
@@ -146,12 +143,19 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
if (mode == AppOpsManager.MODE_ERRORED) {
preference.setSummary(R.string.background_activity_summary_disabled);
} else {
- final boolean checked = mode != AppOpsManager.MODE_IGNORED;
- preference.setSummary(checked ? R.string.background_activity_summary_on
- : R.string.background_activity_summary_off);
+ final boolean restricted = mode == AppOpsManager.MODE_IGNORED;
+ preference.setSummary(restricted ? R.string.restricted_true_label
+ : R.string.restricted_false_label);
}
}
+ @VisibleForTesting
+ void showDialog() {
+ final WarningDialogFragment dialogFragment = new WarningDialogFragment();
+ dialogFragment.setTargetFragment(mFragment, 0 /* requestCode */);
+ dialogFragment.show(mFragment.getFragmentManager(), TAG);
+ }
+
interface WarningConfirmationListener {
void onLimitBackgroundActivity();
}
diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java
index acd81446b57..c4c795b29b1 100644
--- a/src/com/android/settings/fuelgauge/BatteryInfo.java
+++ b/src/com/android/settings/fuelgauge/BatteryInfo.java
@@ -272,7 +272,7 @@ public class BatteryInfo {
void onParsingDone();
}
- private static void parse(BatteryStats stats, BatteryDataParser... parsers) {
+ public static void parse(BatteryStats stats, BatteryDataParser... parsers) {
long startWalltime = 0;
long endWalltime = 0;
long historyStart = 0;
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
index 6af859b4569..a580db1d311 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
@@ -19,9 +19,12 @@ package com.android.settings.fuelgauge.batterytip;
import android.content.Context;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
+import android.text.format.DateUtils;
import android.util.KeyValueListParser;
import android.util.Log;
+import java.time.Duration;
+
/**
* Class to store the policy for battery tips, which comes from
* {@link Settings.Global}
@@ -34,6 +37,8 @@ public class BatteryTipPolicy {
private static final String KEY_BATTERY_SAVER_TIP_ENABLED = "battery_saver_tip_enabled";
private static final String KEY_HIGH_USAGE_ENABLED = "high_usage_enabled";
private static final String KEY_HIGH_USAGE_APP_COUNT = "high_usage_app_count";
+ private static final String KEY_HIGH_USAGE_PERIOD_MS = "high_usage_period_ms";
+ private static final String KEY_HIGH_USAGE_BATTERY_DRAINING = "high_usage_battery_draining";
private static final String KEY_APP_RESTRICTION_ENABLED = "app_restriction_enabled";
private static final String KEY_REDUCED_BATTERY_ENABLED = "reduced_battery_enabled";
private static final String KEY_REDUCED_BATTERY_PERCENT = "reduced_battery_percent";
@@ -80,6 +85,24 @@ public class BatteryTipPolicy {
*/
public final int highUsageAppCount;
+ /**
+ * The size of the window(milliseconds) for checking if the device is being heavily used
+ *
+ * @see Settings.Global#BATTERY_TIP_CONSTANTS
+ * @see #KEY_HIGH_USAGE_PERIOD_MS
+ */
+ public final long highUsagePeriodMs;
+
+ /**
+ * The battery draining threshold to detect whether device is heavily used.
+ * If battery drains more than {@link #highUsageBatteryDraining} in last {@link
+ * #highUsagePeriodMs}, treat device as heavily used.
+ *
+ * @see Settings.Global#BATTERY_TIP_CONSTANTS
+ * @see #KEY_HIGH_USAGE_BATTERY_DRAINING
+ */
+ public final int highUsageBatteryDraining;
+
/**
* {@code true} if app restriction tip is enabled
*
@@ -143,6 +166,9 @@ public class BatteryTipPolicy {
batterySaverTipEnabled = mParser.getBoolean(KEY_BATTERY_SAVER_TIP_ENABLED, true);
highUsageEnabled = mParser.getBoolean(KEY_HIGH_USAGE_ENABLED, true);
highUsageAppCount = mParser.getInt(KEY_HIGH_USAGE_APP_COUNT, 3);
+ highUsagePeriodMs = mParser.getLong(KEY_HIGH_USAGE_PERIOD_MS,
+ Duration.ofHours(2).toMillis());
+ highUsageBatteryDraining = mParser.getInt(KEY_HIGH_USAGE_BATTERY_DRAINING, 25);
appRestrictionEnabled = mParser.getBoolean(KEY_APP_RESTRICTION_ENABLED, true);
reducedBatteryEnabled = mParser.getBoolean(KEY_REDUCED_BATTERY_ENABLED, false);
reducedBatteryPercent = mParser.getInt(KEY_REDUCED_BATTERY_PERCENT, 50);
diff --git a/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParser.java b/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParser.java
new file mode 100644
index 00000000000..cc5aed679dd
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParser.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 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.batterytip;
+
+import android.os.BatteryStats;
+
+import com.android.settings.fuelgauge.BatteryInfo;
+
+/**
+ * DataParser used to go through battery data and detect whether battery is
+ * heavily used.
+ */
+public class HighUsageDataParser implements BatteryInfo.BatteryDataParser {
+ /**
+ * time period to check the battery usage
+ */
+ private final long mTimePeriodMs;
+ /**
+ * treat device as heavily used if battery usage is more than {@code threshold}. 1 means 1%
+ * battery usage.
+ */
+ private int mThreshold;
+ private long mEndTimeMs;
+ private byte mEndBatteryLevel;
+ private byte mLastPeriodBatteryLevel;
+ private int mBatteryDrain;
+
+ public HighUsageDataParser(long timePeriodMs, int threshold) {
+ mTimePeriodMs = timePeriodMs;
+ mThreshold = threshold;
+ }
+
+ @Override
+ public void onParsingStarted(long startTime, long endTime) {
+ mEndTimeMs = endTime;
+ }
+
+ @Override
+ public void onDataPoint(long time, BatteryStats.HistoryItem record) {
+ if (record.currentTime <= mEndTimeMs - mTimePeriodMs) {
+ // Since onDataPoint is invoked sorted by time, so we could use this way to get the
+ // closet battery level 'mTimePeriodMs' time ago.
+ mLastPeriodBatteryLevel = record.batteryLevel;
+ }
+ mEndBatteryLevel = record.batteryLevel;
+ }
+
+ @Override
+ public void onDataGap() {
+ // do nothing
+ }
+
+ @Override
+ public void onParsingDone() {
+ mBatteryDrain = mLastPeriodBatteryLevel - mEndBatteryLevel;
+ }
+
+ /**
+ * Return {@code true} if the battery drain in {@link #mTimePeriodMs} is too much
+ */
+ public boolean isDeviceHeavilyUsed() {
+ return mBatteryDrain > mThreshold;
+ }
+}
+
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
index 3c696671e95..ed3fa04c1e0 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
@@ -19,13 +19,14 @@ package com.android.settings.fuelgauge.batterytip.detectors;
import android.content.Context;
import android.os.BatteryStats;
import android.support.annotation.VisibleForTesting;
-import android.text.format.DateUtils;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.fuelgauge.BatteryInfo;
+import com.android.settings.fuelgauge.batterytip.HighUsageDataParser;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
@@ -43,6 +44,8 @@ public class HighUsageDetector implements BatteryTipDetector {
private List mHighUsageAppList;
private Context mContext;
@VisibleForTesting
+ HighUsageDataParser mDataParser;
+ @VisibleForTesting
BatteryUtils mBatteryUtils;
public HighUsageDetector(Context context, BatteryTipPolicy policy,
@@ -52,32 +55,42 @@ public class HighUsageDetector implements BatteryTipDetector {
mBatteryStatsHelper = batteryStatsHelper;
mHighUsageAppList = new ArrayList<>();
mBatteryUtils = BatteryUtils.getInstance(context);
+ mDataParser = new HighUsageDataParser(mPolicy.highUsagePeriodMs,
+ mPolicy.highUsageBatteryDraining);
}
@Override
public BatteryTip detect() {
final long screenUsageTimeMs = mBatteryUtils.calculateScreenUsageTime(mBatteryStatsHelper);
- //TODO(b/70570352): Change it to detect whether battery drops 25% in last 2 hours
- if (mPolicy.highUsageEnabled && screenUsageTimeMs > DateUtils.HOUR_IN_MILLIS) {
- final List batterySippers = mBatteryStatsHelper.getUsageList();
- for (int i = 0, size = batterySippers.size(); i < size; i++) {
- final BatterySipper batterySipper = batterySippers.get(i);
- if (!mBatteryUtils.shouldHideSipper(batterySipper)) {
- final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
- BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
- BatteryStats.STATS_SINCE_CHARGED);
- mHighUsageAppList.add(new AppInfo.Builder()
- .setPackageName(mBatteryUtils.getPackageName(batterySipper.getUid()))
- .setScreenOnTimeMs(foregroundTimeMs)
- .build());
+ if (mPolicy.highUsageEnabled) {
+ parseBatteryData();
+ if (mDataParser.isDeviceHeavilyUsed()) {
+ final List batterySippers = mBatteryStatsHelper.getUsageList();
+ for (int i = 0, size = batterySippers.size(); i < size; i++) {
+ final BatterySipper batterySipper = batterySippers.get(i);
+ if (!mBatteryUtils.shouldHideSipper(batterySipper)) {
+ final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
+ BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
+ BatteryStats.STATS_SINCE_CHARGED);
+ mHighUsageAppList.add(new AppInfo.Builder()
+ .setPackageName(
+ mBatteryUtils.getPackageName(batterySipper.getUid()))
+ .setScreenOnTimeMs(foregroundTimeMs)
+ .build());
+ }
}
- }
- mHighUsageAppList = mHighUsageAppList.subList(0,
- Math.min(mPolicy.highUsageAppCount, mHighUsageAppList.size()));
- Collections.sort(mHighUsageAppList, Collections.reverseOrder());
+ mHighUsageAppList = mHighUsageAppList.subList(0,
+ Math.min(mPolicy.highUsageAppCount, mHighUsageAppList.size()));
+ Collections.sort(mHighUsageAppList, Collections.reverseOrder());
+ }
}
return new HighUsageTip(screenUsageTimeMs, mHighUsageAppList);
}
+
+ @VisibleForTesting
+ void parseBatteryData() {
+ BatteryInfo.parse(mBatteryStatsHelper.getStats(), mDataParser);
+ }
}
diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
index 70ee20de03d..82ffc38c967 100644
--- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
+++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
@@ -280,7 +280,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
.setButton1Text(R.string.forget)
.setButton1Positive(false)
.setButton1OnClickListener(view -> forgetNetwork())
- .setButton2Text(R.string.support_sign_in_button_text)
+ .setButton2Text(R.string.wifi_sign_in_button_text)
.setButton2Positive(true)
.setButton2OnClickListener(view -> signIntoNetwork());
diff --git a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
index fa10607b342..11f1f5977d6 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
@@ -24,6 +24,7 @@ import android.net.ConnectivityManager;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.PreferenceScreen;
import android.text.BidiFormatter;
@@ -51,7 +52,11 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
private final WifiManager mWifiManager;
private final Lifecycle mLifecycle;
private WifiTetherSwitchBarController mSwitchController;
- private MasterSwitchPreference mPreference;
+ private int mSoftApState;
+ @VisibleForTesting
+ MasterSwitchPreference mPreference;
+ @VisibleForTesting
+ WifiTetherSoftApManager mWifiTetherSoftApManager;
static {
WIFI_TETHER_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
@@ -60,6 +65,12 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
}
public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) {
+ this(context, lifecycle, true /* initSoftApManager */);
+ }
+
+ @VisibleForTesting
+ WifiTetherPreferenceController(Context context, Lifecycle lifecycle,
+ boolean initSoftApManager) {
super(context);
mConnectivityManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -69,6 +80,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
if (lifecycle != null) {
lifecycle.addObserver(this);
}
+ if (initSoftApManager) {
+ initWifiTetherSoftApManager();
+ }
}
@Override
@@ -101,6 +115,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
if (mPreference != null) {
mContext.registerReceiver(mReceiver, WIFI_TETHER_INTENT_FILTER);
clearSummaryForAirplaneMode();
+ if (mWifiTetherSoftApManager != null) {
+ mWifiTetherSoftApManager.registerSoftApCallback();
+ }
}
}
@@ -108,9 +125,36 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
public void onStop() {
if (mPreference != null) {
mContext.unregisterReceiver(mReceiver);
+ if (mWifiTetherSoftApManager != null) {
+ mWifiTetherSoftApManager.unRegisterSoftApCallback();
+ }
}
}
+ @VisibleForTesting
+ void initWifiTetherSoftApManager() {
+ // This manager only handles the number of connected devices, other parts are handled by
+ // normal BroadcastReceiver in this controller
+ mWifiTetherSoftApManager = new WifiTetherSoftApManager(mWifiManager,
+ new WifiTetherSoftApManager.WifiTetherSoftApCallback() {
+ @Override
+ public void onStateChanged(int state, int failureReason) {
+ mSoftApState = state;
+ }
+
+ @Override
+ public void onNumClientsChanged(int numClients) {
+ if (mPreference != null
+ && mSoftApState == WifiManager.WIFI_AP_STATE_ENABLED) {
+ // Only show the number of clients when state is on
+ mPreference.setSummary(mContext.getResources().getQuantityString(
+ R.plurals.wifi_tether_connected_summary, numClients,
+ numClients));
+ }
+ }
+ });
+ }
+
//
// Everything below is copied from WifiApEnabler
//
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSoftApManager.java b/src/com/android/settings/wifi/tether/WifiTetherSoftApManager.java
new file mode 100644
index 00000000000..77a44b08ee8
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherSoftApManager.java
@@ -0,0 +1,47 @@
+package com.android.settings.wifi.tether;
+
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+
+/**
+ * Wrapper for {@link android.net.wifi.WifiManager.SoftApCallback} to pass the robo test
+ */
+public class WifiTetherSoftApManager {
+
+ private WifiManager mWifiManager;
+ private WifiTetherSoftApCallback mWifiTetherSoftApCallback;
+
+ private WifiManager.SoftApCallback mSoftApCallback = new WifiManager.SoftApCallback() {
+ @Override
+ public void onStateChanged(int state, int failureReason) {
+ mWifiTetherSoftApCallback.onStateChanged(state, failureReason);
+ }
+
+ @Override
+ public void onNumClientsChanged(int numClients) {
+ mWifiTetherSoftApCallback.onNumClientsChanged(numClients);
+ }
+ };
+ private Handler mHandler;
+
+ WifiTetherSoftApManager(WifiManager wifiManager,
+ WifiTetherSoftApCallback wifiTetherSoftApCallback) {
+ mWifiManager = wifiManager;
+ mWifiTetherSoftApCallback = wifiTetherSoftApCallback;
+ mHandler = new Handler();
+ }
+
+ public void registerSoftApCallback() {
+ mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
+ }
+
+ public void unRegisterSoftApCallback() {
+ mWifiManager.unregisterSoftApCallback(mSoftApCallback);
+ }
+
+ public interface WifiTetherSoftApCallback {
+ void onStateChanged(int state, int failureReason);
+
+ void onNumClientsChanged(int numClients);
+ }
+}
diff --git a/src/com/android/settings/wrapper/RecoverySystemWrapper.java b/src/com/android/settings/wrapper/RecoverySystemWrapper.java
index 8a369695f2c..c7ce2449433 100644
--- a/src/com/android/settings/wrapper/RecoverySystemWrapper.java
+++ b/src/com/android/settings/wrapper/RecoverySystemWrapper.java
@@ -29,11 +29,10 @@ public class RecoverySystemWrapper {
/**
* Returns whether wipe Euicc data successfully or not.
*
- * @param isWipeEuicc whether we want to wipe Euicc data or not
* @param packageName the package name of the caller app.
*/
public boolean wipeEuiccData(
- Context context, final boolean isWipeEuicc, final String packageName) {
- return RecoverySystem.wipeEuiccData(context, isWipeEuicc, packageName);
+ Context context, final String packageName) {
+ return RecoverySystem.wipeEuiccData(context, packageName);
}
}
diff --git a/tests/robotests/src/com/android/settings/MasterClearTest.java b/tests/robotests/src/com/android/settings/MasterClearTest.java
index 361bc8f319d..ac753c109f2 100644
--- a/tests/robotests/src/com/android/settings/MasterClearTest.java
+++ b/tests/robotests/src/com/android/settings/MasterClearTest.java
@@ -19,20 +19,29 @@ package com.android.settings;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
+import android.accounts.Account;
+import android.accounts.AccountManager;
import android.app.Activity;
import android.app.Fragment;
import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.ScrollView;
@@ -46,6 +55,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowAccountManager;
import org.robolectric.shadows.ShadowActivity;
@RunWith(SettingsRobolectricTestRunner.class)
@@ -55,6 +65,9 @@ import org.robolectric.shadows.ShadowActivity;
shadows = {ShadowUtils.class}
)
public class MasterClearTest {
+ private static final String TEST_ACCOUNT_TYPE = "android.test.account.type";
+ private static final String TEST_CONFIRMATION_PACKAGE = "android.test.confirmation.pkg";
+ private static final String TEST_ACCOUNT_NAME = "test@example.com";
@Mock
private MasterClear mMasterClear;
@@ -62,7 +75,18 @@ public class MasterClearTest {
private ScrollView mScrollView;
@Mock
private LinearLayout mLinearLayout;
+
+ @Mock
+ private PackageManager mPackageManager;
+
+ @Mock
+ private AccountManager mAccountManager;
+
+ @Mock
+ private Activity mMockActivity;
+
private ShadowActivity mShadowActivity;
+ private ShadowAccountManager mShadowAccountManager;
private Activity mActivity;
private View mContentView;
@@ -86,6 +110,7 @@ public class MasterClearTest {
mMasterClear = spy(new MasterClear());
mActivity = Robolectric.setupActivity(Activity.class);
mShadowActivity = shadowOf(mActivity);
+ // mShadowAccountManager = shadowOf(AccountManager.get(mActivity));
mContentView = LayoutInflater.from(mActivity).inflate(R.layout.master_clear, null);
// Make scrollView only have one child
@@ -93,6 +118,32 @@ public class MasterClearTest {
when(mScrollView.getChildCount()).thenReturn(1);
}
+ @Test
+ public void testShowFinalConfirmation_eraseEsimChecked() {
+ ActivityForTest testActivity = new ActivityForTest();
+ when(mMasterClear.getActivity()).thenReturn(testActivity);
+
+ mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim);
+ mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external);
+ mMasterClear.mEsimStorage.setChecked(true);
+ mMasterClear.showFinalConfirmation();
+ assertThat(testActivity.getArgs().getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false))
+ .isTrue();
+ }
+
+ @Test
+ public void testShowFinalConfirmation_eraseEsimUnchecked() {
+ ActivityForTest testActivity = new ActivityForTest();
+ when(mMasterClear.getActivity()).thenReturn(testActivity);
+
+ mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim);
+ mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external);
+ mMasterClear.mEsimStorage.setChecked(false);
+ mMasterClear.showFinalConfirmation();
+ assertThat(testActivity.getArgs().getBoolean(MasterClear.ERASE_ESIMS_EXTRA, true))
+ .isFalse();
+ }
+
@Test
public void testShowWipeEuicc_euiccDisabled() {
prepareEuiccState(
@@ -162,9 +213,61 @@ public class MasterClearTest {
@Test
public void testTryShowAccountConfirmation_unsupported() {
- doReturn(mActivity).when(mMasterClear).getActivity();
- /* Using the default resources, account confirmation shouldn't trigger */
- assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
+ when(mMasterClear.getActivity()).thenReturn(mActivity);
+ /* Using the default resources, account confirmation shouldn't trigger */
+ assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
+ }
+
+ @Test
+ public void testTryShowAccountConfirmation_no_relevant_accounts() {
+ when(mMasterClear.getActivity()).thenReturn(mMockActivity);
+ when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
+ when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
+
+ Account[] accounts = new Account[0];
+ when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
+ when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
+ assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
+ }
+
+ @Test
+ public void testTryShowAccountConfirmation_unresolved() {
+ when(mMasterClear.getActivity()).thenReturn(mMockActivity);
+ when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
+ when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
+ Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
+ when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
+ when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
+ // The package manager should not resolve the confirmation intent targeting the non-existent
+ // confirmation package.
+ when(mMockActivity.getPackageManager()).thenReturn(mPackageManager);
+ assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
+ }
+
+ @Test
+ public void testTryShowAccountConfirmation_ok() {
+ when(mMasterClear.getActivity()).thenReturn(mMockActivity);
+ // Only try to show account confirmation if the appropriate resource overlays are available.
+ when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
+ when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
+ // Add accounts to trigger the search for a resolving intent.
+ Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
+ when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
+ when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
+ // The package manager should not resolve the confirmation intent targeting the non-existent
+ // confirmation package.
+ when(mMockActivity.getPackageManager()).thenReturn(mPackageManager);
+
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.packageName = TEST_CONFIRMATION_PACKAGE;
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = activityInfo;
+ when(mPackageManager.resolveActivity(any(), eq(0))).thenReturn(resolveInfo);
+
+ // Finally mock out the startActivityForResultCall
+ doNothing().when(mMockActivity).startActivityForResult(any(), eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST));
+
+ assertThat(mMasterClear.tryShowAccountConfirmation()).isTrue();
}
private void initScrollView(int height, int scrollY, int childBottom) {
diff --git a/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java b/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java
index 354cacf757c..f4b5f4ca1b8 100644
--- a/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java
+++ b/tests/robotests/src/com/android/settings/ResetNetworkConfirmTest.java
@@ -66,7 +66,7 @@ public class ResetNetworkConfirmTest {
public void testResetNetworkData_resetEsim() {
mResetNetworkConfirm.mEraseEsim = true;
doReturn(true)
- .when(mRecoverySystem).wipeEuiccData(any(Context.class), anyBoolean(), anyString());
+ .when(mRecoverySystem).wipeEuiccData(any(Context.class), anyString());
mResetNetworkConfirm.esimFactoryReset(mActivity, "" /* packageName */);
try {
@@ -77,7 +77,7 @@ public class ResetNetworkConfirmTest {
}
Assert.assertNotNull(mResetNetworkConfirm.mEraseEsimTask);
- verify(mRecoverySystem).wipeEuiccData(any(Context.class), anyBoolean(), anyString());
+ verify(mRecoverySystem).wipeEuiccData(any(Context.class), anyString());
}
@Test
@@ -88,6 +88,6 @@ public class ResetNetworkConfirmTest {
Assert.assertNull(mResetNetworkConfirm.mEraseEsimTask);
verify(mRecoverySystem, never())
- .wipeEuiccData(any(Context.class), anyBoolean(), anyString());
+ .wipeEuiccData(any(Context.class), anyString());
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
index 0c9e394ffdd..30fdccb23ec 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
@@ -22,6 +22,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -38,6 +39,7 @@ import android.content.pm.PackageManager;
import android.os.Build;
import android.os.UserManager;
import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
import android.widget.Button;
import com.android.settings.R;
@@ -92,12 +94,12 @@ public class BackgroundActivityPreferenceControllerTest {
private DevicePolicyManager mDevicePolicyManager;
@Mock
private DevicePolicyManagerWrapper mDevicePolicyManagerWrapper;
- @Mock
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private AdvancedPowerUsageDetail mFragment;
@Mock
private PowerWhitelistBackend mPowerWhitelistBackend;
private BackgroundActivityPreferenceController mController;
- private SwitchPreference mPreference;
+ private Preference mPreference;
private Context mShadowContext;
private BatteryUtils mBatteryUtils;
@@ -125,7 +127,8 @@ public class BackgroundActivityPreferenceControllerTest {
mBatteryUtils = spy(new BatteryUtils(mShadowContext));
doNothing().when(mBatteryUtils).setForceAppStandby(anyInt(), anyString(), anyInt());
- mPreference = new SwitchPreference(mShadowContext);
+ mPreference = new Preference(mShadowContext);
+ mPreference.setKey(BackgroundActivityPreferenceController.KEY_BACKGROUND_ACTIVITY);
mController = spy(new BackgroundActivityPreferenceController(
mContext, mFragment, UID_LOW_SDK, LOW_SDK_PACKAGE, mPowerWhitelistBackend));
mController.mDpm = mDevicePolicyManagerWrapper;
@@ -133,49 +136,33 @@ public class BackgroundActivityPreferenceControllerTest {
}
@Test
- public void testOnPreferenceChange_TurnOnCheck_MethodInvoked() {
- mController.onPreferenceChange(mPreference, true);
+ public void testHandlePreferenceTreeClick_restrictApp_showDialog() {
+ doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).checkOpNoThrow(anyInt(),
+ anyInt(), anyString());
+
+ mController.handlePreferenceTreeClick(mPreference);
+
+ verify(mController).showDialog();
+ }
+
+ @Test
+ public void testHandlePreferenceTreeClick_unRestrictApp_setModeAllowed() {
+ doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager).checkOpNoThrow(anyInt(),
+ anyInt(), anyString());
+
+ mController.handlePreferenceTreeClick(mPreference);
verify(mBatteryUtils).setForceAppStandby(UID_LOW_SDK, LOW_SDK_PACKAGE,
AppOpsManager.MODE_ALLOWED);
- assertThat(mPreference.getSummary())
- .isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on));
}
@Test
- public void testOnPreferenceChange_TurnOnCheckHighSDK_MethodInvoked() {
- mController = new BackgroundActivityPreferenceController(mContext, mFragment, UID_HIGH_SDK,
- HIGH_SDK_PACKAGE, mPowerWhitelistBackend);
- mController.mBatteryUtils = mBatteryUtils;
-
- mController.onPreferenceChange(mPreference, true);
-
- verify(mBatteryUtils).setForceAppStandby(UID_HIGH_SDK, HIGH_SDK_PACKAGE,
- AppOpsManager.MODE_ALLOWED);
- assertThat(mPreference.getSummary())
- .isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on));
- }
-
- @Test
- public void testUpdateState_CheckOn_SetCheckedTrue() {
+ public void testUpdateState_noError_setEnabled() {
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_ALLOWED);
mController.updateState(mPreference);
- assertThat(mPreference.isChecked()).isTrue();
- assertThat(mPreference.isEnabled()).isTrue();
- verify(mController).updateSummary(mPreference);
- }
-
- @Test
- public void testUpdateState_CheckOff_SetCheckedFalse() {
- when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
- LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isChecked()).isFalse();
assertThat(mPreference.isEnabled()).isTrue();
verify(mController).updateSummary(mPreference);
}
@@ -184,7 +171,6 @@ public class BackgroundActivityPreferenceControllerTest {
public void testUpdateState_whitelisted() {
when(mPowerWhitelistBackend.isWhitelisted(LOW_SDK_PACKAGE)).thenReturn(true);
mController.updateState(mPreference);
- assertThat(mPreference.isChecked()).isTrue();
assertThat(mPreference.isEnabled()).isFalse();
assertThat(mPreference.getSummary()).isEqualTo(
mShadowContext.getText(R.string.background_activity_summary_whitelisted));
@@ -202,27 +188,23 @@ public class BackgroundActivityPreferenceControllerTest {
}
@Test
- public void testUpdateSummary_modeDefault_showSummaryOn() {
+ public void testUpdateSummary_modeDefault_showNotRestricted() {
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_DEFAULT);
- final CharSequence expectedSummary = mShadowContext.getText(
- R.string.background_activity_summary_on);
mController.updateSummary(mPreference);
- assertThat(mPreference.getSummary()).isEqualTo(expectedSummary);
+ assertThat(mPreference.getSummary()).isEqualTo("No");
}
@Test
- public void testUpdateSummary_modeIgnored_showSummaryOff() {
+ public void testUpdateSummary_modeIgnored_showRestricted() {
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED);
- final CharSequence expectedSummary = mShadowContext.getText(
- R.string.background_activity_summary_off);
mController.updateSummary(mPreference);
- assertThat(mPreference.getSummary()).isEqualTo(expectedSummary);
+ assertThat(mPreference.getSummary()).isEqualTo("Yes");
}
@Test
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 53c9766ad55..83b32258009 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
@@ -37,6 +37,7 @@ import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
@@ -54,7 +55,7 @@ public class BatteryTipLoaderTest {
BatteryTip.TipType.BATTERY_SAVER,
BatteryTip.TipType.LOW_BATTERY,
BatteryTip.TipType.SUMMARY};
- @Mock
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private BatteryStatsHelper mBatteryStatsHelper;
@Mock
private PowerManager mPowerManager;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
index bb9a37bd50c..78c86f8d3e6 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.spy;
import android.content.Context;
import android.provider.Settings;
+import android.text.format.DateUtils;
import com.android.settings.TestConfig;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -42,6 +43,8 @@ public class BatteryTipPolicyTest {
+ ",battery_saver_tip_enabled=false"
+ ",high_usage_enabled=true"
+ ",high_usage_app_count=5"
+ + ",high_usage_period_ms=2000"
+ + ",high_usage_battery_draining=30"
+ ",app_restriction_enabled=true"
+ ",reduced_battery_enabled=true"
+ ",reduced_battery_percent=30"
@@ -66,6 +69,8 @@ public class BatteryTipPolicyTest {
assertThat(batteryTipPolicy.batterySaverTipEnabled).isFalse();
assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(5);
+ assertThat(batteryTipPolicy.highUsagePeriodMs).isEqualTo(2000);
+ assertThat(batteryTipPolicy.highUsageBatteryDraining).isEqualTo(30);
assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
assertThat(batteryTipPolicy.reducedBatteryEnabled).isTrue();
assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(30);
@@ -85,6 +90,8 @@ public class BatteryTipPolicyTest {
assertThat(batteryTipPolicy.batterySaverTipEnabled).isTrue();
assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(3);
+ assertThat(batteryTipPolicy.highUsagePeriodMs).isEqualTo(2 * DateUtils.HOUR_IN_MILLIS);
+ assertThat(batteryTipPolicy.highUsageBatteryDraining).isEqualTo(25);
assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
assertThat(batteryTipPolicy.reducedBatteryEnabled).isFalse();
assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(50);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParserTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParserTest.java
new file mode 100644
index 00000000000..5bdae0ccc2d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/HighUsageDataParserTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 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.batterytip;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryStats;
+import android.text.format.DateUtils;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.time.Duration;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class HighUsageDataParserTest {
+ private static final long PERIOD_ONE_MINUTE_MS = Duration.ofMinutes(1).toMillis();
+ private static final long END_TIME_MS = 2 * PERIOD_ONE_MINUTE_MS;
+ private static final int THRESHOLD_LOW = 10;
+ private static final int THRESHOLD_HIGH = 20;
+ private HighUsageDataParser mDataParser;
+ private BatteryStats.HistoryItem mFirstItem;
+ private BatteryStats.HistoryItem mSecondItem;
+ private BatteryStats.HistoryItem mThirdItem;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mFirstItem = new BatteryStats.HistoryItem();
+ mFirstItem.batteryLevel = 100;
+ mFirstItem.currentTime = 0;
+ mSecondItem = new BatteryStats.HistoryItem();
+ mSecondItem.batteryLevel = 95;
+ mSecondItem.currentTime = PERIOD_ONE_MINUTE_MS;
+ mThirdItem = new BatteryStats.HistoryItem();
+ mThirdItem.batteryLevel = 80;
+ mThirdItem.currentTime = END_TIME_MS;
+ }
+
+ @Test
+ public void testDataParser_thresholdLow_isHeavilyUsed() {
+ mDataParser = new HighUsageDataParser(PERIOD_ONE_MINUTE_MS, THRESHOLD_LOW);
+ parseData();
+
+ assertThat(mDataParser.isDeviceHeavilyUsed()).isTrue();
+ }
+
+ @Test
+ public void testDataParser_thresholdHigh_notHeavilyUsed() {
+ mDataParser = new HighUsageDataParser(PERIOD_ONE_MINUTE_MS, THRESHOLD_HIGH);
+ parseData();
+
+ assertThat(mDataParser.isDeviceHeavilyUsed()).isFalse();
+ }
+
+ private void parseData() {
+ mDataParser.onParsingStarted(0, END_TIME_MS);
+ mDataParser.onDataPoint(0, mFirstItem);
+ mDataParser.onDataPoint(PERIOD_ONE_MINUTE_MS, mSecondItem);
+ mDataParser.onDataPoint(END_TIME_MS, mThirdItem);
+
+ mDataParser.onParsingDone();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java
index 2a719916fb0..8df7c56d9ef 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java
@@ -18,6 +18,8 @@ package com.android.settings.fuelgauge.batterytip.detectors;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -31,6 +33,7 @@ import com.android.settings.TestConfig;
import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.fuelgauge.batterytip.HighUsageDataParser;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
@@ -55,6 +58,8 @@ public class HighUsageDetectorTest {
private BatteryUtils mBatteryUtils;
@Mock
private BatterySipper mBatterySipper;
+ @Mock
+ private HighUsageDataParser mDataParser;
private BatteryTipPolicy mPolicy;
private HighUsageDetector mHighUsageDetector;
@@ -66,8 +71,10 @@ public class HighUsageDetectorTest {
mContext = RuntimeEnvironment.application;
mPolicy = spy(new BatteryTipPolicy(mContext));
- mHighUsageDetector = new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper);
+ mHighUsageDetector = spy(new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper));
mHighUsageDetector.mBatteryUtils = mBatteryUtils;
+ mHighUsageDetector.mDataParser = mDataParser;
+ doNothing().when(mHighUsageDetector).parseBatteryData();
mUsageList = new ArrayList<>();
mUsageList.add(mBatterySipper);
@@ -82,8 +89,7 @@ public class HighUsageDetectorTest {
@Test
public void testDetect_containsHighUsageApp_tipVisible() {
- doReturn(2 * DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).calculateScreenUsageTime(
- mBatteryStatsHelper);
+ doReturn(true).when(mDataParser).isDeviceHeavilyUsed();
doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList();
doReturn(DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).getProcessTimeMs(
BatteryUtils.StatusType.FOREGROUND, mBatterySipper.uidObj,
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
index 00d9585e4f2..dca69748a95 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
@@ -18,11 +18,15 @@ package com.android.settings.wifi.tether;
import static android.arch.lifecycle.Lifecycle.Event.ON_START;
import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
+
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -65,6 +69,7 @@ import java.util.ArrayList;
shadows = {
WifiTetherPreferenceControllerTest.ShadowWifiTetherSettings.class,
WifiTetherPreferenceControllerTest.ShadowWifiTetherSwitchBarController.class,
+ WifiTetherPreferenceControllerTest.ShadowWifiTetherSoftApManager.class
})
public class WifiTetherPreferenceControllerTest {
@@ -94,8 +99,9 @@ public class WifiTetherPreferenceControllerTest {
when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
when(mScreen.findPreference(anyString())).thenReturn(mPreference);
- when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[] {"1", "2"});
- mController = new WifiTetherPreferenceController(mContext, mLifecycle);
+ when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
+ mController = new WifiTetherPreferenceController(mContext, mLifecycle,
+ false /* initSoftApManager */);
}
@After
@@ -105,8 +111,9 @@ public class WifiTetherPreferenceControllerTest {
@Test
public void isAvailable_noTetherRegex_shouldReturnFalse() {
- when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[] {});
- mController = new WifiTetherPreferenceController(mContext, mLifecycle);
+ when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{});
+ mController = new WifiTetherPreferenceController(mContext, mLifecycle,
+ false /* initSoftApManager */);
assertThat(mController.isAvailable()).isFalse();
}
@@ -244,6 +251,19 @@ public class WifiTetherPreferenceControllerTest {
}
}
+ @Implements(WifiTetherSoftApManager.class)
+ public static final class ShadowWifiTetherSoftApManager {
+ @Implementation
+ public void registerSoftApCallback() {
+ // do nothing
+ }
+
+ @Implementation
+ public void unRegisterSoftApCallback() {
+ // do nothing
+ }
+ }
+
@Implements(WifiTetherSwitchBarController.class)
public static final class ShadowWifiTetherSwitchBarController {