Add 'Recent Conversations' to the conversation page
Test: atest
Bug: 171191376
Change-Id: Id7208312dff0cc022c2f16b3872fae1a9dc09ed7
diff --git a/res/drawable/ic_clear.xml b/res/drawable/ic_clear.xml
index 224425f..b2065f4 100644
--- a/res/drawable/ic_clear.xml
+++ b/res/drawable/ic_clear.xml
@@ -18,7 +18,8 @@
android:viewportWidth="24"
android:viewportHeight="24"
android:width="24dp"
- android:height="24dp">
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal">
<path
android:pathData="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12Z"
android:fillColor="#FFFFFF" />
diff --git a/res/layout/conversations_clear_recents.xml b/res/layout/conversations_clear_recents.xml
new file mode 100644
index 0000000..b790439
--- /dev/null
+++ b/res/layout/conversations_clear_recents.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <Button
+ android:id="@+id/conversation_settings_clear_recents"
+ style="@style/ActionPrimaryButton"
+ android:layout_marginStart="@dimen/screen_margin_sides"
+ android:layout_marginEnd="@dimen/description_margin_sides"
+ android:layout_marginTop="@dimen/zen_mode_settings_button_margin_vertical"
+ android:layout_marginBottom="@dimen/zen_mode_settings_button_margin_vertical"
+ android:text="@string/conversation_settings_clear_recents"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/preference_widget_clear.xml b/res/layout/preference_widget_clear.xml
new file mode 100644
index 0000000..9703eba
--- /dev/null
+++ b/res/layout/preference_widget_clear.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<!-- Settings button -->
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/clear_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:paddingStart="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:scaleType="center"
+ android:src="@drawable/ic_clear"
+ android:contentDescription="@string/clear" />
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c3e5d81..ea27114 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8497,11 +8497,20 @@
<string name="important_conversations_summary">Show at top of conversation section</string>
<!-- [CHAR LIMIT=100] preference category title -->
- <string name="other_conversations">Other conversations</string>
+ <string name="other_conversations">Non-priority conversations</string>
<!-- summary for other conversations list -->
<string name="other_conversations_summary">Conversations you\u2019ve made changes to</string>
+ <!-- [CHAR LIMIT=100] preference category title -->
+ <string name="recent_conversations">Recent conversations</string>
+
+ <!-- [CHAR LIMIT=20] button title -->
+ <string name="conversation_settings_clear_recents">Clear recents</string>
+
+ <!-- a11y string -->
+ <string name="clear">Clear</string>
+
<!-- [CHAR LIMIT=100] Setting to automatically bubble all notifications from favorite conversations -->
<string name="important_bubble">Bubble priority conversations</string>
diff --git a/res/xml/conversation_list_settings.xml b/res/xml/conversation_list_settings.xml
index d528bd7..86b4f81 100644
--- a/res/xml/conversation_list_settings.xml
+++ b/res/xml/conversation_list_settings.xml
@@ -43,12 +43,18 @@
</PreferenceCategory>
</PreferenceCategory>
-
- <!--Other conversations added here -->
+ <!-- Non-priority modified conversations added here -->
<PreferenceCategory
android:title="@string/other_conversations"
android:key="other_conversations"
settings:allowDividerAbove="true"
settings:allowDividerBelow="false" />
+ <!-- Recent conversations added here -->
+ <PreferenceCategory
+ android:title="@string/recent_conversations"
+ android:key="recent_conversations"
+ settings:allowDividerAbove="true"
+ settings:allowDividerBelow="false" />
+
</PreferenceScreen>
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index e23e4a5..c80ee97 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -541,6 +541,15 @@
}
}
+ public void createConversationNotificationChannel(String pkg, int uid,
+ NotificationChannel parent, String conversationId) {
+ try {
+ sINM.createConversationNotificationChannelForPackage(pkg, uid, parent, conversationId);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ }
+ }
+
public ShortcutInfo getConversationInfo(Context context, String pkg, int uid, String id) {
LauncherApps la = context.getSystemService(LauncherApps.class);
diff --git a/src/com/android/settings/notification/app/ConversationListSettings.java b/src/com/android/settings/notification/app/ConversationListSettings.java
index 8f232ca..83fc072 100644
--- a/src/com/android/settings/notification/app/ConversationListSettings.java
+++ b/src/com/android/settings/notification/app/ConversationListSettings.java
@@ -16,8 +16,10 @@
package com.android.settings.notification.app;
+import android.app.people.IPeopleManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.os.ServiceManager;
import android.util.Log;
import com.android.settings.R;
@@ -33,8 +35,15 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
NotificationBackend mBackend = new NotificationBackend();
+ IPeopleManager mPs;
+
protected List<AbstractPreferenceController> mControllers = new ArrayList<>();
+ public ConversationListSettings() {
+ mPs = IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE));
+ }
+
@Override
public int getMetricsCategory() {
return SettingsEnums.NOTIFICATION_CONVERSATION_LIST_SETTINGS;
@@ -53,9 +62,10 @@
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
mControllers = new ArrayList<>();
- mControllers.add(new NoConversationsPreferenceController(context, mBackend));
+ mControllers.add(new NoConversationsPreferenceController(context, mBackend, mPs));
mControllers.add(new PriorityConversationsPreferenceController(context, mBackend));
mControllers.add(new AllConversationsPreferenceController(context, mBackend));
+ mControllers.add(new RecentConversationsPreferenceController(context, mBackend, mPs));
return new ArrayList<>(mControllers);
}
}
diff --git a/src/com/android/settings/notification/app/NoConversationsPreferenceController.java b/src/com/android/settings/notification/app/NoConversationsPreferenceController.java
index 40faadc..475d90d 100644
--- a/src/com/android/settings/notification/app/NoConversationsPreferenceController.java
+++ b/src/com/android/settings/notification/app/NoConversationsPreferenceController.java
@@ -16,9 +16,12 @@
package com.android.settings.notification.app;
+import android.app.people.IPeopleManager;
import android.content.Context;
import android.os.AsyncTask;
+import android.os.RemoteException;
import android.service.notification.ConversationChannelWrapper;
+import android.util.Log;
import android.view.View;
import androidx.preference.Preference;
@@ -27,17 +30,18 @@
import com.android.settings.notification.NotificationBackend;
import com.android.settingslib.widget.LayoutPreference;
-import java.util.List;
-
public class NoConversationsPreferenceController extends ConversationListPreferenceController {
+ private static String TAG = "NoConversationsPC";
private static final String KEY = "no_conversations";
- private List<ConversationChannelWrapper> mConversations;
+ private IPeopleManager mPs;
+ private int mConversationCount = 0;
public NoConversationsPreferenceController(Context context,
- NotificationBackend backend) {
+ NotificationBackend backend, IPeopleManager ps) {
super(context, backend);
+ mPs = ps;
}
@Override
@@ -67,7 +71,12 @@
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... unused) {
- mConversations = mBackend.getConversations(false).getList();
+ mConversationCount = mBackend.getConversations(false).getList().size();
+ try {
+ mConversationCount += mPs.getRecentConversations().getList().size();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error calling PS", e);
+ }
return null;
}
@@ -76,9 +85,9 @@
if (mContext == null) {
return;
}
- pref.findViewById(R.id.onboarding).setVisibility(mConversations.size() == 0
+ pref.findViewById(R.id.onboarding).setVisibility(mConversationCount == 0
? View.VISIBLE : View.GONE);
- preference.setVisible(mConversations.size() == 0);
+ preference.setVisible(mConversationCount == 0);
}
}.execute();
}
diff --git a/src/com/android/settings/notification/app/RecentConversationPreference.java b/src/com/android/settings/notification/app/RecentConversationPreference.java
new file mode 100644
index 0000000..49e2c02
--- /dev/null
+++ b/src/com/android/settings/notification/app/RecentConversationPreference.java
@@ -0,0 +1,80 @@
+/*
+ * 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.content.Context;
+import android.view.View;
+
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+import com.android.settingslib.TwoTargetPreference;
+
+import com.google.common.annotations.VisibleForTesting;
+
+public class RecentConversationPreference extends TwoTargetPreference {
+
+ private OnClearClickListener mOnClearClickListener;
+
+ private View mClearView;
+
+ public interface OnClearClickListener {
+ void onClear();
+ }
+
+ public RecentConversationPreference(Context context) {
+ super(context);
+ }
+
+ public void setOnClearClickListener(
+ OnClearClickListener onClearClickListener) {
+ mOnClearClickListener = onClearClickListener;
+ }
+
+ @VisibleForTesting
+ View getClearView() {
+ return mClearView;
+ }
+
+ @Override
+ protected int getSecondTargetResId() {
+ return R.layout.preference_widget_clear;
+ }
+
+ @VisibleForTesting
+ int getClearId() {
+ return R.id.clear_button;
+ }
+
+ @VisibleForTesting
+ boolean hasClearListener() {
+ return mOnClearClickListener != null;
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+ final View widgetFrame = view.findViewById(android.R.id.widget_frame);
+ widgetFrame.setVisibility(mOnClearClickListener != null ? View.VISIBLE : View.GONE);
+ mClearView = view.findViewById(getClearId());
+ mClearView.setOnClickListener(v -> {
+ if (mOnClearClickListener != null) {
+ mOnClearClickListener.onClear();
+ }
+ });
+ }
+
+}
diff --git a/src/com/android/settings/notification/app/RecentConversationsPreferenceController.java b/src/com/android/settings/notification/app/RecentConversationsPreferenceController.java
new file mode 100644
index 0000000..8be6016
--- /dev/null
+++ b/src/com/android/settings/notification/app/RecentConversationsPreferenceController.java
@@ -0,0 +1,236 @@
+/*
+ * 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.NotificationManager.IMPORTANCE_NONE;
+
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.widget.Button;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceGroup;
+
+import com.android.settings.R;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.LayoutPreference;
+
+import java.text.Collator;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class RecentConversationsPreferenceController extends AbstractPreferenceController {
+
+ private static final String TAG = "RecentConversationsPC";
+ private static final String KEY = "recent_conversations";
+ private List<ConversationChannel> mConversations;
+ private final IPeopleManager mPs;
+ private final NotificationBackend mBackend;
+
+ public RecentConversationsPreferenceController(Context context, NotificationBackend backend,
+ IPeopleManager ps) {
+ super(context);
+ mBackend = backend;
+ mPs = ps;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ Preference getClearAll(PreferenceGroup parent) {
+ LayoutPreference pref = new LayoutPreference(
+ mContext, R.layout.conversations_clear_recents);
+ pref.setOrder(1);
+ Button button = pref.findViewById(R.id.conversation_settings_clear_recents);
+ button.setOnClickListener(v -> {
+ try {
+ mPs.removeAllRecentConversations();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not clear recents", e);
+ }
+ updateState(parent);
+ });
+ return pref;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ PreferenceCategory pref = (PreferenceCategory) preference;
+ // Load conversations
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... unused) {
+ try {
+ mConversations = mPs.getRecentConversations().getList();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could get recents", e);
+ }
+ Collections.sort(mConversations, mConversationComparator);
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void unused) {
+ if (mContext == null) {
+ return;
+ }
+ populateList(mConversations, pref);
+ }
+ }.execute();
+
+ }
+
+ protected void populateList(List<ConversationChannel> conversations,
+ PreferenceGroup containerGroup) {
+ containerGroup.removeAll();
+ if (conversations != null) {
+ populateConversations(conversations, containerGroup);
+ }
+
+ if (containerGroup.getPreferenceCount() == 0) {
+ containerGroup.setVisible(false);
+ } else {
+ containerGroup.setVisible(true);
+ Preference clearAll = getClearAll(containerGroup);
+ if (clearAll != null) {
+ containerGroup.addPreference(clearAll);
+ }
+ }
+ }
+
+ protected void populateConversations(List<ConversationChannel> conversations,
+ PreferenceGroup containerGroup) {
+ int order = 100;
+ for (ConversationChannel conversation : conversations) {
+ if (conversation.getParentNotificationChannel().getImportance() == IMPORTANCE_NONE
+ || (conversation.getParentNotificationChannelGroup() != null
+ && conversation.getParentNotificationChannelGroup().isBlocked())) {
+ continue;
+ }
+ containerGroup.addPreference(
+ createConversationPref(containerGroup, conversation, order++));
+ }
+ }
+
+ protected Preference createConversationPref(PreferenceGroup parent,
+ final ConversationChannel conversation, int order) {
+ final String pkg = conversation.getShortcutInfo().getPackage();
+ final int uid = conversation.getUid();
+ final String conversationId = conversation.getShortcutInfo().getId();
+ RecentConversationPreference pref = new RecentConversationPreference(mContext);
+
+ if (!conversation.hasActiveNotifications()) {
+ pref.setOnClearClickListener(() -> {
+ try {
+ mPs.removeRecentConversation(pkg, UserHandle.getUserId(uid), conversationId);
+ parent.removePreference(pref);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not clear recent", e);
+ }
+ });
+ }
+ pref.setOrder(order);
+
+ pref.setTitle(getTitle(conversation));
+ pref.setSummary(getSummary(conversation));
+ pref.setIcon(mBackend.getConversationDrawable(mContext, conversation.getShortcutInfo(),
+ pkg, uid, false));
+ pref.setKey(conversation.getParentNotificationChannel().getId()
+ + ":" + conversationId);
+ pref.setOnPreferenceClickListener(preference -> {
+ mBackend.createConversationNotificationChannel(
+ pkg, uid,
+ conversation.getParentNotificationChannel(),
+ conversationId);
+ getSubSettingLauncher(conversation, pref.getTitle()).launch();
+ return true;
+ });
+
+ return pref;
+ }
+
+ CharSequence getSummary(ConversationChannel conversation) {
+ return conversation.getParentNotificationChannelGroup() == null
+ ? conversation.getParentNotificationChannel().getName()
+ : mContext.getString(R.string.notification_conversation_summary,
+ conversation.getParentNotificationChannel().getName(),
+ conversation.getParentNotificationChannelGroup().getName());
+ }
+
+ CharSequence getTitle(ConversationChannel conversation) {
+ ShortcutInfo si = conversation.getShortcutInfo();
+ return si.getLabel();
+ }
+
+ SubSettingLauncher getSubSettingLauncher(ConversationChannel conversation,
+ CharSequence title) {
+ Bundle channelArgs = new Bundle();
+ channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, conversation.getUid());
+ channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME,
+ conversation.getShortcutInfo().getPackage());
+ channelArgs.putString(Settings.EXTRA_CHANNEL_ID,
+ conversation.getParentNotificationChannel().getId());
+ channelArgs.putString(Settings.EXTRA_CONVERSATION_ID,
+ conversation.getShortcutInfo().getId());
+
+ return new SubSettingLauncher(mContext)
+ .setDestination(ChannelNotificationSettings.class.getName())
+ .setArguments(channelArgs)
+ .setExtras(channelArgs)
+ .setUserHandle(UserHandle.getUserHandleForUid(conversation.getUid()))
+ .setTitleText(title)
+ .setSourceMetricsCategory(SettingsEnums.NOTIFICATION_CONVERSATION_LIST_SETTINGS);
+ }
+
+ protected Comparator<ConversationChannel> mConversationComparator =
+ new Comparator<ConversationChannel>() {
+ private final Collator sCollator = Collator.getInstance();
+ @Override
+ public int compare(ConversationChannel o1, ConversationChannel o2) {
+ int labelComparison = sCollator.compare(o1.getShortcutInfo().getLabel(),
+ o2.getShortcutInfo().getLabel());
+
+ if (labelComparison == 0) {
+ return o1.getParentNotificationChannel().getId().compareTo(
+ o2.getParentNotificationChannel().getId());
+ }
+
+ return labelComparison;
+ }
+ };
+}
diff --git a/tests/robotests/src/com/android/settings/notification/app/RecentConversationsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/RecentConversationsPreferenceControllerTest.java
new file mode 100644
index 0000000..660b4e1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/app/RecentConversationsPreferenceControllerTest.java
@@ -0,0 +1,275 @@
+/*
+ * 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 com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+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.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.notification.NotificationBackend;
+
+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.shadows.ShadowApplication;
+
+import java.util.ArrayList;
+
+@RunWith(RobolectricTestRunner.class)
+public class RecentConversationsPreferenceControllerTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+ @Mock
+ private NotificationBackend mBackend;
+ @Mock
+ private IPeopleManager mPs;
+
+ private RecentConversationsPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowApplication shadowApplication = ShadowApplication.getInstance();
+ mContext = RuntimeEnvironment.application;
+ mController = new RecentConversationsPreferenceController(mContext, mBackend, mPs);
+ }
+
+ @Test
+ public void isAvailable() {
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void testPopulateList_hideIfNoConversations() {
+ PreferenceCategory outerContainer = mock(PreferenceCategory.class);
+
+ mController.populateList(new ArrayList<>(), outerContainer);
+
+ verify(outerContainer).setVisible(false);
+ verify(outerContainer, never()).addPreference(any());
+ }
+
+ @Test
+ public void testPopulateList_validConversations() {
+ final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ PreferenceScreen ps = preferenceManager.createPreferenceScreen(mContext);
+ PreferenceCategory outerContainer = spy(new PreferenceCategory(mContext));
+ ps.addPreference(outerContainer);
+
+ ConversationChannel ccw = new ConversationChannel(mock(ShortcutInfo.class), 6,
+ new NotificationChannel("hi", "hi", 4),
+ new NotificationChannelGroup("hi", "hi"), 7,
+ true);
+
+ ArrayList<ConversationChannel> list = new ArrayList<>();
+ list.add(ccw);
+
+ mController.populateList(list, outerContainer);
+ // one for the preference, one for the button ro clear all
+ verify(outerContainer, times(2)).addPreference(any());
+ }
+
+ @Test
+ public void populateConversations_blocked() {
+ PreferenceCategory container = mock(PreferenceCategory.class);
+
+ ConversationChannel ccw = new ConversationChannel(mock(ShortcutInfo.class), 6,
+ new NotificationChannel("hi", "hi", 4),
+ new NotificationChannelGroup("hi", "hi"), 7,
+ true);
+
+ ConversationChannel ccw2 = new ConversationChannel(mock(ShortcutInfo.class), 6,
+ new NotificationChannel("hi", "hi", 0),
+ new NotificationChannelGroup("hi", "hi"), 7,
+ true);
+
+ NotificationChannelGroup blockedGroup = new NotificationChannelGroup("hi", "hi");
+ blockedGroup.setBlocked(true);
+ ConversationChannel ccw3 = new ConversationChannel(mock(ShortcutInfo.class), 6,
+ new NotificationChannel("hi", "hi", 4),
+ blockedGroup, 7,
+ true);
+
+ ArrayList<ConversationChannel> list = new ArrayList<>();
+ list.add(ccw);
+ list.add(ccw2);
+ list.add(ccw3);
+
+ mController.populateConversations(list, container);
+
+ verify(container, times(1)).addPreference(any());
+ }
+
+ @Test
+ public void getSummary_withGroup() {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.getLabel()).thenReturn("person");
+ ConversationChannel ccw = new ConversationChannel(mock(ShortcutInfo.class), 6,
+ new NotificationChannel("hi", "channel", 4),
+ new NotificationChannelGroup("hi", "group"), 7,
+ true);
+
+ assertThat(mController.getSummary(ccw).toString()).contains(
+ ccw.getParentNotificationChannelGroup().getName());
+ assertThat(mController.getSummary(ccw).toString()).contains(
+ ccw.getParentNotificationChannel().getName());
+ }
+
+ @Test
+ public void getSummary_noGroup() {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.getLabel()).thenReturn("person");
+ ConversationChannel ccw = new ConversationChannel(mock(ShortcutInfo.class), 6,
+ new NotificationChannel("hi", "channel", 4),
+ null, 7,
+ true);
+
+ assertThat(mController.getSummary(ccw).toString()).isEqualTo(
+ ccw.getParentNotificationChannel().getName());
+ }
+
+ @Test
+ public void getTitle_withShortcut() {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.getLabel()).thenReturn("person");
+ ConversationChannel ccw = new ConversationChannel(si, 6,
+ new NotificationChannel("hi", "channel", 4),
+ new NotificationChannelGroup("hi", "group"), 7,
+ true);
+
+ assertThat(mController.getTitle(ccw).toString()).isEqualTo(si.getLabel());
+ }
+
+ @Test
+ public void testGetSubSettingLauncher() {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.getId()).thenReturn("person");
+ when(si.getPackage()).thenReturn("pkg");
+ ConversationChannel ccw = new ConversationChannel(si, 6,
+ new NotificationChannel("hi", "channel", 4),
+ new NotificationChannelGroup("hi", "group"), 7,
+ true);
+
+
+ Intent intent = mController.getSubSettingLauncher(ccw, "title").toIntent();
+
+ Bundle extras = intent.getExtras();
+ assertThat(extras.getString(AppInfoBase.ARG_PACKAGE_NAME)).isEqualTo(
+ ccw.getShortcutInfo().getPackage());
+ assertThat(extras.getInt(AppInfoBase.ARG_PACKAGE_UID)).isEqualTo(ccw.getUid());
+ assertThat(extras.getString(Settings.EXTRA_CHANNEL_ID)).isEqualTo(
+ ccw.getParentNotificationChannel().getId());
+ assertThat(extras.getString(Settings.EXTRA_CONVERSATION_ID)).isEqualTo(
+ ccw.getShortcutInfo().getId());
+ }
+
+ @Test
+ public void testCreatesChannel() {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.getId()).thenReturn("person");
+ when(si.getPackage()).thenReturn("pkg");
+ ConversationChannel ccw = new ConversationChannel(si, 6,
+ new NotificationChannel("hi", "channel", 4),
+ new NotificationChannelGroup("hi", "group"), 7,
+ true);
+
+ Preference pref = mController.createConversationPref(new PreferenceCategory(mContext),
+ ccw, 100);
+ try {
+ pref.performClick();
+ } catch (RuntimeException e) {
+ // expected since it tries to launch an activity
+ }
+ verify(mBackend).createConversationNotificationChannel(
+ si.getPackage(), ccw.getUid(), ccw.getParentNotificationChannel(), si.getId());
+ }
+
+ @Test
+ public void testRemoveConversation() throws Exception {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.getId()).thenReturn("person");
+ when(si.getPackage()).thenReturn("pkg");
+ ConversationChannel ccw = new ConversationChannel(si, 6,
+ new NotificationChannel("hi", "channel", 4),
+ new NotificationChannelGroup("hi", "group"), 7,
+ false);
+
+ RecentConversationPreference pref =
+ (RecentConversationPreference) mController.createConversationPref(
+ new PreferenceCategory(mContext), ccw, 100);
+ final View view = View.inflate(mContext, pref.getLayoutResource(), null);
+ PreferenceViewHolder holder = spy(PreferenceViewHolder.createInstanceForTests(view));
+ View delete = View.inflate(mContext, pref.getSecondTargetResId(), null);
+ when(holder.findViewById(pref.getClearId())).thenReturn(delete);
+
+ pref.onBindViewHolder(holder);
+ pref.getClearView().performClick();
+
+ verify(mPs).removeRecentConversation(
+ si.getPackage(), UserHandle.getUserId(ccw.getUid()), si.getId());
+ }
+
+ @Test
+ public void testNonremoveableConversation() throws Exception {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.getId()).thenReturn("person");
+ when(si.getPackage()).thenReturn("pkg");
+ ConversationChannel ccw = new ConversationChannel(si, 6,
+ new NotificationChannel("hi", "channel", 4),
+ new NotificationChannelGroup("hi", "group"), 7,
+ true);
+
+ RecentConversationPreference pref =
+ (RecentConversationPreference) mController.createConversationPref(
+ new PreferenceCategory(mContext), ccw, 100);
+ assertThat(pref.hasClearListener()).isFalse();
+ }
+}