Merge "Add Settings support for Bluetooth Multi-A2DP and Multi-HFP"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9e4dc39..5a4db20 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3311,6 +3311,25 @@
</intent-filter>
</activity>
+ <activity android:name="Settings$AdvancedConnectedDeviceActivity"
+ android:label="@string/connected_device_connections_title"
+ android:taskAffinity="com.android.settings"
+ android:parentActivityName="Settings$ConnectedDeviceDashboardActivity"
+ android:enabled="false">
+ <intent-filter android:priority="1">
+ <action android:name="com.android.settings.ADVANCED_CONNECTED_DEVICE_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment" />
+ <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+ android:value="true" />
+ </activity>
+
<provider android:name=".slices.SettingsSliceProvider"
android:authorities="com.android.settings.slices"
android:exported="true">
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index 3cc722b..dd7ef80 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -76,7 +76,7 @@
private static final String TAG = "MasterClear";
private static final int KEYGUARD_REQUEST = 55;
- private static final int CREDENTIAL_CONFIRM_REQUEST = 56;
+ @VisibleForTesting static final int CREDENTIAL_CONFIRM_REQUEST = 56;
static final String ERASE_EXTERNAL_EXTRA = "erase_sd";
static final String ERASE_ESIMS_EXTRA = "erase_esim";
@@ -157,9 +157,12 @@
.setAction("android.accounts.action.PRE_FACTORY_RESET");
// Check to make sure that the intent is supported.
final PackageManager pm = context.getPackageManager();
- final List<ResolveInfo> 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;
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 5e815bc..d13a62d 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -178,5 +178,10 @@
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/Utils.java b/src/com/android/settings/Utils.java
index 1c674b6..f45ac5e 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -515,7 +515,8 @@
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/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 0a4b1f2..026cc2b 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.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 @@
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 eafbaa2..d0d1720 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.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 @@
private List<RegionInfo> mRegions;
private Map<String, List<TimeZoneInfo>> mZoneInfos;
private List<TimeZoneInfo> 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 @@
final ArrayAdapter<RegionInfo> 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 @@
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 @@
private void setSelectByRegion(boolean selectByRegion) {
mSelectByRegion = selectByRegion;
- getView().findViewById(R.id.tz_region_spinner_layout).setVisibility(
+ mRegionSpinnerLayout.setVisibility(
mSelectByRegion ? View.VISIBLE : View.GONE);
List<TimeZoneInfo> 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/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java
index acd8144..c4c795b 100644
--- a/src/com/android/settings/fuelgauge/BatteryInfo.java
+++ b/src/com/android/settings/fuelgauge/BatteryInfo.java
@@ -272,7 +272,7 @@
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 6af859b..a580db1 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
@@ -19,9 +19,12 @@
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 @@
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";
@@ -81,6 +86,24 @@
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
*
* @see Settings.Global#BATTERY_TIP_CONSTANTS
@@ -143,6 +166,9 @@
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 0000000..cc5aed6
--- /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 3c69667..ed3fa04 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 @@
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 @@
private List<AppInfo> mHighUsageAppList;
private Context mContext;
@VisibleForTesting
+ HighUsageDataParser mDataParser;
+ @VisibleForTesting
BatteryUtils mBatteryUtils;
public HighUsageDetector(Context context, BatteryTipPolicy policy,
@@ -52,32 +55,42 @@
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<BatterySipper> 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<BatterySipper> 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 13ffd5b..4d9ad27 100644
--- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
+++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
@@ -280,7 +280,7 @@
.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 fa10607..11f1f59 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.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 @@
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 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 @@
if (lifecycle != null) {
lifecycle.addObserver(this);
}
+ if (initSoftApManager) {
+ initWifiTetherSoftApManager();
+ }
}
@Override
@@ -101,6 +115,9 @@
if (mPreference != null) {
mContext.registerReceiver(mReceiver, WIFI_TETHER_INTENT_FILTER);
clearSummaryForAirplaneMode();
+ if (mWifiTetherSoftApManager != null) {
+ mWifiTetherSoftApManager.registerSoftApCallback();
+ }
}
}
@@ -108,9 +125,36 @@
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 0000000..77a44b0
--- /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/tests/robotests/src/com/android/settings/MasterClearTest.java b/tests/robotests/src/com/android/settings/MasterClearTest.java
index 361bc8f..26cfc3a 100644
--- a/tests/robotests/src/com/android/settings/MasterClearTest.java
+++ b/tests/robotests/src/com/android/settings/MasterClearTest.java
@@ -19,16 +19,24 @@
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;
@@ -46,6 +54,7 @@
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 +64,9 @@
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 +74,18 @@
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 +109,7 @@
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
@@ -162,9 +186,61 @@
@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/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
index 53c9766..83b3225 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 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 @@
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 bb9a37b..78c86f8 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 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 @@
+ ",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 @@
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 @@
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 0000000..5bdae0c
--- /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 2a71991..8df7c56 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 @@
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.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 @@
private BatteryUtils mBatteryUtils;
@Mock
private BatterySipper mBatterySipper;
+ @Mock
+ private HighUsageDataParser mDataParser;
private BatteryTipPolicy mPolicy;
private HighUsageDetector mHighUsageDetector;
@@ -66,8 +71,10 @@
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 @@
@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 00d9585..dca6974 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 @@
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 @@
shadows = {
WifiTetherPreferenceControllerTest.ShadowWifiTetherSettings.class,
WifiTetherPreferenceControllerTest.ShadowWifiTetherSwitchBarController.class,
+ WifiTetherPreferenceControllerTest.ShadowWifiTetherSoftApManager.class
})
public class WifiTetherPreferenceControllerTest {
@@ -94,8 +99,9 @@
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 @@
@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 @@
}
}
+ @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 {