Add list of selected/excluded convos to bubble settings

* Some changes to extend AppConversationListPrefController
* When a user removes something from the selected or
  excluded list that resets the channel setting

Bug: 148619540
Test: make -j40 RunSettingsRoboTests ROBOTEST_FILTER="Bubble"
Change-Id: I7a9ed7b70208dbdefca6c3c09d34d55c65f06408
This commit is contained in:
Mady Mellor
2020-04-17 11:24:50 -07:00
parent dcc79e9942
commit a27918494f
10 changed files with 472 additions and 35 deletions

View File

@@ -0,0 +1,25 @@
<?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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@@ -0,0 +1,24 @@
<?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.
-->
<ImageButton
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/button"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground"
android:scaleType="centerInside"
android:tint="?android:attr/textColorSecondary"
android:src="@drawable/ic_delete_x"/>

View File

@@ -27,4 +27,12 @@
android:title="@string/notification_bubbles_title" android:title="@string/notification_bubbles_title"
settings:allowDividerBelow="false"/> settings:allowDividerBelow="false"/>
<!-- Selected or excluded conversations get added here -->
<PreferenceCategory
android:title="@string/bubble_app_setting_selected_conversation_title"
android:key="bubble_conversations"
android:visibility="gone"
settings:allowDividerAbove="false"
settings:allowDividerBelow="false" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import static android.app.NotificationChannel.DEFAULT_ALLOW_BUBBLE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import android.annotation.Nullable;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
import android.service.notification.ConversationChannelWrapper;
import android.view.View;
import android.widget.ImageView;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.notification.app.AppConversationListPreferenceController;
import com.android.settingslib.RestrictedLockUtils;
import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import java.util.stream.Collectors;
/**
* Displays a list of conversations that have either been selected or excluded from bubbling.
*/
public class AppBubbleListPreferenceController extends AppConversationListPreferenceController {
private static final String KEY = "bubble_conversations";
public AppBubbleListPreferenceController(Context context, NotificationBackend backend) {
super(context, backend);
}
@Override
public void onResume(NotificationBackend.AppRow appRow,
@Nullable NotificationChannel channel, @Nullable NotificationChannelGroup group,
Drawable conversationDrawable,
ShortcutInfo conversationInfo,
RestrictedLockUtils.EnforcedAdmin admin) {
super.onResume(appRow, channel, group, conversationDrawable, conversationInfo, admin);
// In case something changed in the foreground (e.g. via bubble button on notification)
loadConversationsAndPopulate();
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public boolean isAvailable() {
if (!super.isAvailable()) {
return false;
}
if (mAppRow.bubblePreference == BUBBLE_PREFERENCE_NONE) {
return false;
}
return true;
}
@VisibleForTesting
@Override
public List<ConversationChannelWrapper> filterAndSortConversations(
List<ConversationChannelWrapper> conversations) {
return conversations.stream()
.sorted(mConversationComparator)
.filter((c) -> {
if (mAppRow.bubblePreference == BUBBLE_PREFERENCE_SELECTED) {
return c.getNotificationChannel().canBubble();
} else if (mAppRow.bubblePreference == BUBBLE_PREFERENCE_ALL) {
return c.getNotificationChannel().getAllowBubbles() == 0;
}
return false;
})
.collect(Collectors.toList());
}
@Override
protected int getTitleResId() {
// TODO: possible to left align like mocks?
return mAppRow.bubblePreference == BUBBLE_PREFERENCE_SELECTED
? R.string.bubble_app_setting_selected_conversation_title
: R.string.bubble_app_setting_excluded_conversation_title;
}
@VisibleForTesting
@Override
public Preference createConversationPref(final ConversationChannelWrapper conversation) {
final ConversationPreference pref = new ConversationPreference(mContext);
populateConversationPreference(conversation, pref);
pref.setOnClickListener((v) -> {
conversation.getNotificationChannel().setAllowBubbles(DEFAULT_ALLOW_BUBBLE);
mBackend.updateChannel(mAppRow.pkg, mAppRow.uid, conversation.getNotificationChannel());
mPreference.removePreference(pref);
if (mPreference.getPreferenceCount() == 0) {
mPreference.setVisible(false);
}
});
return pref;
}
/** Simple preference with a 'x' button at the end. */
@VisibleForTesting
public static class ConversationPreference extends Preference implements View.OnClickListener {
View.OnClickListener mOnClickListener;
ConversationPreference(Context context) {
super(context);
setWidgetLayoutResource(R.layout.bubble_conversation_remove_button);
}
@Override
public void onBindViewHolder(final PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
ImageView view = holder.itemView.findViewById(R.id.button);
view.setOnClickListener(mOnClickListener);
}
public void setOnClickListener(View.OnClickListener listener) {
mOnClickListener = listener;
}
@Override
public void onClick(View v) {
if (mOnClickListener != null) {
mOnClickListener.onClick(v);
}
}
}
}

View File

@@ -22,6 +22,7 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.notification.AppBubbleListPreferenceController;
import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.NotificationBackend;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
@@ -56,18 +57,20 @@ public class AppBubbleNotificationSettings extends NotificationSettings implemen
@Override @Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
mControllers = getPreferenceControllers(context, this); mControllers = getPreferenceControllers(context, this, mDependentFieldListener);
return new ArrayList<>(mControllers); return new ArrayList<>(mControllers);
} }
protected static List<NotificationPreferenceController> getPreferenceControllers( protected static List<NotificationPreferenceController> getPreferenceControllers(
Context context, AppBubbleNotificationSettings fragment) { Context context, AppBubbleNotificationSettings fragment,
DependentFieldListener listener) {
List<NotificationPreferenceController> controllers = new ArrayList<>(); List<NotificationPreferenceController> controllers = new ArrayList<>();
controllers.add(new HeaderPreferenceController(context, fragment)); controllers.add(new HeaderPreferenceController(context, fragment));
controllers.add(new BubblePreferenceController(context, fragment != null controllers.add(new BubblePreferenceController(context, fragment != null
? fragment.getChildFragmentManager() ? fragment.getChildFragmentManager()
: null, : null,
new NotificationBackend(), true /* isAppPage */)); new NotificationBackend(), true /* isAppPage */, listener));
controllers.add(new AppBubbleListPreferenceController(context, new NotificationBackend()));
return controllers; return controllers;
} }
@@ -114,7 +117,7 @@ public class AppBubbleNotificationSettings extends NotificationSettings implemen
public List<AbstractPreferenceController> createPreferenceControllers(Context public List<AbstractPreferenceController> createPreferenceControllers(Context
context) { context) {
return new ArrayList<>(AppBubbleNotificationSettings.getPreferenceControllers( return new ArrayList<>(AppBubbleNotificationSettings.getPreferenceControllers(
context, null)); context, null, null));
} }
}; };
} }

View File

@@ -19,6 +19,7 @@ package com.android.settings.notification.app;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
@@ -33,6 +34,7 @@ import com.android.settings.applications.AppInfoBase;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.NotificationBackend;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
@@ -42,8 +44,8 @@ public class AppConversationListPreferenceController extends NotificationPrefere
private static final String KEY = "conversations"; private static final String KEY = "conversations";
public static final String ARG_FROM_SETTINGS = "fromSettings"; public static final String ARG_FROM_SETTINGS = "fromSettings";
private List<ConversationChannelWrapper> mConversations; protected List<ConversationChannelWrapper> mConversations = new ArrayList<>();
private PreferenceCategory mPreference; protected PreferenceCategory mPreference;
private boolean mHasSentMsg; private boolean mHasSentMsg;
public AppConversationListPreferenceController(Context context, NotificationBackend backend) { public AppConversationListPreferenceController(Context context, NotificationBackend backend) {
@@ -75,13 +77,23 @@ public class AppConversationListPreferenceController extends NotificationPrefere
@Override @Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
mPreference = (PreferenceCategory) preference; mPreference = (PreferenceCategory) preference;
loadConversationsAndPopulate();
}
protected void loadConversationsAndPopulate() {
if (mAppRow == null) {
return;
}
// Load channel settings // Load channel settings
new AsyncTask<Void, Void, Void>() { new AsyncTask<Void, Void, Void>() {
@Override @Override
protected Void doInBackground(Void... unused) { protected Void doInBackground(Void... unused) {
mHasSentMsg = mBackend.hasSentMessage(mAppRow.pkg, mAppRow.uid); mHasSentMsg = mBackend.hasSentMessage(mAppRow.pkg, mAppRow.uid);
mConversations = mBackend.getConversations(mAppRow.pkg, mAppRow.uid).getList(); ParceledListSlice<ConversationChannelWrapper> list =
Collections.sort(mConversations, mConversationComparator); mBackend.getConversations(mAppRow.pkg, mAppRow.uid);
if (list != null) {
mConversations = filterAndSortConversations(list.getList());
}
return null; return null;
} }
@@ -95,11 +107,22 @@ public class AppConversationListPreferenceController extends NotificationPrefere
}.execute(); }.execute();
} }
protected List<ConversationChannelWrapper> filterAndSortConversations(
List<ConversationChannelWrapper> conversations) {
Collections.sort(conversations, mConversationComparator);
return conversations;
}
protected int getTitleResId() {
return R.string.conversations_category_title;
}
private void populateList() { private void populateList() {
if (mPreference == null) {
return;
}
// TODO: if preference has children, compare with newly loaded list // TODO: if preference has children, compare with newly loaded list
mPreference.removeAll(); mPreference.removeAll();
mPreference.setTitle(R.string.conversations_category_title);
if (mConversations.isEmpty()) { if (mConversations.isEmpty()) {
if (mHasSentMsg) { if (mHasSentMsg) {
mPreference.setVisible(true); mPreference.setVisible(true);
@@ -112,6 +135,7 @@ public class AppConversationListPreferenceController extends NotificationPrefere
} }
} else { } else {
mPreference.setVisible(true); mPreference.setVisible(true);
mPreference.setTitle(getTitleResId());
populateConversations(); populateConversations();
} }
} }
@@ -127,6 +151,12 @@ public class AppConversationListPreferenceController extends NotificationPrefere
protected Preference createConversationPref(final ConversationChannelWrapper conversation) { protected Preference createConversationPref(final ConversationChannelWrapper conversation) {
Preference pref = new Preference(mContext); Preference pref = new Preference(mContext);
populateConversationPreference(conversation, pref);
return pref;
}
protected void populateConversationPreference(final ConversationChannelWrapper conversation,
final Preference pref) {
ShortcutInfo si = conversation.getShortcutInfo(); ShortcutInfo si = conversation.getShortcutInfo();
pref.setTitle(si != null pref.setTitle(si != null
@@ -157,7 +187,6 @@ public class AppConversationListPreferenceController extends NotificationPrefere
.setTitleText(pref.getTitle()) .setTitleText(pref.getTitle())
.setSourceMetricsCategory(SettingsEnums.NOTIFICATION_APP_NOTIFICATION) .setSourceMetricsCategory(SettingsEnums.NOTIFICATION_APP_NOTIFICATION)
.toIntent()); .toIntent());
return pref;
} }
protected Comparator<ConversationChannelWrapper> mConversationComparator = protected Comparator<ConversationChannelWrapper> mConversationComparator =

View File

@@ -49,12 +49,15 @@ public class BubblePreferenceController extends NotificationPreferenceController
private boolean mIsAppPage; private boolean mIsAppPage;
private boolean mHasSentInvalidMsg; private boolean mHasSentInvalidMsg;
private int mNumConversations; private int mNumConversations;
private NotificationSettings.DependentFieldListener mListener;
public BubblePreferenceController(Context context, @Nullable FragmentManager fragmentManager, public BubblePreferenceController(Context context, @Nullable FragmentManager fragmentManager,
NotificationBackend backend, boolean isAppPage) { NotificationBackend backend, boolean isAppPage,
@Nullable NotificationSettings.DependentFieldListener listener) {
super(context, backend); super(context, backend);
mFragmentManager = fragmentManager; mFragmentManager = fragmentManager;
mIsAppPage = isAppPage; mIsAppPage = isAppPage;
mListener = listener;
} }
@Override @Override
@@ -128,6 +131,9 @@ public class BubblePreferenceController extends NotificationPreferenceController
mBackend.setAllowBubbles(mAppRow.pkg, mAppRow.uid, value); mBackend.setAllowBubbles(mAppRow.pkg, mAppRow.uid, value);
} }
} }
if (mListener != null) {
mListener.onFieldValueChanged();
}
} }
return true; return true;
} }

View File

@@ -92,7 +92,7 @@ public class ConversationNotificationSettings extends NotificationSettings {
mControllers.add(new BadgePreferenceController(context, mBackend)); mControllers.add(new BadgePreferenceController(context, mBackend));
mControllers.add(new NotificationsOffPreferenceController(context)); mControllers.add(new NotificationsOffPreferenceController(context));
mControllers.add(new BubblePreferenceController(context, getChildFragmentManager(), mControllers.add(new BubblePreferenceController(context, getChildFragmentManager(),
mBackend, false /* isAppPage */)); mBackend, false /* isAppPage */, null /* dependentFieldListener */));
mControllers.add(new ConversationDemotePreferenceController(context, this, mBackend)); mControllers.add(new ConversationDemotePreferenceController(context, this, mBackend));
mControllers.add(new BubbleCategoryPreferenceController(context)); mControllers.add(new BubbleCategoryPreferenceController(context));
mControllers.add(new BubbleLinkPreferenceController(context)); mControllers.add(new BubbleLinkPreferenceController(context));

View File

@@ -0,0 +1,173 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification.app;
import static android.app.NotificationChannel.DEFAULT_ALLOW_BUBBLE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.os.UserManager;
import android.service.notification.ConversationChannelWrapper;
import androidx.preference.PreferenceCategory;
import com.android.settings.notification.AppBubbleListPreferenceController;
import com.android.settings.notification.NotificationBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class AppBubbleListPreferenceControllerTest {
private Context mContext;
@Mock
private NotificationBackend mBackend;
@Mock
private NotificationManager mNm;
@Mock
private UserManager mUm;
private AppBubbleListPreferenceController mController;
private ParceledListSlice<ConversationChannelWrapper> mConvoList;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ShadowApplication shadowApplication = ShadowApplication.getInstance();
shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
mContext = RuntimeEnvironment.application;
List<ConversationChannelWrapper> convoList = new ArrayList<>();
convoList.add(getConvo(-1, "default"));
convoList.add(getConvo(1, "selected"));
convoList.add(getConvo(0, "excluded"));
mConvoList = new ParceledListSlice<>(convoList);
when(mBackend.getConversations(anyString(), anyInt())).thenReturn(mConvoList);
mController = new AppBubbleListPreferenceController(mContext, mBackend);
}
ConversationChannelWrapper getConvo(int bubbleChannelPref, String channelId) {
ConversationChannelWrapper ccw = new ConversationChannelWrapper();
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getId()).thenReturn(channelId);
when(channel.getAllowBubbles()).thenReturn(bubbleChannelPref);
when(channel.canBubble()).thenReturn(bubbleChannelPref == 1);
ccw.setNotificationChannel(channel);
ccw.setPkg("pkg");
ccw.setUid(1);
ccw.setShortcutInfo(mock(ShortcutInfo.class));
return ccw;
}
@Test
public void isAvailable_BUBBLE_PREFERENCE_NONE_false() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
mController.onResume(appRow, null, null, null, null, null);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_BUBBLE_PREFERENCE_SELECTED_true() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED;
mController.onResume(appRow, null, null, null, null, null);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_BUBBLE_PREFERENCE_ALL_true() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
mController.onResume(appRow, null, null, null, null, null);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void filterAndSortConversations_BUBBLE_PREFERENCE_SELECTED_filtersAllowedBubbles() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED;
mController.onResume(appRow, null, null, null, null, null);
List<ConversationChannelWrapper> result =
mController.filterAndSortConversations(mConvoList.getList());
assertThat(result.size()).isEqualTo(1);
assertThat(result.get(0).getNotificationChannel().getId())
.isEqualTo("selected");
}
@Test
public void filterAndSortConversations_BUBBLE_PREFERENCE_ALL_filtersExcludedBubbles() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
mController.onResume(appRow, null, null, null, null, null);
List<ConversationChannelWrapper> result =
mController.filterAndSortConversations(mConvoList.getList());
assertThat(result.size()).isEqualTo(1);
assertThat(result.get(0).getNotificationChannel().getId())
.isEqualTo("excluded");
}
@Test
public void clickConversationPref_updatesChannel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
appRow.pkg = "PKG";
mController.onResume(appRow, null, null, null, null, null);
mController.mPreference = new PreferenceCategory(mContext);
ConversationChannelWrapper ccw = mConvoList.getList().get(0);
AppBubbleListPreferenceController.ConversationPreference pref =
(AppBubbleListPreferenceController.ConversationPreference)
mController.createConversationPref(ccw);
pref.onClick(null);
verify(ccw.getNotificationChannel()).setAllowBubbles(DEFAULT_ALLOW_BUBBLE);
verify(mBackend).updateChannel(anyString(), anyInt(), any(NotificationChannel.class));
}
}

View File

@@ -80,6 +80,8 @@ public class BubblePreferenceControllerTest {
private PreferenceScreen mScreen; private PreferenceScreen mScreen;
@Mock @Mock
private FragmentManager mFragmentManager; private FragmentManager mFragmentManager;
@Mock
private NotificationSettings.DependentFieldListener mListener;
private BubblePreferenceController mController; private BubblePreferenceController mController;
private BubblePreferenceController mAppPageController; private BubblePreferenceController mAppPageController;
@@ -93,9 +95,9 @@ public class BubblePreferenceControllerTest {
mContext = RuntimeEnvironment.application; mContext = RuntimeEnvironment.application;
when(mFragmentManager.beginTransaction()).thenReturn(mock(FragmentTransaction.class)); when(mFragmentManager.beginTransaction()).thenReturn(mock(FragmentTransaction.class));
mController = spy(new BubblePreferenceController(mContext, mFragmentManager, mBackend, mController = spy(new BubblePreferenceController(mContext, mFragmentManager, mBackend,
false /* isAppPage */)); false /* isAppPage */, mListener));
mAppPageController = spy(new BubblePreferenceController(mContext, mFragmentManager, mAppPageController = spy(new BubblePreferenceController(mContext, mFragmentManager,
mBackend, true /* isAppPage */)); mBackend, true /* isAppPage */, mListener));
} }
@Test @Test
@@ -106,7 +108,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testIsAvailable_notIfAppBlocked() { public void isAvailable_notIfAppBlocked() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.banned = true; appRow.banned = true;
@@ -115,7 +117,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testIsAvailable_notIfChannelBlocked() { public void isAvailable_notIfChannelBlocked() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
@@ -125,7 +127,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testIsAvailable_channel_yesIfAppOff() { public void isAvailable_channel_yesIfAppOff() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_NONE; appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
@@ -137,7 +139,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testIsNotAvailable_ifOffGlobally_app() { public void isNotAvailable_ifOffGlobally_app() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
mController.onResume(appRow, null, null, null, null, null); mController.onResume(appRow, null, null, null, null, null);
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.putInt(mContext.getContentResolver(),
@@ -147,7 +149,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testIsAvailable_notIfOffGlobally_channel() { public void isAvailable_notIfOffGlobally_channel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH); when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
@@ -159,7 +161,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testIsAvailable_app_evenIfOffGlobally() { public void isAvailable_app_evenIfOffGlobally() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
mAppPageController.onResume(appRow, null, null, null, null, null); mAppPageController.onResume(appRow, null, null, null, null, null);
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.putInt(mContext.getContentResolver(),
@@ -169,7 +171,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testIsAvailable_app() { public void isAvailable_app() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
mController.onResume(appRow, null, null, null, null, null); mController.onResume(appRow, null, null, null, null, null);
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
@@ -178,7 +180,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testIsAvailable_defaultChannel() { public void isAvailable_defaultChannel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
@@ -191,7 +193,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testIsAvailable_channel() { public void isAvailable_channel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
@@ -203,7 +205,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testUpdateState_disabledByAdmin() { public void updateState_disabledByAdmin() {
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getId()).thenReturn("something"); when(channel.getId()).thenReturn("something");
mController.onResume(new NotificationBackend.AppRow(), channel, null, mController.onResume(new NotificationBackend.AppRow(), channel, null,
@@ -216,7 +218,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testUpdateState_app_disabledByAdmin() { public void updateState_app_disabledByAdmin() {
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getId()).thenReturn("something"); when(channel.getId()).thenReturn("something");
mAppPageController.onResume(new NotificationBackend.AppRow(), channel, null, mAppPageController.onResume(new NotificationBackend.AppRow(), channel, null,
@@ -229,7 +231,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testUpdateState_channel_channelNotBlockable() { public void updateState_channel_channelNotBlockable() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
@@ -243,7 +245,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testUpdateState_channel() { public void updateState_channel() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
@@ -263,7 +265,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testUpdateState_app() { public void updateState_app() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.label = "App!"; appRow.label = "App!";
@@ -288,7 +290,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testUpdateState_app_offGlobally() { public void updateState_app_offGlobally() {
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.putInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF); NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
@@ -302,7 +304,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testOnPreferenceChange_on_channel() { public void onPreferenceChange_on_channel() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED; appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED;
@@ -321,7 +323,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testOnPreferenceChange_off_channel() { public void onPreferenceChange_off_channel() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED; appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED;
@@ -341,7 +343,7 @@ public class BubblePreferenceControllerTest {
@Test @Test
public void testOnPreferenceChange_app_all() { public void onPreferenceChange_app_all() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_NONE; appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
@@ -379,7 +381,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testOnPreferenceChange_app_selected() { public void onPreferenceChange_app_selected() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
@@ -397,7 +399,7 @@ public class BubblePreferenceControllerTest {
} }
@Test @Test
public void testOnPreferenceChange_app_none() { public void onPreferenceChange_app_none() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL; appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
@@ -413,4 +415,17 @@ public class BubblePreferenceControllerTest {
assertEquals(BUBBLE_PREFERENCE_NONE, appRow.bubblePreference); assertEquals(BUBBLE_PREFERENCE_NONE, appRow.bubblePreference);
verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_NONE)); verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_NONE));
} }
@Test
public void onPreferenceChange_dependentFieldListenerCalled() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
mAppPageController.onResume(appRow, null, null, null, null, null);
BubblePreference pref = new BubblePreference(mContext);
mAppPageController.onPreferenceChange(pref, BUBBLE_PREFERENCE_NONE);
verify(mListener, times(1)).onFieldValueChanged();
}
} }