diff --git a/res/values/strings.xml b/res/values/strings.xml
index bd7a47cee67..28973d76281 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7365,9 +7365,9 @@
Don\'t update
- Your phone mutes sounds & vibrations\n\nUpdate settings to also:\n\n- Hide notifications\n\n- Only show calls from starred contacts
+ Your phone can do more to help you focus.\n\nUpdate settings to:\n\n- Hide notifications completely\n\n- Allow calls from starred contacts and repeat callers
- Update your Do Not Disturb settings?
+ Update Do Not Disturb?
Settings
diff --git a/src/com/android/settings/RestrictedListPreference.java b/src/com/android/settings/RestrictedListPreference.java
index 25d4fc98d8f..d581af6ad26 100644
--- a/src/com/android/settings/RestrictedListPreference.java
+++ b/src/com/android/settings/RestrictedListPreference.java
@@ -16,10 +16,16 @@
package com.android.settings;
+import android.app.ActivityManager;
import android.app.AlertDialog;
+import android.app.KeyguardManager;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.ListPreferenceDialogFragment;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
@@ -32,6 +38,7 @@ import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
+import com.android.settings.Utils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreferenceHelper;
@@ -43,6 +50,8 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
public class RestrictedListPreference extends CustomListPreference {
private final RestrictedPreferenceHelper mHelper;
private final List mRestrictedItems = new ArrayList<>();
+ private boolean mRequiresActiveUnlockedProfile = false;
+ private int mProfileUserId;
public RestrictedListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -68,6 +77,24 @@ public class RestrictedListPreference extends CustomListPreference {
@Override
public void performClick() {
+ if (mRequiresActiveUnlockedProfile) {
+ // Check if the profile is started, first.
+ if (Utils.startQuietModeDialogIfNecessary(getContext(), UserManager.get(getContext()),
+ mProfileUserId)) {
+ return;
+ }
+
+ // Next, check if the profile is unlocked.
+ KeyguardManager manager =
+ (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
+ if (manager.isDeviceLocked(mProfileUserId)) {
+ Intent intent = manager.createConfirmDeviceCredentialIntent(
+ null, null, mProfileUserId);
+ getContext().startActivity(intent);
+ return;
+ }
+ }
+
if (!mHelper.performClick()) {
super.performClick();
}
@@ -92,6 +119,14 @@ public class RestrictedListPreference extends CustomListPreference {
return mHelper.isDisabledByAdmin();
}
+ public void setRequiresActiveUnlockedProfile(boolean reqState) {
+ mRequiresActiveUnlockedProfile = reqState;
+ }
+
+ public void setProfileUserId(int profileUserId) {
+ mProfileUserId = profileUserId;
+ }
+
public boolean isRestrictedForEntry(CharSequence entry) {
if (entry == null) {
return false;
@@ -263,4 +298,4 @@ public class RestrictedListPreference extends CustomListPreference {
this.enforcedAdmin = enforcedAdmin;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index 3fd7ced3e03..2cc623331e2 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -56,6 +56,7 @@ public final class BluetoothDevicePreference extends GearPreference implements
private AlertDialog mDisconnectDialog;
private String contentDescription = null;
+ private boolean mHideSecondTarget = false;
/* Talk-back descriptions for various BT icons */
Resources mResources;
@@ -86,7 +87,8 @@ public final class BluetoothDevicePreference extends GearPreference implements
protected boolean shouldHideSecondTarget() {
return mCachedDevice == null
|| mCachedDevice.getBondState() != BluetoothDevice.BOND_BONDED
- || mUserManager.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH);
+ || mUserManager.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH)
+ || mHideSecondTarget;
}
@Override
@@ -112,6 +114,10 @@ public final class BluetoothDevicePreference extends GearPreference implements
return mCachedDevice;
}
+ public void hideSecondTarget(boolean hideSecondTarget) {
+ mHideSecondTarget = hideSecondTarget;
+ }
+
public void onDeviceAttributesChanged() {
/*
* The preference framework takes care of making sure the value has
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
index 595d9518944..dfcbfca1e06 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java
@@ -56,29 +56,14 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
protected final DevicePreferenceCallback mDevicePreferenceCallback;
protected final Map mPreferenceMap;
protected Context mPrefContext;
+ protected DashboardFragment mFragment;
private final boolean mShowDeviceWithoutNames;
- private DashboardFragment mFragment;
private Preference.OnPreferenceClickListener mDevicePreferenceClickListener = null;
@VisibleForTesting
final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> {
- final CachedBluetoothDevice device =
- ((BluetoothDevicePreference) pref).getBluetoothDevice();
- if (device == null) {
- return;
- }
- final Bundle args = new Bundle();
- args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS,
- device.getDevice().getAddress());
-
- new SubSettingLauncher(mFragment.getContext())
- .setDestination(BluetoothDeviceDetailsFragment.class.getName())
- .setArguments(args)
- .setTitle(R.string.device_details_title)
- .setSourceMetricsCategory(mFragment.getMetricsCategory())
- .launch();
-
+ launchDeviceDetails(pref);
};
private class PreferenceClickListener implements
@@ -201,7 +186,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
public abstract boolean isFilterMatched(CachedBluetoothDevice cachedBluetoothDevice);
/**
- * Update whether to show {@cde cachedBluetoothDevice} in the list.
+ * Update whether to show {@link CachedBluetoothDevice} in the list.
*/
protected void update(CachedBluetoothDevice cachedBluetoothDevice) {
if (isFilterMatched(cachedBluetoothDevice)) {
@@ -239,6 +224,28 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback,
}
}
+ /**
+ * Get {@link CachedBluetoothDevice} from {@link Preference} and it is used to init
+ * {@link SubSettingLauncher} to launch {@link BluetoothDeviceDetailsFragment}
+ */
+ protected void launchDeviceDetails(Preference preference) {
+ final CachedBluetoothDevice device =
+ ((BluetoothDevicePreference) preference).getBluetoothDevice();
+ if (device == null) {
+ return;
+ }
+ final Bundle args = new Bundle();
+ args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS,
+ device.getDevice().getAddress());
+
+ new SubSettingLauncher(mFragment.getContext())
+ .setDestination(BluetoothDeviceDetailsFragment.class.getName())
+ .setArguments(args)
+ .setTitle(R.string.device_details_title)
+ .setSourceMetricsCategory(mFragment.getMetricsCategory())
+ .launch();
+ }
+
/**
* @return {@code true} if {@code cachedBluetoothDevice} is connected
* and the bond state is bonded.
diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
index 55f4bb1dd6b..11702bcefc2 100644
--- a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
@@ -16,11 +16,14 @@
package com.android.settings.bluetooth;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.media.AudioManager;
import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
import android.util.Log;
+
import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -116,4 +119,20 @@ public class ConnectedBluetoothDeviceUpdater extends BluetoothDeviceUpdater {
}
return isFilterMatched;
}
+
+ @Override
+ protected void addPreference(CachedBluetoothDevice cachedDevice) {
+ super.addPreference(cachedDevice);
+ final BluetoothDevice device = cachedDevice.getDevice();
+ if (mPreferenceMap.containsKey(device)) {
+ final BluetoothDevicePreference btPreference =
+ (BluetoothDevicePreference) mPreferenceMap.get(device);
+ btPreference.setOnGearClickListener(null);
+ btPreference.hideSecondTarget(true);
+ btPreference.setOnPreferenceClickListener((Preference p) -> {
+ launchDeviceDetails(p);
+ return true;
+ });
+ }
+ }
}
diff --git a/src/com/android/settings/network/PrivateDnsPreferenceController.java b/src/com/android/settings/network/PrivateDnsPreferenceController.java
index 50224caba1a..47aa4dcffe4 100644
--- a/src/com/android/settings/network/PrivateDnsPreferenceController.java
+++ b/src/com/android/settings/network/PrivateDnsPreferenceController.java
@@ -24,6 +24,10 @@ import android.content.Context;
import android.content.ContentResolver;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
+import android.net.Network;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -31,6 +35,7 @@ import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
+import com.android.internal.util.ArrayUtils;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerMixin;
@@ -38,6 +43,8 @@ import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import java.net.InetAddress;
+import java.util.List;
public class PrivateDnsPreferenceController extends BasePreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
@@ -50,12 +57,15 @@ public class PrivateDnsPreferenceController extends BasePreferenceController
private final Handler mHandler;
private final ContentObserver mSettingsObserver;
+ private final ConnectivityManager mConnectivityManager;
+ private LinkProperties mLatestLinkProperties;
private Preference mPreference;
public PrivateDnsPreferenceController(Context context) {
super(context, KEY_PRIVATE_DNS_SETTINGS);
mHandler = new Handler(Looper.getMainLooper());
mSettingsObserver = new PrivateDnsSettingsObserver(mHandler);
+ mConnectivityManager = context.getSystemService(ConnectivityManager.class);
}
@Override
@@ -80,11 +90,17 @@ public class PrivateDnsPreferenceController extends BasePreferenceController
for (Uri uri : SETTINGS_URIS) {
mContext.getContentResolver().registerContentObserver(uri, false, mSettingsObserver);
}
+ final Network defaultNetwork = mConnectivityManager.getActiveNetwork();
+ if (defaultNetwork != null) {
+ mLatestLinkProperties = mConnectivityManager.getLinkProperties(defaultNetwork);
+ }
+ mConnectivityManager.registerDefaultNetworkCallback(mNetworkCallback, mHandler);
}
@Override
public void onStop() {
mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
}
@Override
@@ -92,13 +108,23 @@ public class PrivateDnsPreferenceController extends BasePreferenceController
final Resources res = mContext.getResources();
final ContentResolver cr = mContext.getContentResolver();
final String mode = PrivateDnsModeDialogPreference.getModeFromSettings(cr);
+ final LinkProperties lp = mLatestLinkProperties;
+ final List dnses = (lp == null) ? null : lp.getValidatedPrivateDnsServers();
+ final boolean dnsesResolved = !ArrayUtils.isEmpty(dnses);
switch (mode) {
case PRIVATE_DNS_MODE_OFF:
return res.getString(R.string.private_dns_mode_off);
case PRIVATE_DNS_MODE_OPPORTUNISTIC:
- return res.getString(R.string.private_dns_mode_opportunistic);
+ // TODO (b/79122154) : create a string specifically for this, instead of
+ // hijacking a string from notifications. This is necessary at this time
+ // because string freeze is in the past and this string has the right
+ // content at this moment.
+ return dnsesResolved ? res.getString(R.string.switch_on_text)
+ : res.getString(R.string.private_dns_mode_opportunistic);
case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
- return PrivateDnsModeDialogPreference.getHostnameFromSettings(cr);
+ return dnsesResolved
+ ? PrivateDnsModeDialogPreference.getHostnameFromSettings(cr)
+ : res.getString(R.string.private_dns_mode_provider_failure);
}
return "";
}
@@ -111,8 +137,25 @@ public class PrivateDnsPreferenceController extends BasePreferenceController
@Override
public void onChange(boolean selfChange) {
if (mPreference != null) {
- PrivateDnsPreferenceController.this.updateState(mPreference);
+ updateState(mPreference);
}
}
}
+
+ private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
+ mLatestLinkProperties = lp;
+ if (mPreference != null) {
+ updateState(mPreference);
+ }
+ }
+ @Override
+ public void onLost(Network network) {
+ mLatestLinkProperties = null;
+ if (mPreference != null) {
+ updateState(mPreference);
+ }
+ }
+ };
}
diff --git a/src/com/android/settings/notification/LockScreenNotificationPreferenceController.java b/src/com/android/settings/notification/LockScreenNotificationPreferenceController.java
index 2dfe8f35ab6..3b4f00eebb5 100644
--- a/src/com/android/settings/notification/LockScreenNotificationPreferenceController.java
+++ b/src/com/android/settings/notification/LockScreenNotificationPreferenceController.java
@@ -19,16 +19,12 @@ package com.android.settings.notification;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
-import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
-import android.app.KeyguardManager;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -101,6 +97,8 @@ public class LockScreenNotificationPreferenceController extends AbstractPreferen
}
if (mProfileUserId != UserHandle.USER_NULL) {
mLockscreenProfile = (RestrictedListPreference) screen.findPreference(mWorkSettingKey);
+ mLockscreenProfile.setRequiresActiveUnlockedProfile(true);
+ mLockscreenProfile.setProfileUserId(mProfileUserId);
} else {
setVisible(screen, mWorkSettingKey, false /* visible */);
setVisible(screen, mWorkSettingCategoryKey, false /* visible */);
@@ -244,39 +242,6 @@ public class LockScreenNotificationPreferenceController extends AbstractPreferen
return false;
}
- @Override
- public boolean handlePreferenceTreeClick(Preference preference) {
- final String key = preference.getKey();
- if (!TextUtils.equals(mWorkSettingKey, key)) {
- return false;
- }
-
- // Check if the profile is started, first.
- if (Utils.startQuietModeDialogIfNecessary(mContext, UserManager.get(mContext),
- mProfileUserId)) {
- return true;
- }
-
- // Next, check if the profile is unlocked.
- KeyguardManager manager =
- (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- if (manager.isDeviceLocked(mProfileUserId)) {
- //TODO: Figure out how to return the user to the current activity so they
- //don't have to navigate to the settings again.
- Intent intent = manager.createConfirmDeviceCredentialIntent(
- null, null, mProfileUserId);
- try {
- ActivityManager.getService().startConfirmDeviceCredentialIntent(intent,
- null /*options*/);
- } catch (RemoteException ignored) {
- }
-
- return true;
- }
-
- return false;
- }
-
private void setRestrictedIfNotificationFeaturesDisabled(CharSequence entry,
CharSequence entryValue, int keyguardNotificationFeatures) {
RestrictedLockUtils.EnforcedAdmin admin =
diff --git a/src/com/android/settings/notification/ZenOnboardingActivity.java b/src/com/android/settings/notification/ZenOnboardingActivity.java
index baca8110ff3..9d71f54c712 100644
--- a/src/com/android/settings/notification/ZenOnboardingActivity.java
+++ b/src/com/android/settings/notification/ZenOnboardingActivity.java
@@ -18,12 +18,10 @@ package com.android.settings.notification;
import android.app.Activity;
import android.app.NotificationManager;
-import android.content.Intent;
+import android.app.NotificationManager.Policy;
import android.os.Bundle;
-import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.view.View;
-import android.widget.CheckBox;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -67,12 +65,13 @@ public class ZenOnboardingActivity extends Activity {
public void save(View button) {
mMetrics.action(MetricsEvent.ACTION_ZEN_ONBOARDING_OK);
- NotificationManager.Policy policy = mNm.getNotificationPolicy();
+ Policy policy = mNm.getNotificationPolicy();
- NotificationManager.Policy newPolicy = new NotificationManager.Policy(
- policy.priorityCategories, NotificationManager.Policy.PRIORITY_SENDERS_STARRED,
+ Policy newPolicy = new NotificationManager.Policy(
+ Policy.PRIORITY_CATEGORY_REPEAT_CALLERS | policy.priorityCategories,
+ Policy.PRIORITY_SENDERS_STARRED,
policy.priorityMessageSenders,
- NotificationManager.Policy.getAllSuppressedVisualEffects());
+ Policy.getAllSuppressedVisualEffects());
mNm.setNotificationPolicy(newPolicy);
finishAndRemoveTask();
diff --git a/src/com/android/settings/widget/SwitchBar.java b/src/com/android/settings/widget/SwitchBar.java
index c33f6032cac..004e89c0ed6 100644
--- a/src/com/android/settings/widget/SwitchBar.java
+++ b/src/com/android/settings/widget/SwitchBar.java
@@ -25,6 +25,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.ColorInt;
import android.support.annotation.StringRes;
+import android.support.annotation.VisibleForTesting;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.TextAppearanceSpan;
@@ -132,6 +133,17 @@ public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedC
(switchView, isChecked) -> setTextViewLabelAndBackground(isChecked));
mRestrictedIcon = findViewById(R.id.restricted_icon);
+ mRestrictedIcon.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mDisabledByAdmin) {
+ mMetricsFeatureProvider.count(mContext,
+ mMetricsTag + "/switch_bar|restricted", 1);
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context,
+ mEnforcedAdmin);
+ }
+ }
+ });
// Default is hide
setVisibility(View.GONE);
@@ -196,6 +208,11 @@ public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedC
mSwitch.setEnabled(enabled);
}
+ @VisibleForTesting
+ View getDelegatingView() {
+ return mDisabledByAdmin ? mRestrictedIcon : mSwitch;
+ }
+
/**
* If admin is not null, disables the text and switch but keeps the view clickable.
* Otherwise, calls setEnabled which will enables the entire view including
@@ -216,6 +233,8 @@ public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedC
mRestrictedIcon.setVisibility(View.GONE);
setEnabled(true);
}
+ setTouchDelegate(new TouchDelegate(new Rect(0, 0, getWidth(), getHeight()),
+ getDelegatingView()));
}
public final ToggleSwitch getSwitch() {
@@ -228,7 +247,8 @@ public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedC
mSwitch.setOnCheckedChangeListener(this);
// Make the entire bar work as a switch
post(() -> setTouchDelegate(
- new TouchDelegate(new Rect(0, 0, getWidth(), getHeight()), mSwitch)));
+ new TouchDelegate(new Rect(0, 0, getWidth(), getHeight()),
+ getDelegatingView())));
}
}
@@ -242,7 +262,8 @@ public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedC
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if ((w > 0) && (h > 0)) {
- setTouchDelegate(new TouchDelegate(new Rect(0, 0, w, h), mSwitch));
+ setTouchDelegate(new TouchDelegate(new Rect(0, 0, w, h),
+ getDelegatingView()));
}
}
diff --git a/tests/robotests/src/com/android/settings/RestrictedListPreferenceTest.java b/tests/robotests/src/com/android/settings/RestrictedListPreferenceTest.java
new file mode 100644
index 00000000000..1cca8428761
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/RestrictedListPreferenceTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.RuntimeEnvironment.application;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.app.KeyguardManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowUserManager;
+import com.android.settingslib.RestrictedPreferenceHelper;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowKeyguardManager;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(
+ shadows = {
+ ShadowUserManager.class,
+ ShadowKeyguardManager.class,
+ })
+public class RestrictedListPreferenceTest {
+ private static final int PROFILE_USER_ID = 11;
+ // From UnlaunchableAppActivity
+ private static final int UNLAUNCHABLE_REASON_QUIET_MODE = 1;
+ private static final String EXTRA_UNLAUNCHABLE_REASON = "unlaunchable_reason";
+
+ private ShadowUserManager mShadowUserManager;
+ private ShadowKeyguardManager mShadowKeyguardManager;
+ private RestrictedListPreference mPreference;
+ private RestrictedPreferenceHelper mMockHelper;
+
+ @Before
+ public void setUp() {
+ mShadowKeyguardManager =
+ Shadows.shadowOf(application.getSystemService(KeyguardManager.class));
+ mMockHelper = mock(RestrictedPreferenceHelper.class);
+ mShadowUserManager = ShadowUserManager.getShadow();
+ mPreference = new RestrictedListPreference(application, mock(AttributeSet.class));
+ mPreference.setProfileUserId(PROFILE_USER_ID);
+ ReflectionHelpers.setField(mPreference, "mHelper", mMockHelper);
+ }
+
+ @Test
+ public void performClick_profileLocked() {
+ mPreference.setRequiresActiveUnlockedProfile(true);
+ mShadowUserManager.setQuietModeEnabled(false);
+ mShadowKeyguardManager.setIsDeviceLocked(PROFILE_USER_ID, true);
+ // Device has to be marked as secure so the real KeyguardManager will create a non-null
+ // intent.
+ mShadowKeyguardManager.setIsDeviceSecure(PROFILE_USER_ID, true);
+ mPreference.performClick();
+ // Make sure that the performClick method on the helper is never reached.
+ verify(mMockHelper, never()).performClick();
+ // Assert that a CONFIRM_DEVICE_CREDENTIAL intent has been started.
+ Intent started = shadowOf(application).getNextStartedActivity();
+ assertThat(started.getExtras().getInt(Intent.EXTRA_USER_ID)).isEqualTo(PROFILE_USER_ID);
+ assertThat(started.getAction())
+ .isEqualTo(KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER);
+ }
+
+ @Test
+ public void performClick_profileDisabled() {
+ mPreference.setRequiresActiveUnlockedProfile(true);
+ mShadowUserManager.setQuietModeEnabled(true);
+ mShadowKeyguardManager.setIsDeviceLocked(PROFILE_USER_ID, false);
+ mPreference.performClick();
+ // Make sure that the performClick method on the helper is never reached.
+ verify(mMockHelper, never()).performClick();
+ // Assert that a new intent for enabling the work profile is started.
+ Intent started = shadowOf(application).getNextStartedActivity();
+ Bundle extras = started.getExtras();
+ int reason = extras.getInt(EXTRA_UNLAUNCHABLE_REASON);
+ assertThat(reason).isEqualTo(UNLAUNCHABLE_REASON_QUIET_MODE);
+ }
+
+ @Test
+ public void performClick_profileAvailable() {
+ // Verify that the helper's perfomClick method is called if the profile is
+ // available and unlocked.
+ mPreference.setRequiresActiveUnlockedProfile(true);
+ mShadowUserManager.setQuietModeEnabled(false);
+ mShadowKeyguardManager.setIsDeviceLocked(PROFILE_USER_ID, false);
+ when(mMockHelper.performClick()).thenReturn(true);
+ mPreference.performClick();
+ verify(mMockHelper).performClick();
+ }
+
+ @Test
+ public void performClick_profileLockedAndUnlockedProfileNotRequired() {
+ // Verify that even if the profile is disabled, if the Preference class does not
+ // require it than the regular flow takes place.
+ mPreference.setRequiresActiveUnlockedProfile(false);
+ mShadowUserManager.setQuietModeEnabled(true);
+ when(mMockHelper.performClick()).thenReturn(true);
+ mPreference.performClick();
+ verify(mMockHelper).performClick();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
index 9d69f59b7d3..56e638a9ed1 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
@@ -15,6 +15,7 @@
*/
package com.android.settings.bluetooth;
+import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -207,4 +208,15 @@ public class ConnectedBluetoothDeviceUpdaterTest {
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
}
+
+ @Test
+ public void addPreference_addPreference_shouldHideSecondTarget() {
+ BluetoothDevicePreference btPreference =
+ new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, true);
+ mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, btPreference);
+
+ mBluetoothDeviceUpdater.addPreference(mCachedBluetoothDevice);
+
+ assertThat(btPreference.shouldHideSecondTarget()).isTrue();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
index 83d4bd530b7..ce40ab69364 100644
--- a/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
@@ -24,17 +24,29 @@ import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.withSettings;
import static org.mockito.Mockito.when;
import android.arch.lifecycle.LifecycleOwner;
import android.content.Context;
import android.content.ContentResolver;
import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.os.Handler;
import android.provider.Settings;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
@@ -46,22 +58,45 @@ import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowContentResolver;
+import org.robolectric.shadows.ShadowServiceManager;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
public class PrivateDnsPreferenceControllerTest {
private final static String HOSTNAME = "dns.example.com";
+ private final static List NON_EMPTY_ADDRESS_LIST;
+ static {
+ try {
+ NON_EMPTY_ADDRESS_LIST = Arrays.asList(
+ InetAddress.getByAddress(new byte[] { 8, 8, 8, 8 }));
+ } catch (UnknownHostException e) {
+ throw new RuntimeException("Invalid hardcoded IP addresss: " + e);
+ }
+ }
@Mock
private PreferenceScreen mScreen;
@Mock
+ private ConnectivityManager mConnectivityManager;
+ @Mock
+ private Network mNetwork;
+ @Mock
private Preference mPreference;
+ @Captor
+ private ArgumentCaptor mCallbackCaptor;
private PrivateDnsPreferenceController mController;
private Context mContext;
private ContentResolver mContentResolver;
@@ -75,15 +110,41 @@ public class PrivateDnsPreferenceControllerTest {
mContext = spy(RuntimeEnvironment.application);
mContentResolver = mContext.getContentResolver();
mShadowContentResolver = Shadow.extract(mContentResolver);
+ when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+ .thenReturn(mConnectivityManager);
+ doNothing().when(mConnectivityManager).registerDefaultNetworkCallback(
+ mCallbackCaptor.capture(), nullable(Handler.class));
when(mScreen.findPreference(anyString())).thenReturn(mPreference);
mController = spy(new PrivateDnsPreferenceController(mContext));
+
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mLifecycle.addObserver(mController);
}
+ private void updateLinkProperties(LinkProperties lp) {
+ NetworkCallback nc = mCallbackCaptor.getValue();
+ // The network callback that has been captured by the captor is the `mNetworkCallback'
+ // member of mController. mController being a spy, it has copied that member from the
+ // original object it was spying on, which means the object returned by the captor
+ // has a reference to the original object instead of the mock as its outer instance
+ // and will call methods and modify members of the original object instead of the spy,
+ // so methods subsequently called on the spy will not be aware of the changes. To work
+ // around this, the following code will create a new instance of the same class with
+ // the same code, but it sets the spy as the outer instance.
+ // A more recent version of Mockito would have made possible to create the spy with
+ // spy(PrivateDnsPreferenceController.class, withSettings().useConstructor(mContext))
+ // and that would have solved the problem by removing the original object entirely
+ // in a more elegant manner, but useConstructor(Object...) is only available starting
+ // with Mockito 2.7.14. Other solutions involve modifying the code under test for
+ // the sake of the test.
+ nc = mock(nc.getClass(), withSettings().useConstructor().outerInstance(mController)
+ .defaultAnswer(CALLS_REAL_METHODS));
+ nc.onLinkPropertiesChanged(mNetwork, lp);
+ }
+
@Test
public void goThroughLifecycle_shouldRegisterUnregisterSettingsObserver() {
mLifecycle.handleLifecycleEvent(ON_START);
@@ -113,20 +174,50 @@ public class PrivateDnsPreferenceControllerTest {
@Test
public void getSummary_PrivateDnsModeOpportunistic() {
+ mLifecycle.handleLifecycleEvent(ON_START);
setPrivateDnsMode(PRIVATE_DNS_MODE_OPPORTUNISTIC);
setPrivateDnsProviderHostname(HOSTNAME);
mController.updateState(mPreference);
verify(mController, atLeastOnce()).getSummary();
verify(mPreference).setSummary(getResourceString(R.string.private_dns_mode_opportunistic));
+
+ LinkProperties lp = mock(LinkProperties.class);
+ when(lp.getValidatedPrivateDnsServers()).thenReturn(NON_EMPTY_ADDRESS_LIST);
+ updateLinkProperties(lp);
+ mController.updateState(mPreference);
+ verify(mPreference).setSummary(getResourceString(R.string.switch_on_text));
+
+ reset(mPreference);
+ lp = mock(LinkProperties.class);
+ when(lp.getValidatedPrivateDnsServers()).thenReturn(Collections.emptyList());
+ updateLinkProperties(lp);
+ mController.updateState(mPreference);
+ verify(mPreference).setSummary(getResourceString(R.string.private_dns_mode_opportunistic));
}
@Test
public void getSummary_PrivateDnsModeProviderHostname() {
+ mLifecycle.handleLifecycleEvent(ON_START);
setPrivateDnsMode(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
setPrivateDnsProviderHostname(HOSTNAME);
mController.updateState(mPreference);
verify(mController, atLeastOnce()).getSummary();
+ verify(mPreference).setSummary(
+ getResourceString(R.string.private_dns_mode_provider_failure));
+
+ LinkProperties lp = mock(LinkProperties.class);
+ when(lp.getValidatedPrivateDnsServers()).thenReturn(NON_EMPTY_ADDRESS_LIST);
+ updateLinkProperties(lp);
+ mController.updateState(mPreference);
verify(mPreference).setSummary(HOSTNAME);
+
+ reset(mPreference);
+ lp = mock(LinkProperties.class);
+ when(lp.getValidatedPrivateDnsServers()).thenReturn(Collections.emptyList());
+ updateLinkProperties(lp);
+ mController.updateState(mPreference);
+ verify(mPreference).setSummary(
+ getResourceString(R.string.private_dns_mode_provider_failure));
}
private void setPrivateDnsMode(String mode) {
diff --git a/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java b/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java
index 57dc855a780..7bc93713977 100644
--- a/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java
+++ b/tests/robotests/src/com/android/settings/notification/ZenOnboardingActivityTest.java
@@ -17,6 +17,7 @@
package com.android.settings.notification;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
@@ -94,7 +95,8 @@ public class ZenOnboardingActivityTest {
verify(mNm).setNotificationPolicy(captor.capture());
Policy actual = captor.getValue();
- assertThat(actual.priorityCategories).isEqualTo(PRIORITY_CATEGORY_ALARMS);
+ assertThat(actual.priorityCategories).isEqualTo(PRIORITY_CATEGORY_ALARMS
+ | PRIORITY_CATEGORY_REPEAT_CALLERS);
assertThat(actual.priorityCallSenders).isEqualTo(Policy.PRIORITY_SENDERS_STARRED);
assertThat(actual.priorityMessageSenders).isEqualTo(Policy.PRIORITY_SENDERS_ANY);
assertThat(actual.suppressedVisualEffects).isEqualTo(
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
index f7fd12f58fe..d83c8148fff 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
@@ -44,6 +44,7 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
private final Map> mRestrictionSources = new HashMap<>();
private final List mUserProfileInfos = new ArrayList<>();
private final Set mManagedProfiles = new HashSet<>();
+ private boolean mIsQuietModeEnabled = false;
@Resetter
public void reset() {
@@ -52,6 +53,7 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
mUserProfileInfos.clear();
mRestrictionSources.clear();
mManagedProfiles.clear();
+ mIsQuietModeEnabled = false;
}
public void setUserInfo(int userHandle, UserInfo userInfo) {
@@ -110,4 +112,13 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
public void addManagedProfile(int userId) {
mManagedProfiles.add(userId);
}
+
+ @Implementation
+ public boolean isQuietModeEnabled(UserHandle userHandle) {
+ return mIsQuietModeEnabled;
+ }
+
+ public void setQuietModeEnabled(boolean enabled) {
+ mIsQuietModeEnabled = enabled;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/widget/SwitchBarTest.java b/tests/robotests/src/com/android/settings/widget/SwitchBarTest.java
index bd99bbbbb8f..818daecd26d 100644
--- a/tests/robotests/src/com/android/settings/widget/SwitchBarTest.java
+++ b/tests/robotests/src/com/android/settings/widget/SwitchBarTest.java
@@ -26,6 +26,7 @@ import android.widget.TextView;
import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -82,4 +83,16 @@ public class SwitchBarTest {
assertThat(((TextView) mBar.findViewById(R.id.switch_text)).getText())
.isEqualTo(mContext.getString(onText));
}
+
+ @Test
+ public void disabledByAdmin_shouldDelegateToRestrictedIcon() {
+ mBar.setDisabledByAdmin(new EnforcedAdmin());
+ assertThat(mBar.getDelegatingView().getId()).isEqualTo(R.id.restricted_icon);
+ }
+
+ @Test
+ public void notDisabledByAdmin_shouldDelegateToSwitch() {
+ mBar.setDisabledByAdmin(null);
+ assertThat(mBar.getDelegatingView().getId()).isEqualTo(R.id.switch_widget);
+ }
}