Merge changes from topic "jr-update" into rvc-dev
* changes:
Update conversation launch point
Add settings for apps that don't use full conversations
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 366254f..2bd708a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8385,6 +8385,12 @@
<!-- [CHAR LIMIT=100] Header for an individual conversation-->
<string name="conversation_category_title">Conversation</string>
+ <!-- [CHAR LIMIT=100] Title for switch that says whether this app can appear in the conversation notification section-->
+ <string name="conversation_section_switch_title">Conversation section</string>
+
+ <!-- [CHAR LIMIT=100] Summary for switch that says whether this app can appear in the conversation notification section-->
+ <string name="conversation_section_switch_summary">Allow <xliff:g id="app">%1$s</xliff:g> to appear in the conversation section</string>
+
<!-- [CHAR LIMIT=NONE] Conversation preference summary, the parent channel this conversation was spawned from (separator) the parent channel group (e.g. an account name)-->
<string name="notification_conversation_summary" translatable="false">"<xliff:g id="parent_category_name">%1$s</xliff:g> • <xliff:g id="parent_category_group_name">%2$s</xliff:g>"</string>
@@ -8403,6 +8409,15 @@
<!-- [CHAR LIMIT=100] link to page listing all conversations -->
<string name="manage_conversations">Manage conversations</string>
+ <!-- [CHAR LIMIT=100] summary text on link to 'all conversations' page, no conversations are priority -->
+ <string name="priority_conversation_count_zero">No priority conversations</string>
+
+ <!-- [CHAR LIMIT=100] summary text on link to 'all conversations' page, some conversations are priority -->
+ <plurals name="priority_conversation_count">
+ <item quantity="one"><xliff:g id="count" example="1">%d</xliff:g> priority conversation</item>
+ <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> priority conversations</item>
+ </plurals>
+
<!-- [CHAR LIMIT=100] preference category title -->
<string name="important_conversations">Priority conversations</string>
diff --git a/res/xml/app_and_notification.xml b/res/xml/app_and_notification.xml
index b499180..6e71f14 100644
--- a/res/xml/app_and_notification.xml
+++ b/res/xml/app_and_notification.xml
@@ -47,6 +47,14 @@
android:order="-997"/>
<Preference
+ android:key="conversations"
+ android:title="@string/conversations_category_title"
+ android:order="-550"
+ settings:controller="com.android.settings.notification.ConversationListSummaryPreferenceController"
+ android:fragment="com.android.settings.notification.app.ConversationListSettings"
+ />
+
+ <Preference
android:key="configure_notification_settings"
android:title="@string/configure_notification_settings"
android:order="-440"
diff --git a/res/xml/app_notification_settings.xml b/res/xml/app_notification_settings.xml
index f0200ce..8ca4e0d 100644
--- a/res/xml/app_notification_settings.xml
+++ b/res/xml/app_notification_settings.xml
@@ -29,13 +29,6 @@
<com.android.settings.notification.app.NotificationFooterPreference
android:key="block_desc" />
- <!--Bubbles -->
- <Preference
- android:key="bubble_pref_link"
- android:title="@string/notification_bubbles_title"
- android:icon="@drawable/ic_create_bubble"
- settings:controller="com.android.settings.notification.app.BubbleSummaryPreferenceController">
- </Preference>
<!-- Conversations added here -->
<PreferenceCategory
@@ -43,13 +36,28 @@
android:key="conversations"
android:visibility="gone"
settings:allowDividerAbove="false"
- settings:allowDividerBelow="false" />
+ settings:allowDividerBelow="false">
+ <com.android.settingslib.RestrictedSwitchPreference
+ android:key="invalid_conversation_switch"
+ android:title="@string/conversation_section_switch_title" />
+ <Preference
+ android:key="invalid_conversation_info"/>
+ </PreferenceCategory>
+
+ <!--Bubbles -->
+ <Preference
+ android:key="bubble_pref_link"
+ android:title="@string/notification_bubbles_title"
+ android:icon="@drawable/ic_create_bubble"
+ settings:allowDividerAbove="false"
+ settings:controller="com.android.settings.notification.app.BubbleSummaryPreferenceController">
+ </Preference>
<!-- Channels/Channel groups added here -->
<PreferenceCategory
android:key="channels"
android:layout="@layout/empty_view"
- settings:allowDividerAbove="false"
+ settings:allowDividerAbove="true"
settings:allowDividerBelow="false" />
<!-- Importance toggle -->
diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml
index 0123ee5..95c7f56 100644
--- a/res/xml/configure_notification_settings.xml
+++ b/res/xml/configure_notification_settings.xml
@@ -39,14 +39,6 @@
</Preference>
<Preference
- android:key="conversations"
- android:title="@string/conversations_category_title"
- android:summary="@string/manage_conversations"
- android:order="3"
- android:fragment="com.android.settings.notification.app.ConversationListSettings"
- />
-
- <Preference
android:key="notification_bubbles"
android:title="@string/notification_bubbles_title"
android:summary="@string/notifications_bubble_setting_on_summary"
diff --git a/src/com/android/settings/notification/ConversationListSummaryPreferenceController.java b/src/com/android/settings/notification/ConversationListSummaryPreferenceController.java
new file mode 100644
index 0000000..5d7ea20
--- /dev/null
+++ b/src/com/android/settings/notification/ConversationListSummaryPreferenceController.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 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 com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+public class ConversationListSummaryPreferenceController extends BasePreferenceController {
+
+ private NotificationBackend mBackend;
+
+ public ConversationListSummaryPreferenceController(Context context, String key) {
+ super(context, key);
+ mBackend = new NotificationBackend();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final int count = mBackend.getConversations(true).getList().size();
+ if (count == 0) {
+ return mContext.getText(R.string.priority_conversation_count_zero);
+ }
+ return mContext.getResources().getQuantityString(
+ R.plurals.priority_conversation_count,
+ count, count);
+ }
+
+ void setBackend(NotificationBackend backend) {
+ mBackend = backend;
+ }
+}
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 6172268..83df323 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -268,15 +268,32 @@
}
}
- public boolean hasSentMessage(String pkg, int uid) {
+ public boolean isInInvalidMsgState(String pkg, int uid) {
try {
- return sINM.hasSentMessage(pkg, uid);
+ return sINM.isInInvalidMsgState(pkg, uid);
} catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e);
return false;
}
}
+ public boolean hasUserDemotedInvalidMsgApp(String pkg, int uid) {
+ try {
+ return sINM.hasUserDemotedInvalidMsgApp(pkg, uid);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return false;
+ }
+ }
+
+ public void setInvalidMsgAppDemoted(String pkg, int uid, boolean isDemoted) {
+ try {
+ sINM.setInvalidMsgAppDemoted(pkg, uid, isDemoted);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ }
+ }
+
/**
* Returns all notification channels associated with the package and uid that will bypass DND
*/
diff --git a/src/com/android/settings/notification/app/AppConversationListPreferenceController.java b/src/com/android/settings/notification/app/AppConversationListPreferenceController.java
index 2fcd2b2..0ba9436 100644
--- a/src/com/android/settings/notification/app/AppConversationListPreferenceController.java
+++ b/src/com/android/settings/notification/app/AppConversationListPreferenceController.java
@@ -46,7 +46,7 @@
protected List<ConversationChannelWrapper> mConversations = new ArrayList<>();
protected PreferenceCategory mPreference;
- private boolean mHasSentMsg;
+ private boolean mIsInInvalidMsgState;
public AppConversationListPreferenceController(Context context, NotificationBackend backend) {
super(context, backend);
@@ -88,7 +88,7 @@
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... unused) {
- mHasSentMsg = mBackend.hasSentMessage(mAppRow.pkg, mAppRow.uid);
+ mIsInInvalidMsgState = mBackend.isInInvalidMsgState(mAppRow.pkg, mAppRow.uid);
ParceledListSlice<ConversationChannelWrapper> list =
mBackend.getConversations(mAppRow.pkg, mAppRow.uid);
if (list != null) {
@@ -121,20 +121,10 @@
if (mPreference == null) {
return;
}
- // TODO: if preference has children, compare with newly loaded list
- mPreference.removeAll();
- if (mConversations.isEmpty()) {
- if (mHasSentMsg) {
- mPreference.setVisible(true);
- Preference notSupportedPref = new Preference(mContext);
- notSupportedPref.setSummary(mContext.getString(
- R.string.convo_not_supported_summary, mAppRow.label));
- mPreference.addPreference(notSupportedPref);
- } else {
- mPreference.setVisible(false);
- }
- } else {
- mPreference.setVisible(true);
+
+ if (!mIsInInvalidMsgState && !mConversations.isEmpty()) {
+ // TODO: if preference has children, compare with newly loaded list
+ mPreference.removeAll();
mPreference.setTitle(getTitleResId());
populateConversations();
}
diff --git a/src/com/android/settings/notification/app/AppNotificationSettings.java b/src/com/android/settings/notification/app/AppNotificationSettings.java
index a422841..58c18ba 100644
--- a/src/com/android/settings/notification/app/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/app/AppNotificationSettings.java
@@ -122,6 +122,8 @@
mControllers.add(new DeletedChannelsPreferenceController(context, mBackend));
mControllers.add(new ChannelListPreferenceController(context, mBackend));
mControllers.add(new AppConversationListPreferenceController(context, mBackend));
+ mControllers.add(new InvalidConversationInfoPreferenceController(context, mBackend));
+ mControllers.add(new InvalidConversationPreferenceController(context, mBackend));
mControllers.add(new BubbleSummaryPreferenceController(context, mBackend));
return new ArrayList<>(mControllers);
}
diff --git a/src/com/android/settings/notification/app/BubblePreferenceController.java b/src/com/android/settings/notification/app/BubblePreferenceController.java
index 0ca2095..1aed156 100644
--- a/src/com/android/settings/notification/app/BubblePreferenceController.java
+++ b/src/com/android/settings/notification/app/BubblePreferenceController.java
@@ -86,7 +86,7 @@
@Override
public void updateState(Preference preference) {
if (mIsAppPage && mAppRow != null) {
- mHasSentInvalidMsg = mBackend.hasSentMessage(mAppRow.pkg, mAppRow.uid);
+ mHasSentInvalidMsg = mBackend.isInInvalidMsgState(mAppRow.pkg, mAppRow.uid);
mNumConversations = mBackend.getConversations(
mAppRow.pkg, mAppRow.uid).getList().size();
// We're on the app specific bubble page which displays a tri-state
diff --git a/src/com/android/settings/notification/app/InvalidConversationInfoPreferenceController.java b/src/com/android/settings/notification/app/InvalidConversationInfoPreferenceController.java
new file mode 100644
index 0000000..ade9653
--- /dev/null
+++ b/src/com/android/settings/notification/app/InvalidConversationInfoPreferenceController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 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.app;
+
+import android.app.NotificationChannel;
+import android.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.notification.NotificationBackend;
+
+public class InvalidConversationInfoPreferenceController extends NotificationPreferenceController {
+
+ private static final String KEY = "invalid_conversation_info";
+
+ public InvalidConversationInfoPreferenceController(Context context,
+ NotificationBackend backend) {
+ super(context, backend);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (mAppRow == null) {
+ return false;
+ }
+ if (mAppRow.banned) {
+ return false;
+ }
+ return mBackend.isInInvalidMsgState(mAppRow.pkg, mAppRow.uid);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ if (mAppRow == null) {
+ return;
+ }
+ preference.setSummary(mContext.getString(
+ R.string.convo_not_supported_summary, mAppRow.label));
+ }
+}
diff --git a/src/com/android/settings/notification/app/InvalidConversationPreferenceController.java b/src/com/android/settings/notification/app/InvalidConversationPreferenceController.java
new file mode 100644
index 0000000..74f5773
--- /dev/null
+++ b/src/com/android/settings/notification/app/InvalidConversationPreferenceController.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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.app;
+
+import android.app.NotificationChannel;
+import android.content.Context;
+
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+public class InvalidConversationPreferenceController extends NotificationPreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ private static final String KEY = "invalid_conversation_switch";
+
+ public InvalidConversationPreferenceController(Context context, NotificationBackend backend) {
+ super(context, backend);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (mAppRow == null) {
+ return false;
+ }
+ if (mAppRow.banned) {
+ return false;
+ }
+ return mBackend.isInInvalidMsgState(mAppRow.pkg, mAppRow.uid);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ if (mAppRow == null) {
+ return;
+ }
+ RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
+ pref.setDisabledByAdmin(mAdmin);
+ pref.setEnabled(!pref.isDisabledByAdmin());
+ pref.setChecked(!mBackend.hasUserDemotedInvalidMsgApp(mAppRow.pkg, mAppRow.uid));
+ preference.setSummary(mContext.getString(
+ R.string.conversation_section_switch_summary, mAppRow.label));
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mAppRow == null) {
+ return false;
+ }
+ mBackend.setInvalidMsgAppDemoted(mAppRow.pkg, mAppRow.uid, !((Boolean) newValue));
+ return true;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/ConversationListSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ConversationListSummaryPreferenceControllerTest.java
new file mode 100644
index 0000000..e9b610b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ConversationListSummaryPreferenceControllerTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 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.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.service.notification.ConversationChannelWrapper;
+
+import com.android.settings.testutils.shadow.ShadowNotificationBackend;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowNotificationBackend.class)
+public class ConversationListSummaryPreferenceControllerTest {
+
+ private ConversationListSummaryPreferenceController mController;
+ private Context mContext;
+ @Mock
+ NotificationBackend mBackend;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mController = new ConversationListSummaryPreferenceController(mContext, "key");
+ mController.setBackend(mBackend);
+ }
+
+ @Test
+ public void getSummary_noPriorityConversations() {
+ List<ConversationChannelWrapper> convos = new ArrayList<>();
+ when(mBackend.getConversations(true)).thenReturn(
+ new ParceledListSlice<>(convos));
+
+ assertThat(mController.getSummary().toString()).contains("No");
+ }
+
+ @Test
+ public void getSummary_somePriorityConversations() {
+ List<ConversationChannelWrapper> convos = new ArrayList<>();
+ convos.add(mock(ConversationChannelWrapper.class));
+ convos.add(mock(ConversationChannelWrapper.class));
+ when(mBackend.getConversations(true)).thenReturn(
+ new ParceledListSlice<>(convos));
+
+ assertThat(mController.getSummary().toString()).contains("2");
+ assertThat(mController.getSummary().toString()).contains("conversations");
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/app/InvalidConversationInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/InvalidConversationInfoPreferenceControllerTest.java
new file mode 100644
index 0000000..aa87539
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/app/InvalidConversationInfoPreferenceControllerTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2020 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.app;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.UserManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.notification.NotificationBackend;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+
+import org.junit.After;
+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.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = SettingsShadowResources.class)
+public class InvalidConversationInfoPreferenceControllerTest {
+
+ 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 InvalidConversationInfoPreferenceController 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 = RuntimeEnvironment.application;
+ mController = spy(new InvalidConversationInfoPreferenceController(mContext, mBackend));
+ }
+
+ @After
+ public void tearDown() {
+ SettingsShadowResources.reset();
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() {
+ mController.isAvailable();
+ mController.updateState(mock(Preference.class));
+ }
+
+ @Test
+ public void testIsAvailable_notIfAppBlocked() {
+ when(mBackend.isInInvalidMsgState(anyString(), anyInt())).thenReturn(true);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "hi";
+ appRow.uid = 0;
+ appRow.banned = true;
+ mController.onResume(appRow, null, null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfInValidMsgState() {
+ when(mBackend.isInInvalidMsgState(anyString(), anyInt())).thenReturn(false);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "hi";
+ appRow.uid = 0;
+ mController.onResume(appRow, null, null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() {
+ when(mBackend.isInInvalidMsgState(anyString(), anyInt())).thenReturn(true);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "hi";
+ appRow.uid = 0;
+ mController.onResume(appRow, null, null, null, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState() {
+ when(mBackend.isInInvalidMsgState(anyString(), anyInt())).thenReturn(true);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "hi";
+ appRow.uid = 0;
+ appRow.label = "plum";
+ mController.onResume(appRow, null, null, null, null, null);
+ Preference pref = new Preference(mContext);
+ mController.updateState(pref);
+ assertTrue(pref.getSummary().toString().contains(appRow.label));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/app/InvalidConversationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/InvalidConversationPreferenceControllerTest.java
new file mode 100644
index 0000000..4bfc1b4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/app/InvalidConversationPreferenceControllerTest.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2020 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.app;
+
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+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.UserManager;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.notification.NotificationBackend;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
+
+import org.junit.After;
+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.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = SettingsShadowResources.class)
+public class InvalidConversationPreferenceControllerTest {
+
+ 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 InvalidConversationPreferenceController 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 = RuntimeEnvironment.application;
+ mController = spy(new InvalidConversationPreferenceController(mContext, mBackend));
+ }
+
+ @After
+ public void tearDown() {
+ SettingsShadowResources.reset();
+ }
+
+ @Test
+ public void testNoCrashIfNoOnResume() {
+ mController.isAvailable();
+ mController.updateState(mock(RestrictedSwitchPreference.class));
+ mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
+ }
+
+ @Test
+ public void testIsAvailable_notIfAppBlocked() {
+ when(mBackend.isInInvalidMsgState(anyString(), anyInt())).thenReturn(true);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "hi";
+ appRow.uid = 0;
+ appRow.banned = true;
+ mController.onResume(appRow, null, null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_notIfInValidMsgState() {
+ when(mBackend.isInInvalidMsgState(anyString(), anyInt())).thenReturn(false);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "hi";
+ appRow.uid = 0;
+ mController.onResume(appRow, null, null, null, null, null);
+ assertFalse(mController.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable() {
+ when(mBackend.isInInvalidMsgState(anyString(), anyInt())).thenReturn(true);
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "hi";
+ appRow.uid = 0;
+ mController.onResume(appRow, null, null, null, null, null);
+ assertTrue(mController.isAvailable());
+ }
+
+ @Test
+ public void testUpdateState_disabledByAdmin() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "hi";
+ appRow.uid = 0;
+ mController.onResume(appRow, null, null,
+ null, null, mock(RestrictedLockUtils.EnforcedAdmin.class));
+
+ Preference pref = new RestrictedSwitchPreference(mContext);
+ mController.updateState(pref);
+
+ assertFalse(pref.isEnabled());
+ }
+
+ @Test
+ public void testUpdateState_notChecked() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "pkg";
+ mController.onResume(appRow, null, null, null, null, null);
+
+ when(mBackend.hasUserDemotedInvalidMsgApp(anyString(), anyInt())).thenReturn(false);
+
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
+ mController.updateState(pref);
+ assertTrue(pref.isChecked());
+ }
+
+ @Test
+ public void testUpdateState_checked() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "pkg";
+ mController.onResume(appRow, null, null, null, null, null);
+
+ when(mBackend.hasUserDemotedInvalidMsgApp(anyString(), anyInt())).thenReturn(true);
+
+ RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
+ mController.updateState(pref);
+ assertFalse(pref.isChecked());
+ }
+
+ @Test
+ public void testOnPreferenceChange_toggleEnabled() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "pkg";
+ mController.onResume(appRow, null, null, null, null, null);
+
+ when(mBackend.hasUserDemotedInvalidMsgApp(anyString(), anyInt())).thenReturn(true);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(mContext);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, true);
+
+ verify(mBackend, times(1)).setInvalidMsgAppDemoted(any(), anyInt(), eq(false));
+ }
+
+ @Test
+ public void testOnPreferenceChange_toggleDisabled() {
+ NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+ appRow.pkg = "pkg";
+ mController.onResume(appRow, null, null, null, null, null);
+
+ when(mBackend.hasUserDemotedInvalidMsgApp(anyString(), anyInt())).thenReturn(false);
+
+ RestrictedSwitchPreference pref =
+ new RestrictedSwitchPreference(mContext);
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
+ mController.displayPreference(mScreen);
+ mController.updateState(pref);
+
+ mController.onPreferenceChange(pref, false);
+
+ verify(mBackend, times(1)).setInvalidMsgAppDemoted(any(), anyInt(), eq(true));
+ }
+}