Merge changes from topics "jr-all-convo", "jr-default-vic"

* changes:
  Add a default behavior setting for VICs
  Add screen for all conversations
This commit is contained in:
Julia Reynolds
2020-02-19 12:24:57 +00:00
committed by Android (Google) Code Review
32 changed files with 1118 additions and 162 deletions

View File

@@ -8296,6 +8296,21 @@
<!-- [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=100] Setting to automatically bubble all notifications from favorite conversations -->
<string name="important_bubble">Bubble important 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,51 @@
<?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"/>
<SwitchPreference
android:key="important_bubble"
android:title="@string/important_bubble"
android:icon="@drawable/ic_create_bubble"
settings:controller="com.android.settings.notification.app.ImportantConversationBubblePreferenceController"/>
<!-- 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

@@ -35,13 +35,13 @@ public class AllowSoundPreferenceController extends NotificationPreferenceContro
private static final String TAG = "AllowSoundPrefContr";
private static final String KEY_IMPORTANCE = "allow_sound";
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
public AllowSoundPreferenceController(Context context,
NotificationSettings.ImportanceListener importanceListener,
NotificationSettings.DependentFieldListener dependentFieldListener,
NotificationBackend backend) {
super(context, backend);
mImportanceListener = importanceListener;
mDependentFieldListener = dependentFieldListener;
}
@Override
@@ -77,7 +77,7 @@ public class AllowSoundPreferenceController extends NotificationPreferenceContro
mChannel.setImportance(importance);
mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
saveChannel();
mImportanceListener.onImportanceChanged();
mDependentFieldListener.onFieldValueChanged();
}
return true;
}

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

@@ -100,18 +100,18 @@ public class AppNotificationSettings extends NotificationSettings {
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
mControllers = new ArrayList<>();
mControllers.add(new HeaderPreferenceController(context, this));
mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
mControllers.add(new BlockPreferenceController(context, mDependentFieldListener, mBackend));
mControllers.add(new BadgePreferenceController(context, mBackend));
mControllers.add(new AllowSoundPreferenceController(
context, mImportanceListener, mBackend));
context, mDependentFieldListener, mBackend));
mControllers.add(new ImportancePreferenceController(
context, mImportanceListener, mBackend));
context, mDependentFieldListener, mBackend));
mControllers.add(new MinImportancePreferenceController(
context, mImportanceListener, mBackend));
context, mDependentFieldListener, mBackend));
mControllers.add(new HighImportancePreferenceController(
context, mImportanceListener, mBackend));
context, mDependentFieldListener, mBackend));
mControllers.add(new SoundPreferenceController(context, this,
mImportanceListener, mBackend));
mDependentFieldListener, mBackend));
mControllers.add(new LightsPreferenceController(context, mBackend));
mControllers.add(new VibrationPreferenceController(context, mBackend));
mControllers.add(new VisibilityPreferenceController(context, new LockPatternUtils(context),
@@ -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

@@ -36,13 +36,13 @@ public class BlockPreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin, SwitchBar.OnSwitchChangeListener {
private static final String KEY_BLOCK = "block";
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
public BlockPreferenceController(Context context,
NotificationSettings.ImportanceListener importanceListener,
NotificationSettings.DependentFieldListener dependentFieldListener,
NotificationBackend backend) {
super(context, backend);
mImportanceListener = importanceListener;
mDependentFieldListener = dependentFieldListener;
}
@Override
@@ -124,7 +124,7 @@ public class BlockPreferenceController extends NotificationPreferenceController
mAppRow.banned = blocked;
mBackend.setNotificationsEnabledForPackage(mAppRow.pkg, mAppRow.uid, !blocked);
}
mImportanceListener.onImportanceChanged();
mDependentFieldListener.onFieldValueChanged();
}
String getSwitchBarText() {

View File

@@ -109,17 +109,17 @@ public class ChannelNotificationSettings extends NotificationSettings {
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
mControllers = new ArrayList<>();
mControllers.add(new HeaderPreferenceController(context, this));
mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
mControllers.add(new BlockPreferenceController(context, mDependentFieldListener, mBackend));
mControllers.add(new ImportancePreferenceController(
context, mImportanceListener, mBackend));
context, mDependentFieldListener, mBackend));
mControllers.add(new MinImportancePreferenceController(
context, mImportanceListener, mBackend));
context, mDependentFieldListener, mBackend));
mControllers.add(new HighImportancePreferenceController(
context, mImportanceListener, mBackend));
context, mDependentFieldListener, mBackend));
mControllers.add(new AllowSoundPreferenceController(
context, mImportanceListener, mBackend));
context, mDependentFieldListener, mBackend));
mControllers.add(new SoundPreferenceController(context, this,
mImportanceListener, mBackend));
mDependentFieldListener, mBackend));
mControllers.add(new VibrationPreferenceController(context, mBackend));
mControllers.add(new AppLinkPreferenceController(context));
mControllers.add(new DescriptionPreferenceController(context));

View File

@@ -16,7 +16,10 @@
package com.android.settings.notification.app;
import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.Preference;
@@ -29,10 +32,12 @@ public class ConversationImportantPreferenceController extends NotificationPrefe
private static final String TAG = "ConvoImpPC";
private static final String KEY = "important";
private final NotificationSettings.DependentFieldListener mDependentFieldListener;
public ConversationImportantPreferenceController(Context context,
NotificationBackend backend) {
NotificationBackend backend, NotificationSettings.DependentFieldListener listener) {
super(context, backend);
mDependentFieldListener = listener;
}
@Override
@@ -67,8 +72,17 @@ public class ConversationImportantPreferenceController extends NotificationPrefe
}
final boolean value = (Boolean) newValue;
mChannel.setImportantConversation(value);
if (value && bubbleImportantConversations()) {
mChannel.setAllowBubbles(true);
mDependentFieldListener.onFieldValueChanged();
}
saveChannel();
return true;
}
private boolean bubbleImportantConversations() {
return Settings.Secure.getInt(mContext.getContentResolver(),
BUBBLE_IMPORTANT_CONVERSATIONS, 1) == 1;
}
}

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;
}
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 =
(left, right) -> {
if (left.getNotificationChannel().isImportantConversation()
!= right.getNotificationChannel().isImportantConversation()) {
// important first
return Boolean.compare(right.getNotificationChannel().isImportantConversation(),
left.getNotificationChannel().isImportantConversation());
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<AbstractPreferenceController> 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

@@ -79,14 +79,15 @@ public class ConversationNotificationSettings extends NotificationSettings {
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
mControllers = new ArrayList<>();
mControllers.add(new ConversationHeaderPreferenceController(context, this));
mControllers.add(new ConversationImportantPreferenceController(context, mBackend));
mControllers.add(new ConversationImportantPreferenceController(
context, mBackend, mDependentFieldListener));
mControllers.add(new DefaultImportancePreferenceController(
context, mImportanceListener, mBackend));
context, mDependentFieldListener, mBackend));
mControllers.add(new AddToHomeScreenPreferenceController(context, mBackend));
mControllers.add(new HighImportancePreferenceController(
context, mImportanceListener, mBackend));
context, mDependentFieldListener, mBackend));
mControllers.add(new SoundPreferenceController(context, this,
mImportanceListener, mBackend));
mDependentFieldListener, mBackend));
mControllers.add(new VibrationPreferenceController(context, mBackend));
mControllers.add(new AppLinkPreferenceController(context));
mControllers.add(new DescriptionPreferenceController(context));

View File

@@ -32,13 +32,13 @@ public class DefaultImportancePreferenceController extends NotificationPreferenc
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final String KEY = "alerting";
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
public DefaultImportancePreferenceController(Context context,
NotificationSettings.ImportanceListener importanceListener,
NotificationSettings.DependentFieldListener dependentFieldListener,
NotificationBackend backend) {
super(context, backend);
mImportanceListener = importanceListener;
mDependentFieldListener = dependentFieldListener;
}
@Override
@@ -78,7 +78,7 @@ public class DefaultImportancePreferenceController extends NotificationPreferenc
mChannel.setImportance(checked ? IMPORTANCE_DEFAULT : IMPORTANCE_LOW);
mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
saveChannel();
mImportanceListener.onImportanceChanged();
mDependentFieldListener.onFieldValueChanged();
}
return true;
}

View File

@@ -32,13 +32,13 @@ public class HighImportancePreferenceController extends NotificationPreferenceCo
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final String KEY_IMPORTANCE = "high_importance";
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
public HighImportancePreferenceController(Context context,
NotificationSettings.ImportanceListener importanceListener,
NotificationSettings.DependentFieldListener dependentFieldListener,
NotificationBackend backend) {
super(context, backend);
mImportanceListener = importanceListener;
mDependentFieldListener = dependentFieldListener;
}
@Override
@@ -78,7 +78,7 @@ public class HighImportancePreferenceController extends NotificationPreferenceCo
mChannel.setImportance(checked ? IMPORTANCE_HIGH : IMPORTANCE_DEFAULT);
mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
saveChannel();
mImportanceListener.onImportanceChanged();
mDependentFieldListener.onFieldValueChanged();
}
return true;
}

View File

@@ -33,13 +33,13 @@ public class ImportancePreferenceController extends NotificationPreferenceContro
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final String KEY_IMPORTANCE = "importance";
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
public ImportancePreferenceController(Context context,
NotificationSettings.ImportanceListener importanceListener,
NotificationSettings.DependentFieldListener dependentFieldListener,
NotificationBackend backend) {
super(context, backend);
mImportanceListener = importanceListener;
mDependentFieldListener = dependentFieldListener;
}
@Override
@@ -91,7 +91,7 @@ public class ImportancePreferenceController extends NotificationPreferenceContro
mChannel.setImportance(importance);
mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
saveChannel();
mImportanceListener.onImportanceChanged();
mDependentFieldListener.onFieldValueChanged();
}
return true;
}

View File

@@ -0,0 +1,71 @@
/*
* 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.provider.Settings.Global.NOTIFICATION_BUBBLES;
import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
import android.content.Context;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.TogglePreferenceController;
public class ImportantConversationBubblePreferenceController extends TogglePreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final String TAG = "ImpConvBubPrefContr";
@VisibleForTesting
static final int ON = 1;
@VisibleForTesting
static final int OFF = 0;
public ImportantConversationBubblePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
private boolean isGloballyEnabled() {
return Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, OFF) == ON;
}
@Override
public int getAvailabilityStatus() {
return isGloballyEnabled() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
}
@Override
public boolean isSliceable() {
return false;
}
@Override
public boolean isChecked() {
return Settings.Secure.getInt(mContext.getContentResolver(),
BUBBLE_IMPORTANT_CONVERSATIONS, ON) == ON;
}
@Override
public boolean setChecked(boolean isChecked) {
return Settings.Secure.putInt(mContext.getContentResolver(),
BUBBLE_IMPORTANT_CONVERSATIONS, isChecked ? ON : OFF);
}
}

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

@@ -32,13 +32,13 @@ public class MinImportancePreferenceController extends NotificationPreferenceCon
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final String KEY_IMPORTANCE = "min_importance";
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
public MinImportancePreferenceController(Context context,
NotificationSettings.ImportanceListener importanceListener,
NotificationSettings.DependentFieldListener dependentFieldListener,
NotificationBackend backend) {
super(context, backend);
mImportanceListener = importanceListener;
mDependentFieldListener = dependentFieldListener;
}
@Override
@@ -78,7 +78,7 @@ public class MinImportancePreferenceController extends NotificationPreferenceCon
mChannel.setImportance(checked ? IMPORTANCE_MIN : IMPORTANCE_LOW);
mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
saveChannel();
mImportanceListener.onImportanceChanged();
mDependentFieldListener.onFieldValueChanged();
}
return true;
}

View File

@@ -33,7 +33,6 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
@@ -79,7 +78,7 @@ abstract public class NotificationSettings extends DashboardFragment {
protected boolean mListeningToPackageRemove;
protected List<NotificationPreferenceController> mControllers = new ArrayList<>();
protected ImportanceListener mImportanceListener = new ImportanceListener();
protected DependentFieldListener mDependentFieldListener = new DependentFieldListener();
protected Intent mIntent;
protected Bundle mArgs;
@@ -328,8 +327,8 @@ abstract public class NotificationSettings extends DashboardFragment {
}
};
protected class ImportanceListener {
protected void onImportanceChanged() {
protected class DependentFieldListener {
protected void onFieldValueChanged() {
final PreferenceScreen screen = getPreferenceScreen();
for (NotificationPreferenceController controller : mControllers) {
controller.displayPreference(screen);

View File

@@ -40,16 +40,16 @@ public class SoundPreferenceController extends NotificationPreferenceController
private static final String KEY_SOUND = "ringtone";
private final SettingsPreferenceFragment mFragment;
private final NotificationSettings.ImportanceListener mListener;
private final NotificationSettings.DependentFieldListener mListener;
private NotificationSoundPreference mPreference;
protected static final int CODE = 200;
public SoundPreferenceController(Context context, SettingsPreferenceFragment hostFragment,
NotificationSettings.ImportanceListener importanceListener,
NotificationSettings.DependentFieldListener dependentFieldListener,
NotificationBackend backend) {
super(context, backend);
mFragment = hostFragment;
mListener = importanceListener;
mListener = dependentFieldListener;
}
@Override
@@ -121,7 +121,7 @@ public class SoundPreferenceController extends NotificationPreferenceController
}
// the importance hasn't changed, but the importance description might as a result of
// user's selection.
mListener.onImportanceChanged();
mListener.onFieldValueChanged();
return true;
}
return false;

View File

@@ -68,7 +68,7 @@ public class AllowSoundPreferenceControllerTest {
private PreferenceScreen mScreen;
@Mock
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
private AllowSoundPreferenceController mController;
@@ -80,7 +80,7 @@ public class AllowSoundPreferenceControllerTest {
shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
mContext = RuntimeEnvironment.application;
mController =
spy(new AllowSoundPreferenceController(mContext, mImportanceListener, mBackend));
spy(new AllowSoundPreferenceController(mContext, mDependentFieldListener, mBackend));
}
@Test
@@ -213,7 +213,7 @@ public class AllowSoundPreferenceControllerTest {
mController.onPreferenceChange(pref, true);
assertEquals(IMPORTANCE_UNSPECIFIED, mController.mChannel.getImportance());
verify(mImportanceListener, times(1)).onImportanceChanged();
verify(mDependentFieldListener, times(1)).onFieldValueChanged();
}
@Test
@@ -232,6 +232,6 @@ public class AllowSoundPreferenceControllerTest {
verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
assertEquals(IMPORTANCE_LOW, mController.mChannel.getImportance());
verify(mImportanceListener, times(1)).onImportanceChanged();
verify(mDependentFieldListener, times(1)).onFieldValueChanged();
}
}

View File

@@ -69,7 +69,7 @@ public class BlockPreferenceControllerTest {
private UserManager mUm;
@Mock
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
private BlockPreferenceController mController;
@Mock
@@ -83,7 +83,7 @@ public class BlockPreferenceControllerTest {
shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
mContext = RuntimeEnvironment.application;
mController = spy(new BlockPreferenceController(mContext, mImportanceListener, mBackend));
mController = spy(new BlockPreferenceController(mContext, mDependentFieldListener, mBackend));
mSwitch = new SwitchBar(mContext);
when(mPreference.findViewById(R.id.switch_bar)).thenReturn(mSwitch);
}

View File

@@ -19,12 +19,14 @@ 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 android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
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.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;
@@ -34,6 +36,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.UserManager;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -64,6 +67,8 @@ public class ConversationImportantPreferenceControllerTest {
private UserManager mUm;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
@Mock
private NotificationSettings.DependentFieldListener mDependentFieldListener;
private ConversationImportantPreferenceController mController;
@@ -74,7 +79,8 @@ public class ConversationImportantPreferenceControllerTest {
shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
mContext = RuntimeEnvironment.application;
mController = spy(new ConversationImportantPreferenceController(mContext, mBackend));
mController = spy(new ConversationImportantPreferenceController(
mContext, mBackend, mDependentFieldListener));
}
@Test
@@ -133,9 +139,12 @@ public class ConversationImportantPreferenceControllerTest {
@Test
public void testOnPreferenceChange_on() {
Settings.Secure.putInt(mContext.getContentResolver(),
BUBBLE_IMPORTANT_CONVERSATIONS, 0);
NotificationChannel channel =
new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_DEFAULT);
channel.setImportantConversation(false);
channel.setAllowBubbles(false);
mController.onResume(new NotificationBackend.AppRow(), channel, null, null, null, null);
RestrictedSwitchPreference pref =
@@ -145,14 +154,41 @@ public class ConversationImportantPreferenceControllerTest {
mController.onPreferenceChange(pref, true);
assertTrue(channel.isImportantConversation());
assertFalse(channel.canBubble());
verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
verify(mDependentFieldListener, never()).onFieldValueChanged();
}
@Test
public void testOnPreferenceChange_on_bubble() {
Settings.Secure.putInt(mContext.getContentResolver(),
BUBBLE_IMPORTANT_CONVERSATIONS, 1);
NotificationChannel channel =
new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_DEFAULT);
channel.setImportantConversation(false);
channel.setAllowBubbles(false);
mController.onResume(new NotificationBackend.AppRow(), channel, null, null, null, null);
RestrictedSwitchPreference pref =
new RestrictedSwitchPreference(RuntimeEnvironment.application);
mController.updateState(pref);
mController.onPreferenceChange(pref, true);
assertTrue(channel.isImportantConversation());
assertTrue(channel.canBubble());
verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
verify(mDependentFieldListener).onFieldValueChanged();
}
@Test
public void testOnPreferenceChange_off() {
Settings.Secure.putInt(mContext.getContentResolver(),
BUBBLE_IMPORTANT_CONVERSATIONS, 1);
NotificationChannel channel =
new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
channel.setImportantConversation(true);
channel.setAllowBubbles(false);
mController.onResume(new NotificationBackend.AppRow(), channel, null, null, null, null);
RestrictedSwitchPreference pref =
@@ -164,6 +200,8 @@ public class ConversationImportantPreferenceControllerTest {
mController.onPreferenceChange(pref, false);
assertFalse(channel.isImportantConversation());
assertFalse(channel.canBubble());
verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
verify(mDependentFieldListener, never()).onFieldValueChanged();
}
}

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";
}
}
}

View File

@@ -62,7 +62,7 @@ public class DefaultImportancePreferenceControllerTest {
@Mock
private NotificationBackend mBackend;
@Mock
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
@Mock
private UserManager mUm;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -78,7 +78,7 @@ public class DefaultImportancePreferenceControllerTest {
shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
mContext = RuntimeEnvironment.application;
mController = spy(new DefaultImportancePreferenceController(
mContext, mImportanceListener, mBackend));
mContext, mDependentFieldListener, mBackend));
}
@Test
@@ -224,7 +224,7 @@ public class DefaultImportancePreferenceControllerTest {
mController.onPreferenceChange(pref, false);
assertEquals(IMPORTANCE_LOW, channel.getImportance());
verify(mImportanceListener, times(1)).onImportanceChanged();
verify(mDependentFieldListener, times(1)).onFieldValueChanged();
}
@Test
@@ -241,6 +241,6 @@ public class DefaultImportancePreferenceControllerTest {
mController.onPreferenceChange(pref, true);
assertEquals(IMPORTANCE_DEFAULT, channel.getImportance());
verify(mImportanceListener, times(1)).onImportanceChanged();
verify(mDependentFieldListener, times(1)).onFieldValueChanged();
}
}

View File

@@ -61,7 +61,7 @@ public class HighImportancePreferenceControllerTest {
@Mock
private NotificationBackend mBackend;
@Mock
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
@Mock
private UserManager mUm;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -77,7 +77,7 @@ public class HighImportancePreferenceControllerTest {
shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
mContext = RuntimeEnvironment.application;
mController = spy(new HighImportancePreferenceController(
mContext, mImportanceListener, mBackend));
mContext, mDependentFieldListener, mBackend));
}
@Test
@@ -223,6 +223,6 @@ public class HighImportancePreferenceControllerTest {
mController.onPreferenceChange(pref, false);
assertEquals(IMPORTANCE_DEFAULT, channel.getImportance());
verify(mImportanceListener, times(1)).onImportanceChanged();
verify(mDependentFieldListener, times(1)).onFieldValueChanged();
}
}

View File

@@ -67,7 +67,7 @@ public class ImportancePreferenceControllerTest {
@Mock
private NotificationBackend mBackend;
@Mock
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
@Mock
private UserManager mUm;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -83,7 +83,7 @@ public class ImportancePreferenceControllerTest {
shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
mContext = RuntimeEnvironment.application;
mController = spy(new ImportancePreferenceController(
mContext, mImportanceListener, mBackend));
mContext, mDependentFieldListener, mBackend));
}
@Test

View File

@@ -0,0 +1,140 @@
/*
* 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.provider.Settings.Global.NOTIFICATION_BUBBLES;
import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
import static com.android.settings.notification.app.ImportantConversationBubblePreferenceController.OFF;
import static com.android.settings.notification.app.ImportantConversationBubblePreferenceController.ON;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
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;
@RunWith(RobolectricTestRunner.class)
public class ImportantConversationBubblePreferenceControllerTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
private ImportantConversationBubblePreferenceController mController;
@Mock
private TwoStatePreference mPreference;
private static final String KEY = "important_bubble";
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new ImportantConversationBubblePreferenceController(mContext, KEY);
when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
}
@Test
public void testGetAvailabilityStatus_globallyOn() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void testGetAvailabilityStatus_globallyOff() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
}
@Test
public void updateState_preferenceSetCheckedWhenSettingIsOn() {
Settings.Secure.putInt(mContext.getContentResolver(), BUBBLE_IMPORTANT_CONVERSATIONS, ON);
mController.updateState(mPreference);
verify(mPreference).setChecked(true);
}
@Test
public void updateState_preferenceSetUncheckedWhenSettingIsOff() {
Settings.Secure.putInt(mContext.getContentResolver(), BUBBLE_IMPORTANT_CONVERSATIONS, OFF);
mController.updateState(mPreference);
verify(mPreference).setChecked(false);
}
@Test
public void isChecked_settingIsOff_shouldReturnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(), BUBBLE_IMPORTANT_CONVERSATIONS, OFF);
assertThat(mController.isChecked()).isFalse();
}
@Test
public void isChecked_settingIsOn_shouldReturnTrue() {
Settings.Secure.putInt(mContext.getContentResolver(), BUBBLE_IMPORTANT_CONVERSATIONS, ON);
assertThat(mController.isChecked()).isTrue();
}
@Test
public void setChecked_setFalse_disablesSetting() {
Settings.Secure.putInt(mContext.getContentResolver(), BUBBLE_IMPORTANT_CONVERSATIONS, ON);
mController.setChecked(false);
int updatedValue = Settings.Secure.getInt(mContext.getContentResolver(),
BUBBLE_IMPORTANT_CONVERSATIONS, -1);
assertThat(updatedValue).isEqualTo(OFF);
}
@Test
public void setChecked_setTrue_enablesSetting() {
Settings.Secure.putInt(mContext.getContentResolver(), BUBBLE_IMPORTANT_CONVERSATIONS, OFF);
mController.setChecked(true);
int updatedValue = Settings.Secure.getInt(mContext.getContentResolver(),
BUBBLE_IMPORTANT_CONVERSATIONS, -1);
assertThat(updatedValue).isEqualTo(ON);
}
@Test
public void isSliceable_returnsFalse() {
assertThat(mController.isSliceable()).isFalse();
}
}

View File

@@ -61,7 +61,7 @@ public class MinImportancePreferenceControllerTest {
@Mock
private NotificationBackend mBackend;
@Mock
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
@Mock
private UserManager mUm;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -77,7 +77,7 @@ public class MinImportancePreferenceControllerTest {
shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
mContext = RuntimeEnvironment.application;
mController = spy(new MinImportancePreferenceController(
mContext, mImportanceListener, mBackend));
mContext, mDependentFieldListener, mBackend));
}
@Test
@@ -223,6 +223,6 @@ public class MinImportancePreferenceControllerTest {
mController.onPreferenceChange(pref, true);
assertEquals(IMPORTANCE_MIN, channel.getImportance());
verify(mImportanceListener, times(1)).onImportanceChanged();
verify(mDependentFieldListener, times(1)).onFieldValueChanged();
}
}

View File

@@ -80,7 +80,7 @@ public class SoundPreferenceControllerTest {
@Mock
private SettingsPreferenceFragment mFragment;
@Mock
private NotificationSettings.ImportanceListener mImportanceListener;
private NotificationSettings.DependentFieldListener mDependentFieldListener;
private SoundPreferenceController mController;
@@ -92,7 +92,7 @@ public class SoundPreferenceControllerTest {
shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
mContext = RuntimeEnvironment.application;
mController = spy(new SoundPreferenceController(
mContext, mFragment, mImportanceListener, mBackend));
mContext, mFragment, mDependentFieldListener, mBackend));
}
@Test
@@ -303,7 +303,7 @@ public class SoundPreferenceControllerTest {
mController.onActivityResult(SoundPreferenceController.CODE, 1, new Intent("hi"));
verify(pref, times(1)).onActivityResult(anyInt(), anyInt(), any());
verify(mImportanceListener, times(1)).onImportanceChanged();
verify(mDependentFieldListener, times(1)).onFieldValueChanged();
}
@Test