Loading src/com/android/settings/notification/AppNotificationSettings.java +2 −1 Original line number Diff line number Diff line Loading @@ -211,7 +211,8 @@ public class AppNotificationSettings extends NotificationSettingsBase { MasterSwitchPreference channelPref = new MasterSwitchPreference( getPrefContext()); channelPref.setSwitchEnabled(mSuspendedAppsAdmin == null && isChannelBlockable(mAppRow.systemApp, channel)); && isChannelBlockable(mAppRow.systemApp, channel) && isChannelConfigurable(channel)); channelPref.setKey(channel.getId()); channelPref.setTitle(channel.getName()); channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE); Loading src/com/android/settings/notification/ChannelNotificationSettings.java +3 −3 Original line number Diff line number Diff line Loading @@ -213,7 +213,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase { private void setupVibrate() { mVibrate = (RestrictedSwitchPreference) findPreference(KEY_VIBRATE); mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin); mVibrate.setEnabled(!(mAppRow.lockedImportance || mVibrate.isDisabledByAdmin())); mVibrate.setEnabled(!mVibrate.isDisabledByAdmin() && isChannelConfigurable(mChannel)); mVibrate.setChecked(mChannel.shouldVibrate()); mVibrate.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override Loading @@ -230,7 +230,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase { private void setupRingtone() { mRingtone = (NotificationSoundPreference) findPreference(KEY_RINGTONE); mRingtone.setRingtone(mChannel.getSound()); mRingtone.setEnabled(!(mAppRow.lockedImportance)); mRingtone.setEnabled(isChannelConfigurable(mChannel)); mRingtone.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { Loading Loading @@ -286,7 +286,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase { channelArgs.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true); channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg); channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId()); mImportance.setEnabled(mSuspendedAppsAdmin == null && !mAppRow.lockedImportance); 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(), Loading src/com/android/settings/notification/NotificationBackend.java +17 −2 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.os.UserHandle; import android.util.IconDrawableFactory; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.Utils; public class NotificationBackend { Loading Loading @@ -60,15 +61,28 @@ public class NotificationBackend { row.systemApp = Utils.isSystemPackage(context.getResources(), pm, app); final String[] nonBlockablePkgs = context.getResources().getStringArray( com.android.internal.R.array.config_nonBlockableNotificationPackages); markAppRowWithBlockables(nonBlockablePkgs, row, app.packageName); return row; } @VisibleForTesting static void markAppRowWithBlockables(String[] nonBlockablePkgs, AppRow row, String packageName) { if (nonBlockablePkgs != null) { int N = nonBlockablePkgs.length; for (int i = 0; i < N; i++) { if (app.packageName.equals(nonBlockablePkgs[i])) { String pkg = nonBlockablePkgs[i]; if (pkg == null) { continue; } else if (pkg.contains(":")) { // Interpret as channel; lock only this channel for this app. if (packageName.equals(pkg.split(":", 2)[0])) { row.lockedChannelId = pkg.split(":", 2 )[1]; } } else if (packageName.equals(nonBlockablePkgs[i])) { row.systemApp = row.lockedImportance = true; } } } return row; } public boolean getNotificationsBanned(String pkg, int uid) { Loading Loading @@ -184,6 +198,7 @@ public class NotificationBackend { public boolean first; // first app in section public boolean systemApp; public boolean lockedImportance; public String lockedChannelId; public boolean showBadge; public int userId; } Loading src/com/android/settings/notification/NotificationSettingsBase.java +8 −3 Original line number Diff line number Diff line Loading @@ -293,8 +293,8 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen private void setupImportanceToggle() { mImportanceToggle = (RestrictedSwitchPreference) findPreference(KEY_ALLOW_SOUND); mImportanceToggle.setDisabledByAdmin(mSuspendedAppsAdmin); mImportanceToggle.setEnabled(!(mAppRow.lockedImportance || mImportanceToggle.isDisabledByAdmin())); mImportanceToggle.setEnabled(isChannelConfigurable(mChannel) && !mImportanceToggle.isDisabledByAdmin()); mImportanceToggle.setChecked(mChannel.getImportance() >= IMPORTANCE_DEFAULT || mChannel.getImportance() == IMPORTANCE_UNSPECIFIED); mImportanceToggle.setOnPreferenceChangeListener( Loading @@ -315,7 +315,7 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen protected void setupPriorityPref(boolean priority) { mPriority = (RestrictedSwitchPreference) findPreference(KEY_BYPASS_DND); mPriority.setDisabledByAdmin(mSuspendedAppsAdmin); mPriority.setEnabled(!(mAppRow.lockedImportance || mPriority.isDisabledByAdmin())); mPriority.setEnabled(isChannelConfigurable(mChannel) && !mPriority.isDisabledByAdmin()); mPriority.setChecked(priority); mPriority.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override Loading Loading @@ -447,10 +447,15 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen return lockscreenSecure; } protected boolean isChannelConfigurable(NotificationChannel channel) { return !channel.getId().equals(mAppRow.lockedChannelId); } protected boolean isChannelBlockable(boolean systemApp, NotificationChannel channel) { if (!mAppRow.systemApp) { return true; } return channel.isBlockableSystem() || channel.getImportance() == NotificationManager.IMPORTANCE_NONE; } Loading tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java 0 → 100644 +134 −0 Original line number Diff line number Diff line /* * 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.NotificationManager; import android.app.NotificationManager.Policy; import android.content.Context; import android.support.v7.preference.Preference; import com.android.settings.R; import com.android.settings.notification.NotificationBackend.AppRow; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; import org.robolectric.annotation.Config; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class NotificationBackendTest { @Test public void testMarkAppRow_unblockablePackage() { AppRow appRow = new AppRow(); String packageName = "foo.bar.unblockable"; appRow.pkg = packageName; String[] nonBlockablePkgs = new String[2]; nonBlockablePkgs[0] = packageName; nonBlockablePkgs[1] = "some.other.package"; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName); // This package has a package lock but no locked channels assertTrue(appRow.lockedImportance); assertNull(appRow.lockedChannelId); } @Test public void testMarkAppRow_unblockableChannelOrPkg() { String channelBlockName = "foo.bar.pkgWithChannel"; String pkgBlockName = "foo.bar.pkgBlock"; String[] nonBlockablePkgs = new String[2]; nonBlockablePkgs[0] = pkgBlockName; nonBlockablePkgs[1] = channelBlockName + ":SpecificChannel"; // This package has a channel level lock but no full package lock AppRow channelBlockApp = new AppRow(); channelBlockApp.pkg = channelBlockName; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, channelBlockApp, channelBlockName); assertFalse(channelBlockApp.lockedImportance); assertEquals("SpecificChannel", channelBlockApp.lockedChannelId); // This other package has the reverse AppRow pkgBlock = new AppRow(); pkgBlock.pkg = pkgBlockName; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, pkgBlock, pkgBlockName); assertTrue(pkgBlock.lockedImportance); assertNull(pkgBlock.lockedChannelId); // This third package has no locks at all AppRow otherAppRow = new AppRow(); otherAppRow.pkg ="foo.bar.nothingBlocked"; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, otherAppRow, "foo.bar.nothingBlocked"); assertFalse(otherAppRow.lockedImportance); assertNull(otherAppRow.lockedChannelId); } @Test public void testMarkAppRow_unblockableChannelAndPkg() { AppRow appRow = new AppRow(); String packageName = "foo.bar.unblockable"; appRow.pkg = packageName; String[] nonBlockablePkgs = new String[2]; nonBlockablePkgs[0] = "foo.bar.unblockable"; nonBlockablePkgs[1] = "foo.bar.unblockable:SpecificChannel"; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName); // This package has both a channel lock and a package lock assertTrue(appRow.lockedImportance); assertEquals("SpecificChannel", appRow.lockedChannelId); } @Test public void testMarkAppRow_channelNameWithColons() { AppRow appRow = new AppRow(); String packageName = "foo.bar.unblockable"; String channelName = "SpecificChannel:1234:abc:defg"; appRow.pkg = packageName; String[] nonBlockablePkgs = new String[1]; nonBlockablePkgs[0] = packageName + ":" + channelName; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName); assertEquals(channelName, appRow.lockedChannelId); } @Test public void testMarkAppRow_blocklistWithNullEntries() { AppRow appRow = new AppRow(); String packageName = "foo.bar.unblockable"; appRow.pkg = packageName; String[] nonBlockablePkgs = new String[6]; // extra long list with some entries left null nonBlockablePkgs[2] = "foo.bar.unblockable"; nonBlockablePkgs[4] = "foo.bar.unblockable:SpecificChannel"; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName); assertTrue(appRow.lockedImportance); assertEquals("SpecificChannel", appRow.lockedChannelId); } } Loading
src/com/android/settings/notification/AppNotificationSettings.java +2 −1 Original line number Diff line number Diff line Loading @@ -211,7 +211,8 @@ public class AppNotificationSettings extends NotificationSettingsBase { MasterSwitchPreference channelPref = new MasterSwitchPreference( getPrefContext()); channelPref.setSwitchEnabled(mSuspendedAppsAdmin == null && isChannelBlockable(mAppRow.systemApp, channel)); && isChannelBlockable(mAppRow.systemApp, channel) && isChannelConfigurable(channel)); channelPref.setKey(channel.getId()); channelPref.setTitle(channel.getName()); channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE); Loading
src/com/android/settings/notification/ChannelNotificationSettings.java +3 −3 Original line number Diff line number Diff line Loading @@ -213,7 +213,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase { private void setupVibrate() { mVibrate = (RestrictedSwitchPreference) findPreference(KEY_VIBRATE); mVibrate.setDisabledByAdmin(mSuspendedAppsAdmin); mVibrate.setEnabled(!(mAppRow.lockedImportance || mVibrate.isDisabledByAdmin())); mVibrate.setEnabled(!mVibrate.isDisabledByAdmin() && isChannelConfigurable(mChannel)); mVibrate.setChecked(mChannel.shouldVibrate()); mVibrate.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override Loading @@ -230,7 +230,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase { private void setupRingtone() { mRingtone = (NotificationSoundPreference) findPreference(KEY_RINGTONE); mRingtone.setRingtone(mChannel.getSound()); mRingtone.setEnabled(!(mAppRow.lockedImportance)); mRingtone.setEnabled(isChannelConfigurable(mChannel)); mRingtone.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { Loading Loading @@ -286,7 +286,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase { channelArgs.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true); channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mPkg); channelArgs.putString(Settings.EXTRA_CHANNEL_ID, mChannel.getId()); mImportance.setEnabled(mSuspendedAppsAdmin == null && !mAppRow.lockedImportance); 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(), Loading
src/com/android/settings/notification/NotificationBackend.java +17 −2 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.os.UserHandle; import android.util.IconDrawableFactory; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.Utils; public class NotificationBackend { Loading Loading @@ -60,15 +61,28 @@ public class NotificationBackend { row.systemApp = Utils.isSystemPackage(context.getResources(), pm, app); final String[] nonBlockablePkgs = context.getResources().getStringArray( com.android.internal.R.array.config_nonBlockableNotificationPackages); markAppRowWithBlockables(nonBlockablePkgs, row, app.packageName); return row; } @VisibleForTesting static void markAppRowWithBlockables(String[] nonBlockablePkgs, AppRow row, String packageName) { if (nonBlockablePkgs != null) { int N = nonBlockablePkgs.length; for (int i = 0; i < N; i++) { if (app.packageName.equals(nonBlockablePkgs[i])) { String pkg = nonBlockablePkgs[i]; if (pkg == null) { continue; } else if (pkg.contains(":")) { // Interpret as channel; lock only this channel for this app. if (packageName.equals(pkg.split(":", 2)[0])) { row.lockedChannelId = pkg.split(":", 2 )[1]; } } else if (packageName.equals(nonBlockablePkgs[i])) { row.systemApp = row.lockedImportance = true; } } } return row; } public boolean getNotificationsBanned(String pkg, int uid) { Loading Loading @@ -184,6 +198,7 @@ public class NotificationBackend { public boolean first; // first app in section public boolean systemApp; public boolean lockedImportance; public String lockedChannelId; public boolean showBadge; public int userId; } Loading
src/com/android/settings/notification/NotificationSettingsBase.java +8 −3 Original line number Diff line number Diff line Loading @@ -293,8 +293,8 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen private void setupImportanceToggle() { mImportanceToggle = (RestrictedSwitchPreference) findPreference(KEY_ALLOW_SOUND); mImportanceToggle.setDisabledByAdmin(mSuspendedAppsAdmin); mImportanceToggle.setEnabled(!(mAppRow.lockedImportance || mImportanceToggle.isDisabledByAdmin())); mImportanceToggle.setEnabled(isChannelConfigurable(mChannel) && !mImportanceToggle.isDisabledByAdmin()); mImportanceToggle.setChecked(mChannel.getImportance() >= IMPORTANCE_DEFAULT || mChannel.getImportance() == IMPORTANCE_UNSPECIFIED); mImportanceToggle.setOnPreferenceChangeListener( Loading @@ -315,7 +315,7 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen protected void setupPriorityPref(boolean priority) { mPriority = (RestrictedSwitchPreference) findPreference(KEY_BYPASS_DND); mPriority.setDisabledByAdmin(mSuspendedAppsAdmin); mPriority.setEnabled(!(mAppRow.lockedImportance || mPriority.isDisabledByAdmin())); mPriority.setEnabled(isChannelConfigurable(mChannel) && !mPriority.isDisabledByAdmin()); mPriority.setChecked(priority); mPriority.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override Loading Loading @@ -447,10 +447,15 @@ abstract public class NotificationSettingsBase extends SettingsPreferenceFragmen return lockscreenSecure; } protected boolean isChannelConfigurable(NotificationChannel channel) { return !channel.getId().equals(mAppRow.lockedChannelId); } protected boolean isChannelBlockable(boolean systemApp, NotificationChannel channel) { if (!mAppRow.systemApp) { return true; } return channel.isBlockableSystem() || channel.getImportance() == NotificationManager.IMPORTANCE_NONE; } Loading
tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java 0 → 100644 +134 −0 Original line number Diff line number Diff line /* * 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.NotificationManager; import android.app.NotificationManager.Policy; import android.content.Context; import android.support.v7.preference.Preference; import com.android.settings.R; import com.android.settings.notification.NotificationBackend.AppRow; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; import org.robolectric.annotation.Config; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class NotificationBackendTest { @Test public void testMarkAppRow_unblockablePackage() { AppRow appRow = new AppRow(); String packageName = "foo.bar.unblockable"; appRow.pkg = packageName; String[] nonBlockablePkgs = new String[2]; nonBlockablePkgs[0] = packageName; nonBlockablePkgs[1] = "some.other.package"; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName); // This package has a package lock but no locked channels assertTrue(appRow.lockedImportance); assertNull(appRow.lockedChannelId); } @Test public void testMarkAppRow_unblockableChannelOrPkg() { String channelBlockName = "foo.bar.pkgWithChannel"; String pkgBlockName = "foo.bar.pkgBlock"; String[] nonBlockablePkgs = new String[2]; nonBlockablePkgs[0] = pkgBlockName; nonBlockablePkgs[1] = channelBlockName + ":SpecificChannel"; // This package has a channel level lock but no full package lock AppRow channelBlockApp = new AppRow(); channelBlockApp.pkg = channelBlockName; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, channelBlockApp, channelBlockName); assertFalse(channelBlockApp.lockedImportance); assertEquals("SpecificChannel", channelBlockApp.lockedChannelId); // This other package has the reverse AppRow pkgBlock = new AppRow(); pkgBlock.pkg = pkgBlockName; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, pkgBlock, pkgBlockName); assertTrue(pkgBlock.lockedImportance); assertNull(pkgBlock.lockedChannelId); // This third package has no locks at all AppRow otherAppRow = new AppRow(); otherAppRow.pkg ="foo.bar.nothingBlocked"; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, otherAppRow, "foo.bar.nothingBlocked"); assertFalse(otherAppRow.lockedImportance); assertNull(otherAppRow.lockedChannelId); } @Test public void testMarkAppRow_unblockableChannelAndPkg() { AppRow appRow = new AppRow(); String packageName = "foo.bar.unblockable"; appRow.pkg = packageName; String[] nonBlockablePkgs = new String[2]; nonBlockablePkgs[0] = "foo.bar.unblockable"; nonBlockablePkgs[1] = "foo.bar.unblockable:SpecificChannel"; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName); // This package has both a channel lock and a package lock assertTrue(appRow.lockedImportance); assertEquals("SpecificChannel", appRow.lockedChannelId); } @Test public void testMarkAppRow_channelNameWithColons() { AppRow appRow = new AppRow(); String packageName = "foo.bar.unblockable"; String channelName = "SpecificChannel:1234:abc:defg"; appRow.pkg = packageName; String[] nonBlockablePkgs = new String[1]; nonBlockablePkgs[0] = packageName + ":" + channelName; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName); assertEquals(channelName, appRow.lockedChannelId); } @Test public void testMarkAppRow_blocklistWithNullEntries() { AppRow appRow = new AppRow(); String packageName = "foo.bar.unblockable"; appRow.pkg = packageName; String[] nonBlockablePkgs = new String[6]; // extra long list with some entries left null nonBlockablePkgs[2] = "foo.bar.unblockable"; nonBlockablePkgs[4] = "foo.bar.unblockable:SpecificChannel"; NotificationBackend.markAppRowWithBlockables(nonBlockablePkgs, appRow, packageName); assertTrue(appRow.lockedImportance); assertEquals("SpecificChannel", appRow.lockedChannelId); } }