diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 98afa5af12e..fbc7490957a 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -22,7 +22,7 @@
16dip
40dip
- 24dp
+ 32dp
48dp
64dip
72dip
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4b2c67344dc..cae56078d8f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2259,9 +2259,9 @@
Wi-Fi calling
- Turn on Wi-Fi Calling
+ Extend call coverage with Wi\u2011Fi
- Extend coverage by calling over Wi-Fi
+ Turn on Wi\u2011Fi calling
Calling preference
@@ -4842,6 +4842,41 @@
Your tablet was used heavily and this consumed a lot of battery. Your battery is behaving normally.\n\n Your tablet was used for about %1$s since last full charge.\n\n Total usage:
Your device was used heavily and this consumed a lot of battery. Your battery is behaving normally.\n\n Your device was used for about %1$s since last full charge.\n\n Total usage:
+
+
+ - Restrict %1$d app
+ - Restrict %1$d apps
+
+
+
+ - %1$d recently restricted
+ - %1$d apps recently restricted
+
+
+
+ - %1$s has high battery usage
+ - %2$d apps have high battery usage
+
+
+ App changes are in progress
+
+
+
+ - Restrict app?
+ - Restrict %1$d apps?
+
+
+ To save battery, you can stop this app from running in the background when it’s not being used.
+
+ Restrict
+
+ Remove restriction for %1$s?
+
+ This app will be able to use battery in the background. This may cause your battery to be used up faster.
+
+ Remove
+
+ Not now
Smart battery manager
diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml
index 267fce98114..c86df68b2fc 100644
--- a/res/xml/location_settings.xml
+++ b/res/xml/location_settings.xml
@@ -49,5 +49,9 @@
+ android:title="@string/location_category_location_services"/>
+
+
diff --git a/src/com/android/settings/CryptKeeperConfirm.java b/src/com/android/settings/CryptKeeperConfirm.java
index d61fd983f3c..227120089ee 100644
--- a/src/com/android/settings/CryptKeeperConfirm.java
+++ b/src/com/android/settings/CryptKeeperConfirm.java
@@ -16,6 +16,7 @@
package com.android.settings;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.StatusBarManager;
import android.content.Context;
@@ -35,11 +36,11 @@ import android.widget.Button;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import java.util.Locale;
-public class CryptKeeperConfirm extends InstrumentedPreferenceFragment {
+public class CryptKeeperConfirm extends InstrumentedFragment {
private static final String TAG = "CryptKeeperConfirm";
@@ -153,6 +154,12 @@ public class CryptKeeperConfirm extends InstrumentedPreferenceFragment {
mFinalButton.setOnClickListener(mFinalClickListener);
}
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getActivity().setTitle(R.string.crypt_keeper_confirm_title);
+ }
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index 47aa8a6b9a9..4f5c6b95bfc 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -55,7 +55,7 @@ import android.widget.ScrollView;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.ConfirmLockPattern;
import com.android.settingslib.RestrictedLockUtils;
@@ -72,7 +72,7 @@ import java.util.List;
*
* This is the initial screen.
*/
-public class MasterClear extends InstrumentedPreferenceFragment {
+public class MasterClear extends InstrumentedFragment {
private static final String TAG = "MasterClear";
@VisibleForTesting static final int KEYGUARD_REQUEST = 55;
diff --git a/src/com/android/settings/MasterClearConfirm.java b/src/com/android/settings/MasterClearConfirm.java
index 9b324c3fddf..59736fd4cf5 100644
--- a/src/com/android/settings/MasterClearConfirm.java
+++ b/src/com/android/settings/MasterClearConfirm.java
@@ -33,7 +33,7 @@ import android.widget.Button;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import com.android.settingslib.RestrictedLockUtils;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -48,7 +48,7 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
*
* This is the confirmation screen.
*/
-public class MasterClearConfirm extends InstrumentedPreferenceFragment {
+public class MasterClearConfirm extends InstrumentedFragment {
private View mContentView;
private boolean mEraseSdCard;
diff --git a/src/com/android/settings/ProxySelector.java b/src/com/android/settings/ProxySelector.java
index a72525c2640..79767fb7aa3 100644
--- a/src/com/android/settings/ProxySelector.java
+++ b/src/com/android/settings/ProxySelector.java
@@ -41,9 +41,9 @@ import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
-public class ProxySelector extends InstrumentedPreferenceFragment implements DialogCreatable {
+public class ProxySelector extends InstrumentedFragment implements DialogCreatable {
private static final String TAG = "ProxySelector";
EditText mHostnameField;
@@ -58,11 +58,6 @@ public class ProxySelector extends InstrumentedPreferenceFragment implements Dia
private SettingsDialogFragment mDialogFragment;
private View mView;
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -179,6 +174,8 @@ public class ProxySelector extends InstrumentedPreferenceFragment implements Dia
String title = intent.getStringExtra("title");
if (!TextUtils.isEmpty(title)) {
activity.setTitle(title);
+ } else {
+ activity.setTitle(R.string.proxy_settings_title);
}
}
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index f64f6dce93a..5cbee6385df 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -43,7 +43,7 @@ import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.telephony.PhoneConstants;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.ConfirmLockPattern;
import com.android.settingslib.RestrictedLockUtils;
@@ -61,7 +61,7 @@ import java.util.List;
*
* This is the initial screen.
*/
-public class ResetNetwork extends InstrumentedPreferenceFragment {
+public class ResetNetwork extends InstrumentedFragment {
private static final String TAG = "ResetNetwork";
// Arbitrary to avoid conficts
diff --git a/src/com/android/settings/ResetNetworkConfirm.java b/src/com/android/settings/ResetNetworkConfirm.java
index bc0fa774a75..78e83394101 100644
--- a/src/com/android/settings/ResetNetworkConfirm.java
+++ b/src/com/android/settings/ResetNetworkConfirm.java
@@ -42,8 +42,8 @@ import android.widget.Toast;
import com.android.ims.ImsManager;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.telephony.PhoneConstants;
-import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.wrapper.RecoverySystemWrapper;
+import com.android.settings.core.InstrumentedFragment;
import com.android.settingslib.RestrictedLockUtils;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -58,7 +58,7 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
*
* This is the confirmation screen.
*/
-public class ResetNetworkConfirm extends InstrumentedPreferenceFragment {
+public class ResetNetworkConfirm extends InstrumentedFragment {
private View mContentView;
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
diff --git a/src/com/android/settings/TrustedCredentialsSettings.java b/src/com/android/settings/TrustedCredentialsSettings.java
index 491419a7212..86340be98f6 100644
--- a/src/com/android/settings/TrustedCredentialsSettings.java
+++ b/src/com/android/settings/TrustedCredentialsSettings.java
@@ -65,7 +65,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
@@ -75,7 +75,7 @@ import java.util.List;
import java.util.Set;
import java.util.function.IntConsumer;
-public class TrustedCredentialsSettings extends InstrumentedPreferenceFragment
+public class TrustedCredentialsSettings extends InstrumentedFragment
implements TrustedCredentialsDialogBuilder.DelegateInterface {
public static final String ARG_SHOW_NEW_FOR_USER = "ARG_SHOW_NEW_FOR_USER";
@@ -117,7 +117,8 @@ public class TrustedCredentialsSettings extends InstrumentedPreferenceFragment
private final int mContentView;
private final boolean mSwitch;
- private Tab(String tag, int label, int view, int progress, int contentView, boolean withSwitch) {
+ private Tab(String tag, int label, int view, int progress, int contentView,
+ boolean withSwitch) {
mTag = tag;
mLabel = label;
mView = view;
diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java
index 7e73a9b5001..770b1d6e4a8 100644
--- a/src/com/android/settings/applications/RunningServiceDetails.java
+++ b/src/com/android/settings/applications/RunningServiceDetails.java
@@ -34,7 +34,7 @@ import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.Utils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settingslib.utils.ThreadUtils;
@@ -45,7 +45,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
-public class RunningServiceDetails extends InstrumentedPreferenceFragment
+public class RunningServiceDetails extends InstrumentedFragment
implements RunningState.OnRefreshUiListener {
static final String TAG = "RunningServicesDetails";
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
index 97f6d4bec5e..d116f91d601 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
@@ -251,16 +251,16 @@ public class DefaultAutofillPicker extends DefaultAppPickerFragment {
static final class AutofillSettingIntentProvider implements SettingIntentProvider {
private final String mSelectedKey;
- private final PackageManager mPackageManager;
+ private final Context mContext;
- public AutofillSettingIntentProvider(PackageManager packageManager, String key) {
+ public AutofillSettingIntentProvider(Context context, String key) {
mSelectedKey = key;
- mPackageManager = packageManager;
+ mContext = context;
}
@Override
public Intent getIntent() {
- final List resolveInfos = mPackageManager.queryIntentServices(
+ final List resolveInfos = mContext.getPackageManager().queryIntentServices(
AUTOFILL_PROBE, PackageManager.GET_META_DATA);
for (ResolveInfo resolveInfo : resolveInfos) {
@@ -270,7 +270,7 @@ public class DefaultAutofillPicker extends DefaultAppPickerFragment {
if (TextUtils.equals(mSelectedKey, flattenKey)) {
final String settingsActivity;
try {
- settingsActivity = new AutofillServiceInfo(mPackageManager, serviceInfo)
+ settingsActivity = new AutofillServiceInfo(mContext, serviceInfo)
.getSettingsActivity();
} catch (SecurityException e) {
// Service does not declare the proper permission, ignore it.
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceController.java
index 508cc63cfc3..b159d1dbd8c 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceController.java
@@ -53,7 +53,7 @@ public class DefaultAutofillPreferenceController extends DefaultAppPreferenceCon
}
final DefaultAutofillPicker.AutofillSettingIntentProvider intentProvider =
new DefaultAutofillPicker.AutofillSettingIntentProvider(
- mPackageManager.getPackageManager(), info.getKey());
+ mContext, info.getKey());
return intentProvider.getIntent();
}
diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java
index 4f045a28061..690c79559af 100644
--- a/src/com/android/settings/dashboard/DashboardSummary.java
+++ b/src/com/android/settings/dashboard/DashboardSummary.java
@@ -268,6 +268,7 @@ public class DashboardSummary extends InstrumentedFragment
mSummaryLoader.updateSummaryToCache(category);
mStagingCategory = category;
if (mSuggestionControllerMixin == null) {
+ mAdapter.setCategory(mStagingCategory);
return;
}
if (mSuggestionControllerMixin.isSuggestionLoaded()) {
diff --git a/src/com/android/settings/fingerprint/FingerprintAuthenticateSidecar.java b/src/com/android/settings/fingerprint/FingerprintAuthenticateSidecar.java
index d649c0bfc7f..1fa59a2fb60 100644
--- a/src/com/android/settings/fingerprint/FingerprintAuthenticateSidecar.java
+++ b/src/com/android/settings/fingerprint/FingerprintAuthenticateSidecar.java
@@ -21,12 +21,12 @@ import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
import android.os.CancellationSignal;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
/**
* Sidecar fragment to handle the state around fingerprint authentication
*/
-public class FingerprintAuthenticateSidecar extends InstrumentedPreferenceFragment {
+public class FingerprintAuthenticateSidecar extends InstrumentedFragment {
private static final String TAG = "FingerprintAuthenticateSidecar";
diff --git a/src/com/android/settings/fingerprint/FingerprintRemoveSidecar.java b/src/com/android/settings/fingerprint/FingerprintRemoveSidecar.java
index 462d09ee9e1..7caca3fff57 100644
--- a/src/com/android/settings/fingerprint/FingerprintRemoveSidecar.java
+++ b/src/com/android/settings/fingerprint/FingerprintRemoveSidecar.java
@@ -21,7 +21,7 @@ import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import android.os.UserHandle;
import java.util.Queue;
@@ -31,7 +31,7 @@ import android.util.Log;
/**
* Sidecar fragment to handle the state around fingerprint removal.
*/
-public class FingerprintRemoveSidecar extends InstrumentedPreferenceFragment {
+public class FingerprintRemoveSidecar extends InstrumentedFragment {
private static final String TAG = "FingerprintRemoveSidecar";
private Listener mListener;
@@ -99,20 +99,6 @@ public class FingerprintRemoveSidecar extends InstrumentedPreferenceFragment {
setRetainInstance(true);
}
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- }
-
- @Override
- public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- }
-
public void setListener(Listener listener) {
if (mListener == null && listener != null) {
while (!mFingerprintsRemoved.isEmpty()) {
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index de027a3a199..e073456db82 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -46,6 +46,8 @@ import com.android.settings.Utils;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.wrapper.DevicePolicyManagerWrapper;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment;
@@ -69,7 +71,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
ButtonActionDialogFragment.AppButtonsDialogListener,
AnomalyDialogFragment.AnomalyDialogListener,
LoaderManager.LoaderCallbacks>,
- BackgroundActivityPreferenceController.WarningConfirmationListener {
+ BatteryTipPreferenceController.BatteryTipListener {
public static final String TAG = "AdvancedPowerUsageDetail";
public static final String EXTRA_UID = "extra_uid";
@@ -373,8 +375,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
}
@Override
- public void onLimitBackgroundActivity() {
- mBackgroundActivityPreferenceController.setRestricted(
+ public void onBatteryTipHandled(BatteryTip batteryTip) {
+ mBackgroundActivityPreferenceController.updateSummary(
findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
}
}
diff --git a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
index 01e41825074..21bd4b73181 100644
--- a/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceController.java
@@ -14,27 +14,22 @@
package com.android.settings.fuelgauge;
-import android.app.AlertDialog;
import android.app.AppOpsManager;
-import android.app.Dialog;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.content.DialogInterface;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Bundle;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
-import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
-import android.util.Log;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
import com.android.settings.wrapper.DevicePolicyManagerWrapper;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
@@ -99,15 +94,6 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
return mTargetPackage != null;
}
- /**
- * Called from the warning dialog, if the user decides to go ahead and disable background
- * activity for this package
- */
- public void setRestricted(Preference preference) {
- mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_IGNORED);
- updateSummary(preference);
- }
-
@Override
public String getPreferenceKey() {
return KEY_BACKGROUND_ACTIVITY;
@@ -119,20 +105,13 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
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;
+ showDialog(restricted);
}
return false;
}
- @VisibleForTesting
- void updateSummary(Preference preference) {
+ public void updateSummary(Preference preference) {
if (mPowerWhitelistBackend.isWhitelisted(mTargetPackage)) {
preference.setSummary(R.string.background_activity_summary_whitelisted);
return;
@@ -150,42 +129,16 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
}
@VisibleForTesting
- void showDialog() {
- final WarningDialogFragment dialogFragment = new WarningDialogFragment();
+ void showDialog(boolean restricted) {
+ final AppInfo appInfo = new AppInfo.Builder()
+ .setPackageName(mTargetPackage)
+ .build();
+ BatteryTip tip = restricted
+ ? new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo)
+ : new RestrictAppTip(BatteryTip.StateType.NEW, appInfo);
+
+ final BatteryTipDialogFragment dialogFragment = BatteryTipDialogFragment.newInstance(tip);
dialogFragment.setTargetFragment(mFragment, 0 /* requestCode */);
dialogFragment.show(mFragment.getFragmentManager(), TAG);
}
-
- interface WarningConfirmationListener {
- void onLimitBackgroundActivity();
- }
-
- /**
- * Warning dialog to show to the user as turning off background activity can lead to
- * apps misbehaving as their background task scheduling guarantees will no longer be honored.
- */
- public static class WarningDialogFragment extends InstrumentedDialogFragment {
- @Override
- public int getMetricsCategory() {
- // TODO (b/65494831): add metric id
- return 0;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final WarningConfirmationListener listener =
- (WarningConfirmationListener) getTargetFragment();
- return new AlertDialog.Builder(getContext())
- .setTitle(R.string.background_activity_warning_dialog_title)
- .setMessage(R.string.background_activity_warning_dialog_text)
- .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- listener.onLimitBackgroundActivity();
- }
- })
- .setNegativeButton(R.string.dlg_cancel, null)
- .create();
- }
- }
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
index b51474defaa..66ce3caad01 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragment.java
@@ -34,6 +34,10 @@ import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController.
import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
+
+import java.util.List;
/**
* Dialog Fragment to show action dialog for each anomaly
@@ -84,6 +88,39 @@ public class BatteryTipDialogFragment extends InstrumentedDialogFragment impleme
.setView(view)
.setPositiveButton(android.R.string.ok, null)
.create();
+ case BatteryTip.TipType.APP_RESTRICTION:
+ final RestrictAppTip restrictAppTip = (RestrictAppTip) mBatteryTip;
+ final List restrictedAppList = restrictAppTip.getRestrictAppList();
+ final int num = restrictedAppList.size();
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context)
+ .setTitle(context.getResources().getQuantityString(
+ R.plurals.battery_tip_restrict_app_dialog_title, num, num))
+ .setMessage(getString(R.string.battery_tip_restrict_app_dialog_message))
+ .setPositiveButton(R.string.battery_tip_restrict_app_dialog_ok, this)
+ .setNegativeButton(android.R.string.cancel, null);
+
+ // TODO(b/72385333): consider building dialog with 5+ apps when strings are done
+ if (num > 1) {
+ final RecyclerView restrictionView = (RecyclerView) LayoutInflater.from(
+ context).inflate(R.layout.recycler_view, null);
+ restrictionView.setLayoutManager(new LinearLayoutManager(context));
+ restrictionView.setAdapter(new HighUsageAdapter(context, restrictedAppList));
+ builder.setView(restrictionView);
+ }
+
+ return builder.create();
+ case BatteryTip.TipType.REMOVE_APP_RESTRICTION:
+ final UnrestrictAppTip unrestrictAppTip = (UnrestrictAppTip) mBatteryTip;
+ final CharSequence name = Utils.getApplicationLabel(context,
+ unrestrictAppTip.getPackageName());
+
+ return new AlertDialog.Builder(context)
+ .setTitle(getString(R.string.battery_tip_unrestrict_app_dialog_title, name))
+ .setMessage(R.string.battery_tip_unrestrict_app_dialog_message)
+ .setPositiveButton(R.string.battery_tip_unrestrict_app_dialog_ok, this)
+ .setNegativeButton(R.string.battery_tip_unrestrict_app_dialog_cancel, null)
+ .create();
default:
throw new IllegalArgumentException("unknown type " + mBatteryTip.getType());
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
index ced34616879..a61584168bc 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
@@ -26,6 +26,7 @@ import com.android.settings.fuelgauge.batterytip.detectors.EarlyWarningDetector;
import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector;
import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
import com.android.settings.fuelgauge.batterytip.detectors.SmartBatteryDetector;
+import com.android.settings.fuelgauge.batterytip.detectors.RestrictAppDetector;
import com.android.settings.fuelgauge.batterytip.detectors.SummaryDetector;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
@@ -70,6 +71,7 @@ public class BatteryTipLoader extends AsyncLoader> {
tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect());
tips.add(new EarlyWarningDetector(policy, context).detect());
tips.add(new SummaryDetector(policy).detect());
+ tips.add(new RestrictAppDetector(policy).detect());
Collections.sort(tips);
return tips;
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
index 5781afd61f2..5eec3229f80 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
@@ -17,12 +17,17 @@
package com.android.settings.fuelgauge.batterytip;
import android.app.Fragment;
+import android.content.Context;
import com.android.settings.SettingsActivity;
import com.android.settings.fuelgauge.batterytip.actions.BatterySaverAction;
import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
+import com.android.settings.fuelgauge.batterytip.actions.RestrictAppAction;
import com.android.settings.fuelgauge.batterytip.actions.SmartBatteryAction;
+import com.android.settings.fuelgauge.batterytip.actions.UnrestrictAppAction;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
/**
* Utility class for {@link BatteryTip}
@@ -42,7 +47,11 @@ public class BatteryTipUtils {
case BatteryTip.TipType.SMART_BATTERY_MANAGER:
return new SmartBatteryAction(settingsActivity, fragment);
case BatteryTip.TipType.BATTERY_SAVER:
- return new BatterySaverAction(settingsActivity.getApplicationContext());
+ return new BatterySaverAction(settingsActivity);
+ case BatteryTip.TipType.APP_RESTRICTION:
+ return new RestrictAppAction(settingsActivity, (RestrictAppTip) batteryTip);
+ case BatteryTip.TipType.REMOVE_APP_RESTRICTION:
+ return new UnrestrictAppAction(settingsActivity, (UnrestrictAppTip) batteryTip);
default:
return null;
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java b/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java
index 60aa6c8832a..6c129d8a9be 100644
--- a/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java
+++ b/src/com/android/settings/fuelgauge/batterytip/HighUsageAdapter.java
@@ -77,7 +77,9 @@ public class HighUsageAdapter extends RecyclerView.Adapter appInfos = mRestrictAppTip.getRestrictAppList();
+
+ for (int i = 0, size = appInfos.size(); i < size; i++) {
+ final String packageName = appInfos.get(i).packageName;
+ // Force app standby, then app can't run in the background
+ mBatteryUtils.setForceAppStandby(mBatteryUtils.getPackageUid(packageName), packageName,
+ AppOpsManager.MODE_IGNORED);
+ }
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java b/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java
new file mode 100644
index 00000000000..16812f69afa
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/actions/UnrestrictAppAction.java
@@ -0,0 +1,48 @@
+/*
+ * 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.actions;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
+
+/**
+ * Action to clear the restriction to the app
+ */
+public class UnrestrictAppAction extends BatteryTipAction {
+ private UnrestrictAppTip mUnRestrictAppTip;
+ private BatteryUtils mBatteryUtils;
+
+ public UnrestrictAppAction(Context context, UnrestrictAppTip tip) {
+ super(context);
+ mUnRestrictAppTip = tip;
+ mBatteryUtils = BatteryUtils.getInstance(context);
+ }
+
+ /**
+ * Handle the action when user clicks positive button
+ */
+ @Override
+ public void handlePositiveAction() {
+ final String packageName = mUnRestrictAppTip.getPackageName();
+ // Clear force app standby, then app can run in the background
+ mBatteryUtils.setForceAppStandby(mBatteryUtils.getPackageUid(packageName), packageName,
+ AppOpsManager.MODE_ALLOWED);
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetector.java
new file mode 100644
index 00000000000..46e241a24d5
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/RestrictAppDetector.java
@@ -0,0 +1,46 @@
+/*
+ * 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.detectors;
+
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Detector whether to show summary tip. This detector should be executed as the last
+ * {@link BatteryTipDetector} since it need the most up-to-date {@code visibleTips}
+ */
+public class RestrictAppDetector implements BatteryTipDetector {
+ private BatteryTipPolicy mPolicy;
+
+ public RestrictAppDetector(BatteryTipPolicy policy) {
+ mPolicy = policy;
+ }
+
+ @Override
+ public BatteryTip detect() {
+ // TODO(b/70570352): Detect restrict apps here, get data from database
+ final List highUsageApps = new ArrayList<>();
+ return new RestrictAppTip(
+ highUsageApps.isEmpty() ? BatteryTip.StateType.INVISIBLE : BatteryTip.StateType.NEW,
+ highUsageApps);
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
index 09ebc4b5b14..59cd5ee4226 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
@@ -50,7 +50,8 @@ public abstract class BatteryTip implements Comparable, Parcelable {
TipType.SMART_BATTERY_MANAGER,
TipType.APP_RESTRICTION,
TipType.REDUCED_BATTERY,
- TipType.LOW_BATTERY})
+ TipType.LOW_BATTERY,
+ TipType.REMOVE_APP_RESTRICTION})
public @interface TipType {
int SMART_BATTERY_MANAGER = 0;
int APP_RESTRICTION = 1;
@@ -59,6 +60,7 @@ public abstract class BatteryTip implements Comparable, Parcelable {
int REDUCED_BATTERY = 4;
int LOW_BATTERY = 5;
int SUMMARY = 6;
+ int REMOVE_APP_RESTRICTION = 7;
}
private static final String KEY_PREFIX = "key_battery_tip";
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
new file mode 100644
index 00000000000..1d84d7fb94b
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTip.java
@@ -0,0 +1,106 @@
+/*
+ * 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.tips;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Parcel;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tip to suggest user to restrict some bad apps
+ */
+public class RestrictAppTip extends BatteryTip {
+ private List mRestrictAppList;
+
+ public RestrictAppTip(@StateType int state, List restrictApps) {
+ super(TipType.APP_RESTRICTION, state, true /* showDialog */);
+ mRestrictAppList = restrictApps;
+ }
+
+ public RestrictAppTip(@StateType int state, AppInfo appInfo) {
+ super(TipType.APP_RESTRICTION, state, true /* showDialog */);
+ mRestrictAppList = new ArrayList<>();
+ mRestrictAppList.add(appInfo);
+ }
+
+ @VisibleForTesting
+ RestrictAppTip(Parcel in) {
+ super(in);
+ mRestrictAppList = in.createTypedArrayList(AppInfo.CREATOR);
+ }
+
+ @Override
+ public CharSequence getTitle(Context context) {
+ final int num = mRestrictAppList.size();
+ return context.getResources().getQuantityString(
+ mState == StateType.HANDLED
+ ? R.plurals.battery_tip_restrict_handled_title
+ : R.plurals.battery_tip_restrict_title,
+ num, num);
+ }
+
+ @Override
+ public CharSequence getSummary(Context context) {
+ final int num = mRestrictAppList.size();
+ final CharSequence appLabel = num > 0 ? Utils.getApplicationLabel(context,
+ mRestrictAppList.get(0).packageName) : "";
+ return mState == StateType.HANDLED
+ ? context.getString(R.string.battery_tip_restrict_handled_summary)
+ : context.getResources().getQuantityString(R.plurals.battery_tip_restrict_summary,
+ num, appLabel, num);
+ }
+
+ @Override
+ public int getIconId() {
+ return mState == StateType.HANDLED
+ ? R.drawable.ic_perm_device_information_green_24dp
+ : R.drawable.ic_battery_alert_24dp;
+ }
+
+ @Override
+ public void updateState(BatteryTip tip) {
+ mState = tip.mState;
+ }
+
+ public List getRestrictAppList() {
+ return mRestrictAppList;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeTypedList(mRestrictAppList);
+ }
+
+ public static final Creator CREATOR = new Creator() {
+ public BatteryTip createFromParcel(Parcel in) {
+ return new RestrictAppTip(in);
+ }
+
+ public BatteryTip[] newArray(int size) {
+ return new RestrictAppTip[size];
+ }
+ };
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java
new file mode 100644
index 00000000000..ec67f6a47b1
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTip.java
@@ -0,0 +1,84 @@
+/*
+ * 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.tips;
+
+import android.content.Context;
+import android.os.Parcel;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+
+/**
+ * Tip to suggest user to remove app restriction. This is the empty tip and it is only used in
+ * {@link com.android.settings.fuelgauge.AdvancedPowerUsageDetail} to create dialog.
+ */
+public class UnrestrictAppTip extends BatteryTip {
+ private AppInfo mAppInfo;
+
+ public UnrestrictAppTip(@StateType int state, AppInfo appInfo) {
+ super(TipType.REMOVE_APP_RESTRICTION, state, true /* showDialog */);
+ mAppInfo = appInfo;
+ }
+
+ @VisibleForTesting
+ UnrestrictAppTip(Parcel in) {
+ super(in);
+ mAppInfo = in.readParcelable(getClass().getClassLoader());
+ }
+
+ @Override
+ public CharSequence getTitle(Context context) {
+ // Don't need title since this is an empty tip
+ return null;
+ }
+
+ @Override
+ public CharSequence getSummary(Context context) {
+ // Don't need summary since this is an empty tip
+ return null;
+ }
+
+ @Override
+ public int getIconId() {
+ return 0;
+ }
+
+ public String getPackageName() {
+ return mAppInfo.packageName;
+ }
+
+ @Override
+ public void updateState(BatteryTip tip) {
+ mState = tip.mState;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeParcelable(mAppInfo, flags);
+ }
+
+ public static final Creator CREATOR = new Creator() {
+ public BatteryTip createFromParcel(Parcel in) {
+ return new UnrestrictAppTip(in);
+ }
+
+ public BatteryTip[] newArray(int size) {
+ return new UnrestrictAppTip[size];
+ }
+ };
+}
diff --git a/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java b/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java
index 38b64a598db..3243e56c2f6 100644
--- a/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java
+++ b/src/com/android/settings/inputmethod/UserDictionaryAddWordFragment.java
@@ -27,7 +27,7 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.inputmethod.UserDictionaryAddWordContents.LocaleRenderer;
@@ -41,7 +41,7 @@ import java.util.Locale;
* As opposed to the UserDictionaryActivity, this is only invoked within Settings
* from the UserDictionarySettings.
*/
-public class UserDictionaryAddWordFragment extends InstrumentedPreferenceFragment
+public class UserDictionaryAddWordFragment extends InstrumentedFragment
implements AdapterView.OnItemSelectedListener,
com.android.internal.app.LocalePicker.LocaleSelectionListener {
@@ -55,7 +55,6 @@ public class UserDictionaryAddWordFragment extends InstrumentedPreferenceFragmen
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
- getActivity().getActionBar().setTitle(R.string.user_dict_settings_title);
// Keep the instance so that we remember mContents when configuration changes (eg rotation)
setRetainInstance(true);
}
diff --git a/src/com/android/settings/location/LocationFooterPreferenceController.java b/src/com/android/settings/location/LocationFooterPreferenceController.java
new file mode 100644
index 00000000000..f15d43748de
--- /dev/null
+++ b/src/com/android/settings/location/LocationFooterPreferenceController.java
@@ -0,0 +1,223 @@
+/*
+ * 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.location;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.location.LocationManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.util.Log;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.widget.FooterPreference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Preference controller for location footer preference category
+ */
+public class LocationFooterPreferenceController extends LocationBasePreferenceController
+ implements LifecycleObserver, OnPause {
+ private static final String TAG = "LocationFooter";
+ private static final String KEY_LOCATION_FOOTER = "location_footer";
+ private static final Intent INJECT_INTENT =
+ new Intent(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION);
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+ private Collection mFooterInjectors;
+
+ public LocationFooterPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context, lifecycle);
+ mContext = context;
+ mPackageManager = mContext.getPackageManager();
+ mFooterInjectors = new ArrayList<>();
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_LOCATION_FOOTER;
+ }
+
+ /**
+ * Insert footer preferences. Send a {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION}
+ * broadcast to receivers who have injected a footer
+ */
+ @Override
+ public void updateState(Preference preference) {
+ PreferenceCategory category = (PreferenceCategory) preference;
+ category.removeAll();
+ mFooterInjectors.clear();
+ Collection footerData = getFooterData();
+ for (FooterData data : footerData) {
+ // Generate a footer preference with the given text
+ FooterPreference footerPreference = new FooterPreference(preference.getContext());
+ String footerString;
+ try {
+ footerString =
+ mPackageManager
+ .getResourcesForApplication(data.applicationInfo)
+ .getString(data.footerStringRes);
+ } catch (NameNotFoundException exception) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(
+ TAG,
+ "Resources not found for application "
+ + data.applicationInfo.packageName);
+ }
+ continue;
+ }
+ footerPreference.setTitle(footerString);
+ // Inject the footer
+ category.addPreference(footerPreference);
+ // Send broadcast to the injector announcing a footer has been injected
+ sendBroadcastFooterDisplayed(data.componentName);
+ mFooterInjectors.add(data.componentName);
+ }
+ }
+
+ /**
+ * Do nothing on location mode changes.
+ */
+ @Override
+ public void onLocationModeChanged(int mode, boolean restricted) {}
+
+ /**
+ * Location footer preference group should be displayed if there is at least one footer to
+ * inject.
+ */
+ @Override
+ public boolean isAvailable() {
+ return !getFooterData().isEmpty();
+ }
+
+ /**
+ * Send a {@link LocationManager#SETTINGS_FOOTER_REMOVED_ACTION} broadcast to footer injectors
+ * when LocationFragment is on pause
+ */
+ @Override
+ public void onPause() {
+ // Send broadcast to the footer injectors. Notify them the footer is not visible.
+ for (ComponentName componentName : mFooterInjectors) {
+ final Intent intent = new Intent(LocationManager.SETTINGS_FOOTER_REMOVED_ACTION);
+ intent.setComponent(componentName);
+ mContext.sendBroadcast(intent);
+ }
+ }
+
+ /**
+ * Send a {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION} broadcast to a footer
+ * injector.
+ */
+ @VisibleForTesting
+ void sendBroadcastFooterDisplayed(ComponentName componentName) {
+ Intent intent = new Intent(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION);
+ intent.setComponent(componentName);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Return a list of strings with text provided by ACTION_INJECT_FOOTER broadcast receivers.
+ */
+ private Collection getFooterData() {
+ // Fetch footer text from system apps
+ final List resolveInfos =
+ mPackageManager.queryBroadcastReceivers(
+ INJECT_INTENT, PackageManager.GET_META_DATA);
+ if (resolveInfos == null) {
+ if (Log.isLoggable(TAG, Log.ERROR)) {
+ Log.e(TAG, "Unable to resolve intent " + INJECT_INTENT);
+ return Collections.emptyList();
+ }
+ } else if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Found broadcast receivers: " + resolveInfos);
+ }
+
+ final Collection footerDataList = new ArrayList<>(resolveInfos.size());
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+ final ApplicationInfo appInfo = activityInfo.applicationInfo;
+
+ // If a non-system app tries to inject footer, ignore it
+ if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "Ignoring attempt to inject footer from app not in system image: "
+ + resolveInfo);
+ continue;
+ }
+ }
+
+ // Get the footer text resource id from broadcast receiver's metadata
+ if (activityInfo.metaData == null) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "No METADATA in broadcast receiver " + activityInfo.name);
+ continue;
+ }
+ }
+
+ final int footerTextRes =
+ activityInfo.metaData.getInt(LocationManager.METADATA_SETTINGS_FOOTER_STRING);
+ if (footerTextRes == 0) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(
+ TAG,
+ "No mapping of integer exists for "
+ + LocationManager.METADATA_SETTINGS_FOOTER_STRING);
+ }
+ continue;
+ }
+ footerDataList.add(
+ new FooterData(
+ footerTextRes,
+ appInfo,
+ new ComponentName(activityInfo.packageName, activityInfo.name)));
+ }
+ return footerDataList;
+ }
+
+ /**
+ * Contains information related to a footer.
+ */
+ private static class FooterData {
+
+ // The string resource of the footer
+ final int footerStringRes;
+
+ // Application info of receiver injecting this footer
+ final ApplicationInfo applicationInfo;
+
+ // The component that injected the footer. It must be a receiver of broadcast
+ // LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION
+ final ComponentName componentName;
+
+ FooterData(int footerRes, ApplicationInfo appInfo, ComponentName componentName) {
+ this.footerStringRes = footerRes;
+ this.applicationInfo = appInfo;
+ this.componentName = componentName;
+ }
+ }
+}
diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java
index 3cc5b847f15..510c1625a58 100644
--- a/src/com/android/settings/location/LocationSettings.java
+++ b/src/com/android/settings/location/LocationSettings.java
@@ -131,9 +131,10 @@ public class LocationSettings extends DashboardFragment {
controllers.add(new LocationForWorkPreferenceController(context, lifecycle));
controllers.add(
new RecentLocationRequestPreferenceController(context, fragment, lifecycle));
+ controllers.add(new LocationScanningPreferenceController(context));
controllers.add(
new LocationServicePreferenceController(context, fragment, lifecycle));
- controllers.add(new LocationScanningPreferenceController(context));
+ controllers.add(new LocationFooterPreferenceController(context, lifecycle));
return controllers;
}
diff --git a/src/com/android/settings/network/NetworkScorerPicker.java b/src/com/android/settings/network/NetworkScorerPicker.java
index 187c9ce3ebc..34accf24671 100644
--- a/src/com/android/settings/network/NetworkScorerPicker.java
+++ b/src/com/android/settings/network/NetworkScorerPicker.java
@@ -50,7 +50,6 @@ public class NetworkScorerPicker extends InstrumentedPreferenceFragment implemen
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
- addPreferencesFromResource(R.xml.network_scorer_picker_prefs);
updateCandidates();
}
@@ -69,6 +68,11 @@ public class NetworkScorerPicker extends InstrumentedPreferenceFragment implemen
return view;
}
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.network_scorer_picker_prefs;
+ }
+
@VisibleForTesting
public void updateCandidates() {
final PreferenceScreen screen = getPreferenceScreen();
diff --git a/src/com/android/settings/nfc/AndroidBeam.java b/src/com/android/settings/nfc/AndroidBeam.java
index 707017beaf4..8377f143f08 100644
--- a/src/com/android/settings/nfc/AndroidBeam.java
+++ b/src/com/android/settings/nfc/AndroidBeam.java
@@ -29,7 +29,7 @@ import android.widget.Switch;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.HelpUtils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.ShowAdminSupportDetailsDialog;
@@ -38,7 +38,7 @@ import com.android.settingslib.RestrictedLockUtils;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-public class AndroidBeam extends InstrumentedPreferenceFragment
+public class AndroidBeam extends InstrumentedFragment
implements SwitchBar.OnSwitchChangeListener {
private View mView;
private NfcAdapter mNfcAdapter;
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index 11efec3ca7f..a9c5c032769 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -64,7 +64,7 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SetupWizardUtils;
import com.android.settings.Utils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import com.android.settings.notification.RedactionInterstitial;
import com.android.settings.widget.ImeAwareEditText;
import com.android.setupwizardlib.GlifLayout;
@@ -168,7 +168,7 @@ public class ChooseLockPassword extends SettingsActivity {
layout.setFitsSystemWindows(false);
}
- public static class ChooseLockPasswordFragment extends InstrumentedPreferenceFragment
+ public static class ChooseLockPasswordFragment extends InstrumentedFragment
implements OnClickListener, OnEditorActionListener, TextWatcher,
SaveAndFinishWorker.Listener {
private static final String KEY_FIRST_PIN = "first_pin";
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index 972fac8715f..0df1a11c9f2 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -43,7 +43,7 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SetupWizardUtils;
import com.android.settings.Utils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import com.android.settings.notification.RedactionInterstitial;
import com.android.setupwizardlib.GlifLayout;
@@ -153,7 +153,7 @@ public class ChooseLockPattern extends SettingsActivity {
return super.onKeyDown(keyCode, event);
}
- public static class ChooseLockPatternFragment extends InstrumentedPreferenceFragment
+ public static class ChooseLockPatternFragment extends InstrumentedFragment
implements View.OnClickListener, SaveAndFinishWorker.Listener {
public static final int CONFIRM_EXISTING_REQUEST = 55;
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
index 5b18925f1c7..3f2675029d0 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java
@@ -52,13 +52,13 @@ import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.Utils;
-import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.core.InstrumentedFragment;
import com.android.settings.fingerprint.FingerprintUiHelper;
/**
* Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
*/
-public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedPreferenceFragment
+public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFragment
implements FingerprintUiHelper.Callback {
public static final String PACKAGE = "com.android.settings";
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index 20182cbd92a..dbca2fc12d0 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -113,11 +113,6 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
}
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index 1e881505027..c87ff431e8f 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -104,11 +104,6 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
}
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
diff --git a/src/com/android/settings/security/CryptKeeperSettings.java b/src/com/android/settings/security/CryptKeeperSettings.java
index 7d5ee9d4357..64f5abb5bc5 100644
--- a/src/com/android/settings/security/CryptKeeperSettings.java
+++ b/src/com/android/settings/security/CryptKeeperSettings.java
@@ -157,6 +157,7 @@ public class CryptKeeperSettings extends InstrumentedPreferenceFragment {
}
}
}
+ activity.setTitle(R.string.crypt_keeper_encrypt_title);
}
/**
diff --git a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
index 11f1f5977d6..826ed426019 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java
@@ -39,13 +39,12 @@ import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
-import java.util.List;
-
public class WifiTetherPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
- public static final IntentFilter WIFI_TETHER_INTENT_FILTER;
private static final String WIFI_TETHER_SETTINGS = "wifi_tether";
+ private static final IntentFilter AIRPLANE_INTENT_FILTER = new IntentFilter(
+ Intent.ACTION_AIRPLANE_MODE_CHANGED);
private final ConnectivityManager mConnectivityManager;
private final String[] mWifiRegexs;
@@ -58,12 +57,6 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
@VisibleForTesting
WifiTetherSoftApManager mWifiTetherSoftApManager;
- static {
- WIFI_TETHER_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- WIFI_TETHER_INTENT_FILTER.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
- WIFI_TETHER_INTENT_FILTER.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- }
-
public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) {
this(context, lifecycle, true /* initSoftApManager */);
}
@@ -113,7 +106,7 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
@Override
public void onStart() {
if (mPreference != null) {
- mContext.registerReceiver(mReceiver, WIFI_TETHER_INTENT_FILTER);
+ mContext.registerReceiver(mReceiver, AIRPLANE_INTENT_FILTER);
clearSummaryForAirplaneMode();
if (mWifiTetherSoftApManager != null) {
mWifiTetherSoftApManager.registerSoftApCallback();
@@ -140,6 +133,7 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
@Override
public void onStateChanged(int state, int failureReason) {
mSoftApState = state;
+ handleWifiApStateChanged(state, failureReason);
}
@Override
@@ -162,34 +156,21 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(action)) {
- int state = intent.getIntExtra(
- WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED);
- int reason = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON,
- WifiManager.SAP_START_FAILURE_GENERAL);
- handleWifiApStateChanged(state, reason);
- } else if (ConnectivityManager.ACTION_TETHER_STATE_CHANGED.equals(action)) {
- List active = intent.getStringArrayListExtra(
- ConnectivityManager.EXTRA_ACTIVE_TETHER);
- List errored = intent.getStringArrayListExtra(
- ConnectivityManager.EXTRA_ERRORED_TETHER);
- updateTetherState(active.toArray(), errored.toArray());
- } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
+ if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
clearSummaryForAirplaneMode();
}
}
};
- private void handleWifiApStateChanged(int state, int reason) {
+ @VisibleForTesting
+ void handleWifiApStateChanged(int state, int reason) {
switch (state) {
case WifiManager.WIFI_AP_STATE_ENABLING:
mPreference.setSummary(R.string.wifi_tether_starting);
break;
case WifiManager.WIFI_AP_STATE_ENABLED:
- /**
- * Summary on enable is handled by tether
- * broadcast notice
- */
+ WifiConfiguration wifiConfig = mWifiManager.getWifiApConfiguration();
+ updateConfigSummary(wifiConfig);
break;
case WifiManager.WIFI_AP_STATE_DISABLING:
mPreference.setSummary(R.string.wifi_tether_stopping);
@@ -208,32 +189,6 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
}
}
- private void updateTetherState(Object[] tethered, Object[] errored) {
- boolean wifiTethered = matchRegex(tethered);
- boolean wifiErrored = matchRegex(errored);
-
- if (wifiTethered) {
- WifiConfiguration wifiConfig = mWifiManager.getWifiApConfiguration();
- updateConfigSummary(wifiConfig);
- } else if (wifiErrored) {
- mPreference.setSummary(R.string.wifi_error);
- } else {
- mPreference.setSummary(R.string.wifi_hotspot_off_subtext);
- }
- }
-
- private boolean matchRegex(Object[] tethers) {
- for (Object o : tethers) {
- String s = (String) o;
- for (String regex : mWifiRegexs) {
- if (s.matches(regex)) {
- return true;
- }
- }
- }
- return false;
- }
-
private void updateConfigSummary(WifiConfiguration wifiConfig) {
final String s = mContext.getString(
com.android.internal.R.string.wifi_tether_configure_ssid_default);
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
index 627bf32d215..699e5ae880b 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java
@@ -21,6 +21,7 @@ import static android.net.ConnectivityManager.TETHERING_WIFI;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
@@ -36,12 +37,19 @@ import com.android.settingslib.core.lifecycle.events.OnStop;
public class WifiTetherSwitchBarController implements SwitchWidgetController.OnSwitchChangeListener,
LifecycleObserver, OnStart, OnStop {
+ private static final IntentFilter WIFI_INTENT_FILTER;
+
private final Context mContext;
private final SwitchWidgetController mSwitchBar;
private final ConnectivityManager mConnectivityManager;
private final DataSaverBackend mDataSaverBackend;
private final WifiManager mWifiManager;
+ static {
+ WIFI_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ WIFI_INTENT_FILTER.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ }
+
WifiTetherSwitchBarController(Context context, SwitchWidgetController switchBar) {
mContext = context;
mSwitchBar = switchBar;
@@ -56,8 +64,7 @@ public class WifiTetherSwitchBarController implements SwitchWidgetController.OnS
@Override
public void onStart() {
mSwitchBar.startListening();
- mContext.registerReceiver(mReceiver,
- WifiTetherPreferenceController.WIFI_TETHER_INTENT_FILTER);
+ mContext.registerReceiver(mReceiver, WIFI_INTENT_FILTER);
}
@Override
diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceControllerTest.java
index bc72ee4ee3f..4e8a79efa9e 100644
--- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAutofillPreferenceControllerTest.java
@@ -104,10 +104,5 @@ public class DefaultAutofillPreferenceControllerTest {
final DefaultAppInfo info = mController.getDefaultAppInfo();
assertThat(info).isNotNull();
-
- mController.getSettingIntent(info);
-
- verify(mPackageManager.getPackageManager()).queryIntentServices(
- DefaultAutofillPicker.AUTOFILL_PROBE, PackageManager.GET_META_DATA);
}
}
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java
index a1c8d67e034..d9c709e72e9 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardSummaryTest.java
@@ -67,6 +67,8 @@ public class DashboardSummaryTest {
private ConditionManager mConditionManager;
@Mock
private SummaryLoader mSummaryLoader;
+ @Mock
+ private SuggestionControllerMixin mSuggestionControllerMixin;
private Context mContext;
private DashboardSummary mSummary;
@@ -111,12 +113,31 @@ public class DashboardSummaryTest {
@Test
public void updateCategory_shouldGetCategoryFromFeatureProvider() {
+ ReflectionHelpers.setField(mSummary, "mSuggestionControllerMixin",
+ mSuggestionControllerMixin);
+
+ when(mSuggestionControllerMixin.isSuggestionLoaded()).thenReturn(true);
doReturn(mock(Activity.class)).when(mSummary).getActivity();
mSummary.onAttach(mContext);
mSummary.updateCategory();
verify(mSummaryLoader).updateSummaryToCache(nullable(DashboardCategory.class));
verify(mDashboardFeatureProvider).getTilesForCategory(CategoryKey.CATEGORY_HOMEPAGE);
+ verify(mAdapter).setCategory(any());
+ }
+
+ @Test
+ public void updateCategory_shouldGetCategoryFromFeatureProvider_evenIfSuggestionDisabled() {
+ when(mFeatureFactory.suggestionsFeatureProvider.isSuggestionEnabled(any(Context.class)))
+ .thenReturn(false);
+
+ doReturn(mock(Activity.class)).when(mSummary).getActivity();
+ mSummary.onAttach(mContext);
+ mSummary.updateCategory();
+
+ verify(mSummaryLoader).updateSummaryToCache(nullable(DashboardCategory.class));
+ verify(mDashboardFeatureProvider).getTilesForCategory(CategoryKey.CATEGORY_HOMEPAGE);
+ verify(mAdapter).setCategory(any());
}
@Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
index 30fdccb23ec..0cfe135d9c7 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundActivityPreferenceControllerTest.java
@@ -142,18 +142,17 @@ public class BackgroundActivityPreferenceControllerTest {
mController.handlePreferenceTreeClick(mPreference);
- verify(mController).showDialog();
+ verify(mController).showDialog(false /* restrict */);
}
@Test
- public void testHandlePreferenceTreeClick_unRestrictApp_setModeAllowed() {
+ public void testHandlePreferenceTreeClick_unRestrictApp_showDialog() {
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);
+ verify(mController).showDialog(true /* restrict */);
}
@Test
@@ -211,17 +210,4 @@ public class BackgroundActivityPreferenceControllerTest {
public void testIsAvailable_ReturnTrue() {
assertThat(mController.isAvailable()).isTrue();
}
-
- @Test
- public void testWarningDialog() {
- BackgroundActivityPreferenceController.WarningDialogFragment dialogFragment =
- new BackgroundActivityPreferenceController.WarningDialogFragment();
- dialogFragment.setTargetFragment(mFragment, 0);
- FragmentTestUtil.startFragment(dialogFragment);
- final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
- ShadowAlertDialog shadowDialog = shadowOf(dialog);
- final Button okButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
- shadowDialog.clickOn(okButton.getId());
- verify(mFragment).onLimitBackgroundActivity();
- }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index fe907510117..6bc6ee71664 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -32,6 +32,7 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -523,4 +524,27 @@ public class BatteryUtilsTest {
public void testIsLegacyApp_SdkLargerOrEqualThanO_ReturnFalse() {
assertThat(mBatteryUtils.isLegacyApp(HIGH_SDK_PACKAGE)).isFalse();
}
+
+ @Test
+ public void testSetForceAppStandby_forcePreOApp_forceTwoRestrictions() {
+ mBatteryUtils.setForceAppStandby(UID, LOW_SDK_PACKAGE, AppOpsManager.MODE_IGNORED);
+
+ // Restrict both OP_RUN_IN_BACKGROUND and OP_RUN_ANY_IN_BACKGROUND
+ verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID, LOW_SDK_PACKAGE,
+ AppOpsManager.MODE_IGNORED);
+ verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID, LOW_SDK_PACKAGE,
+ AppOpsManager.MODE_IGNORED);
+ }
+
+ @Test
+ public void testSetForceAppStandby_forceOApp_forceOneRestriction() {
+ mBatteryUtils.setForceAppStandby(UID, HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED);
+
+ // Don't restrict OP_RUN_IN_BACKGROUND because it is already been restricted for O app
+ verify(mAppOpsManager, never()).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID,
+ HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED);
+ // Restrict OP_RUN_ANY_IN_BACKGROUND
+ verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID,
+ HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
index ddee31461c3..ec7238449dd 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipDialogFragmentTest.java
@@ -27,10 +27,13 @@ import android.text.format.DateUtils;
import com.android.settings.R;
import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.testutils.shadow.ShadowRuntimePermissionPresenter;
+import com.android.settings.testutils.shadow.ShadowUtils;
import org.junit.Before;
import org.junit.Test;
@@ -47,14 +50,18 @@ import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
- shadows = ShadowRuntimePermissionPresenter.class)
+ shadows = ShadowUtils.class)
public class BatteryTipDialogFragmentTest {
private static final String PACKAGE_NAME = "com.android.app";
+ private static final String DISPLAY_NAME = "app";
private static final long SCREEN_TIME_MS = DateUtils.HOUR_IN_MILLIS;
private BatteryTipDialogFragment mDialogFragment;
private Context mContext;
private HighUsageTip mHighUsageTip;
+ private RestrictAppTip mRestrictedOneAppTip;
+ private RestrictAppTip mRestrictAppsTip;
+ private UnrestrictAppTip mUnrestrictAppTip;
@Before
public void setUp() {
@@ -64,9 +71,22 @@ public class BatteryTipDialogFragmentTest {
FakeFeatureFactory.setupForTest();
List highUsageTips = new ArrayList<>();
- highUsageTips.add(new AppInfo.Builder().setScreenOnTimeMs(SCREEN_TIME_MS).setPackageName(
- PACKAGE_NAME).build());
+ final AppInfo appInfo = new AppInfo.Builder()
+ .setScreenOnTimeMs(SCREEN_TIME_MS)
+ .setPackageName(PACKAGE_NAME)
+ .build();
+ highUsageTips.add(appInfo);
mHighUsageTip = new HighUsageTip(SCREEN_TIME_MS, highUsageTips);
+
+ final List restrictApps = new ArrayList<>();
+ restrictApps.add(appInfo);
+ mRestrictedOneAppTip = new RestrictAppTip(BatteryTip.StateType.NEW,
+ new ArrayList<>(restrictApps));
+ restrictApps.add(appInfo);
+ mRestrictAppsTip = new RestrictAppTip(BatteryTip.StateType.NEW,
+ new ArrayList<>(restrictApps));
+
+ mUnrestrictAppTip = new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo);
}
@Test
@@ -82,5 +102,49 @@ public class BatteryTipDialogFragmentTest {
mContext.getString(R.string.battery_tip_dialog_message, "1h"));
}
+ @Test
+ public void testOnCreateDialog_restrictOneAppTip_fireRestrictOneAppDialog() {
+ mDialogFragment = BatteryTipDialogFragment.newInstance(mRestrictedOneAppTip);
+
+ FragmentTestUtil.startFragment(mDialogFragment);
+
+ final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
+ ShadowAlertDialog shadowDialog = shadowOf(dialog);
+
+ assertThat(shadowDialog.getTitle()).isEqualTo("Restrict app?");
+ assertThat(shadowDialog.getMessage()).isEqualTo(
+ mContext.getString(R.string.battery_tip_restrict_app_dialog_message));
+ }
+
+ @Test
+ public void testOnCreateDialog_restrictAppsTip_fireRestrictAppsDialog() {
+ mDialogFragment = BatteryTipDialogFragment.newInstance(mRestrictAppsTip);
+
+ FragmentTestUtil.startFragment(mDialogFragment);
+
+ final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
+ ShadowAlertDialog shadowDialog = shadowOf(dialog);
+
+ assertThat(shadowDialog.getTitle()).isEqualTo("Restrict 2 apps?");
+ assertThat(shadowDialog.getMessage()).isEqualTo(
+ mContext.getString(R.string.battery_tip_restrict_app_dialog_message));
+ assertThat(shadowDialog.getView()).isNotNull();
+ }
+
+ @Test
+ public void testOnCreateDialog_unRestrictAppTip_fireUnRestrictDialog() {
+ mDialogFragment = BatteryTipDialogFragment.newInstance(mUnrestrictAppTip);
+ ShadowUtils.setApplicationLabel(PACKAGE_NAME, DISPLAY_NAME);
+
+ FragmentTestUtil.startFragment(mDialogFragment);
+
+ final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
+ ShadowAlertDialog shadowDialog = shadowOf(dialog);
+
+ assertThat(shadowDialog.getTitle()).isEqualTo("Remove restriction for app?");
+ assertThat(shadowDialog.getMessage()).isEqualTo(
+ mContext.getString(R.string.battery_tip_unrestrict_app_dialog_message));
+ }
+
}
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 83b32258009..09e67edbdbc 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
@@ -51,6 +51,7 @@ import java.util.List;
public class BatteryTipLoaderTest {
private static final int[] TIP_ORDER = {
BatteryTip.TipType.SMART_BATTERY_MANAGER,
+ BatteryTip.TipType.APP_RESTRICTION,
BatteryTip.TipType.HIGH_DEVICE_USAGE,
BatteryTip.TipType.BATTERY_SAVER,
BatteryTip.TipType.LOW_BATTERY,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java
new file mode 100644
index 00000000000..47785d555b3
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/actions/RestrictAppActionTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.actions;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class RestrictAppActionTest {
+ private static final String PACKAGE_NAME_1 = "com.android.app1";
+ private static final String PACKAGE_NAME_2 = "com.android.app2";
+
+ @Mock
+ private BatteryUtils mBatteryUtils;
+ private Context mContext;
+ private RestrictAppAction mRestrictAppAction;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ final List mAppInfos = new ArrayList<>();
+ mAppInfos.add(new AppInfo.Builder()
+ .setPackageName(PACKAGE_NAME_1)
+ .build());
+ mAppInfos.add(new AppInfo.Builder()
+ .setPackageName(PACKAGE_NAME_2)
+ .build());
+
+ mRestrictAppAction = new RestrictAppAction(mContext, new RestrictAppTip(
+ BatteryTip.StateType.NEW, mAppInfos));
+ mRestrictAppAction.mBatteryUtils = mBatteryUtils;
+ }
+
+ @Test
+ public void testHandlePositiveAction() {
+ mRestrictAppAction.handlePositiveAction();
+
+ verify(mBatteryUtils).setForceAppStandby(anyInt(), eq(PACKAGE_NAME_1),
+ eq(AppOpsManager.MODE_IGNORED));
+ verify(mBatteryUtils).setForceAppStandby(anyInt(), eq(PACKAGE_NAME_2),
+ eq(AppOpsManager.MODE_IGNORED));
+ }
+
+
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
new file mode 100644
index 00000000000..e1dea17c1f6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/RestrictAppTipTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.tips;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Parcel;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class RestrictAppTipTest {
+ private static final String PACKAGE_NAME = "com.android.app";
+ private static final String DISPLAY_NAME = "app";
+
+ private Context mContext;
+ private RestrictAppTip mNewBatteryTip;
+ private RestrictAppTip mHandledBatteryTip;
+ private List mUsageAppList;
+ @Mock
+ private ApplicationInfo mApplicationInfo;
+ @Mock
+ private PackageManager mPackageManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(PACKAGE_NAME,
+ PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_ANY_USER);
+ doReturn(DISPLAY_NAME).when(mApplicationInfo).loadLabel(mPackageManager);
+
+ mUsageAppList = new ArrayList<>();
+ mUsageAppList.add(new AppInfo.Builder()
+ .setPackageName(PACKAGE_NAME)
+ .build());
+ mNewBatteryTip = new RestrictAppTip(BatteryTip.StateType.NEW, mUsageAppList);
+ mHandledBatteryTip = new RestrictAppTip(BatteryTip.StateType.HANDLED, mUsageAppList);
+ }
+
+ @Test
+ public void testParcelable() {
+ Parcel parcel = Parcel.obtain();
+ mNewBatteryTip.writeToParcel(parcel, mNewBatteryTip.describeContents());
+ parcel.setDataPosition(0);
+
+ final RestrictAppTip parcelTip = new RestrictAppTip(parcel);
+
+ assertThat(parcelTip.getType()).isEqualTo(BatteryTip.TipType.APP_RESTRICTION);
+ assertThat(parcelTip.getState()).isEqualTo(BatteryTip.StateType.NEW);
+ final AppInfo app = parcelTip.getRestrictAppList().get(0);
+ assertThat(app.packageName).isEqualTo(PACKAGE_NAME);
+ }
+
+ @Test
+ public void testGetTitle_stateNew_showRestrictTitle() {
+ assertThat(mNewBatteryTip.getTitle(mContext)).isEqualTo("Restrict 1 app");
+ }
+
+ @Test
+ public void testGetTitle_stateHandled_showHandledTitle() {
+ assertThat(mHandledBatteryTip.getTitle(mContext)).isEqualTo("1 recently restricted");
+ }
+
+ @Test
+ public void testGetSummary_stateNew_showRestrictSummary() {
+ assertThat(mNewBatteryTip.getSummary(mContext)).isEqualTo(
+ "app has high battery usage");
+ }
+
+ @Test
+ public void testGetSummary_stateHandled_showHandledSummary() {
+ assertThat(mHandledBatteryTip.getSummary(mContext)).isEqualTo(
+ "App changes are in progress");
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTipTest.java
new file mode 100644
index 00000000000..a83a1587b64
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/UnrestrictAppTipTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.tips;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.AppInfo;
+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;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class UnrestrictAppTipTest {
+ private static final String PACKAGE_NAME = "com.android.app";
+
+ private UnrestrictAppTip mBatteryTip;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ AppInfo appInfo = new AppInfo.Builder()
+ .setPackageName(PACKAGE_NAME)
+ .build();
+ mBatteryTip = new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo);
+ }
+
+ @Test
+ public void testParcelable() {
+ Parcel parcel = Parcel.obtain();
+ mBatteryTip.writeToParcel(parcel, mBatteryTip.describeContents());
+ parcel.setDataPosition(0);
+
+ final UnrestrictAppTip parcelTip = new UnrestrictAppTip(parcel);
+
+ assertThat(parcelTip.getType()).isEqualTo(BatteryTip.TipType.REMOVE_APP_RESTRICTION);
+ assertThat(parcelTip.getState()).isEqualTo(BatteryTip.StateType.NEW);
+ assertThat(parcelTip.getPackageName()).isEqualTo(PACKAGE_NAME);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/location/LocationFooterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/LocationFooterPreferenceControllerTest.java
new file mode 100644
index 00000000000..da00010ee53
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/location/LocationFooterPreferenceControllerTest.java
@@ -0,0 +1,220 @@
+/*
+ * 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.location;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.arch.lifecycle.LifecycleOwner;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+/** Unit tests for {@link LocationFooterPreferenceController} */
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class LocationFooterPreferenceControllerTest {
+
+ @Mock
+ private PreferenceCategory mPreferenceCategory;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private Resources mResources;
+ private Context mContext;
+ private LocationFooterPreferenceController mController;
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
+ private static final int TEST_RES_ID = 1234;
+ private static final String TEST_TEXT = "text";
+
+ @Before
+ public void setUp() throws NameNotFoundException {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+ when(mPreferenceCategory.getContext()).thenReturn(mContext);
+ mController = spy(new LocationFooterPreferenceController(mContext, mLifecycle));
+ when(mPackageManager.getResourcesForApplication(any(ApplicationInfo.class)))
+ .thenReturn(mResources);
+ when(mResources.getString(TEST_RES_ID)).thenReturn(TEST_TEXT);
+ doNothing().when(mPreferenceCategory).removeAll();
+ }
+
+ @Test
+ public void isAvailable_hasValidFooter_returnsTrue() throws NameNotFoundException {
+ final List testResolveInfos = new ArrayList<>();
+ testResolveInfos.add(
+ getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
+ when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+ .thenReturn(testResolveInfos);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_noSystemApp_returnsFalse() throws NameNotFoundException {
+ final List testResolveInfos = new ArrayList<>();
+ testResolveInfos.add(
+ getTestResolveInfo(/*isSystemApp*/ false, /*hasRequiredMetadata*/ true));
+ when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+ .thenReturn(testResolveInfos);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_noRequiredMetadata_returnsFalse() throws NameNotFoundException {
+ final List testResolveInfos = new ArrayList<>();
+ testResolveInfos.add(
+ getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ false));
+ when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+ .thenReturn(testResolveInfos);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void sendBroadcastFooterInject() {
+ ArgumentCaptor intent = ArgumentCaptor.forClass(Intent.class);
+ final ActivityInfo activityInfo =
+ getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true).activityInfo;
+ mController.sendBroadcastFooterDisplayed(
+ new ComponentName(activityInfo.packageName, activityInfo.name));
+ verify(mContext).sendBroadcast(intent.capture());
+ assertThat(intent.getValue().getAction())
+ .isEqualTo(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION);
+ }
+
+ @Test
+ public void updateState_sendBroadcast() throws NameNotFoundException {
+ final List testResolveInfos = new ArrayList<>();
+ testResolveInfos.add(
+ getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
+ when(mPackageManager.queryBroadcastReceivers(any(), anyInt()))
+ .thenReturn(testResolveInfos);
+ mController.updateState(mPreferenceCategory);
+ ArgumentCaptor intent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcast(intent.capture());
+ assertThat(intent.getValue().getAction())
+ .isEqualTo(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION);
+ }
+
+ @Test
+ public void updateState_addPreferences() throws NameNotFoundException {
+ final List testResolveInfos = new ArrayList<>();
+ testResolveInfos.add(
+ getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
+ when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+ .thenReturn(testResolveInfos);
+ mController.updateState(mPreferenceCategory);
+ ArgumentCaptor pref = ArgumentCaptor.forClass(Preference.class);
+ verify(mPreferenceCategory).addPreference(pref.capture());
+ assertThat(pref.getValue().getTitle()).isEqualTo(TEST_TEXT);
+ }
+
+ @Test
+ public void updateState_notSystemApp_ignore() throws NameNotFoundException {
+ final List testResolveInfos = new ArrayList<>();
+ testResolveInfos.add(
+ getTestResolveInfo(/*isSystemApp*/ false, /*hasRequiredMetadata*/ true));
+ when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+ .thenReturn(testResolveInfos);
+ mController.updateState(mPreferenceCategory);
+ verify(mPreferenceCategory, never()).addPreference(any(Preference.class));
+ verify(mContext, never()).sendBroadcast(any(Intent.class));
+ }
+
+ @Test
+ public void updateState_thenOnPause_sendBroadcasts() throws NameNotFoundException {
+ final List testResolveInfos = new ArrayList<>();
+ testResolveInfos.add(
+ getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
+ when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
+ .thenReturn(testResolveInfos);
+ mController.updateState(mPreferenceCategory);
+ ArgumentCaptor intent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcast(intent.capture());
+ assertThat(intent.getValue().getAction())
+ .isEqualTo(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION);
+
+ mController.onPause();
+ verify(mContext, times(2)).sendBroadcast(intent.capture());
+ assertThat(intent.getValue().getAction())
+ .isEqualTo(LocationManager.SETTINGS_FOOTER_REMOVED_ACTION);
+ }
+
+ @Test
+ public void onPause_doNotSendBroadcast() {
+ mController.onPause();
+ verify(mContext, never()).sendBroadcast(any(Intent.class));
+ }
+
+ /**
+ * Returns a ResolveInfo object for testing
+ * @param isSystemApp If true, the application is a system app.
+ * @param hasRequiredMetaData If true, the broadcast receiver has a valid value for
+ * {@link LocationManager#METADATA_SETTINGS_FOOTER_STRING}
+ */
+ private ResolveInfo getTestResolveInfo(boolean isSystemApp, boolean hasRequiredMetaData) {
+ ResolveInfo testResolveInfo = new ResolveInfo();
+ ApplicationInfo testAppInfo = new ApplicationInfo();
+ if (isSystemApp) {
+ testAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ }
+ ActivityInfo testActivityInfo = new ActivityInfo();
+ testActivityInfo.name = "TestActivityName";
+ testActivityInfo.packageName = "TestPackageName";
+ testActivityInfo.applicationInfo = testAppInfo;
+ if (hasRequiredMetaData) {
+ testActivityInfo.metaData = new Bundle();
+ testActivityInfo.metaData.putInt(
+ LocationManager.METADATA_SETTINGS_FOOTER_STRING, TEST_RES_ID);
+ }
+ testResolveInfo.activityInfo = testActivityInfo;
+ return testResolveInfo;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
index 06910862d3b..b4169913a8d 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
@@ -27,6 +27,9 @@ import com.android.settings.wrapper.FingerprintManagerWrapper;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import java.util.HashMap;
+import java.util.Map;
+
@Implements(Utils.class)
public class ShadowUtils {
@@ -34,6 +37,7 @@ public class ShadowUtils {
private static boolean sIsUserAMonkey;
private static boolean sIsDemoUser;
private static ComponentName sDeviceOwnerComponentName;
+ private static Map sAppNameMap;
@Implementation
public static int enforceSameOwner(Context context, int userId) {
@@ -89,4 +93,19 @@ public class ShadowUtils {
public static int getManagedProfileId(UserManager um, int parentUserId) {
return UserHandle.USER_NULL;
}
+
+ @Implementation
+ public static CharSequence getApplicationLabel(Context context, String packageName) {
+ if (sAppNameMap != null) {
+ return sAppNameMap.get(packageName);
+ }
+ return null;
+ }
+
+ public static void setApplicationLabel(String packageName, String appLabel) {
+ if (sAppNameMap == null) {
+ sAppNameMap = new HashMap<>();
+ }
+ sAppNameMap.put(packageName, appLabel);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/widget/AppPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/AppPreferenceTest.java
index d4890942c6d..6556f1c7136 100644
--- a/tests/robotests/src/com/android/settings/widget/AppPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/widget/AppPreferenceTest.java
@@ -36,6 +36,8 @@ import org.robolectric.annotation.Config;
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class AppPreferenceTest {
+ private static final int EXPECTED_APP_ICON_SIZE_DP = 32;
+
private Context mContext;
private View mRootView;
private AppPreference mPref;
@@ -75,4 +77,12 @@ public class AppPreferenceTest {
assertThat(mHolder.findViewById(R.id.summary_container).getVisibility())
.isEqualTo(View.GONE);
}
+
+ @Test
+ public void foobar_testName() {
+ // Can't use isEquals() to compare float. Use isWithIn().of() instead.
+ assertThat(mContext.getResources().getDimension(R.dimen.secondary_app_icon_size))
+ .isWithin(0.01f)
+ .of(EXPECTED_APP_ICON_SIZE_DP);
+ }
}
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 dca69748a95..4b18fcff273 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java
@@ -73,7 +73,8 @@ import java.util.ArrayList;
})
public class WifiTetherPreferenceControllerTest {
- @Mock
+ private static final String SSID = "Pixel";
+
private Context mContext;
@Mock
private ConnectivityManager mConnectivityManager;
@@ -81,6 +82,8 @@ public class WifiTetherPreferenceControllerTest {
private WifiManager mWifiManager;
@Mock
private PreferenceScreen mScreen;
+ @Mock
+ private WifiConfiguration mWifiConfiguration;
private WifiTetherPreferenceController mController;
private Lifecycle mLifecycle;
@@ -90,6 +93,8 @@ public class WifiTetherPreferenceControllerTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
FakeFeatureFactory.setupForTest();
@@ -98,10 +103,13 @@ public class WifiTetherPreferenceControllerTest {
.thenReturn(mConnectivityManager);
when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+ when(mWifiManager.getWifiApConfiguration()).thenReturn(mWifiConfiguration);
+ mWifiConfiguration.SSID = SSID;
when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
mController = new WifiTetherPreferenceController(mContext, mLifecycle,
false /* initSoftApManager */);
+ mController.displayPreference(mScreen);
}
@After
@@ -127,7 +135,6 @@ public class WifiTetherPreferenceControllerTest {
public void startAndStop_shouldRegisterUnregisterReceiver() {
final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
- mController.displayPreference(mScreen);
mLifecycle.handleLifecycleEvent(ON_START);
mLifecycle.handleLifecycleEvent(ON_STOP);
@@ -166,48 +173,6 @@ public class WifiTetherPreferenceControllerTest {
verify(pref).setChecked(true);
}
- @Test
- public void testReceiver_apStateChangedToDisabled_shouldUpdatePreferenceSummary() {
- mController.displayPreference(mScreen);
- receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_DISABLED);
- assertThat(mPreference.getSummary().toString()).isEqualTo(
- RuntimeEnvironment.application.getString(R.string.wifi_hotspot_off_subtext));
- }
-
- @Test
- public void testReceiver_apStateChangedToDisabling_shouldUpdatePreferenceSummary() {
- mController.displayPreference(mScreen);
- receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_DISABLING);
- assertThat(mPreference.getSummary().toString()).isEqualTo(
- RuntimeEnvironment.application.getString(R.string.wifi_tether_stopping));
- }
-
- @Test
- public void testReceiver_apStateChangedToEnabling_shouldUpdatePreferenceSummary() {
- mController.displayPreference(mScreen);
- receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_ENABLING);
- assertThat(mPreference.getSummary().toString()).isEqualTo(
- RuntimeEnvironment.application.getString(R.string.wifi_tether_starting));
- }
-
- @Test
- public void testReceiver_apStateChangedToEnabled_shouldNotUpdatePreferenceSummary() {
- mController.displayPreference(mScreen);
- receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_DISABLED);
- assertThat(mPreference.getSummary().toString()).isEqualTo(
- RuntimeEnvironment.application.getString(R.string.wifi_hotspot_off_subtext));
-
- // When turning on the hotspot, we receive STATE_ENABLING followed by STATE_ENABLED. The
- // first should change the status to wifi_tether_starting, and the second should not change
- // this.
- receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_ENABLING);
- assertThat(mPreference.getSummary().toString()).isEqualTo(
- RuntimeEnvironment.application.getString(R.string.wifi_tether_starting));
- receiveApStateChangedBroadcast(WifiManager.WIFI_AP_STATE_ENABLED);
- assertThat(mPreference.getSummary().toString()).isEqualTo(
- RuntimeEnvironment.application.getString(R.string.wifi_tether_starting));
- }
-
@Test
public void testReceiver_goingToAirplaneMode_shouldClearPreferenceSummary() {
final ContentResolver cr = mock(ContentResolver.class);
@@ -224,22 +189,32 @@ public class WifiTetherPreferenceControllerTest {
}
@Test
- public void testReceiver_tetherEnabled_shouldUpdatePreferenceSummary() {
- mController.displayPreference(mScreen);
- final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
- final Intent broadcast = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
- final ArrayList activeTethers = new ArrayList<>();
- activeTethers.add("1");
- broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, activeTethers);
- broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
- new ArrayList<>());
- final WifiConfiguration configuration = new WifiConfiguration();
- configuration.SSID = "test-ap";
- when(mWifiManager.getWifiApConfiguration()).thenReturn(configuration);
+ public void testHandleWifiApStateChanged_stateEnabling_showEnablingSummary() {
+ mController.handleWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0 /* reason */);
- receiver.onReceive(RuntimeEnvironment.application, broadcast);
+ assertThat(mPreference.getSummary()).isEqualTo("Turning hotspot on\u2026");
+ }
- verify(mContext).getString(eq(R.string.wifi_tether_enabled_subtext), any());
+ @Test
+ public void testHandleWifiApStateChanged_stateEnabled_showEnabledSummary() {
+ mController.handleWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0 /* reason */);
+
+ assertThat(mPreference.getSummary()).isEqualTo("Pixel is active");
+ }
+
+ @Test
+ public void testHandleWifiApStateChanged_stateDisabling_showDisablingSummary() {
+ mController.handleWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0 /* reason */);
+
+ assertThat(mPreference.getSummary()).isEqualTo("Turning off hotspot\u2026");
+ }
+
+ @Test
+ public void testHandleWifiApStateChanged_stateDisabled_showDisabledSummary() {
+ mController.handleWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0 /* reason */);
+
+ assertThat(mPreference.getSummary()).isEqualTo(
+ "Not sharing internet or content with other devices");
}
@Implements(WifiTetherSettings.class)
@@ -285,17 +260,4 @@ public class WifiTetherPreferenceControllerTest {
onStopCalled = true;
}
}
-
- /**
- * Helper to cause the controller to receive a WIFI_AP_STATE_CHANGED_ACTION with a specific
- * state.
- *
- * @param state - the state, as specified by one of the WifiManager.WIFI_AP_STATE_* values
- */
- private void receiveApStateChangedBroadcast(int state) {
- final BroadcastReceiver receiver = ReflectionHelpers.getField(mController, "mReceiver");
- final Intent broadcast = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- broadcast.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, state);
- receiver.onReceive(RuntimeEnvironment.application, broadcast);
- }
}