Add screen for all conversations

Test: atest
Bug: 149583455
Change-Id: I9704082d097ef2bb17c051703e380f573056318c
This commit is contained in:
Julia Reynolds
2020-02-14 16:45:15 -05:00
parent 184e2f34ab
commit 1ee9dce20c
11 changed files with 776 additions and 91 deletions

View File

@@ -8268,6 +8268,18 @@
<!-- [CHAR LIMIT=NONE] Summary for promote_conversation_title on notifications settings page -->
<string name="promote_conversation_summary">Add to the conversation section</string>
<!-- [CHAR LIMIT=100] link to page listing all conversations -->
<string name="manage_conversations">Manage conversations</string>
<!-- [CHAR LIMIT=100] preference category title -->
<string name="important_conversations">Important conversations</string>
<!-- [CHAR LIMIT=100] preference category title -->
<string name="all_conversations">All conversations</string>
<!-- [CHAR LIMIT=NONE] description of how notifications from important conversations behave -->
<string name="important_conversation_behavior_summary">Important conversations show at the top of the pull-down shade. You can also set them to bubble and interrupt Do Not Disturb.</string>
<!-- Importance title strings for the Importance page. Also the second part of the importance
summary on the channel page-->

View File

@@ -49,6 +49,16 @@
</Preference>
</PreferenceCategory>
<PreferenceCategory
android:key="conversations"
android:title="@string/conversations_category_title"
android:order="5">
<Preference
android:key="manage_conversations"
android:title="@string/manage_conversations"
android:fragment="com.android.settings.notification.app.ConversationListSettings"/>
</PreferenceCategory>
<PreferenceCategory
android:key="configure_notifications_lock"
android:title="@string/lock_screen_notifications_title"

View File

@@ -0,0 +1,46 @@
<?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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="conversation_list">
<PreferenceCategory
android:title="@string/important_conversations"
android:key="important_conversations"
android:visibility="gone"
settings:allowDividerAbove="false"
settings:allowDividerBelow="true" >
<Preference
android:key="behavior"
android:summary="@string/important_conversation_behavior_summary"/>
<!-- Important conversations added here -->
<PreferenceCategory
android:key="important_conversations_list"
settings:allowDividerAbove="false"
settings:allowDividerBelow="false" >
</PreferenceCategory>
</PreferenceCategory>
<!--Other conversations added here -->
<PreferenceCategory
android:title="@string/all_conversations"
android:key="all_conversations"
settings:allowDividerAbove="true"
settings:allowDividerBelow="false" />
</PreferenceScreen>

View File

@@ -261,6 +261,15 @@ public class NotificationBackend {
}
}
public ParceledListSlice<ConversationChannelWrapper> getConversations(boolean onlyImportant) {
try {
return sINM.getConversations(onlyImportant);
} catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e);
return ParceledListSlice.emptyList();
}
}
/**
* Returns all notification channels associated with the package and uid that will bypass DND
*/
@@ -514,6 +523,9 @@ public class NotificationBackend {
public Drawable getConversationDrawable(Context context, ShortcutInfo info, String pkg,
int uid) {
if (info == null) {
return null;
}
ConversationIconFactory iconFactory = new ConversationIconFactory(context,
context.getSystemService(LauncherApps.class),
context.getPackageManager(), IconDrawableFactory.newInstance(context),

View File

@@ -0,0 +1,73 @@
/*
* 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.os.AsyncTask;
import android.service.notification.ConversationChannelWrapper;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.settings.notification.NotificationBackend;
import java.util.Collections;
import java.util.List;
public class AllConversationsPreferenceController extends ConversationListPreferenceController {
private static final String KEY = "all_conversations";
private List<ConversationChannelWrapper> mConversations;
public AllConversationsPreferenceController(Context context,
NotificationBackend backend) {
super(context, backend);
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public void updateState(Preference preference) {
PreferenceCategory pref = (PreferenceCategory) preference;
// Load conversations
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... unused) {
mConversations = mBackend.getConversations(false).getList();
Collections.sort(mConversations, mConversationComparator);
return null;
}
@Override
protected void onPostExecute(Void unused) {
if (mContext == null) {
return;
}
populateList(mConversations, pref, pref);
}
}.execute();
}
}

View File

@@ -0,0 +1,163 @@
/*
* 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.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.NotificationBackend;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class AppConversationListPreferenceController extends NotificationPreferenceController {
private static final String KEY = "conversations";
public static final String ARG_FROM_SETTINGS = "fromSettings";
private List<ConversationChannelWrapper> mConversations;
private PreferenceCategory mPreference;
public AppConversationListPreferenceController(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;
}
if (mChannel != null) {
if (mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid)
|| NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
return false;
}
}
return true;
}
@Override
public void updateState(Preference preference) {
mPreference = (PreferenceCategory) preference;
// Load channel settings
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... unused) {
mConversations = mBackend.getConversations(mAppRow.pkg, mAppRow.uid).getList();
Collections.sort(mConversations, mConversationComparator);
return null;
}
@Override
protected void onPostExecute(Void unused) {
if (mContext == null) {
return;
}
populateList();
}
}.execute();
}
private void populateList() {
// TODO: if preference has children, compare with newly loaded list
mPreference.removeAll();
mPreference.setTitle(R.string.conversations_category_title);
if (mConversations.isEmpty()) {
mPreference.setVisible(false);
} else {
mPreference.setVisible(true);
populateConversations();
}
}
private void populateConversations() {
for (ConversationChannelWrapper conversation : mConversations) {
if (conversation.getNotificationChannel().isDemoted()) {
continue;
}
mPreference.addPreference(createConversationPref(conversation));
}
}
protected Preference createConversationPref(final ConversationChannelWrapper conversation) {
Preference pref = new Preference(mContext);
ShortcutInfo si = conversation.getShortcutInfo();
pref.setTitle(si != null
? si.getShortLabel()
: conversation.getNotificationChannel().getName());
pref.setSummary(conversation.getNotificationChannel().getGroup() != null
? mContext.getString(R.string.notification_conversation_summary,
conversation.getParentChannelLabel(), conversation.getGroupLabel())
: conversation.getParentChannelLabel());
if (si != null) {
pref.setIcon(mBackend.getConversationDrawable(mContext, si, mAppRow.pkg, mAppRow.uid));
}
pref.setKey(conversation.getNotificationChannel().getId());
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,
conversation.getNotificationChannel().getParentChannelId());
channelArgs.putString(Settings.EXTRA_CONVERSATION_ID,
conversation.getNotificationChannel().getConversationId());
channelArgs.putBoolean(ARG_FROM_SETTINGS, true);
pref.setIntent(new SubSettingLauncher(mContext)
.setDestination(ChannelNotificationSettings.class.getName())
.setArguments(channelArgs)
.setExtras(channelArgs)
.setTitleText(pref.getTitle())
.setSourceMetricsCategory(SettingsEnums.NOTIFICATION_APP_NOTIFICATION)
.toIntent());
return pref;
}
protected Comparator<ConversationChannelWrapper> mConversationComparator =
(left, right) -> {
if (left.getNotificationChannel().isImportantConversation()
!= right.getNotificationChannel().isImportantConversation()) {
// important first
return Boolean.compare(right.getNotificationChannel().isImportantConversation(),
left.getNotificationChannel().isImportantConversation());
}
return left.getNotificationChannel().getId().compareTo(
right.getNotificationChannel().getId());
};
}

View File

@@ -123,7 +123,7 @@ public class AppNotificationSettings extends NotificationSettings {
mControllers.add(new DeletedChannelsPreferenceController(context, mBackend));
mControllers.add(new BubbleSummaryPreferenceController(context, mBackend));
mControllers.add(new ChannelListPreferenceController(context, mBackend));
mControllers.add(new ConversationListPreferenceController(context, mBackend));
mControllers.add(new AppConversationListPreferenceController(context, mBackend));
return new ArrayList<>(mControllers);
}
}

View File

@@ -16,37 +16,38 @@
package com.android.settings.notification.app;
import android.app.NotificationChannel;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
import android.text.TextUtils;
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 java.util.Collections;
import java.text.Collator;
import java.util.Comparator;
import java.util.List;
public class ConversationListPreferenceController extends NotificationPreferenceController {
public abstract class ConversationListPreferenceController extends AbstractPreferenceController {
private static final String KEY = "conversations";
public static final String ARG_FROM_SETTINGS = "fromSettings";
private static final String KEY = "all_conversations";
private List<ConversationChannelWrapper> mConversations;
private PreferenceCategory mPreference;
protected final NotificationBackend mBackend;
public ConversationListPreferenceController(Context context, NotificationBackend backend) {
super(context, backend);
public ConversationListPreferenceController(Context context,
NotificationBackend backend) {
super(context);
mBackend = backend;
}
@Override
@@ -56,108 +57,93 @@ public class ConversationListPreferenceController extends NotificationPreference
@Override
public boolean isAvailable() {
if (mAppRow == null) {
return false;
}
if (mAppRow.banned) {
return false;
}
if (mChannel != null) {
if (mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid)
|| NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
return false;
}
}
return true;
}
@Override
public void updateState(Preference preference) {
mPreference = (PreferenceCategory) preference;
// Load channel settings
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... unused) {
mConversations = mBackend.getConversations(mAppRow.pkg, mAppRow.uid).getList();
Collections.sort(mConversations, mConversationComparator);
return null;
}
@Override
protected void onPostExecute(Void unused) {
if (mContext == null) {
return;
}
populateList();
}
}.execute();
}
private void populateList() {
protected void populateList(List<ConversationChannelWrapper> conversations,
PreferenceGroup outerContainer, PreferenceGroup innerContainer) {
// TODO: if preference has children, compare with newly loaded list
mPreference.removeAll();
mPreference.setTitle(R.string.conversations_category_title);
if (mConversations.isEmpty()) {
mPreference.setVisible(false);
if (conversations.isEmpty()) {
outerContainer.setVisible(false);
} else {
mPreference.setVisible(true);
populateConversations();
outerContainer.setVisible(true);
populateConversations(conversations, innerContainer);
}
}
private void populateConversations() {
for (ConversationChannelWrapper conversation : mConversations) {
protected void populateConversations(List<ConversationChannelWrapper> conversations,
PreferenceGroup containerGroup) {
containerGroup.removeAll();
for (ConversationChannelWrapper conversation : conversations) {
if (conversation.getNotificationChannel().isDemoted()) {
continue;
}
mPreference.addPreference(createConversationPref(conversation));
containerGroup.addPreference(createConversationPref(conversation));
}
}
protected Preference createConversationPref(final ConversationChannelWrapper conversation) {
Preference pref = new Preference(mContext);
ShortcutInfo si = conversation.getShortcutInfo();
pref.setTitle(si != null
? si.getShortLabel()
: conversation.getNotificationChannel().getName());
pref.setSummary(conversation.getNotificationChannel().getGroup() != null
? mContext.getString(R.string.notification_conversation_summary,
conversation.getParentChannelLabel(), conversation.getGroupLabel())
: conversation.getParentChannelLabel());
if (si != null) {
pref.setIcon(mBackend.getConversationDrawable(mContext, si, mAppRow.pkg, mAppRow.uid));
}
pref.setTitle(getTitle(conversation));
pref.setSummary(getSummary(conversation));
pref.setIcon(mBackend.getConversationDrawable(mContext, conversation.getShortcutInfo(),
conversation.getPkg(), conversation.getUid()));
pref.setKey(conversation.getNotificationChannel().getId());
pref.setIntent(getIntent(conversation, pref.getTitle()));
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,
conversation.getNotificationChannel().getParentChannelId());
channelArgs.putString(Settings.EXTRA_CONVERSATION_ID,
conversation.getNotificationChannel().getConversationId());
channelArgs.putBoolean(ARG_FROM_SETTINGS, true);
pref.setIntent(new SubSettingLauncher(mContext)
.setDestination(ChannelNotificationSettings.class.getName())
.setArguments(channelArgs)
.setExtras(channelArgs)
.setTitleText(pref.getTitle())
.setSourceMetricsCategory(SettingsEnums.NOTIFICATION_APP_NOTIFICATION)
.toIntent());
return pref;
}
protected Comparator<ConversationChannelWrapper> mConversationComparator =
(left, right) -> {
if (left.getNotificationChannel().isImportantConversation()
!= right.getNotificationChannel().isImportantConversation()) {
// important first
return Boolean.compare(right.getNotificationChannel().isImportantConversation(),
left.getNotificationChannel().isImportantConversation());
CharSequence getSummary(ConversationChannelWrapper conversation) {
return TextUtils.isEmpty(conversation.getGroupLabel())
? conversation.getParentChannelLabel()
: mContext.getString(R.string.notification_conversation_summary,
conversation.getParentChannelLabel(), conversation.getGroupLabel());
}
CharSequence getTitle(ConversationChannelWrapper conversation) {
ShortcutInfo si = conversation.getShortcutInfo();
return si != null
? si.getShortLabel()
: conversation.getNotificationChannel().getName();
}
Intent getIntent(ConversationChannelWrapper conversation, CharSequence title) {
Bundle channelArgs = new Bundle();
channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, conversation.getUid());
channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, conversation.getPkg());
channelArgs.putString(Settings.EXTRA_CHANNEL_ID,
conversation.getNotificationChannel().getId());
channelArgs.putString(Settings.EXTRA_CONVERSATION_ID,
conversation.getNotificationChannel().getConversationId());
return new SubSettingLauncher(mContext)
.setDestination(ChannelNotificationSettings.class.getName())
.setArguments(channelArgs)
.setExtras(channelArgs)
.setTitleText(title)
.setSourceMetricsCategory(SettingsEnums.NOTIFICATION_CONVERSATION_LIST_SETTINGS)
.toIntent();
}
protected Comparator<ConversationChannelWrapper> mConversationComparator =
new Comparator<ConversationChannelWrapper>() {
private final Collator sCollator = Collator.getInstance();
@Override
public int compare(ConversationChannelWrapper o1, ConversationChannelWrapper o2) {
if (o1.getShortcutInfo() != null && o2.getShortcutInfo() == null) {
return -1;
}
if (o1.getShortcutInfo() == null && o2.getShortcutInfo() != null) {
return 1;
}
if (o1.getShortcutInfo() == null && o2.getShortcutInfo() == null) {
return o1.getNotificationChannel().getId().compareTo(
o2.getNotificationChannel().getId());
}
return sCollator.compare(o1.getShortcutInfo().getShortLabel(),
o2.getShortcutInfo().getShortLabel());
}
return left.getNotificationChannel().getId().compareTo(
right.getNotificationChannel().getId());
};
}

View File

@@ -0,0 +1,67 @@
/*
* 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.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.notification.NotificationBackend;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
public class ConversationListSettings extends DashboardFragment {
private static final String TAG = "ConvoListSettings";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
NotificationBackend mBackend = new NotificationBackend();
protected List<ConversationListPreferenceController> mControllers = new ArrayList<>();
@Override
public int getMetricsCategory() {
return SettingsEnums.NOTIFICATION_CONVERSATION_LIST_SETTINGS;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.conversation_list_settings;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
mControllers = new ArrayList<>();
mControllers.add(new ImportantConversationsPreferenceController(context, mBackend));
mControllers.add(new AllConversationsPreferenceController(context, mBackend));
return new ArrayList<>(mControllers);
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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.os.AsyncTask;
import android.service.notification.ConversationChannelWrapper;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.settings.notification.NotificationBackend;
import java.util.Collections;
import java.util.List;
public class ImportantConversationsPreferenceController extends
ConversationListPreferenceController {
private static final String KEY = "important_conversations";
private static final String LIST_KEY = "important_conversations_list";
private List<ConversationChannelWrapper> mConversations;
public ImportantConversationsPreferenceController(Context context,
NotificationBackend backend) {
super(context, backend);
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public void updateState(Preference preference) {
PreferenceCategory pref = (PreferenceCategory) preference;
// Load conversations
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... unused) {
mConversations = mBackend.getConversations(true).getList();
Collections.sort(mConversations, mConversationComparator);
return null;
}
@Override
protected void onPostExecute(Void unused) {
if (mContext == null) {
return;
}
populateList(mConversations, pref, pref.findPreference(LIST_KEY));
}
}.execute();
}
}

View File

@@ -0,0 +1,241 @@
/*
* 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_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 com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.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.never;
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.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.UserManager;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
import android.view.View;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.notification.NotificationBackend;
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.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class ConversationListPreferenceControllerTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private NotificationBackend mBackend;
private TestPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ShadowApplication shadowApplication = ShadowApplication.getInstance();
mContext = RuntimeEnvironment.application;
mController = new TestPreferenceController(mContext, mBackend);
}
@Test
public void isAvailable() {
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void testPopulateList_hideIfNoConversations() {
PreferenceCategory outerContainer = mock(PreferenceCategory.class);
PreferenceCategory innerContainer = mock(PreferenceCategory.class);
mController.populateList(new ArrayList<>(), outerContainer, innerContainer);
verify(outerContainer).setVisible(false);
verify(innerContainer, never()).addPreference(any());
}
@Test
public void testPopulateList_validConversations() {
PreferenceCategory outerContainer = mock(PreferenceCategory.class);
PreferenceCategory innerContainer = mock(PreferenceCategory.class);
ConversationChannelWrapper ccw = new ConversationChannelWrapper();
ccw.setNotificationChannel(mock(NotificationChannel.class));
ccw.setPkg("pkg");
ccw.setUid(1);
ccw.setShortcutInfo(mock(ShortcutInfo.class));
ArrayList<ConversationChannelWrapper> list = new ArrayList<>();
list.add(ccw);
mController.populateList(list, outerContainer, innerContainer);
verify(outerContainer).setVisible(true);
verify(innerContainer, times(1)).addPreference(any());
}
@Test
public void populateConversations() {
PreferenceCategory container = mock(PreferenceCategory.class);
ConversationChannelWrapper ccw = new ConversationChannelWrapper();
ccw.setNotificationChannel(mock(NotificationChannel.class));
ccw.setPkg("pkg");
ccw.setUid(1);
ccw.setShortcutInfo(mock(ShortcutInfo.class));
ConversationChannelWrapper ccwDemoted = new ConversationChannelWrapper();
NotificationChannel demoted = new NotificationChannel("a", "a", 2);
demoted.setDemoted(true);
ccwDemoted.setNotificationChannel(demoted);
ccwDemoted.setPkg("pkg");
ccwDemoted.setUid(1);
ccwDemoted.setShortcutInfo(mock(ShortcutInfo.class));
ArrayList<ConversationChannelWrapper> list = new ArrayList<>();
list.add(ccw);
list.add(ccwDemoted);
mController.populateConversations(list, container);
verify(container, times(1)).addPreference(any());
}
@Test
public void getSummary_withGroup() {
ConversationChannelWrapper ccw = new ConversationChannelWrapper();
NotificationChannel channel = new NotificationChannel("a", "child", 2);
ccw.setNotificationChannel(channel);
ccw.setPkg("pkg");
ccw.setUid(1);
ccw.setShortcutInfo(mock(ShortcutInfo.class));
ccw.setGroupLabel("group");
ccw.setParentChannelLabel("parent");
assertThat(mController.getSummary(ccw).toString()).contains(ccw.getGroupLabel());
assertThat(mController.getSummary(ccw).toString()).contains(ccw.getParentChannelLabel());
}
@Test
public void getSummary_noGroup() {
ConversationChannelWrapper ccw = new ConversationChannelWrapper();
NotificationChannel channel = new NotificationChannel("a", "child", 2);
ccw.setNotificationChannel(channel);
ccw.setPkg("pkg");
ccw.setUid(1);
ccw.setShortcutInfo(mock(ShortcutInfo.class));
ccw.setParentChannelLabel("parent");
assertThat(mController.getSummary(ccw).toString()).isEqualTo(ccw.getParentChannelLabel());
}
@Test
public void getTitle_withShortcut() {
ConversationChannelWrapper ccw = new ConversationChannelWrapper();
NotificationChannel channel = new NotificationChannel("a", "child", 2);
ccw.setNotificationChannel(channel);
ccw.setPkg("pkg");
ccw.setUid(1);
ShortcutInfo si = mock(ShortcutInfo.class);
when(si.getShortLabel()).thenReturn("conversation name");
ccw.setShortcutInfo(si);
ccw.setGroupLabel("group");
ccw.setParentChannelLabel("parent");
assertThat(mController.getTitle(ccw).toString()).isEqualTo(si.getShortLabel());
}
@Test
public void getTitle_noShortcut() {
ConversationChannelWrapper ccw = new ConversationChannelWrapper();
NotificationChannel channel = new NotificationChannel("a", "child", 2);
ccw.setNotificationChannel(channel);
ccw.setPkg("pkg");
ccw.setUid(1);
ccw.setParentChannelLabel("parent");
assertThat(mController.getTitle(ccw).toString()).isEqualTo(
ccw.getNotificationChannel().getName());
}
@Test
public void testGetIntent() {
ConversationChannelWrapper ccw = new ConversationChannelWrapper();
NotificationChannel channel = new NotificationChannel("a", "child", 2);
channel.setConversationId("parent", "convo id");
ccw.setNotificationChannel(channel);
ccw.setPkg("pkg");
ccw.setUid(1);
ccw.setParentChannelLabel("parent label");
Intent intent = mController.getIntent(ccw, "title");
Bundle extras = intent.getExtras();
assertThat(extras.getString(AppInfoBase.ARG_PACKAGE_NAME)).isEqualTo(ccw.getPkg());
assertThat(extras.getInt(AppInfoBase.ARG_PACKAGE_UID)).isEqualTo(ccw.getUid());
assertThat(extras.getString(Settings.EXTRA_CHANNEL_ID)).isEqualTo(
ccw.getNotificationChannel().getId());
assertThat(extras.getString(Settings.EXTRA_CONVERSATION_ID)).isEqualTo(
ccw.getNotificationChannel().getConversationId());
}
private final class TestPreferenceController extends ConversationListPreferenceController {
private TestPreferenceController(Context context, NotificationBackend backend) {
super(context, backend);
}
@Override
public String getPreferenceKey() {
return "test";
}
}
}