diff --git a/res/xml/legacy_channel_notification_settings.xml b/res/xml/app_notification_settings.xml
similarity index 51%
rename from res/xml/legacy_channel_notification_settings.xml
rename to res/xml/app_notification_settings.xml
index 519bf5d4875..00a914202eb 100644
--- a/res/xml/legacy_channel_notification_settings.xml
+++ b/res/xml/app_notification_settings.xml
@@ -15,7 +15,15 @@
-->
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+
+
+
-
-
+
-
-
+
-
-
+
+
+
+
+
diff --git a/res/xml/channel_notification_settings.xml b/res/xml/channel_notification_settings.xml
new file mode 100644
index 00000000000..aaadce4f1a9
--- /dev/null
+++ b/res/xml/channel_notification_settings.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/upgraded_app_notification_settings.xml b/res/xml/notification_group_settings.xml
similarity index 55%
rename from res/xml/upgraded_app_notification_settings.xml
rename to res/xml/notification_group_settings.xml
index f9a3304267e..c1381974467 100644
--- a/res/xml/upgraded_app_notification_settings.xml
+++ b/res/xml/notification_group_settings.xml
@@ -17,11 +17,24 @@
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/upgraded_channel_notification_settings.xml b/res/xml/upgraded_channel_notification_settings.xml
deleted file mode 100644
index ee23435576b..00000000000
--- a/res/xml/upgraded_channel_notification_settings.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/com/android/settings/notification/AllowSoundPreferenceController.java b/src/com/android/settings/notification/AllowSoundPreferenceController.java
new file mode 100644
index 00000000000..dcd5e45de93
--- /dev/null
+++ b/src/com/android/settings/notification/AllowSoundPreferenceController.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+public class AllowSoundPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+ private static final String TAG = "AllowSoundPrefContr";
+ private static final String KEY_IMPORTANCE = "allow_sound";
+ private NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+ public AllowSoundPreferenceController(Context context,
+ NotificationSettingsBase.ImportanceListener importanceListener,
+ NotificationBackend backend) {
+ super(context, backend);
+ mImportanceListener = importanceListener;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_IMPORTANCE;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ return mChannel != null && NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId());
+
+ }
+
+ public void updateState(Preference preference) {
+ if (mChannel != null) {
+ RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+ pref.setDisabledByAdmin(mAdmin);
+ pref.setEnabled(isChannelConfigurable() && !pref.isDisabledByAdmin());
+ pref.setChecked(mChannel.getImportance() >= IMPORTANCE_DEFAULT
+ || mChannel.getImportance() == IMPORTANCE_UNSPECIFIED);
+ } else { Log.i(TAG, "tried to updatestate on a null channel?!"); }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mChannel != null) {
+ final int importance =
+ ((Boolean) newValue ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_LOW);
+ mChannel.setImportance(importance);
+ mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+ saveChannel();
+ mImportanceListener.onImportanceChanged();
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/settings/notification/AppLinkPreferenceController.java b/src/com/android/settings/notification/AppLinkPreferenceController.java
new file mode 100644
index 00000000000..ff5945b5141
--- /dev/null
+++ b/src/com/android/settings/notification/AppLinkPreferenceController.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+
+/**
+ * Controls link to reach more preference settings inside the app.
+ */
+public class AppLinkPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin {
+
+ private static final String TAG = "AppLinkPrefContr";
+ private static final String KEY_APP_LINK = "app_link";
+
+ public AppLinkPreferenceController(Context context) {
+ super(context, null);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_APP_LINK;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ return mAppRow.settingsIntent != null;
+ }
+
+ public void updateState(Preference preference) {
+ if (mAppRow != null) {
+ preference.setIntent(mAppRow.settingsIntent);
+ }
+ }
+}
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
index 95c9560d165..af168d60c80 100644
--- a/src/com/android/settings/notification/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -16,12 +16,10 @@
package com.android.settings.notification;
-import android.app.Activity;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
-import android.app.NotificationManager;
+import android.content.Context;
import android.content.Intent;
-import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings;
@@ -29,43 +27,29 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceGroup;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Switch;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
-import com.android.settings.applications.LayoutPreference;
-import com.android.settings.notification.NotificationBackend.AppRow;
-import com.android.settings.widget.EntityHeaderController;
import com.android.settings.widget.MasterSwitchPreference;
-import com.android.settings.widget.SwitchBar;
-import com.android.settingslib.RestrictedSwitchPreference;
-import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-
/** These settings are per app, so should not be returned in global search results. */
public class AppNotificationSettings extends NotificationSettingsBase {
private static final String TAG = "AppNotificationSettings";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static String KEY_GENERAL_CATEGORY = "categories";
- private static String KEY_DELETED = "deleted";
private List mChannelGroupList;
- private List mChannelGroups = new ArrayList();
- private FooterPreference mDeletedChannels;
@Override
public int getMetricsCategory() {
@@ -84,24 +68,13 @@ public class AppNotificationSettings extends NotificationSettingsBase {
if (getPreferenceScreen() != null) {
getPreferenceScreen().removeAll();
- mChannelGroups.clear();
- mDeletedChannels = null;
- mShowLegacyChannelConfig = false;
+ mDynamicPreferences.clear();
}
- addPreferencesFromResource(R.xml.notification_settings);
- getPreferenceScreen().setOrderingAsAdded(true);
- setupBlock();
- addHeaderPref();
-
- mShowLegacyChannelConfig = mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid);
if (mShowLegacyChannelConfig) {
- mChannel = mBackend.getChannel(
- mAppRow.pkg, mAppRow.uid, NotificationChannel.DEFAULT_CHANNEL_ID);
- populateDefaultChannelPrefs();
+ addPreferencesFromResource(R.xml.channel_notification_settings);
} else {
- addPreferencesFromResource(R.xml.upgraded_app_notification_settings);
- setupBadge();
+ addPreferencesFromResource(R.xml.app_notification_settings);
// Load channel settings
new AsyncTask() {
@Override
@@ -117,41 +90,59 @@ public class AppNotificationSettings extends NotificationSettingsBase {
return;
}
populateList();
- addAppLinkPref();
}
}.execute();
}
+ getPreferenceScreen().setOrderingAsAdded(true);
- updateDependents(mAppRow.banned);
+ for (NotificationPreferenceController controller : mControllers) {
+ controller.onResume(mAppRow, mChannel, mChannelGroup, mSuspendedAppsAdmin);
+ controller.displayPreference(getPreferenceScreen());
+ }
+ updatePreferenceStates();
}
- private void addHeaderPref() {
- ArrayMap rows = new ArrayMap<>();
- rows.put(mAppRow.pkg, mAppRow);
- collectConfigActivities(rows);
- final Activity activity = getActivity();
- final Preference pref = EntityHeaderController
- .newInstance(activity, this /* fragment */, null /* header */)
- .setRecyclerView(getListView(), getLifecycle())
- .setIcon(mAppRow.icon)
- .setLabel(mAppRow.label)
- .setPackageName(mAppRow.pkg)
- .setUid(mAppRow.uid)
- .setHasAppInfoLink(true)
- .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
- EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE)
- .done(activity, getPrefContext());
- pref.setKey(KEY_HEADER);
- getPreferenceScreen().addPreference(pref);
+ @Override
+ protected String getLogTag() {
+ return TAG;
}
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.notification_settings;
+ }
+
+ @Override
+ protected List getPreferenceControllers(Context context) {
+ mControllers = new ArrayList<>();
+ mControllers.add(new HeaderPreferenceController(context, this));
+ mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
+ mControllers.add(new BadgePreferenceController(context, mBackend));
+ mControllers.add(new AllowSoundPreferenceController(
+ context, mImportanceListener, mBackend));
+ mControllers.add(new ImportancePreferenceController(context));
+ mControllers.add(new SoundPreferenceController(context, this,
+ mImportanceListener, mBackend));
+ mControllers.add(new LightsPreferenceController(context, mBackend));
+ mControllers.add(new VibrationPreferenceController(context, mBackend));
+ mControllers.add(new VisibilityPreferenceController(context, new LockPatternUtils(context),
+ mBackend));
+ mControllers.add(new DndPreferenceController(context, getLifecycle(), mBackend));
+ mControllers.add(new AppLinkPreferenceController(context));
+ mControllers.add(new DescriptionPreferenceController(context));
+ mControllers.add(new NotificationsOffPreferenceController(context));
+ mControllers.add(new DeletedChannelsPreferenceController(context, mBackend));
+ return new ArrayList<>(mControllers);
+ }
+
+
private void populateList() {
- if (!mChannelGroups.isEmpty()) {
+ if (!mDynamicPreferences.isEmpty()) {
// If there's anything in mChannelGroups, we've called populateChannelList twice.
// Clear out existing channels and log.
Log.w(TAG, "Notification channel group posted twice to settings - old size " +
- mChannelGroups.size() + ", new size " + mChannelGroupList.size());
- for (Preference p : mChannelGroups) {
+ mDynamicPreferences.size() + ", new size " + mChannelGroupList.size());
+ for (Preference p : mDynamicPreferences) {
getPreferenceScreen().removePreference(p);
}
}
@@ -160,7 +151,7 @@ public class AppNotificationSettings extends NotificationSettingsBase {
groupCategory.setTitle(R.string.notification_channels);
groupCategory.setKey(KEY_GENERAL_CATEGORY);
getPreferenceScreen().addPreference(groupCategory);
- mChannelGroups.add(groupCategory);
+ mDynamicPreferences.add(groupCategory);
Preference empty = new Preference(getPrefContext());
empty.setTitle(R.string.no_channels);
@@ -168,20 +159,8 @@ public class AppNotificationSettings extends NotificationSettingsBase {
groupCategory.addPreference(empty);
} else {
populateGroupList();
- int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
- if (deletedChannelCount > 0 &&
- getPreferenceScreen().findPreference(KEY_DELETED) == null) {
- mDeletedChannels = new FooterPreference(getPrefContext());
- mDeletedChannels.setSelectable(false);
- mDeletedChannels.setTitle(getResources().getQuantityString(
- R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
- mDeletedChannels.setEnabled(false);
- mDeletedChannels.setKey(KEY_DELETED);
- mDeletedChannels.setOrder(ORDER_LAST);
- getPreferenceScreen().addPreference(mDeletedChannels);
- }
+ mImportanceListener.onImportanceChanged();
}
- updateDependents(mAppRow.banned);
}
private void populateGroupList() {
@@ -190,7 +169,7 @@ public class AppNotificationSettings extends NotificationSettingsBase {
groupCategory.setKey(KEY_GENERAL_CATEGORY);
groupCategory.setOrderingAsAdded(true);
getPreferenceScreen().addPreference(groupCategory);
- mChannelGroups.add(groupCategory);
+ mDynamicPreferences.add(groupCategory);
for (NotificationChannelGroup group : mChannelGroupList) {
final List channels = group.getChannels();
int N = channels.size();
@@ -240,91 +219,6 @@ public class AppNotificationSettings extends NotificationSettingsBase {
parent.addPreference(groupPref);
}
- void setupBadge() {
- mBadge = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BADGE);
- mBadge.setDisabledByAdmin(mSuspendedAppsAdmin);
- if (mChannel == null) {
- mBadge.setChecked(mAppRow.showBadge);
- } else {
- mBadge.setChecked(mChannel.canShowBadge());
- }
- mBadge.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final boolean value = (Boolean) newValue;
- if (mChannel == null) {
- mBackend.setShowBadge(mPkg, mUid, value);
- } else {
- mChannel.setShowBadge(value);
- mChannel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- }
- return true;
- }
- });
- }
-
- protected void setupBlock() {
- View switchBarContainer = LayoutInflater.from(
- getPrefContext()).inflate(R.layout.styled_switch_bar, null);
- mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
- mSwitchBar.show();
- mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
- mSwitchBar.setChecked(!mAppRow.banned);
- mSwitchBar.addOnSwitchChangeListener(new SwitchBar.OnSwitchChangeListener() {
- @Override
- public void onSwitchChanged(Switch switchView, boolean isChecked) {
- if (mShowLegacyChannelConfig && mChannel != null) {
- final int importance = isChecked ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE;
- mImportanceToggle.setChecked(importance == IMPORTANCE_UNSPECIFIED);
- mChannel.setImportance(importance);
- mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- }
- mBackend.setNotificationsEnabledForPackage(mPkgInfo.packageName, mUid, isChecked);
- mAppRow.banned = true;
- updateDependents(!isChecked);
- }
- });
-
- mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
- mBlockBar.setOrder(ORDER_FIRST);
- mBlockBar.setKey(KEY_BLOCK);
- getPreferenceScreen().addPreference(mBlockBar);
-
- if (mAppRow.systemApp && !mAppRow.banned) {
- setVisible(mBlockBar, false);
- }
-
- setupBlockDesc(R.string.app_notifications_off_desc);
- }
-
- protected void updateDependents(boolean banned) {
- for (PreferenceCategory category : mChannelGroups) {
- setVisible(category, !banned);
- }
- if (mDeletedChannels != null) {
- setVisible(mDeletedChannels, !banned);
- }
- setVisible(mBlockedDesc, banned);
- setVisible(mBadge, !banned);
- if (mShowLegacyChannelConfig) {
- setVisible(mImportanceToggle, !banned);
- setVisible(mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
- || (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
- && mDndVisualEffectsSuppressed));
- setVisible(mVisibilityOverride, !banned &&
- checkCanBeVisible(NotificationManager.IMPORTANCE_LOW) && isLockScreenSecure());
- }
- if (mAppLink != null) {
- setVisible(mAppLink, !banned);
- }
- if (mAppRow.systemApp && !mAppRow.banned) {
- setVisible(mBlockBar, false);
- }
- }
-
-
private Comparator mChannelGroupComparator =
new Comparator() {
diff --git a/src/com/android/settings/notification/BadgePreferenceController.java b/src/com/android/settings/notification/BadgePreferenceController.java
new file mode 100644
index 00000000000..6b72c50bb71
--- /dev/null
+++ b/src/com/android/settings/notification/BadgePreferenceController.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.provider.Settings.Secure.NOTIFICATION_BADGING;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+public class BadgePreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+ private static final String TAG = "BadgePrefContr";
+ private static final String KEY_BADGE = "badge";
+ private static final int SYSTEM_WIDE_ON = 1;
+ private static final int SYSTEM_WIDE_OFF = 0;
+
+ public BadgePreferenceController(Context context,
+ NotificationBackend backend) {
+ super(context, backend);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_BADGE;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ if (mAppRow == null && mChannel == null) {
+ return false;
+ }
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
+ NOTIFICATION_BADGING, SYSTEM_WIDE_ON) == SYSTEM_WIDE_OFF) {
+ return false;
+ }
+ if (mChannel != null && !mAppRow.showBadge) {
+ return false;
+ }
+ return true;
+ }
+
+ public void updateState(Preference preference) {
+ if (mAppRow != null) {
+ RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+ pref.setDisabledByAdmin(mAdmin);
+ if (mChannel != null) {
+ pref.setChecked(mChannel.canShowBadge());
+ pref.setEnabled(isChannelConfigurable() && !pref.isDisabledByAdmin());
+ } else {
+ pref.setChecked(mAppRow.showBadge);
+ }
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean showBadge = (Boolean) newValue;
+ if (mChannel != null) {
+ mChannel.setShowBadge(showBadge);
+ saveChannel();
+ } else if (mAppRow != null){
+ mAppRow.showBadge = showBadge;
+ mBackend.setShowBadge(mAppRow.pkg, mAppRow.uid, showBadge);
+ }
+ return true;
+ }
+
+}
diff --git a/src/com/android/settings/notification/BlockPreferenceController.java b/src/com/android/settings/notification/BlockPreferenceController.java
new file mode 100644
index 00000000000..5c366eafc96
--- /dev/null
+++ b/src/com/android/settings/notification/BlockPreferenceController.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.widget.Switch;
+
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.widget.SwitchBar;
+
+public class BlockPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin, SwitchBar.OnSwitchChangeListener {
+
+ private static final String KEY_BLOCK = "block";
+ private NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+ public BlockPreferenceController(Context context,
+ NotificationSettingsBase.ImportanceListener importanceListener,
+ NotificationBackend backend) {
+ super(context, backend);
+ mImportanceListener = importanceListener;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_BLOCK;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (mAppRow == null) {
+ return false;
+ }
+ if (mChannel != null) {
+ return isChannelBlockable();
+ } else if (mChannelGroup != null && mChannelGroup.getGroup() != null) {
+ return isChannelGroupBlockable();
+ } else {
+ return !mAppRow.systemApp || (mAppRow.systemApp && mAppRow.banned);
+ }
+ }
+
+ public void updateState(Preference preference) {
+ LayoutPreference pref = (LayoutPreference) preference;
+ SwitchBar bar = pref.findViewById(R.id.switch_bar);
+ if (bar != null) {
+ bar.show();
+ try {
+ bar.addOnSwitchChangeListener(this);
+ } catch (IllegalStateException e) {
+ // an exception is thrown if you try to add the listener twice
+ }
+ bar.setDisabledByAdmin(mAdmin);
+
+ if (mChannel != null) {
+ bar.setChecked(!mAppRow.banned
+ && mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
+ } else if (mChannelGroup != null && mChannelGroup.getGroup() != null) {
+ bar.setChecked(!mAppRow.banned && !mChannelGroup.isBlocked());
+ } else {
+ bar.setChecked(!mAppRow.banned);
+ }
+ }
+ }
+
+ @Override
+ public void onSwitchChanged(Switch switchView, boolean isChecked) {
+ boolean blocked = !isChecked;
+ if (mChannel != null) {
+ final int originalImportance = mChannel.getImportance();
+ // setting the initial state of the switch in updateState() triggers this callback.
+ // It's always safe to override the importance if it's meant to be blocked or if
+ // it was blocked and we are unblocking it.
+ if (blocked || originalImportance == IMPORTANCE_NONE) {
+ final int importance = blocked ? IMPORTANCE_NONE
+ : DEFAULT_CHANNEL_ID.equals(mChannel.getId())
+ ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_DEFAULT;
+ mChannel.setImportance(importance);
+ saveChannel();
+ }
+ } else if (mChannelGroup != null && mChannelGroup.getGroup() != null) {
+ mChannelGroup.setBlocked(blocked);
+ mBackend.updateChannelGroup(mAppRow.pkg, mAppRow.uid, mChannelGroup.getGroup());
+ } else if (mAppRow != null) {
+ mAppRow.banned = blocked;
+ mBackend.setNotificationsEnabledForPackage(mAppRow.pkg, mAppRow.uid, !blocked);
+ }
+ mImportanceListener.onImportanceChanged();
+ }
+}
diff --git a/src/com/android/settings/notification/ChannelGroupNotificationSettings.java b/src/com/android/settings/notification/ChannelGroupNotificationSettings.java
index 7837ec866df..68dd91bfdfc 100644
--- a/src/com/android/settings/notification/ChannelGroupNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelGroupNotificationSettings.java
@@ -16,20 +16,15 @@
package com.android.settings.notification;
-import android.app.Activity;
import android.app.NotificationChannel;
+import android.content.Context;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
-import com.android.settings.applications.LayoutPreference;
-import com.android.settings.widget.EntityHeaderController;
-import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.Collections;
@@ -38,12 +33,6 @@ import java.util.List;
public class ChannelGroupNotificationSettings extends NotificationSettingsBase {
private static final String TAG = "ChannelGroupSettings";
- private static String KEY_DELETED = "deleted";
-
- private EntityHeaderController mHeaderPref;
- private List mChannels = new ArrayList();
- private FooterPreference mDeletedChannels;
-
@Override
public int getMetricsCategory() {
return MetricsEvent.NOTIFICATION_CHANNEL_GROUP;
@@ -52,137 +41,68 @@ public class ChannelGroupNotificationSettings extends NotificationSettingsBase {
@Override
public void onResume() {
super.onResume();
- if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannelGroup == null) {
+ if (mAppRow == null || mChannelGroup == null || mChannelGroup.getGroup() == null) {
Log.w(TAG, "Missing package or uid or packageinfo or group");
finish();
return;
}
- if (getPreferenceScreen() != null) {
- getPreferenceScreen().removeAll();
- }
- addPreferencesFromResource(R.xml.notification_settings);
- setupBlock();
- addHeaderPref();
- addAppLinkPref();
- addFooterPref();
populateChannelList();
-
- updateDependents(mChannelGroup.isBlocked());
+ for (NotificationPreferenceController controller : mControllers) {
+ controller.onResume(mAppRow, mChannel, mChannelGroup, mSuspendedAppsAdmin);
+ controller.displayPreference(getPreferenceScreen());
+ }
+ updatePreferenceStates();
}
@Override
- void setupBadge() {
+ protected String getLogTag() {
+ return TAG;
+ }
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.notification_group_settings;
+ }
+
+ @Override
+ protected List getPreferenceControllers(Context context) {
+ mControllers = new ArrayList<>();
+ mControllers.add(new HeaderPreferenceController(context, this));
+ mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
+ mControllers.add(new AppLinkPreferenceController(context));
+ mControllers.add(new NotificationsOffPreferenceController(context));
+ mControllers.add(new DescriptionPreferenceController(context));
+ return new ArrayList<>(mControllers);
}
private void populateChannelList() {
- if (!mChannels.isEmpty()) {
- // If there's anything in mChannels, we've called populateChannelList twice.
+ if (!mDynamicPreferences.isEmpty()) {
+ // If there's anything in mDynamicPreferences, we've called populateChannelList twice.
// Clear out existing channels and log.
Log.w(TAG, "Notification channel group posted twice to settings - old size " +
- mChannels.size() + ", new size " + mChannels.size());
- for (Preference p : mChannels) {
+ mDynamicPreferences.size() + ", new size " + mDynamicPreferences.size());
+ for (Preference p : mDynamicPreferences) {
getPreferenceScreen().removePreference(p);
}
}
- if (mChannelGroup.getChannels().isEmpty()) {
+ if (mChannelGroup.getGroup().getChannels().isEmpty()) {
Preference empty = new Preference(getPrefContext());
empty.setTitle(R.string.no_channels);
empty.setEnabled(false);
getPreferenceScreen().addPreference(empty);
- mChannels.add(empty);
+ mDynamicPreferences.add(empty);
} else {
- final List channels = mChannelGroup.getChannels();
+ final List channels = mChannelGroup.getGroup().getChannels();
Collections.sort(channels, mChannelComparator);
for (NotificationChannel channel : channels) {
- mChannels.add(populateSingleChannelPrefs(
- getPreferenceScreen(), channel, getImportanceSummary(channel)));
+ mDynamicPreferences.add(populateSingleChannelPrefs(
+ getPreferenceScreen(), channel,
+ ImportancePreferenceController.getImportanceSummary(
+ getPrefContext(), channel)));
}
- int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
- if (deletedChannelCount > 0) {
- mDeletedChannels = new FooterPreference(getPrefContext());
- mDeletedChannels.setSelectable(false);
- mDeletedChannels.setTitle(getResources().getQuantityString(
- R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
- mDeletedChannels.setEnabled(false);
- mDeletedChannels.setKey(KEY_DELETED);
- mDeletedChannels.setOrder(ORDER_LAST);
- getPreferenceScreen().addPreference(mDeletedChannels);
- mChannels.add(mDeletedChannels);
- }
}
-
- updateDependents(mAppRow.banned);
- }
-
- private void addHeaderPref() {
- ArrayMap rows = new ArrayMap<>();
- rows.put(mAppRow.pkg, mAppRow);
- collectConfigActivities(rows);
- final Activity activity = getActivity();
- mHeaderPref = EntityHeaderController
- .newInstance(activity, this /* fragment */, null /* header */)
- .setRecyclerView(getListView(), getLifecycle());
- final Preference pref = mHeaderPref
- .setIcon(mAppRow.icon)
- .setLabel(mChannelGroup.getName())
- .setSummary(mAppRow.label)
- .setPackageName(mAppRow.pkg)
- .setUid(mAppRow.uid)
- .setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
- EntityHeaderController.ActionType.ACTION_NONE)
- .setHasAppInfoLink(true)
- .done(activity, getPrefContext());
- getPreferenceScreen().addPreference(pref);
- }
-
- private void addFooterPref() {
- if (!TextUtils.isEmpty(mChannelGroup.getDescription())) {
- FooterPreference descPref = new FooterPreference(getPrefContext());
- descPref.setOrder(ORDER_LAST);
- descPref.setSelectable(false);
- descPref.setTitle(mChannelGroup.getDescription());
- getPreferenceScreen().addPreference(descPref);
- mChannels.add(descPref);
- }
- }
-
- private void setupBlock() {
- View switchBarContainer = LayoutInflater.from(
- getPrefContext()).inflate(R.layout.styled_switch_bar, null);
- mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
- mSwitchBar.show();
- mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
- mSwitchBar.setChecked(!mChannelGroup.isBlocked());
- mSwitchBar.addOnSwitchChangeListener((switchView, isChecked) -> {
- mChannelGroup.setBlocked(!isChecked);
- mBackend.updateChannelGroup(mPkg, mUid, mChannelGroup);
- updateDependents(!isChecked);
- });
-
- mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
- mBlockBar.setOrder(ORDER_FIRST);
- mBlockBar.setKey(KEY_BLOCK);
- getPreferenceScreen().addPreference(mBlockBar);
-
- if (!isChannelGroupBlockable(mChannelGroup)) {
- setVisible(mBlockBar, false);
- }
-
- setupBlockDesc(R.string.channel_group_notifications_off_desc);
- }
-
- protected void updateDependents(boolean banned) {
- for (Preference channel : mChannels) {
- setVisible(channel, !banned);
- }
- if (mAppLink != null) {
- setVisible(mAppLink, !banned);
- }
- setVisible(mBlockBar, isChannelGroupBlockable(mChannelGroup));
- setVisible(mBlockedDesc, mAppRow.banned || mChannelGroup.isBlocked());
}
}
diff --git a/src/com/android/settings/notification/ChannelImportanceSettings.java b/src/com/android/settings/notification/ChannelImportanceSettings.java
index 9e9ffd6aa55..bffed08079c 100644
--- a/src/com/android/settings/notification/ChannelImportanceSettings.java
+++ b/src/com/android/settings/notification/ChannelImportanceSettings.java
@@ -37,6 +37,7 @@ import com.android.settings.R;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settings.widget.RadioButtonPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
@@ -60,8 +61,8 @@ public class ChannelImportanceSettings extends NotificationSettingsBase
@Override
public void onResume() {
super.onResume();
- if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannel == null) {
- Log.w(TAG, "Missing package or uid or packageinfo or channel");
+ if (mAppRow == null || mChannel == null) {
+ Log.w(TAG, "Missing package or channel");
finish();
return;
}
@@ -69,10 +70,19 @@ public class ChannelImportanceSettings extends NotificationSettingsBase
}
@Override
- void setupBadge() {}
+ protected String getLogTag() {
+ return TAG;
+ }
@Override
- void updateDependents(boolean banned) {}
+ protected int getPreferenceScreenResId() {
+ return R.xml.notification_importance;
+ }
+
+ @Override
+ protected List getPreferenceControllers(Context context) {
+ return null;
+ }
@Override
public void onPause() {
@@ -81,11 +91,6 @@ public class ChannelImportanceSettings extends NotificationSettingsBase
private PreferenceScreen createPreferenceHierarchy() {
PreferenceScreen root = getPreferenceScreen();
- if (root != null) {
- root.removeAll();
- }
- addPreferencesFromResource(R.xml.notification_importance);
- root = getPreferenceScreen();
for (int i = 0; i < root.getPreferenceCount(); i++) {
Preference pref = root.getPreference(i);
@@ -148,8 +153,9 @@ public class ChannelImportanceSettings extends NotificationSettingsBase
// but the sound you had selected was "Silence",
// then set sound for this channel to your default sound,
// because you probably intended to cause this channel to actually start making sound.
- if (oldImportance < IMPORTANCE_DEFAULT && !hasValidSound(mChannel) &&
- mChannel.getImportance() >= IMPORTANCE_DEFAULT) {
+ if (oldImportance < IMPORTANCE_DEFAULT
+ && !SoundPreferenceController.hasValidSound(mChannel)
+ && mChannel.getImportance() >= IMPORTANCE_DEFAULT) {
mChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION),
mChannel.getAudioAttributes());
mChannel.lockFields(USER_LOCKED_SOUND);
diff --git a/src/com/android/settings/notification/ChannelNotificationSettings.java b/src/com/android/settings/notification/ChannelNotificationSettings.java
index 9484f7e6bf5..ea17a05c6d3 100644
--- a/src/com/android/settings/notification/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/ChannelNotificationSettings.java
@@ -16,59 +16,23 @@
package com.android.settings.notification;
-import android.app.Activity;
-import android.app.NotificationChannel;
-import android.app.NotificationChannelGroup;
-import android.app.NotificationManager;
+import android.content.Context;
import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.AsyncTask;
-import android.provider.Settings;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceGroup;
+import android.preference.PreferenceManager;
import android.text.TextUtils;
-import android.text.BidiFormatter;
-import android.text.SpannableStringBuilder;
-import android.util.ArrayMap;
import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Switch;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
-import com.android.settings.RingtonePreference;
-import com.android.settings.Utils;
-import com.android.settings.applications.AppInfoBase;
-import com.android.settings.applications.LayoutPreference;
-import com.android.settings.widget.EntityHeaderController;
-import com.android.settings.widget.SwitchBar;
-import com.android.settingslib.RestrictedSwitchPreference;
-import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import java.util.ArrayList;
+import java.util.List;
public class ChannelNotificationSettings extends NotificationSettingsBase {
private static final String TAG = "ChannelSettings";
- private static final String KEY_LIGHTS = "lights";
- private static final String KEY_VIBRATE = "vibrate";
- private static final String KEY_RINGTONE = "ringtone";
- private static final String KEY_IMPORTANCE = "importance";
- private static final String KEY_ADVANCED = "advanced";
-
- private Preference mImportance;
- private RestrictedSwitchPreference mLights;
- private RestrictedSwitchPreference mVibrate;
- private NotificationSoundPreference mRingtone;
- private FooterPreference mFooter;
- private NotificationChannelGroup mChannelGroup;
- private EntityHeaderController mHeaderPref;
- private PreferenceGroup mAdvanced;
-
@Override
public int getMetricsCategory() {
return MetricsEvent.NOTIFICATION_TOPIC_NOTIFICATION;
@@ -83,308 +47,52 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
return;
}
- if (getPreferenceScreen() != null) {
- getPreferenceScreen().removeAll();
+ for (NotificationPreferenceController controller : mControllers) {
+ controller.onResume(mAppRow, mChannel, mChannelGroup, mSuspendedAppsAdmin);
+ controller.displayPreference(getPreferenceScreen());
}
- addPreferencesFromResource(R.xml.notification_settings);
- setupBlock();
- addHeaderPref();
- addAppLinkPref();
- addFooterPref();
-
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
- populateDefaultChannelPrefs();
- mShowLegacyChannelConfig = true;
- } else {
- populateUpgradedChannelPrefs();
-
- if (mChannel.getGroup() != null) {
- mChannelGroup = mBackend.getGroup(mPkg, mUid, mChannel.getGroup());
- if (mChannelGroup != null) {
- setChannelGroupLabel(mChannelGroup.getName());
- }
- }
- }
-
- updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
- }
-
- private void populateUpgradedChannelPrefs() {
- addPreferencesFromResource(R.xml.upgraded_channel_notification_settings);
- setupBadge();
- setupPriorityPref(mChannel.canBypassDnd());
- setupVisOverridePref(mChannel.getLockscreenVisibility());
- setupLights();
- setupVibrate();
- setupRingtone();
- setupImportance();
- mAdvanced = (PreferenceGroup) findPreference(KEY_ADVANCED);
- }
-
- private void addHeaderPref() {
- ArrayMap rows = new ArrayMap<>();
- rows.put(mAppRow.pkg, mAppRow);
- collectConfigActivities(rows);
- final Activity activity = getActivity();
- mHeaderPref = EntityHeaderController
- .newInstance(activity, this /* fragment */, null /* header */)
- .setRecyclerView(getListView(), getLifecycle());
- final Preference pref = mHeaderPref
- .setIcon(mAppRow.icon)
- .setLabel(mChannel.getName())
- .setSummary(mAppRow.label)
- .setPackageName(mAppRow.pkg)
- .setUid(mAppRow.uid)
- .setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
- EntityHeaderController.ActionType.ACTION_NONE)
- .setHasAppInfoLink(true)
- .done(activity, getPrefContext());
- getPreferenceScreen().addPreference(pref);
- }
-
- private void setChannelGroupLabel(CharSequence groupName) {
- final SpannableStringBuilder summary = new SpannableStringBuilder();
- BidiFormatter bidi = BidiFormatter.getInstance();
- summary.append(bidi.unicodeWrap(mAppRow.label.toString()));
- if (groupName != null) {
- summary.append(bidi.unicodeWrap(mContext.getText(
- R.string.notification_header_divider_symbol_with_spaces)));
- summary.append(bidi.unicodeWrap(groupName.toString()));
- }
- final Activity activity = getActivity();
- mHeaderPref.setSummary(summary.toString());
- mHeaderPref.done(activity, getPrefContext());
- }
-
- private void addFooterPref() {
- if (!TextUtils.isEmpty(mChannel.getDescription())) {
- FooterPreference descPref = new FooterPreference(getPrefContext());
- descPref.setOrder(ORDER_LAST);
- descPref.setSelectable(false);
- descPref.setTitle(mChannel.getDescription());
- getPreferenceScreen().addPreference(descPref);
- }
- }
-
- protected void setupBadge() {
- mBadge = (RestrictedSwitchPreference) getPreferenceScreen().findPreference(KEY_BADGE);
- mBadge.setDisabledByAdmin(mSuspendedAppsAdmin);
- mBadge.setEnabled(mAppRow.showBadge);
- mBadge.setChecked(mChannel.canShowBadge());
-
- mBadge.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final boolean value = (Boolean) newValue;
- mChannel.setShowBadge(value);
- mChannel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- return true;
- }
- });
- }
-
- private void setupLights() {
- mLights = (RestrictedSwitchPreference) findPreference(KEY_LIGHTS);
- mLights.setDisabledByAdmin(mSuspendedAppsAdmin);
- mLights.setChecked(mChannel.shouldShowLights());
- mLights.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final boolean lights = (Boolean) newValue;
- mChannel.enableLights(lights);
- mChannel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- return true;
- }
- });
- }
-
- private void setupVibrate() {
- mVibrate = (RestrictedSwitchPreference) findPreference(KEY_VIBRATE);
- mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin);
- mVibrate.setEnabled(!mVibrate.isDisabledByAdmin() && isChannelConfigurable(mChannel));
- mVibrate.setChecked(mChannel.shouldVibrate());
- mVibrate.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final boolean vibrate = (Boolean) newValue;
- mChannel.enableVibration(vibrate);
- mChannel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- return true;
- }
- });
- }
-
- private void setupRingtone() {
- mRingtone = (NotificationSoundPreference) findPreference(KEY_RINGTONE);
- mRingtone.setRingtone(mChannel.getSound());
- mRingtone.setEnabled(isChannelConfigurable(mChannel));
- mRingtone.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- mChannel.setSound((Uri) newValue, mChannel.getAudioAttributes());
- mChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- return false;
- }
- });
- }
-
- private void setupBlock() {
- View switchBarContainer = LayoutInflater.from(
- getPrefContext()).inflate(R.layout.styled_switch_bar, null);
- mSwitchBar = switchBarContainer.findViewById(R.id.switch_bar);
- mSwitchBar.show();
- mSwitchBar.setDisabledByAdmin(mSuspendedAppsAdmin);
- mSwitchBar.setChecked(mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
- mSwitchBar.addOnSwitchChangeListener(new SwitchBar.OnSwitchChangeListener() {
- @Override
- public void onSwitchChanged(Switch switchView, boolean isChecked) {
- int importance = 0;
- if (mShowLegacyChannelConfig) {
- importance = isChecked ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE;
- mImportanceToggle.setChecked(importance == IMPORTANCE_UNSPECIFIED);
- } else {
- importance = isChecked ? IMPORTANCE_LOW : IMPORTANCE_NONE;
- mImportance.setSummary(getImportanceSummary(importance));
- }
- mChannel.setImportance(importance);
- mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
- }
- });
-
- mBlockBar = new LayoutPreference(getPrefContext(), switchBarContainer);
- mBlockBar.setOrder(ORDER_FIRST);
- mBlockBar.setKey(KEY_BLOCK);
- getPreferenceScreen().addPreference(mBlockBar);
-
- if (!isChannelBlockable(mChannel)) {
- setVisible(mBlockBar, false);
- }
-
- setupBlockDesc(R.string.channel_notifications_off_desc);
- }
-
- private void setupImportance() {
- mImportance = findPreference(KEY_IMPORTANCE);
- Bundle channelArgs = new Bundle();
- channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
- channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg);
- channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId());
- mImportance.setEnabled(mSuspendedAppsAdmin == null && isChannelConfigurable(mChannel));
- // Set up intent to show importance selection only if this setting is enabled.
- if (mImportance.isEnabled()) {
- Intent channelIntent = Utils.onBuildStartFragmentIntent(getActivity(),
- ChannelImportanceSettings.class.getName(),
- channelArgs, null, R.string.notification_importance_title, null,
- false, getMetricsCategory());
- mImportance.setIntent(channelIntent);
- }
- mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
- }
-
- private String getImportanceSummary(int importance) {
- String title;
- String summary = null;
- switch (importance) {
- case IMPORTANCE_UNSPECIFIED:
- title = getContext().getString(R.string.notification_importance_unspecified);
- break;
- case NotificationManager.IMPORTANCE_MIN:
- title = getContext().getString(R.string.notification_importance_min_title);
- summary = getContext().getString(R.string.notification_importance_min);
- break;
- case NotificationManager.IMPORTANCE_LOW:
- title = getContext().getString(R.string.notification_importance_low_title);
- summary = getContext().getString(R.string.notification_importance_low);
- break;
- case NotificationManager.IMPORTANCE_DEFAULT:
- title = getContext().getString(R.string.notification_importance_default_title);
- if (hasValidSound(mChannel)) {
- summary = getContext().getString(R.string.notification_importance_default);
- } else {
- summary = getContext().getString(R.string.notification_importance_low);
- }
- break;
- case NotificationManager.IMPORTANCE_HIGH:
- case NotificationManager.IMPORTANCE_MAX:
- title = getContext().getString(R.string.notification_importance_high_title);
- if (hasValidSound(mChannel)) {
- summary = getContext().getString(R.string.notification_importance_high);
- } else {
- summary = getContext().getString(R.string.notification_importance_high_silent);
- }
- break;
- default:
- return "";
- }
-
- if (summary != null) {
- return getContext().getString(R.string.notification_importance_divider, title, summary);
- } else {
- return title;
- }
- }
-
- @Override
- public boolean onPreferenceTreeClick(Preference preference) {
- if (preference instanceof RingtonePreference) {
- mRingtone.onPrepareRingtonePickerIntent(mRingtone.getIntent());
- startActivityForResult(preference.getIntent(), 200);
- return true;
- }
- return super.onPreferenceTreeClick(preference);
+ updatePreferenceStates();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (mRingtone != null) {
- mRingtone.onActivityResult(requestCode, resultCode, data);
- }
- if (mChannel != null) {
- mImportance.setSummary(getImportanceSummary(mChannel.getImportance()));
+ for (NotificationPreferenceController controller : mControllers) {
+ if (controller instanceof PreferenceManager.OnActivityResultListener) {
+ ((PreferenceManager.OnActivityResultListener) controller)
+ .onActivityResult(requestCode, resultCode, data);
+ }
}
}
- boolean canPulseLight() {
- if (!getResources()
- .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
- return false;
- }
- return Settings.System.getInt(getContentResolver(),
- Settings.System.NOTIFICATION_LIGHT_PULSE, 0) == 1;
+ @Override
+ protected String getLogTag() {
+ return TAG;
}
- void updateDependents(boolean banned) {
- PreferenceGroup parent;
- if (mShowLegacyChannelConfig) {
- parent = getPreferenceScreen();
- setVisible(mImportanceToggle, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
- } else {
- setVisible(mAdvanced, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
- setVisible(mImportance, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
- setVisible(mAdvanced, mLights, checkCanBeVisible(
- NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight());
- setVisible(mVibrate, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
- setVisible(mRingtone, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT));
- parent = mAdvanced;
- }
- setVisible(parent, mBadge, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
- setVisible(parent, mPriority, checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
- || (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
- && mDndVisualEffectsSuppressed));
- setVisible(parent, mVisibilityOverride, isLockScreenSecure()
- &&checkCanBeVisible(NotificationManager.IMPORTANCE_LOW));
- setVisible(mBlockedDesc, mChannel.getImportance() == IMPORTANCE_NONE);
- if (mAppLink != null) {
- setVisible(mAppLink, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
- }
- if (mFooter != null) {
- setVisible(mFooter, checkCanBeVisible(NotificationManager.IMPORTANCE_MIN));
- }
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.channel_notification_settings;
+ }
+
+ @Override
+ protected List getPreferenceControllers(Context context) {
+ mControllers = new ArrayList<>();
+ mControllers.add(new HeaderPreferenceController(context, this));
+ mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
+ mControllers.add(new ImportancePreferenceController(context));
+ mControllers.add(new AllowSoundPreferenceController(
+ context, mImportanceListener, mBackend));
+ mControllers.add(new SoundPreferenceController(context, this,
+ mImportanceListener, mBackend));
+ mControllers.add(new VibrationPreferenceController(context, mBackend));
+ mControllers.add(new AppLinkPreferenceController(context));
+ mControllers.add(new DescriptionPreferenceController(context));
+ mControllers.add(new VisibilityPreferenceController(context, new LockPatternUtils(context),
+ mBackend));
+ mControllers.add(new LightsPreferenceController(context, mBackend));
+ mControllers.add(new BadgePreferenceController(context, mBackend));
+ mControllers.add(new DndPreferenceController(context, getLifecycle(), mBackend));
+ mControllers.add(new NotificationsOffPreferenceController(context));
+ return new ArrayList<>(mControllers);
}
}
diff --git a/src/com/android/settings/notification/DeletedChannelsPreferenceController.java b/src/com/android/settings/notification/DeletedChannelsPreferenceController.java
new file mode 100644
index 00000000000..16eb9edb742
--- /dev/null
+++ b/src/com/android/settings/notification/DeletedChannelsPreferenceController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+
+public class DeletedChannelsPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin {
+
+ private static final String KEY_DELETED = "deleted";
+
+ public DeletedChannelsPreferenceController(Context context, NotificationBackend backend) {
+ super(context, backend);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_DELETED;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ // only visible on app screen
+ if (mChannel != null || hasValidGroup()) {
+ return false;
+ }
+
+ return mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid) > 0;
+ }
+
+ public void updateState(Preference preference) {
+ if (mAppRow != null) {
+ int deletedChannelCount = mBackend.getDeletedChannelCount(mAppRow.pkg, mAppRow.uid);
+ preference.setTitle(mContext.getResources().getQuantityString(
+ R.plurals.deleted_channels, deletedChannelCount, deletedChannelCount));
+ }
+ preference.setEnabled(false);
+ preference.setSelectable(false);
+ }
+}
diff --git a/src/com/android/settings/notification/DescriptionPreferenceController.java b/src/com/android/settings/notification/DescriptionPreferenceController.java
new file mode 100644
index 00000000000..fae2f5f222d
--- /dev/null
+++ b/src/com/android/settings/notification/DescriptionPreferenceController.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.text.TextUtils;
+
+import com.android.settings.core.PreferenceControllerMixin;
+
+public class DescriptionPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin {
+
+ private static final String KEY_DESC = "desc";
+
+ public DescriptionPreferenceController(Context context) {
+ super(context, null);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_DESC;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ if (mChannel == null && !hasValidGroup()) {
+ return false;
+ }
+ if (mChannel != null && !TextUtils.isEmpty(mChannel.getDescription())) {
+ return true;
+ }
+ if (hasValidGroup() && !TextUtils.isEmpty(mChannelGroup.getDescription())) {
+ return true;
+ }
+ return false;
+ }
+
+ public void updateState(Preference preference) {
+ if (mAppRow != null) {
+ if (mChannel != null) {
+ preference.setTitle(mChannel.getDescription());
+ } else if (hasValidGroup()) {
+ preference.setTitle(mChannelGroup.getDescription());
+ }
+ }
+ preference.setEnabled(false);
+ preference.setSelectable(false);
+ }
+}
diff --git a/src/com/android/settings/notification/DndPreferenceController.java b/src/com/android/settings/notification/DndPreferenceController.java
new file mode 100644
index 00000000000..af60401754d
--- /dev/null
+++ b/src/com/android/settings/notification/DndPreferenceController.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+public class DndPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener,
+ LifecycleObserver, OnResume {
+
+ private static final String KEY_BYPASS_DND = "bypass_dnd";
+ private boolean mVisualEffectsSuppressed;
+
+ public DndPreferenceController(Context context, Lifecycle lifecycle,
+ NotificationBackend backend) {
+ super(context, backend);
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ NotificationManager.Policy policy = mNm.getNotificationPolicy();
+ mVisualEffectsSuppressed = policy != null && policy.suppressedVisualEffects != 0;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_BYPASS_DND;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ return checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
+ || (checkCanBeVisible(NotificationManager.IMPORTANCE_LOW)
+ && mVisualEffectsSuppressed);
+ }
+
+ public void updateState(Preference preference) {
+ if (mChannel != null) {
+ RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+ pref.setDisabledByAdmin(mAdmin);
+ pref.setEnabled(isChannelConfigurable() && !pref.isDisabledByAdmin());
+ pref.setChecked(mChannel.canBypassDnd());
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mChannel != null) {
+ final boolean bypassZenMode = (Boolean) newValue;
+ mChannel.setBypassDnd(bypassZenMode);
+ mChannel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
+ saveChannel();
+ }
+ return true;
+ }
+
+}
diff --git a/src/com/android/settings/notification/HeaderPreferenceController.java b/src/com/android/settings/notification/HeaderPreferenceController.java
new file mode 100644
index 00000000000..3d51b25ea7a
--- /dev/null
+++ b/src/com/android/settings/notification/HeaderPreferenceController.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static com.android.settings.widget.EntityHeaderController.PREF_KEY_APP_HEADER;
+
+import android.content.Context;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.Preference;
+import android.text.BidiFormatter;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.widget.EntityHeaderController;
+
+public class HeaderPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin {
+
+ private final PreferenceFragment mFragment;
+
+ public HeaderPreferenceController(Context context, PreferenceFragment fragment) {
+ super(context, null);
+ mFragment = fragment;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return PREF_KEY_APP_HEADER;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mAppRow != null;
+ }
+
+ public void updateState(Preference preference) {
+ if (mAppRow != null && mFragment != null) {
+ LayoutPreference pref = (LayoutPreference) preference;
+ EntityHeaderController controller = EntityHeaderController
+ .newInstance(mFragment.getActivity(), mFragment,
+ pref.findViewById(R.id.entity_header));
+ pref = controller.setIcon(mAppRow.icon)
+ .setLabel(getLabel())
+ .setSummary(getSummary())
+ .setPackageName(mAppRow.pkg)
+ .setUid(mAppRow.uid)
+ .setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
+ EntityHeaderController.ActionType.ACTION_NONE)
+ .setHasAppInfoLink(true)
+ .done(mFragment.getActivity(), mContext);
+ pref.findViewById(R.id.entity_header).setVisibility(View.VISIBLE);
+ }
+ }
+
+ CharSequence getLabel() {
+ return mChannel != null ? mChannel.getName()
+ : mChannelGroup != null && mChannelGroup.getGroup() != null
+ ? mChannelGroup.getGroup().getName()
+ : mAppRow.label;
+ }
+
+ CharSequence getSummary() {
+ if (mChannel != null) {
+ if (mChannelGroup != null && mChannelGroup.getGroup() != null
+ && !TextUtils.isEmpty(mChannelGroup.getGroup().getName())) {
+ final SpannableStringBuilder summary = new SpannableStringBuilder();
+ BidiFormatter bidi = BidiFormatter.getInstance();
+ summary.append(bidi.unicodeWrap(mAppRow.label.toString()));
+ summary.append(bidi.unicodeWrap(mContext.getText(
+ R.string.notification_header_divider_symbol_with_spaces)));
+ summary.append(bidi.unicodeWrap(mChannelGroup.getGroup().getName().toString()));
+ return summary;
+ } else {
+ return mAppRow.label;
+ }
+ } else if (mChannelGroup != null && mChannelGroup.getGroup() != null) {
+ return mAppRow.label;
+ } else {
+ return "";
+ }
+ }
+}
diff --git a/src/com/android/settings/notification/ImportancePreferenceController.java b/src/com/android/settings/notification/ImportancePreferenceController.java
new file mode 100644
index 00000000000..ba47c54a4e2
--- /dev/null
+++ b/src/com/android/settings/notification/ImportancePreferenceController.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.core.PreferenceControllerMixin;
+
+public class ImportancePreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin {
+
+ private static final String KEY_IMPORTANCE = "importance";
+
+ // Ironically doesn't take an importance listener because the importance is not changed
+ // by this controller's preference but by the screen it links to.
+ public ImportancePreferenceController(Context context) {
+ super(context, null);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_IMPORTANCE;
+ }
+
+ private int getMetricsCategory() {
+ return MetricsProto.MetricsEvent.NOTIFICATION_TOPIC_NOTIFICATION;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ if (mChannel == null) {
+ return false;
+ }
+ return !NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId());
+ }
+
+ public void updateState(Preference preference) {
+ if (mAppRow!= null && mChannel != null) {
+ preference.setEnabled(mAdmin == null && isChannelConfigurable());
+ Bundle channelArgs = new Bundle();
+ channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mAppRow.uid);
+ channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mAppRow.pkg);
+ channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId());
+ if (preference.isEnabled()) {
+ Intent channelIntent = Utils.onBuildStartFragmentIntent(mContext,
+ ChannelImportanceSettings.class.getName(),
+ channelArgs, null, R.string.notification_importance_title, null,
+ false, getMetricsCategory());
+ preference.setIntent(channelIntent);
+ preference.setSummary(getImportanceSummary(mContext, mChannel));
+ }
+ }
+ }
+
+ protected static String getImportanceSummary(Context context, NotificationChannel channel) {
+ String title;
+ String summary = null;
+ int importance = channel.getImportance();
+ switch (importance) {
+ case IMPORTANCE_UNSPECIFIED:
+ title = context.getString(R.string.notification_importance_unspecified);
+ break;
+ case NotificationManager.IMPORTANCE_MIN:
+ title = context.getString(R.string.notification_importance_min_title);
+ summary = context.getString(R.string.notification_importance_min);
+ break;
+ case NotificationManager.IMPORTANCE_LOW:
+ title = context.getString(R.string.notification_importance_low_title);
+ summary = context.getString(R.string.notification_importance_low);
+ break;
+ case NotificationManager.IMPORTANCE_DEFAULT:
+ title = context.getString(R.string.notification_importance_default_title);
+ if (SoundPreferenceController.hasValidSound(channel)) {
+ summary = context.getString(R.string.notification_importance_default);
+ } else {
+ summary = context.getString(R.string.notification_importance_low);
+ }
+ break;
+ case NotificationManager.IMPORTANCE_HIGH:
+ case NotificationManager.IMPORTANCE_MAX:
+ title = context.getString(R.string.notification_importance_high_title);
+ if (SoundPreferenceController.hasValidSound(channel)) {
+ summary = context.getString(R.string.notification_importance_high);
+ } else {
+ summary = context.getString(R.string.notification_importance_high_silent);
+ }
+ break;
+ default:
+ return "";
+ }
+
+ if (summary != null) {
+ return context.getString(R.string.notification_importance_divider, title, summary);
+ } else {
+ return title;
+ }
+ }
+}
diff --git a/src/com/android/settings/notification/LightsPreferenceController.java b/src/com/android/settings/notification/LightsPreferenceController.java
new file mode 100644
index 00000000000..230c3e295d2
--- /dev/null
+++ b/src/com/android/settings/notification/LightsPreferenceController.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+public class LightsPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+ private static final String KEY_LIGHTS = "lights";
+
+ public LightsPreferenceController(Context context, NotificationBackend backend) {
+ super(context, backend);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_LIGHTS;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ if (mChannel == null) {
+ return false;
+ }
+ return checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT) && canPulseLight()
+ && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId());
+ }
+
+ public void updateState(Preference preference) {
+ if (mChannel != null) {
+ RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+ pref.setDisabledByAdmin(mAdmin);
+ pref.setEnabled(isChannelConfigurable() && !pref.isDisabledByAdmin());
+ pref.setChecked(mChannel.shouldShowLights());
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mChannel != null) {
+ final boolean lights = (Boolean) newValue;
+ mChannel.enableLights(lights);
+ saveChannel();
+ }
+ return true;
+ }
+
+ boolean canPulseLight() {
+ if (!mContext.getResources()
+ .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
+ return false;
+ }
+ return Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0) == 1;
+ }
+
+}
diff --git a/src/com/android/settings/notification/NotificationFooterPreference.java b/src/com/android/settings/notification/NotificationFooterPreference.java
new file mode 100644
index 00000000000..d44ebee2473
--- /dev/null
+++ b/src/com/android/settings/notification/NotificationFooterPreference.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import android.content.Context;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.text.method.LinkMovementMethod;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.settingslib.R;
+
+/**
+ * FooterPreference that can have any key or ordering.
+ */
+public class NotificationFooterPreference extends Preference {
+
+ public NotificationFooterPreference(Context context, AttributeSet attrs) {
+ super(context, attrs, TypedArrayUtils.getAttr(
+ context, R.attr.footerPreferenceStyle, android.R.attr.preferenceStyle));
+ init();
+ }
+
+ public NotificationFooterPreference(Context context) {
+ this(context, null);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ TextView title = holder.itemView.findViewById(android.R.id.title);
+ title.setMovementMethod(new LinkMovementMethod());
+ title.setClickable(false);
+ title.setLongClickable(false);
+ }
+
+ private void init() {
+ setIcon(R.drawable.ic_info_outline_24dp);
+ setSelectable(false);
+ }
+}
diff --git a/src/com/android/settings/notification/NotificationPreferenceController.java b/src/com/android/settings/notification/NotificationPreferenceController.java
new file mode 100644
index 00000000000..b1ef69eaacf
--- /dev/null
+++ b/src/com/android/settings/notification/NotificationPreferenceController.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import android.annotation.Nullable;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
+
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.Objects;
+
+/**
+ * Parent class for preferences appearing on notification setting pages at the app,
+ * notification channel group, or notification channel level.
+ */
+public abstract class NotificationPreferenceController extends AbstractPreferenceController
+{
+ private static final String TAG = "ChannelPrefContr";
+ @Nullable protected NotificationChannel mChannel;
+ @Nullable protected NotificationChannelGroupWrapper mChannelGroup;
+ protected RestrictedLockUtils.EnforcedAdmin mAdmin;
+ protected NotificationBackend.AppRow mAppRow;
+ protected final NotificationManager mNm;
+ protected final NotificationBackend mBackend;
+ protected final Context mContext;
+ protected final UserManager mUm;
+ protected final PackageManager mPm;
+ protected Preference mPreference;
+
+ public NotificationPreferenceController(Context context, NotificationBackend backend) {
+ super(context);
+ mContext = context;
+ mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mBackend = backend;
+ mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mPm = mContext.getPackageManager();
+ }
+
+ /**
+ * Returns true if field's parent object is not blocked.
+ */
+ @Override
+ public boolean isAvailable() {
+ if (mAppRow == null) {
+ return false;
+ }
+ if (mAppRow.banned) {
+ return false;
+ }
+ if (mChannel != null) {
+ return mChannel.getImportance() != IMPORTANCE_NONE;
+ }
+ if (mChannelGroup != null && mChannelGroup.getGroup() == null) {
+ return !mChannelGroup.isBlocked();
+ }
+ return true;
+ }
+
+ /**
+ * Displays or removes preference in this controller.
+ */
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ if (isAvailable()) {
+ final Preference preference = screen.findPreference(getPreferenceKey());
+ if (mPreference != null && preference == null) {
+ screen.addPreference(mPreference);
+ }
+ if (preference != null) {
+ mPreference = preference;
+ }
+ if (this instanceof Preference.OnPreferenceChangeListener) {
+ mPreference.setOnPreferenceChangeListener(
+ (Preference.OnPreferenceChangeListener) this);
+ }
+ } else {
+ findAndRemovePreference(screen, getPreferenceKey());
+ }
+ }
+
+ // finds the preference recursively and removes it from its parent
+ private void findAndRemovePreference(PreferenceGroup prefGroup, String key) {
+ final int preferenceCount = prefGroup.getPreferenceCount();
+ for (int i = preferenceCount - 1; i >= 0; i--) {
+ final Preference preference = prefGroup.getPreference(i);
+ final String curKey = preference.getKey();
+
+ if (curKey != null && curKey.equals(key)) {
+ mPreference = preference;
+ prefGroup.removePreference(preference);
+ }
+
+ if (preference instanceof PreferenceGroup) {
+ findAndRemovePreference((PreferenceGroup) preference, key);
+ }
+ }
+ }
+
+ protected void onResume(NotificationBackend.AppRow appRow,
+ @Nullable NotificationChannel channel, @Nullable NotificationChannelGroupWrapper group,
+ RestrictedLockUtils.EnforcedAdmin admin) {
+ mAppRow = appRow;
+ mChannel = channel;
+ mChannelGroup = group;
+ mAdmin = admin;
+ }
+
+ protected boolean checkCanBeVisible(int minImportanceVisible) {
+ if (mChannel == null) {
+ Log.w(TAG, "No channel");
+ return false;
+ }
+
+ int importance = mChannel.getImportance();
+ if (importance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
+ return true;
+ }
+ return importance >= minImportanceVisible;
+ }
+
+ protected void saveChannel() {
+ if (mChannel != null && mAppRow != null) {
+ mBackend.updateChannel(mAppRow.pkg, mAppRow.uid, mChannel);
+ }
+ }
+
+ protected boolean isChannelConfigurable() {
+ if (mChannel != null && mAppRow != null) {
+ return !Objects.equals(mChannel.getId(), mAppRow.lockedChannelId);
+ }
+ return false;
+ }
+
+ protected boolean isChannelBlockable() {
+ if (mChannel != null && mAppRow != null) {
+ if (!mAppRow.systemApp) {
+ return true;
+ }
+
+ return mChannel.isBlockableSystem()
+ || mChannel.getImportance() == IMPORTANCE_NONE;
+ }
+ return false;
+ }
+
+ protected boolean isChannelGroupBlockable() {
+ if (mChannelGroup != null && mChannelGroup.getGroup() == null && mAppRow != null) {
+ if (!mAppRow.systemApp) {
+ return true;
+ }
+
+ return mChannelGroup.isBlocked();
+ }
+ return false;
+ }
+
+ protected boolean hasValidGroup() {
+ return mChannelGroup != null && mChannelGroup.getGroup() != null;
+ }
+}
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index 3f366e1be3e..e9da5d96f32 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -27,10 +27,15 @@ import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.LayoutPreference;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
import com.android.settings.widget.MasterSwitchPreference;
import com.android.settings.widget.SwitchBar;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.widget.FooterPreference;
import android.app.Notification;
@@ -53,10 +58,12 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.SearchIndexableResource;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -68,66 +75,30 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
-abstract public class NotificationSettingsBase extends SettingsPreferenceFragment {
+abstract public class NotificationSettingsBase extends DashboardFragment {
private static final String TAG = "NotifiSettingsBase";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final Intent APP_NOTIFICATION_PREFS_CATEGORY_INTENT
- = new Intent(Intent.ACTION_MAIN)
- .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES);
-
- protected static final int ORDER_FIRST = -500;
- protected static final int ORDER_LAST = 1000;
-
- protected static final String KEY_APP_LINK = "app_link";
- protected static final String KEY_HEADER = "header";
- protected static final String KEY_BLOCK = "block";
- protected static final String KEY_BADGE = "badge";
- protected static final String KEY_BYPASS_DND = "bypass_dnd";
- protected static final String KEY_VISIBILITY_OVERRIDE = "visibility_override";
- protected static final String KEY_BLOCKED_DESC = "block_desc";
- protected static final String KEY_ALLOW_SOUND = "allow_sound";
-
protected PackageManager mPm;
- protected UserManager mUm;
protected NotificationBackend mBackend = new NotificationBackend();
- protected LockPatternUtils mLockPatternUtils;
protected NotificationManager mNm;
protected Context mContext;
- protected boolean mCreated;
+
protected int mUid;
protected int mUserId;
protected String mPkg;
protected PackageInfo mPkgInfo;
- protected RestrictedSwitchPreference mBadge;
- protected RestrictedSwitchPreference mPriority;
- protected RestrictedDropDownPreference mVisibilityOverride;
- protected RestrictedSwitchPreference mImportanceToggle;
- protected LayoutPreference mBlockBar;
- protected SwitchBar mSwitchBar;
- protected FooterPreference mBlockedDesc;
- protected Preference mAppLink;
-
protected EnforcedAdmin mSuspendedAppsAdmin;
- protected boolean mDndVisualEffectsSuppressed;
-
- protected NotificationChannelGroup mChannelGroup;
+ protected NotificationChannelGroupWrapper mChannelGroup;
protected NotificationChannel mChannel;
protected NotificationBackend.AppRow mAppRow;
- protected boolean mShowLegacyChannelConfig = false;
+ protected boolean mShowLegacyChannelConfig = false;
protected boolean mListeningToPackageRemove;
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- if (DEBUG) Log.d(TAG, "onActivityCreated mCreated=" + mCreated);
- if (mCreated) {
- Log.w(TAG, "onActivityCreated: ignoring duplicate call");
- return;
- }
- mCreated = true;
- }
+ protected List mControllers = new ArrayList<>();
+ protected List mDynamicPreferences = new ArrayList<>();
+ protected ImportanceListener mImportanceListener = new ImportanceListener();
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -143,7 +114,6 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
}
mPm = getPackageManager();
- mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mNm = NotificationManager.from(mContext);
mPkg = args != null && args.containsKey(AppInfoBase.ARG_PACKAGE_NAME)
@@ -187,35 +157,41 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
return;
}
mAppRow = mBackend.loadAppRow(mContext, mPm, mPkgInfo);
+ if (mAppRow == null) {
+ Log.w(TAG, "Can't load package");
+ finish();
+ return;
+ }
+ collectConfigActivities();
Bundle args = getArguments();
mChannel = (args != null && args.containsKey(Settings.EXTRA_CHANNEL_ID)) ?
mBackend.getChannel(mPkg, mUid, args.getString(Settings.EXTRA_CHANNEL_ID)) : null;
- mChannelGroup = (args != null && args.containsKey(Settings.EXTRA_CHANNEL_GROUP_ID)) ?
- mBackend.getGroupWithChannels(mPkg, mUid,
+ NotificationChannelGroup group =
+ (args != null && args.containsKey(Settings.EXTRA_CHANNEL_GROUP_ID))
+ ? mBackend.getGroupWithChannels(mPkg, mUid,
args.getString(Settings.EXTRA_CHANNEL_GROUP_ID))
- : null;
+ : null;
+ if (group != null) {
+ mChannelGroup = new NotificationChannelGroupWrapper(group);
+ }
mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended(
mContext, mPkg, mUserId);
- NotificationManager.Policy policy = mNm.getNotificationPolicy();
- mDndVisualEffectsSuppressed = policy == null ? false : policy.suppressedVisualEffects != 0;
- mSuspendedAppsAdmin = RestrictedLockUtils.checkIfApplicationIsSuspended(
- mContext, mPkg, mUserId);
- }
+ mShowLegacyChannelConfig = mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid)
+ || (mChannel != null
+ && NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId()));
- protected void setVisible(Preference p, boolean visible) {
- setVisible(getPreferenceScreen(), p, visible);
- }
-
- protected void setVisible(PreferenceGroup parent, Preference p, boolean visible) {
- final boolean isVisible = parent.findPreference(p.getKey()) != null;
- if (isVisible == visible) return;
- if (visible) {
- parent.addPreference(p);
- } else {
- parent.removePreference(p);
+ if (mShowLegacyChannelConfig) {
+ mChannel = mBackend.getChannel(
+ mAppRow.pkg, mAppRow.uid, NotificationChannel.DEFAULT_CHANNEL_ID);
+ }
+ if (mChannel != null && !TextUtils.isEmpty(mChannel.getGroup())) {
+ group = mBackend.getGroup(mPkg, mUid, mChannel.getGroup());
+ if (group != null) {
+ mChannelGroup = new NotificationChannelGroupWrapper(group);
+ }
}
}
@@ -224,49 +200,34 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
getActivity().finish();
}
- private List queryNotificationConfigActivities() {
- if (DEBUG) Log.d(TAG, "APP_NOTIFICATION_PREFS_CATEGORY_INTENT is "
- + APP_NOTIFICATION_PREFS_CATEGORY_INTENT);
+ protected void collectConfigActivities() {
+ Intent intent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
+ .setPackage(mAppRow.pkg);
final List resolveInfos = mPm.queryIntentActivities(
- APP_NOTIFICATION_PREFS_CATEGORY_INTENT,
+ intent,
0 //PackageManager.MATCH_DEFAULT_ONLY
);
- return resolveInfos;
- }
-
- protected void collectConfigActivities(ArrayMap rows) {
- final List resolveInfos = queryNotificationConfigActivities();
- applyConfigActivities(rows, resolveInfos);
- }
-
- private void applyConfigActivities(ArrayMap rows,
- List resolveInfos) {
if (DEBUG) Log.d(TAG, "Found " + resolveInfos.size() + " preference activities"
+ (resolveInfos.size() == 0 ? " ;_;" : ""));
for (ResolveInfo ri : resolveInfos) {
final ActivityInfo activityInfo = ri.activityInfo;
final ApplicationInfo appInfo = activityInfo.applicationInfo;
- final NotificationBackend.AppRow row = rows.get(appInfo.packageName);
- if (row == null) {
- if (DEBUG) Log.v(TAG, "Ignoring notification preference activity ("
- + activityInfo.name + ") for unknown package "
- + activityInfo.packageName);
- continue;
- }
- if (row.settingsIntent != null) {
+ if (mAppRow.settingsIntent != null) {
if (DEBUG) Log.v(TAG, "Ignoring duplicate notification preference activity ("
+ activityInfo.name + ") for package "
+ activityInfo.packageName);
continue;
}
- row.settingsIntent = new Intent(APP_NOTIFICATION_PREFS_CATEGORY_INTENT)
+ mAppRow.settingsIntent = intent
+ .setPackage(null)
.setClassName(activityInfo.packageName, activityInfo.name);
if (mChannel != null) {
- row.settingsIntent.putExtra(Notification.EXTRA_CHANNEL_ID, mChannel.getId());
+ mAppRow.settingsIntent.putExtra(Notification.EXTRA_CHANNEL_ID, mChannel.getId());
}
if (mChannelGroup != null) {
- row.settingsIntent.putExtra(
- Notification.EXTRA_CHANNEL_GROUP_ID, mChannelGroup.getId());
+ mAppRow.settingsIntent.putExtra(
+ Notification.EXTRA_CHANNEL_GROUP_ID, mChannelGroup.getGroup().getId());
}
}
}
@@ -292,134 +253,6 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
return null;
}
- protected void addAppLinkPref() {
- if (mAppRow.settingsIntent != null && mAppLink == null) {
- addPreferencesFromResource(R.xml.inapp_notification_settings);
- mAppLink = findPreference(KEY_APP_LINK);
- mAppLink.setIntent(mAppRow.settingsIntent);
- }
- }
-
- protected void populateDefaultChannelPrefs() {
- if (mPkgInfo != null && mChannel != null) {
- addPreferencesFromResource(R.xml.legacy_channel_notification_settings);
- setupPriorityPref(mChannel.canBypassDnd());
- setupVisOverridePref(mChannel.getLockscreenVisibility());
- setupImportanceToggle();
- setupBadge();
- }
- mSwitchBar.setChecked(!mAppRow.banned
- && mChannel.getImportance() != NotificationManager.IMPORTANCE_NONE);
- }
-
- abstract void setupBadge();
-
- abstract void updateDependents(boolean banned);
-
- // 'allow sound'
- private void setupImportanceToggle() {
- mImportanceToggle = (RestrictedSwitchPreference) findPreference(KEY_ALLOW_SOUND);
- mImportanceToggle.setDisabledByAdmin(mSuspendedAppsAdmin);
- mImportanceToggle.setEnabled(isChannelConfigurable(mChannel)
- && !mImportanceToggle.isDisabledByAdmin());
- mImportanceToggle.setChecked(mChannel.getImportance() >= IMPORTANCE_DEFAULT
- || mChannel.getImportance() == IMPORTANCE_UNSPECIFIED);
- mImportanceToggle.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final int importance =
- ((Boolean) newValue ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_LOW);
- mChannel.setImportance(importance);
- mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- updateDependents(mChannel.getImportance() == IMPORTANCE_NONE);
- return true;
- }
- });
- }
-
- protected void setupPriorityPref(boolean priority) {
- mPriority = (RestrictedSwitchPreference) findPreference(KEY_BYPASS_DND);
- mPriority.setDisabledByAdmin(mSuspendedAppsAdmin);
- mPriority.setEnabled(isChannelConfigurable(mChannel) && !mPriority.isDisabledByAdmin());
- mPriority.setChecked(priority);
- mPriority.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final boolean bypassZenMode = (Boolean) newValue;
- mChannel.setBypassDnd(bypassZenMode);
- mChannel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- return true;
- }
- });
- }
-
- protected void setupVisOverridePref(int sensitive) {
- mVisibilityOverride =
- (RestrictedDropDownPreference) findPreference(KEY_VISIBILITY_OVERRIDE);
- ArrayList entries = new ArrayList<>();
- ArrayList values = new ArrayList<>();
-
- mVisibilityOverride.clearRestrictedItems();
- if (getLockscreenNotificationsEnabled() && getLockscreenAllowPrivateNotifications()) {
- final String summaryShowEntry =
- getString(R.string.lock_screen_notifications_summary_show);
- final String summaryShowEntryValue =
- Integer.toString(NotificationManager.VISIBILITY_NO_OVERRIDE);
- entries.add(summaryShowEntry);
- values.add(summaryShowEntryValue);
- setRestrictedIfNotificationFeaturesDisabled(summaryShowEntry, summaryShowEntryValue,
- DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS
- | DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
- }
-
- final String summaryHideEntry = getString(R.string.lock_screen_notifications_summary_hide);
- final String summaryHideEntryValue = Integer.toString(Notification.VISIBILITY_PRIVATE);
- entries.add(summaryHideEntry);
- values.add(summaryHideEntryValue);
- setRestrictedIfNotificationFeaturesDisabled(summaryHideEntry, summaryHideEntryValue,
- DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
- entries.add(getString(R.string.lock_screen_notifications_summary_disable));
- values.add(Integer.toString(Notification.VISIBILITY_SECRET));
- mVisibilityOverride.setEntries(entries.toArray(new CharSequence[entries.size()]));
- mVisibilityOverride.setEntryValues(values.toArray(new CharSequence[values.size()]));
-
- if (sensitive == NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
- mVisibilityOverride.setValue(Integer.toString(getGlobalVisibility()));
- } else {
- mVisibilityOverride.setValue(Integer.toString(sensitive));
- }
- mVisibilityOverride.setSummary("%s");
-
- mVisibilityOverride.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- int sensitive = Integer.parseInt((String) newValue);
- if (sensitive == getGlobalVisibility()) {
- sensitive = NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
- }
- mChannel.setLockscreenVisibility(sensitive);
- mChannel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
- mBackend.updateChannel(mPkg, mUid, mChannel);
- return true;
- }
- });
- mVisibilityOverride.setDisabledByAdmin(mSuspendedAppsAdmin);
- }
-
- protected void setupBlockDesc(int summaryResId) {
- mBlockedDesc = new FooterPreference(getPrefContext());
- mBlockedDesc.setSelectable(false);
- mBlockedDesc.setTitle(summaryResId);
- mBlockedDesc.setEnabled(false);
- mBlockedDesc.setOrder(50);
- mBlockedDesc.setKey(KEY_BLOCKED_DESC);
- getPreferenceScreen().addPreference(mBlockedDesc);
- }
-
protected Preference populateSingleChannelPrefs(PreferenceGroup parent,
final NotificationChannel channel, String summary) {
MasterSwitchPreference channelPref = new MasterSwitchPreference(
@@ -461,105 +294,48 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
return channelPref;
}
- protected boolean checkCanBeVisible(int minImportanceVisible) {
- int importance = mChannel.getImportance();
- if (importance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
- return true;
- }
- return importance >= minImportanceVisible;
- }
-
- protected String getImportanceSummary(NotificationChannel channel) {
- switch (channel.getImportance()) {
- case NotificationManager.IMPORTANCE_UNSPECIFIED:
- return getContext().getString(R.string.notification_importance_unspecified);
- case NotificationManager.IMPORTANCE_NONE:
- return getContext().getString(R.string.notification_toggle_off);
- case NotificationManager.IMPORTANCE_MIN:
- return getContext().getString(R.string.notification_importance_min);
- case NotificationManager.IMPORTANCE_LOW:
- return getContext().getString(R.string.notification_importance_low);
- case NotificationManager.IMPORTANCE_DEFAULT:
- if (hasValidSound(channel)) {
- return getContext().getString(R.string.notification_importance_default);
- } else { // Silent
- return getContext().getString(R.string.notification_importance_low);
- }
- case NotificationManager.IMPORTANCE_HIGH:
- case NotificationManager.IMPORTANCE_MAX:
- default:
- if (hasValidSound(channel)) {
- return getContext().getString(R.string.notification_importance_high);
- } else { // Silent
- return getContext().getString(R.string.notification_importance_high_silent);
- }
- }
- }
-
- private void setRestrictedIfNotificationFeaturesDisabled(CharSequence entry,
- CharSequence entryValue, int keyguardNotificationFeatures) {
- RestrictedLockUtils.EnforcedAdmin admin =
- RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
- mContext, keyguardNotificationFeatures, mUserId);
- if (admin != null) {
- RestrictedDropDownPreference.RestrictedItem item =
- new RestrictedDropDownPreference.RestrictedItem(entry, entryValue, admin);
- mVisibilityOverride.addRestrictedItem(item);
- }
- }
-
- private int getGlobalVisibility() {
- int globalVis = NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
- if (!getLockscreenNotificationsEnabled()) {
- globalVis = Notification.VISIBILITY_SECRET;
- } else if (!getLockscreenAllowPrivateNotifications()) {
- globalVis = Notification.VISIBILITY_PRIVATE;
- }
- return globalVis;
- }
-
- private boolean getLockscreenNotificationsEnabled() {
- return Settings.Secure.getInt(getContentResolver(),
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0;
- }
-
- private boolean getLockscreenAllowPrivateNotifications() {
- return Settings.Secure.getInt(getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0;
- }
-
- protected boolean isLockScreenSecure() {
- if (mLockPatternUtils == null) {
- mLockPatternUtils = new LockPatternUtils(getActivity());
- }
- boolean lockscreenSecure = mLockPatternUtils.isSecure(UserHandle.myUserId());
- UserInfo parentUser = mUm.getProfileParent(UserHandle.myUserId());
- if (parentUser != null){
- lockscreenSecure |= mLockPatternUtils.isSecure(parentUser.id);
- }
-
- return lockscreenSecure;
- }
-
protected boolean isChannelConfigurable(NotificationChannel channel) {
- return !channel.getId().equals(mAppRow.lockedChannelId);
+ if (channel != null && mAppRow != null) {
+ return !channel.getId().equals(mAppRow.lockedChannelId);
+ }
+ return false;
}
protected boolean isChannelBlockable(NotificationChannel channel) {
- if (!mAppRow.systemApp) {
- return true;
- }
+ if (channel != null && mAppRow != null) {
+ if (!mAppRow.systemApp) {
+ return true;
+ }
- return channel.isBlockableSystem()
- || channel.getImportance() == NotificationManager.IMPORTANCE_NONE;
+ return channel.isBlockableSystem()
+ || channel.getImportance() == NotificationManager.IMPORTANCE_NONE;
+ }
+ return false;
}
protected boolean isChannelGroupBlockable(NotificationChannelGroup group) {
- if (!mAppRow.systemApp) {
- return true;
- }
+ if (group != null && mAppRow != null) {
+ if (!mAppRow.systemApp) {
+ return true;
+ }
- return group.isBlocked();
+ return group.isBlocked();
+ }
+ return false;
+ }
+
+ protected void setVisible(Preference p, boolean visible) {
+ setVisible(getPreferenceScreen(), p, visible);
+ }
+
+ protected void setVisible(PreferenceGroup parent, Preference p, boolean visible) {
+ final boolean isVisible = parent.findPreference(p.getKey()) != null;
+ if (isVisible == visible) return;
+ if (visible) {
+ parent.addPreference(p);
+ } else {
+ parent.removePreference(p);
+ }
}
protected void startListeningToPackageRemove() {
@@ -604,7 +380,45 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen
return left.getId().compareTo(right.getId());
};
- boolean hasValidSound(NotificationChannel channel) {
- return channel.getSound() != null && !Uri.EMPTY.equals(channel.getSound());
+ /**
+ * These screens aren't searchable - they only make sense in the context of an app, so
+ * surfacing a generic version would be impossible.
+ */
+ public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider() {
+ @Override
+ public List getXmlResourcesToIndex(Context context,
+ boolean enabled) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public List getPreferenceControllers(Context context) {
+ return getPreferenceControllers(context);
+ }
+ };
+
+ protected class ImportanceListener {
+ protected void onImportanceChanged() {
+ final PreferenceScreen screen = getPreferenceScreen();
+ for (NotificationPreferenceController controller : mControllers) {
+ controller.displayPreference(screen);
+ }
+ updatePreferenceStates();
+
+ boolean hideDynamicFields = false;
+ if (mAppRow == null || mAppRow.banned) {
+ hideDynamicFields = true;
+ } else {
+ if (mChannel != null) {
+ hideDynamicFields = mChannel.getImportance() == IMPORTANCE_NONE;
+ } else if (mChannelGroup != null) {
+ hideDynamicFields = mChannelGroup.isBlocked();
+ }
+ }
+ for (Preference preference : mDynamicPreferences) {
+ setVisible(getPreferenceScreen(), preference, !hideDynamicFields);
+ }
+ }
}
}
diff --git a/src/com/android/settings/notification/NotificationsOffPreferenceController.java b/src/com/android/settings/notification/NotificationsOffPreferenceController.java
new file mode 100644
index 00000000000..74591cfd4ce
--- /dev/null
+++ b/src/com/android/settings/notification/NotificationsOffPreferenceController.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.widget.FooterPreference;
+
+public class NotificationsOffPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin {
+
+ private static final String KEY_BLOCKED_DESC = "block_desc";
+
+ public NotificationsOffPreferenceController(Context context) {
+ super(context, null);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_BLOCKED_DESC;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (mAppRow == null) {
+ return false;
+ }
+ // Available only when other controllers are unavailable - this UI replaces the UI that
+ // would give more detailed notification controls.
+ return !super.isAvailable();
+ }
+
+ public void updateState(Preference preference) {
+ if (mAppRow != null) {
+ if (mChannel != null) {
+ preference.setTitle(R.string.channel_notifications_off_desc);
+ } else if (mChannelGroup != null && mChannelGroup.getGroup() == null) {
+ preference.setTitle(R.string.channel_group_notifications_off_desc);
+ } else {
+ preference.setTitle(R.string.app_notifications_off_desc);
+ }
+ }
+ preference.setEnabled(false);
+ preference.setSelectable(false);
+ }
+}
diff --git a/src/com/android/settings/notification/SoundPreferenceController.java b/src/com/android/settings/notification/SoundPreferenceController.java
new file mode 100644
index 00000000000..e4414b6908a
--- /dev/null
+++ b/src/com/android/settings/notification/SoundPreferenceController.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.preference.PreferenceManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.core.PreferenceControllerMixin;
+
+public class SoundPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener,
+ PreferenceManager.OnActivityResultListener {
+
+ private static final String KEY_SOUND = "ringtone";
+ private final SettingsPreferenceFragment mFragment;
+ private final NotificationSettingsBase.ImportanceListener mListener;
+ private NotificationSoundPreference mPreference;
+ protected static final int CODE = 200;
+
+ public SoundPreferenceController(Context context, SettingsPreferenceFragment hostFragment,
+ NotificationSettingsBase.ImportanceListener importanceListener,
+ NotificationBackend backend) {
+ super(context, backend);
+ mFragment = hostFragment;
+ mListener = importanceListener;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_SOUND;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ if (mChannel == null) {
+ return false;
+ }
+ return checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
+ && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId());
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = (NotificationSoundPreference) screen.findPreference(getPreferenceKey());
+ }
+
+ public void updateState(Preference preference) {
+ if (mAppRow!= null && mChannel != null) {
+ NotificationSoundPreference pref = (NotificationSoundPreference) preference;
+ pref.setEnabled(mAdmin == null && isChannelConfigurable());
+ pref.setRingtone(mChannel.getSound());
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mChannel != null) {
+ mChannel.setSound((Uri) newValue, mChannel.getAudioAttributes());
+ saveChannel();
+ }
+ return true;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (KEY_SOUND.equals(preference.getKey()) && mFragment != null) {
+ NotificationSoundPreference pref = (NotificationSoundPreference) preference;
+ pref.onPrepareRingtonePickerIntent(pref.getIntent());
+ mFragment.startActivityForResult(preference.getIntent(), CODE);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (CODE == requestCode) {
+ if (mPreference != null) {
+ mPreference.onActivityResult(requestCode, resultCode, data);
+ }
+ // the importance hasn't changed, but the importance description might as a result of
+ // user's selection.
+ mListener.onImportanceChanged();
+ return true;
+ }
+ return false;
+ }
+
+ protected static boolean hasValidSound(NotificationChannel channel) {
+ return channel != null
+ && channel.getSound() != null && !Uri.EMPTY.equals(channel.getSound());
+ }
+}
diff --git a/src/com/android/settings/notification/VibrationPreferenceController.java b/src/com/android/settings/notification/VibrationPreferenceController.java
new file mode 100644
index 00000000000..f9b786dc2d8
--- /dev/null
+++ b/src/com/android/settings/notification/VibrationPreferenceController.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Vibrator;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+public class VibrationPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+ private static final String KEY_VIBRATE = "vibrate";
+ private final Vibrator mVibrator;
+
+ public VibrationPreferenceController(Context context, NotificationBackend backend) {
+ super(context, backend);
+ mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_VIBRATE;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable() || mChannel == null) {
+ return false;
+ }
+ return checkCanBeVisible(NotificationManager.IMPORTANCE_DEFAULT)
+ && !NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())
+ && mVibrator != null
+ && mVibrator.hasVibrator();
+ }
+
+ public void updateState(Preference preference) {
+ if (mChannel != null) {
+ RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+ pref.setDisabledByAdmin(mAdmin);
+ pref.setEnabled(!pref.isDisabledByAdmin() && isChannelConfigurable());
+ pref.setChecked(mChannel.shouldVibrate());
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mChannel != null) {
+ final boolean vibrate = (Boolean) newValue;
+ mChannel.enableVibration(vibrate);
+ saveChannel();
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/settings/notification/VisibilityPreferenceController.java b/src/com/android/settings/notification/VisibilityPreferenceController.java
new file mode 100644
index 00000000000..76caac0c852
--- /dev/null
+++ b/src/com/android/settings/notification/VisibilityPreferenceController.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
+import android.support.v7.preference.Preference;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.RestrictedLockUtils;
+
+import java.util.ArrayList;
+
+public class VisibilityPreferenceController extends NotificationPreferenceController
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
+
+ private static final String TAG = "ChannelVisPrefContr";
+ private static final String KEY_VISIBILITY_OVERRIDE = "visibility_override";
+ private LockPatternUtils mLockPatternUtils;
+
+ public VisibilityPreferenceController(Context context, LockPatternUtils utils,
+ NotificationBackend backend) {
+ super(context, backend);
+ mLockPatternUtils = utils;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_VISIBILITY_OVERRIDE;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+ if (mChannel == null || mAppRow.banned) {
+ return false;
+ }
+ return checkCanBeVisible(NotificationManager.IMPORTANCE_LOW) && isLockScreenSecure();
+ }
+
+ public void updateState(Preference preference) {
+ if (mChannel != null && mAppRow != null) {
+ RestrictedDropDownPreference pref = (RestrictedDropDownPreference) preference;
+ ArrayList entries = new ArrayList<>();
+ ArrayList values = new ArrayList<>();
+
+ pref.clearRestrictedItems();
+ if (getLockscreenNotificationsEnabled() && getLockscreenAllowPrivateNotifications()) {
+ final String summaryShowEntry =
+ mContext.getString(R.string.lock_screen_notifications_summary_show);
+ final String summaryShowEntryValue =
+ Integer.toString(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ entries.add(summaryShowEntry);
+ values.add(summaryShowEntryValue);
+ setRestrictedIfNotificationFeaturesDisabled(pref, summaryShowEntry,
+ summaryShowEntryValue,
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS
+ | DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+ }
+
+ final String summaryHideEntry =
+ mContext.getString(R.string.lock_screen_notifications_summary_hide);
+ final String summaryHideEntryValue = Integer.toString(Notification.VISIBILITY_PRIVATE);
+ entries.add(summaryHideEntry);
+ values.add(summaryHideEntryValue);
+ setRestrictedIfNotificationFeaturesDisabled(pref,
+ summaryHideEntry, summaryHideEntryValue,
+ DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+ entries.add(mContext.getString(R.string.lock_screen_notifications_summary_disable));
+ values.add(Integer.toString(Notification.VISIBILITY_SECRET));
+ pref.setEntries(entries.toArray(new CharSequence[entries.size()]));
+ pref.setEntryValues(values.toArray(new CharSequence[values.size()]));
+
+ if (mChannel.getLockscreenVisibility()
+ == NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
+ pref.setValue(Integer.toString(getGlobalVisibility()));
+ } else {
+ pref.setValue(Integer.toString(mChannel.getLockscreenVisibility()));
+ }
+ pref.setSummary("%s");
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mChannel != null) {
+ int sensitive = Integer.parseInt((String) newValue);
+ if (sensitive == getGlobalVisibility()) {
+ sensitive = NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
+ }
+ mChannel.setLockscreenVisibility(sensitive);
+ mChannel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
+ saveChannel();
+ }
+ return true;
+ }
+
+ private void setRestrictedIfNotificationFeaturesDisabled(RestrictedDropDownPreference pref,
+ CharSequence entry, CharSequence entryValue, int keyguardNotificationFeatures) {
+ RestrictedLockUtils.EnforcedAdmin admin =
+ RestrictedLockUtils.checkIfKeyguardFeaturesDisabled(
+ mContext, keyguardNotificationFeatures, mAppRow.userId);
+ if (admin != null) {
+ RestrictedDropDownPreference.RestrictedItem item =
+ new RestrictedDropDownPreference.RestrictedItem(entry, entryValue, admin);
+ pref.addRestrictedItem(item);
+ }
+ }
+
+ private int getGlobalVisibility() {
+ int globalVis = NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
+ if (!getLockscreenNotificationsEnabled()) {
+ globalVis = Notification.VISIBILITY_SECRET;
+ } else if (!getLockscreenAllowPrivateNotifications()) {
+ globalVis = Notification.VISIBILITY_PRIVATE;
+ }
+ return globalVis;
+ }
+
+ private boolean getLockscreenNotificationsEnabled() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0;
+ }
+
+ private boolean getLockscreenAllowPrivateNotifications() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0;
+ }
+
+ protected boolean isLockScreenSecure() {
+ boolean lockscreenSecure = mLockPatternUtils.isSecure(UserHandle.myUserId());
+ UserInfo parentUser = mUm.getProfileParent(UserHandle.myUserId());
+ if (parentUser != null){
+ lockscreenSecure |= mLockPatternUtils.isSecure(parentUser.id);
+ }
+
+ return lockscreenSecure;
+ }
+
+}
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 69b2f9fa5d3..98e0bf48f67 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -67,7 +67,10 @@ import com.android.settings.location.LocationSettings;
import com.android.settings.location.ScanningSettings;
import com.android.settings.network.NetworkDashboardFragment;
import com.android.settings.nfc.PaymentSettings;
+import com.android.settings.notification.AppNotificationSettings;
+import com.android.settings.notification.ChannelGroupNotificationSettings;
import com.android.settings.notification.ChannelImportanceSettings;
+import com.android.settings.notification.ChannelNotificationSettings;
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.SoundSettings;
import com.android.settings.notification.ZenModeAutomationSettings;
@@ -173,6 +176,10 @@ public final class SearchIndexableResources {
addIndex(LockscreenDashboardFragment.class);
addIndex(ZenModeBehaviorSettings.class);
addIndex(ZenModeAutomationSettings.class);
+ addIndex(AppNotificationSettings.class);
+ addIndex(ChannelNotificationSettings.class);
+ addIndex(ChannelImportanceSettings.class);
+ addIndex(ChannelGroupNotificationSettings.class);
}
private SearchIndexableResources() {
diff --git a/src/com/android/settings/wrapper/NotificationChannelGroupWrapper.java b/src/com/android/settings/wrapper/NotificationChannelGroupWrapper.java
new file mode 100644
index 00000000000..dbfff1a6c4b
--- /dev/null
+++ b/src/com/android/settings/wrapper/NotificationChannelGroupWrapper.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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.wrapper;
+
+import android.app.NotificationChannelGroup;
+
+/**
+ * Wrapper for {@link NotificationChannelGroup} until roboletric supports O MR1.
+ */
+public class NotificationChannelGroupWrapper {
+
+ private final NotificationChannelGroup mGroup;
+
+ public NotificationChannelGroupWrapper(NotificationChannelGroup group) {
+ mGroup = group;
+ }
+
+ /**
+ * Get the real group object so we can call APIs directly on it.
+ */
+ public NotificationChannelGroup getGroup() {
+ return mGroup;
+ }
+
+ public String getDescription() {
+ if (mGroup != null) {
+ return mGroup.getDescription();
+ }
+ return null;
+ }
+
+ public void setDescription(String desc) {
+ if (mGroup != null) {
+ mGroup.setDescription(desc);
+ }
+ }
+
+ public boolean isBlocked() {
+ if (mGroup != null) {
+ return mGroup.isBlocked();
+ }
+ return true;
+ }
+
+ public void setBlocked(boolean blocked) {
+ if (mGroup != null) {
+ mGroup.setBlocked(blocked);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/assets/grandfather_not_implementing_indexable b/tests/robotests/assets/grandfather_not_implementing_indexable
index 608e44c459c..e6285ad27ab 100644
--- a/tests/robotests/assets/grandfather_not_implementing_indexable
+++ b/tests/robotests/assets/grandfather_not_implementing_indexable
@@ -19,8 +19,6 @@ com.android.settings.wifi.WifiInfo
com.android.settings.applications.VrListenerSettings
com.android.settings.inputmethod.UserDictionaryList
com.android.settings.datausage.DataSaverSummary
-com.android.settings.notification.ChannelNotificationSettings
-com.android.settings.notification.ChannelGroupNotificationSettings
com.android.settings.datausage.AppDataUsage
com.android.settings.datausage.DataPlanUsageSummary
com.android.settings.accessibility.FontSizePreferenceFragmentForSetupWizard
@@ -55,7 +53,6 @@ com.android.settings.applications.InstalledAppDetails
com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment
com.android.settings.print.PrintServiceSettingsFragment
com.android.settings.wfd.WifiDisplaySettings
-com.android.settings.notification.AppNotificationSettings
com.android.settings.deviceinfo.PrivateVolumeSettings
com.android.settings.users.AppRestrictionsFragment
com.android.settings.deviceinfo.PrivateVolumeUnmount
diff --git a/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider b/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider
index b3290725c98..a71b040b080 100644
--- a/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider
+++ b/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider
@@ -1 +1 @@
-com.android.settings.fuelgauge.PowerUsageSummary
+com.android.settings.fuelgauge.PowerUsageSummary
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/AllowSoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/AllowSoundPreferenceControllerTest.java
new file mode 100644
index 00000000000..9ba8706b4ca
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/AllowSoundPreferenceControllerTest.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class AllowSoundPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+
+ @Mock
+ NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+ private AllowSoundPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController =
+ spy(new AllowSoundPreferenceController(mContext, mImportanceListener, mBackend));
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(RestrictedSwitchPreference.class));
+ mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
+ }
+
+ @Test
+ public void testIsAvailable_notIfNull() throws Exception {
+ mController.onResume(null, mock(NotificationChannel.class), null, null);
+ assertFalse(mController.isAvailable());
+
+ mController.onResume(mock(NotificationBackend.AppRow.class), null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfAppBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, mock(NotificationChannel.class), null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfAppCreatedChannel() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something new");
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+ RestrictedLockUtils.EnforcedAdmin.class));
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_notConfigurable() throws Exception {
+ String lockedId = "locked";
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = lockedId;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn(lockedId);
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_configurable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertTrue(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_checkedForHighImportanceChannel() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+ assertTrue(pref.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_checkedForUnspecifiedImportanceChannel() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_UNSPECIFIED);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+ assertTrue(pref.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_notCheckedForLowImportanceChannel() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+ assertFalse(pref.isChecked());
+ }
+
+ @Test
+ public void testOnPreferenceChange_on() {
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+ pref.setChecked(true);
+ mController.onPreferenceChange(pref, true);
+
+ assertEquals(IMPORTANCE_UNSPECIFIED, mController.mChannel.getImportance());
+ verify(mImportanceListener, times(1)).onImportanceChanged();
+ }
+
+ @Test
+ public void testOnPreferenceChange_off() {
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ pref.setChecked(false);
+ mController.onPreferenceChange(pref, false);
+
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ assertEquals(IMPORTANCE_LOW, mController.mChannel.getImportance());
+ verify(mImportanceListener, times(1)).onImportanceChanged();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/AppLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/AppLinkPreferenceControllerTest.java
new file mode 100644
index 00000000000..b440704d260
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/AppLinkPreferenceControllerTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class AppLinkPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+
+ private AppLinkPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = spy(new AppLinkPreferenceController(mContext));
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(Preference.class));
+ }
+
+ @Test
+ public void testIsAvailable_notIfNull() throws Exception {
+ mController.onResume(null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfAppBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfChannelBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notNoIntent() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.settingsIntent = new Intent("test");
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ Intent intent = new Intent("action");
+ appRow.settingsIntent = intent;
+ mController.onResume(appRow, null, null, null);
+
+ Preference pref = new Preference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertEquals(intent, pref.getIntent());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/BadgePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BadgePreferenceControllerTest.java
new file mode 100644
index 00000000000..60524782af0
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/BadgePreferenceControllerTest.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.provider.Settings.Secure.NOTIFICATION_BADGING;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+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.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class BadgePreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+
+ private BadgePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = spy(new BadgePreferenceController(mContext, mBackend));
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(RestrictedSwitchPreference.class));
+ mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
+ }
+
+ @Test
+ public void testIsAvailable_notIfAppBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, mock(NotificationChannel.class), null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfChannelBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_channel_notIfAppOff() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.showBadge = false;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfOffGlobally() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+ Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BADGING, 0);
+
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_app() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ mController.onResume(appRow, null, null, null);
+ Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BADGING, 1);
+
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_channel() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.showBadge = true;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+ Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BADGING, 1);
+
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+ RestrictedLockUtils.EnforcedAdmin.class));
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_channelNotConfigurable() throws Exception {
+ String lockedId = "locked";
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = lockedId;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn(lockedId);
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_channel() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = "a";
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.canShowBadge()).thenReturn(true);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertTrue(pref.isChecked());
+
+ when(channel.canShowBadge()).thenReturn(false);
+ mController.onResume(appRow, channel, null, null);
+ mController.updateState(pref);
+
+ assertFalse(pref.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_app() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.showBadge = true;
+ mController.onResume(appRow, null, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+ assertTrue(pref.isChecked());
+
+ appRow.showBadge = false;
+ mController.onResume(appRow, null, null, null);
+
+ mController.updateState(pref);
+ assertFalse(pref.isChecked());
+ }
+
+ @Test
+ public void testOnPreferenceChange_on_channel() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.showBadge = true;
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW);
+ channel.setShowBadge(false);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, true);
+ assertTrue(channel.canShowBadge());
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testOnPreferenceChange_off_channel() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.showBadge = true;
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+ channel.setShowBadge(true);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, false);
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ assertFalse(channel.canShowBadge());
+ }
+
+ @Test
+ public void testOnPreferenceChange_on_app() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.showBadge = false;
+ mController.onResume(appRow, null, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, true);
+
+ assertTrue(appRow.showBadge);
+ verify(mBackend, times(1)).setShowBadge(any(), anyInt(), eq(true));
+ }
+
+ @Test
+ public void testOnPreferenceChange_off_app() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.showBadge = true;
+ mController.onResume(appRow, null, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, false);
+
+ assertFalse(appRow.showBadge);
+ verify(mBackend, times(1)).setShowBadge(any(), anyInt(), eq(false));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java
new file mode 100644
index 00000000000..9014f4ee5c1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/BlockPreferenceControllerTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.widget.SwitchBar;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class BlockPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+
+ @Mock
+ NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+ private BlockPreferenceController mController;
+ @Mock
+ private LayoutPreference mPreference;
+ private SwitchBar mSwitch;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = spy(new BlockPreferenceController(mContext, mImportanceListener, mBackend));
+ mSwitch = new SwitchBar(mContext);
+ when(mPreference.findViewById(R.id.switch_bar)).thenReturn(mSwitch);
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(LayoutPreference.class));
+ mController.onSwitchChanged(null, false);
+ }
+
+ @Test
+ public void testIsAvailable_notIfNull() throws Exception {
+ mController.onResume(null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfChannelNotBlockable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = true;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfGroupNotBlockable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = true;
+ mController.onResume(appRow, null, mock(NotificationChannelGroupWrapper.class), null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfAppNotBlockable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = true;
+ mController.onResume(appRow, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_systemApp() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = true;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_nonSystemApp() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = false;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_app() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, null, null, null);
+ mController.updateState(mPreference);
+
+ assertNotNull(mPreference.findViewById(R.id.switch_bar));
+
+ assertFalse(mSwitch.isChecked());
+
+ appRow.banned = false;
+ mController.onResume(appRow, null, null, null);
+ mController.updateState(mPreference);
+
+ assertTrue(mSwitch.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_group() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ when(group.getGroup()).thenReturn(mock(NotificationChannelGroup.class));
+ when(group.isBlocked()).thenReturn(true);
+ mController.onResume(appRow, null, group, null);
+ mController.updateState(mPreference);
+
+ assertFalse(mSwitch.isChecked());
+
+ appRow.banned = true;
+ mController.onResume(appRow, null, group, null);
+ when(group.isBlocked()).thenReturn(true);
+ mController.updateState(mPreference);
+
+ assertFalse(mSwitch.isChecked());
+
+ appRow.banned = false;
+ mController.onResume(appRow, null, group, null);
+ when(group.isBlocked()).thenReturn(false);
+ mController.updateState(mPreference);
+
+ assertTrue(mSwitch.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_channelBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, null, null);
+ mController.updateState(mPreference);
+
+ assertFalse(mSwitch.isChecked());
+
+ appRow.banned = true;
+ channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+ mController.updateState(mPreference);
+
+ assertFalse(mSwitch.isChecked());
+
+ appRow.banned = false;
+ channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+ mController.updateState(mPreference);
+
+ assertTrue(mSwitch.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_noCrashIfCalledTwice() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+ mController.onResume(appRow, channel, null, null);
+ mController.updateState(mPreference);
+ mController.updateState(mPreference);
+ }
+
+ @Test
+ public void testUpdateState_doesNotResetImportance() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+ mController.onResume(appRow, channel, null, null);
+ mController.updateState(mPreference);
+
+ assertEquals(IMPORTANCE_LOW, channel.getImportance());
+ }
+
+ @Test
+ public void testOnSwitchChanged_channel_default() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_UNSPECIFIED);
+ mController.onResume(appRow, channel, null, null);
+ mController.updateState(mPreference);
+
+ mController.onSwitchChanged(null, false);
+ assertEquals(IMPORTANCE_NONE, channel.getImportance());
+
+ mController.onSwitchChanged(null, true);
+ assertEquals(IMPORTANCE_UNSPECIFIED, channel.getImportance());
+
+ verify(mBackend, times(2)).updateChannel(any(), anyInt(), any());
+
+ }
+
+ @Test
+ public void testOnSwitchChanged_channel_nonDefault() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+ mController.updateState(mPreference);
+
+ mController.onSwitchChanged(null, false);
+ assertEquals(IMPORTANCE_NONE, channel.getImportance());
+
+ mController.onSwitchChanged(null, true);
+ assertEquals(IMPORTANCE_DEFAULT, channel.getImportance());
+
+ verify(mBackend, times(2)).updateChannel(any(), anyInt(), any());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/DeletedChannelsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/DeletedChannelsPreferenceControllerTest.java
new file mode 100644
index 00000000000..fd903f9322d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/DeletedChannelsPreferenceControllerTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+
+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.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DeletedChannelsPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+
+ private DeletedChannelsPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = new DeletedChannelsPreferenceController(mContext, mBackend);
+ }
+
+ @Test
+ public void noCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(Preference.class));
+ }
+
+ @Test
+ public void isAvailable_appScreen_notIfAppBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void isAvailable_groupScreen_never() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ mController.onResume(appRow, null, mock(NotificationChannelGroupWrapper.class), null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void isAvailable_channelScreen_never() throws Exception {
+ mController.onResume(
+ new NotificationBackend.AppRow(), mock(NotificationChannel.class), null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void isAvailable_appScreen_notIfNoDeletedChannels() throws Exception {
+ when(mBackend.getDeletedChannelCount(any(), anyInt())).thenReturn(0);
+ mController.onResume(new NotificationBackend.AppRow(), null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void isAvailable_appScreen() throws Exception {
+ when(mBackend.getDeletedChannelCount(any(), anyInt())).thenReturn(1);
+ mController.onResume(new NotificationBackend.AppRow(), null, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void updateState() throws Exception {
+ when(mBackend.getDeletedChannelCount(any(), anyInt())).thenReturn(1);
+ mController.onResume(new NotificationBackend.AppRow(), null, null, null);
+
+ Preference pref = mock(Preference.class);
+ mController.updateState(pref);
+
+ verify(pref, times(1)).setEnabled(false);
+ verify(pref, times(1)).setSelectable(false);
+ verify(mBackend, times(1)).getDeletedChannelCount(any(), anyInt());
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(CharSequence.class);
+ verify(pref, times(1)).setTitle(argumentCaptor.capture());
+ assertTrue(argumentCaptor.getValue().toString().contains("1"));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/DescriptionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/DescriptionPreferenceControllerTest.java
new file mode 100644
index 00000000000..1776a9b2f0b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/DescriptionPreferenceControllerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+
+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 org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class DescriptionPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+
+ private DescriptionPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = spy(new DescriptionPreferenceController(mContext));
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(Preference.class));
+ }
+
+ @Test
+ public void testIsAvailable_notIfNull() throws Exception {
+ mController.onResume(null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfChannelGroupBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ when(group.isBlocked()).thenReturn(true);
+ mController.onResume(appRow, null, group, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfChannelBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfNoChannelDesc() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfNoChannelGroupDesc() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ when(group.getGroup()).thenReturn(mock(NotificationChannelGroup.class));
+ mController.onResume(appRow, null, group, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_channel() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ when(channel.getDescription()).thenReturn("AAA");
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_channelGroup() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ when(group.getGroup()).thenReturn(mock(NotificationChannelGroup.class));
+ when(group.getDescription()).thenReturn("something");
+ when(group.isBlocked()).thenReturn(false);
+ mController.onResume(appRow, null, group, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_channel() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ when(channel.getDescription()).thenReturn("AAA");
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new Preference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertEquals("AAA", pref.getTitle());
+ assertFalse(pref.isEnabled());
+ assertFalse(pref.isSelectable());
+ }
+
+ @Test
+ public void testUpdateState_channelGroup() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ when(group.getGroup()).thenReturn(mock(NotificationChannelGroup.class));
+ when(group.getDescription()).thenReturn("something");
+ mController.onResume(appRow, null, group, null);
+
+ Preference pref = new Preference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertEquals("something", pref.getTitle());
+ assertFalse(pref.isEnabled());
+ assertFalse(pref.isSelectable());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/DndPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/DndPreferenceControllerTest.java
new file mode 100644
index 00000000000..241e279088b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/DndPreferenceControllerTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class DndPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+ @Mock
+ private Lifecycle mLifecycle;
+
+ private DndPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = spy(new DndPreferenceController(mContext, mLifecycle, mBackend));
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(RestrictedSwitchPreference.class));
+ mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
+ mController.onResume();
+ }
+
+ @Test
+ public void testIsAvailable_notIfNotImportant_noVisEffects() throws Exception {
+ when(mNm.getNotificationPolicy()).thenReturn(new NotificationManager.Policy(0, 0, 0, 0));
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
+ mController.onResume();
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfNotImportant_visEffects() throws Exception {
+ when(mNm.getNotificationPolicy()).thenReturn(new NotificationManager.Policy(0, 0, 0, 1));
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_MIN);
+ mController.onResume();
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_importance_noVisEffects() throws Exception {
+ when(mNm.getNotificationPolicy()).thenReturn(new NotificationManager.Policy(0, 0, 0, 0));
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+ mController.onResume();
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_important_visEffects() throws Exception {
+ when(mNm.getNotificationPolicy()).thenReturn(new NotificationManager.Policy(0, 0, 0, 1));
+ assertTrue(mNm.getNotificationPolicy().suppressedVisualEffects != 0);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
+ mController.onResume();
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+ RestrictedLockUtils.EnforcedAdmin.class));
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_notConfigurable() throws Exception {
+ String lockedId = "locked";
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = lockedId;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn(lockedId);
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_configurable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertTrue(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_bypassDnd() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.canBypassDnd()).thenReturn(true);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+ assertTrue(pref.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_noBypassDnd() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.canBypassDnd()).thenReturn(false);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+ assertFalse(pref.isChecked());
+ }
+
+ @Test
+ public void testOnPreferenceChange_on() {
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, true);
+
+ assertTrue(channel.canBypassDnd());
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testOnPreferenceChange_off() {
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, false);
+
+ assertFalse(channel.canBypassDnd());
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/HeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/HeaderPreferenceControllerTest.java
new file mode 100644
index 00000000000..385376f7be1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/HeaderPreferenceControllerTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.app.Activity;
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v14.preference.PreferenceFragment;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class HeaderPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+
+ private HeaderPreferenceController mController;
+ @Mock
+ private LayoutPreference mPreference;
+ @Mock
+ private View mView;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ PreferenceFragment fragment = mock(PreferenceFragment.class);
+ when(fragment.getContext()).thenReturn(mContext);
+ Activity activity = mock(Activity.class);
+ when(activity.getApplicationContext()).thenReturn(mContext);
+ when(fragment.getActivity()).thenReturn(activity);
+ mController = spy(new HeaderPreferenceController(mContext, fragment));
+ when(mPreference.findViewById(anyInt())).thenReturn(mView);
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(LayoutPreference.class));
+ }
+
+ @Test
+ public void testIsAvailable_notIfNull() throws Exception {
+ mController.onResume(null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, null, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testGetLabel() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.label = "bananas";
+ mController.onResume(appRow, null, null, null);
+ assertEquals(appRow.label, mController.getLabel());
+
+ NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+ NotificationChannelGroupWrapper gWrapper = new NotificationChannelGroupWrapper(group);
+ mController.onResume(appRow, null, gWrapper, null);
+ assertEquals(group.getName(), mController.getLabel());
+
+ NotificationChannel channel = new NotificationChannel("cid", "cname", IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, gWrapper, null);
+ assertEquals(channel.getName(), mController.getLabel());
+ }
+
+ @Test
+ public void testGetSummary() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.label = "bananas";
+ mController.onResume(appRow, null, null, null);
+ assertEquals("", mController.getSummary());
+
+ NotificationChannelGroup group = new NotificationChannelGroup("id", "name");
+ NotificationChannelGroupWrapper gWrapper = new NotificationChannelGroupWrapper(group);
+ mController.onResume(appRow, null, gWrapper, null);
+ assertEquals(appRow.label, mController.getSummary());
+
+ NotificationChannel channel = new NotificationChannel("cid", "cname", IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, gWrapper, null);
+ assertTrue(mController.getSummary().toString().contains(group.getName()));
+ assertTrue(mController.getSummary().toString().contains(appRow.label));
+
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.getSummary().toString().contains(group.getName()));
+ assertTrue(mController.getSummary().toString().contains(appRow.label));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/ImportancePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ImportancePreferenceControllerTest.java
new file mode 100644
index 00000000000..aebd6c9c4b9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ImportancePreferenceControllerTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+import android.text.TextUtils;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+
+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 org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class ImportancePreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+
+ private ImportancePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = spy(new ImportancePreferenceController(mContext));
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(Preference.class));
+ }
+
+ @Test
+ public void testIsAvailable_notIfNull() throws Exception {
+ mController.onResume(null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfAppBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, mock(NotificationChannel.class), null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfChannelBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notForDefaultChannel() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+ RestrictedLockUtils.EnforcedAdmin.class));
+
+ Preference pref = new Preference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ assertNull(pref.getIntent());
+ }
+
+ @Test
+ public void testUpdateState_notConfigurable() throws Exception {
+ String lockedId = "locked";
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = lockedId;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn(lockedId);
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new Preference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ assertNull(pref.getIntent());
+ }
+
+ @Test
+ public void testUpdateState() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new Preference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertTrue(pref.isEnabled());
+ assertNotNull(pref.getIntent());
+ assertFalse(TextUtils.isEmpty(pref.getSummary()));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/LightsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/LightsPreferenceControllerTest.java
new file mode 100644
index 00000000000..017cb88818e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/LightsPreferenceControllerTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.provider.Settings.System.NOTIFICATION_LIGHT_PULSE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O, shadows = {
+ SettingsShadowResources.class,
+})
+public class LightsPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+
+ private LightsPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = spy(new LightsPreferenceController(mContext, mBackend));
+
+ // By default allow lights
+ SettingsShadowResources.overrideResource(
+ com.android.internal.R.bool.config_intrusiveNotificationLed, true);
+ Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_LIGHT_PULSE, 1);
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(RestrictedSwitchPreference.class));
+ mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
+ }
+
+ @Test
+ public void testIsAvailable_notIfConfigNotAllowed() throws Exception {
+ SettingsShadowResources.overrideResource(
+ com.android.internal.R.bool.config_intrusiveNotificationLed, false);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfSettingNotAllowed() throws Exception {
+ Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_LIGHT_PULSE, 0);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfNotImportant() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfDefaultChannel() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+ RestrictedLockUtils.EnforcedAdmin.class));
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_notConfigurable() throws Exception {
+ String lockedId = "locked";
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = lockedId;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn(lockedId);
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_configurable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertTrue(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_lightsOn() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.shouldShowLights()).thenReturn(true);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+ assertTrue(pref.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_lightsOff() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.shouldShowLights()).thenReturn(false);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+ assertFalse(pref.isChecked());
+ }
+
+ @Test
+ public void testOnPreferenceChange_on() {
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_DEFAULT);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, true);
+
+ assertTrue(channel.shouldShowLights());
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testOnPreferenceChange_off() {
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, false);
+
+ assertFalse(channel.shouldShowLights());
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationPreferenceControllerTest.java
new file mode 100644
index 00000000000..d6857402861
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/NotificationPreferenceControllerTest.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class NotificationPreferenceControllerTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+
+ private TestPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = new TestPreferenceController(mContext, mBackend);
+ }
+
+ @Test
+ public void noCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(Preference.class));
+ assertFalse(mController.checkCanBeVisible(IMPORTANCE_UNSPECIFIED));
+ mController.saveChannel();
+ assertFalse(mController.isChannelConfigurable());
+ assertFalse(mController.isChannelBlockable());
+ assertFalse(mController.isChannelGroupBlockable());
+ }
+
+ @Test
+ public void isAvailable_notIfNull() throws Exception {
+ mController.onResume(null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void isAvailable_notIfAppBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, mock(NotificationChannel.class),
+ mock(NotificationChannelGroupWrapper.class), null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void isAvailable_notIfChannelBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void isAvailable_notIfChannelGroupBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+
+ mController.onResume(appRow, channel, group, null);
+ when(group.isBlocked()).thenReturn(true);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void isAvailable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_DEFAULT);
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ when(group.isBlocked()).thenReturn(false);
+
+ mController.onResume(appRow, channel, group, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testOnResume() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ RestrictedLockUtils.EnforcedAdmin admin = mock(RestrictedLockUtils.EnforcedAdmin.class);
+
+ mController.onResume(appRow, channel, group, admin);
+
+ assertEquals(appRow, mController.mAppRow);
+ assertEquals(channel, mController.mChannel);
+ assertEquals(group, mController.mChannelGroup);
+ assertEquals(admin, mController.mAdmin);
+ }
+
+ @Test
+ public void testCanBeVisible_unspecified() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_UNSPECIFIED);
+
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.checkCanBeVisible(IMPORTANCE_MIN));
+ }
+
+ @Test
+ public void testCanBeVisible_sameImportance() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.checkCanBeVisible(IMPORTANCE_LOW));
+ }
+
+ @Test
+ public void testCanBeVisible_greaterImportance() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.checkCanBeVisible(IMPORTANCE_MIN));
+ }
+
+ @Test
+ public void testCanBeVisible_lesserImportance() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
+
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.checkCanBeVisible(IMPORTANCE_DEFAULT));
+ }
+
+ @Test
+ public void testSaveImportance() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_DEFAULT);
+
+ mController.onResume(appRow, channel, null, null);
+ mController.saveChannel();
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testIsConfigurable() {
+ String sameId = "bananas";
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = sameId;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn(sameId);
+
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isChannelConfigurable());
+
+ when(channel.getId()).thenReturn("something new");
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isChannelConfigurable());
+ }
+
+ @Test
+ public void testIsChannelBlockable_nonSystemAppsBlockable() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = false;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.isBlockableSystem()).thenReturn(false);
+
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isChannelBlockable());
+ }
+
+ @Test
+ public void testIsChannelBlockable_mostSystemAppsNotBlockable() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = true;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.isBlockableSystem()).thenReturn(false);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
+
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isChannelBlockable());
+ }
+
+ @Test
+ public void testIsChannelBlockable_someSystemAppsAreBlockable() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = true;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.isBlockableSystem()).thenReturn(true);
+
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isChannelBlockable());
+ }
+
+ @Test
+ public void testIsChannelBlockable_canUndoSystemBlock() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = true;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.isBlockableSystem()).thenReturn(false);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isChannelBlockable());
+ }
+
+ @Test
+ public void testIsChannelGroupBlockable_nonSystemBlockable() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = false;
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ when(group.isBlocked()).thenReturn(false);
+
+ mController.onResume(appRow, null, group, null);
+ assertTrue(mController.isChannelGroupBlockable());
+ }
+
+ @Test
+ public void testIsChannelGroupBlockable_SystemNotBlockable() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = true;
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ when(group.isBlocked()).thenReturn(false);
+
+ mController.onResume(appRow, null, group, null);
+ assertFalse(mController.isChannelGroupBlockable());
+ }
+
+ @Test
+ public void testIsChannelGroupBlockable_canUndoSystemBlock() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.systemApp = true;
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ when(group.isBlocked()).thenReturn(true);
+
+ mController.onResume(appRow, null, group, null);
+ assertTrue(mController.isChannelGroupBlockable());
+ }
+
+ private final class TestPreferenceController extends NotificationPreferenceController {
+
+ public TestPreferenceController(Context context,
+ NotificationBackend backend) {
+ super(context, backend);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return null;
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationsOffPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationsOffPreferenceControllerTest.java
new file mode 100644
index 00000000000..e1f9eb70b7a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/NotificationsOffPreferenceControllerTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.wrapper.NotificationChannelGroupWrapper;
+
+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 org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class NotificationsOffPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+
+ private NotificationsOffPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = spy(new NotificationsOffPreferenceController(mContext));
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(Preference.class));
+ }
+
+ @Test
+ public void testIsAvailable_yesIfAppBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, null, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_yesIfChannelGroupBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ when(group.isBlocked()).thenReturn(true);
+ mController.onResume(appRow, null, group, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_yesIfChannelBlocked() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_channel() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new Preference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertTrue(pref.getTitle().toString().contains("category"));
+ assertFalse(pref.isEnabled());
+ assertFalse(pref.isSelectable());
+ }
+
+ @Test
+ public void testUpdateState_channelGroup() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannelGroupWrapper group = mock(NotificationChannelGroupWrapper.class);
+ when(group.isBlocked()).thenReturn(true);
+ mController.onResume(appRow, null, group, null);
+
+ Preference pref = new Preference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertTrue(pref.getTitle().toString().contains("group"));
+ assertFalse(pref.isEnabled());
+ assertFalse(pref.isSelectable());
+ }
+
+ @Test
+ public void testUpdateState_app() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.banned = true;
+ mController.onResume(appRow, null, null, null);
+
+ Preference pref = new Preference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertTrue(pref.getTitle().toString().contains("app"));
+ assertFalse(pref.isEnabled());
+ assertFalse(pref.isSelectable());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/SoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/SoundPreferenceControllerTest.java
new file mode 100644
index 00000000000..1d5a791d28b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/SoundPreferenceControllerTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+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.app.Fragment;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.AttributeSet;
+
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class SoundPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private UserManager mUm;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+ @Mock
+ private SettingsPreferenceFragment mFragment;
+ @Mock
+ private NotificationSettingsBase.ImportanceListener mImportanceListener;
+
+ private SoundPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = spy(new SoundPreferenceController(
+ mContext, mFragment, mImportanceListener, mBackend));
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(NotificationSoundPreference.class));
+ mController.onPreferenceChange(mock(NotificationSoundPreference.class), Uri.EMPTY);
+ mController.handlePreferenceTreeClick(mock(NotificationSoundPreference.class));
+ mController.onActivityResult(1, 1, null);
+ mController.hasValidSound(null);
+ }
+
+ @Test
+ public void testIsAvailable_notIfChannelNull() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ mController.onResume(appRow, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfNotImportant() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfDefaultChannel() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testDisplayPreference_savesPreference() throws Exception {
+ NotificationSoundPreference pref = mock(NotificationSoundPreference.class);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+
+ mController.onActivityResult(SoundPreferenceController.CODE, 1, new Intent());
+ verify(pref, times(1)).onActivityResult(anyInt(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+ RestrictedLockUtils.EnforcedAdmin.class));
+
+ Preference pref = new NotificationSoundPreference(mContext, mock(AttributeSet.class));
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_notConfigurable() throws Exception {
+ String lockedId = "locked";
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = lockedId;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn(lockedId);
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new NotificationSoundPreference(mContext, mock(AttributeSet.class));
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_configurable() throws Exception {
+ Uri sound = Settings.System.DEFAULT_ALARM_ALERT_URI;
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ when(channel.getSound()).thenReturn(sound);
+ mController.onResume(appRow, channel, null, null);
+
+ NotificationSoundPreference pref =
+ new NotificationSoundPreference(mContext, mock(AttributeSet.class));
+ mController.updateState(pref);
+
+ assertEquals(sound, pref.onRestoreRingtone());
+ assertTrue(pref.isEnabled());
+ }
+
+ @Test
+ public void testOnPreferenceChange() throws Exception {
+ Uri sound = Settings.System.DEFAULT_ALARM_ALERT_URI;
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
+ channel.setSound(sound, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+
+ NotificationSoundPreference pref =
+ new NotificationSoundPreference(mContext, mock(AttributeSet.class));
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, Uri.EMPTY);
+ assertEquals(Uri.EMPTY, channel.getSound());
+ assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, channel.getAudioAttributes());
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testOnPreferenceTreeClick_incorrectPref() throws Exception {
+ NotificationSoundPreference pref = mock(NotificationSoundPreference.class);
+ mController.handlePreferenceTreeClick(pref);
+
+ verify(pref, never()).onPrepareRingtonePickerIntent(any());
+ verify(mFragment, never()).startActivityForResult(any(), anyInt());
+ }
+
+
+ @Test
+ public void testOnPreferenceTreeClick_correctPref() throws Exception {
+ NotificationSoundPreference pref =
+ spy(new NotificationSoundPreference(mContext, mock(AttributeSet.class)));
+ pref.setKey(mController.getPreferenceKey());
+ mController.handlePreferenceTreeClick(pref);
+
+ verify(pref, times(1)).onPrepareRingtonePickerIntent(any());
+ verify(mFragment, times(1)).startActivityForResult(any(), anyInt());
+ }
+
+ @Test
+ public void testOnActivityResult() {
+ NotificationSoundPreference pref = mock(NotificationSoundPreference.class);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+
+ mController.onActivityResult(SoundPreferenceController.CODE, 1, new Intent("hi"));
+ verify(pref, times(1)).onActivityResult(anyInt(), anyInt(), any());
+ verify(mImportanceListener, times(1)).onImportanceChanged();
+ }
+
+ @Test
+ public void testHasValidSound() {
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+ assertTrue(mController.hasValidSound(channel));
+
+ channel.setSound(Uri.EMPTY, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+ assertFalse(mController.hasValidSound(channel));
+
+ channel.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+ assertFalse(mController.hasValidSound(channel));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/VibrationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VibrationPreferenceControllerTest.java
new file mode 100644
index 00000000000..4695590cb64
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/VibrationPreferenceControllerTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserManager;
+import android.os.Vibrator;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O)
+public class VibrationPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ Vibrator mVibrator;
+ @Mock
+ private UserManager mUm;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+
+ private VibrationPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ shadowApplication.setSystemService(Context.VIBRATOR_SERVICE, mVibrator);
+ mContext = shadowApplication.getApplicationContext();
+ mController = spy(new VibrationPreferenceController(mContext, mBackend));
+
+ // by default allow vibration
+ when(mVibrator.hasVibrator()).thenReturn(true);
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(RestrictedSwitchPreference.class));
+ mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
+ }
+
+ @Test
+ public void testIsAvailable_notSystemDoesNotHave() throws Exception {
+ when(mVibrator.hasVibrator()).thenReturn(false);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfNotImportant() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfDefaultChannel() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+ RestrictedLockUtils.EnforcedAdmin.class));
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_notConfigurable() throws Exception {
+ String lockedId = "locked";
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.lockedChannelId = lockedId;
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn(lockedId);
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_configurable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(appRow, channel, null, null);
+
+ Preference pref = new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ assertTrue(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_vibrateOn() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.shouldVibrate()).thenReturn(true);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+ assertTrue(pref.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_vibrateOff() throws Exception {
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.shouldVibrate()).thenReturn(false);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+ assertFalse(pref.isChecked());
+ }
+
+ @Test
+ public void testOnPreferenceChange_on() {
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_DEFAULT);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, true);
+
+ assertTrue(channel.shouldVibrate());
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testOnPreferenceChange_off() {
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(RuntimeEnvironment.application);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, false);
+
+ assertFalse(channel.shouldVibrate());
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/VisibilityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VisibilityPreferenceControllerTest.java
new file mode 100644
index 00000000000..ed658feafc6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/VisibilityPreferenceControllerTest.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2017 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.notification;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+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.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.Build;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowRestrictionUtils;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = Build.VERSION_CODES.O, shadows = {
+ ShadowRestrictionUtils.class,
+})
+public class VisibilityPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private NotificationManager mNm;
+ @Mock
+ private LockPatternUtils mLockUtils;
+ @Mock
+ private UserManager mUm;
+ @Mock
+ private DevicePolicyManager mDm;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mScreen;
+
+ private VisibilityPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
+ shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
+ shadowApplication.setSystemService(Context.DEVICE_POLICY_SERVICE, mDm);
+ mContext = shadowApplication.getApplicationContext();
+ mController = spy(new VisibilityPreferenceController(mContext, mLockUtils, mBackend));
+
+ // by default the lockscreen is secure
+ when(mLockUtils.isSecure(anyInt())).thenReturn(true);
+ // and notifications are visible in redacted form
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+ // and not restricted
+ ShadowRestrictionUtils.setRestricted(false);
+ // with no managed profile
+ UserInfo userInfo = new UserInfo();
+ when(mUm.getUserInfo(anyInt())).thenReturn(userInfo);
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() throws Exception {
+ mController.isAvailable();
+ mController.updateState(mock(RestrictedDropDownPreference.class));
+ mController.onPreferenceChange(mock(RestrictedDropDownPreference.class), true);
+ }
+
+ @Test
+ public void testIsAvailable_notSecure() throws Exception {
+ when(mLockUtils.isSecure(anyInt())).thenReturn(false);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfNotImportant() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_MIN);
+ mController.onResume(appRow, channel, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel =
+ new NotificationChannel(DEFAULT_CHANNEL_ID, "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+
+ channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
+ mController.onResume(appRow, channel, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin_disableSecure() throws Exception {
+ ShadowRestrictionUtils.setRestricted(true);
+ UserInfo userInfo = new UserInfo(2, "user 2", UserInfo.FLAG_MANAGED_PROFILE);
+ when(mUm.getUserInfo(anyInt())).thenReturn(userInfo);
+ List components = new ArrayList<>();
+ components.add(new ComponentName("", ""));
+ when(mDm.getActiveAdminsAsUser(anyInt())).thenReturn(components);
+ when(mDm.getKeyguardDisabledFeatures(any(), anyInt()))
+ .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+ RestrictedLockUtils.EnforcedAdmin.class));
+
+ RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+ mController.updateState(pref);
+
+ verify(pref, times(2)).addRestrictedItem(any());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin_disableUnredacted() throws Exception {
+ ShadowRestrictionUtils.setRestricted(true);
+ UserInfo userInfo = new UserInfo(2, "user 2", UserInfo.FLAG_MANAGED_PROFILE);
+ when(mUm.getUserInfo(anyInt())).thenReturn(userInfo);
+ List components = new ArrayList<>();
+ components.add(new ComponentName("", ""));
+ when(mDm.getActiveAdminsAsUser(anyInt())).thenReturn(components);
+ when(mDm.getKeyguardDisabledFeatures(any(), anyInt()))
+ .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getId()).thenReturn("something");
+ mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
+ RestrictedLockUtils.EnforcedAdmin.class));
+
+ RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+ mController.updateState(pref);
+
+ verify(pref, times(1)).addRestrictedItem(any());
+ }
+
+ @Test
+ public void testUpdateState_noLockScreenNotificationsGlobally() throws Exception {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+ mController.updateState(pref);
+
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class);
+ verify(pref, times(1)).setEntryValues(argumentCaptor.capture());
+ assertFalse(Arrays.asList(argumentCaptor.getValue())
+ .contains(VISIBILITY_NO_OVERRIDE));
+ }
+
+ @Test
+ public void testUpdateState_noPrivateLockScreenNotificationsGlobally() throws Exception {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+ mController.updateState(pref);
+
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class);
+ verify(pref, times(1)).setEntryValues(argumentCaptor.capture());
+ assertFalse(Arrays.asList(argumentCaptor.getValue())
+ .contains(VISIBILITY_NO_OVERRIDE));
+ }
+
+ @Test
+ public void testUpdateState_noGlobalRestriction() throws Exception {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+ mController.updateState(pref);
+
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String[].class);
+ verify(pref, times(1)).setEntryValues(argumentCaptor.capture());
+ List values = Arrays.asList(argumentCaptor.getValue());
+ assertEquals(3, values.size());
+ assertTrue(values.contains(String.valueOf(VISIBILITY_NO_OVERRIDE)));
+ assertTrue(values.contains(String.valueOf(Notification.VISIBILITY_PRIVATE)));
+ assertTrue(values.contains(String.valueOf(Notification.VISIBILITY_SECRET)));
+ }
+
+ @Test
+ public void testUpdateState_noChannelOverride() throws Exception {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getLockscreenVisibility()).thenReturn(VISIBILITY_NO_OVERRIDE);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+ mController.updateState(pref);
+
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
+ verify(pref, times(1)).setValue(argumentCaptor.capture());
+
+ assertEquals(String.valueOf(Notification.VISIBILITY_PRIVATE), argumentCaptor.getValue());
+ }
+
+ @Test
+ public void testUpdateState_channelOverride() throws Exception {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = mock(NotificationChannel.class);
+ when(channel.getLockscreenVisibility()).thenReturn(Notification.VISIBILITY_SECRET);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+ mController.updateState(pref);
+
+ ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
+ verify(pref, times(1)).setValue(argumentCaptor.capture());
+
+ assertEquals(String.valueOf(Notification.VISIBILITY_SECRET), argumentCaptor.getValue());
+ }
+
+ @Test
+ public void testOnPreferenceChange_noOverride() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", 4);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, String.valueOf(Notification.VISIBILITY_PRIVATE));
+
+ assertEquals(VISIBILITY_NO_OVERRIDE, channel.getLockscreenVisibility());
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testOnPreferenceChange_override() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ NotificationChannel channel = new NotificationChannel("", "", 4);
+ channel.setLockscreenVisibility(VISIBILITY_NO_OVERRIDE);
+ mController.onResume(appRow, channel, null, null);
+
+ RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, String.valueOf(Notification.VISIBILITY_SECRET));
+
+ assertEquals(Notification.VISIBILITY_SECRET, channel.getLockscreenVisibility());
+ verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
+ }
+}