Merge "Allow conversations to be demoted out of the conversation space"
This commit is contained in:
committed by
Android (Google) Code Review
commit
3bd8c209b1
@@ -105,6 +105,7 @@ public final class NotificationChannel implements Parcelable {
|
||||
private static final String ATT_ORIG_IMP = "orig_imp";
|
||||
private static final String ATT_PARENT_CHANNEL = "parent";
|
||||
private static final String ATT_CONVERSATION_ID = "conv_id";
|
||||
private static final String ATT_DEMOTE = "dem";
|
||||
private static final String DELIMITER = ",";
|
||||
|
||||
/**
|
||||
@@ -194,6 +195,7 @@ public final class NotificationChannel implements Parcelable {
|
||||
private boolean mImportanceLockedDefaultApp;
|
||||
private String mParentId = null;
|
||||
private String mConversationId = null;
|
||||
private boolean mDemoted = false;
|
||||
|
||||
/**
|
||||
* Creates a notification channel.
|
||||
@@ -260,6 +262,7 @@ public final class NotificationChannel implements Parcelable {
|
||||
mOriginalImportance = in.readInt();
|
||||
mParentId = in.readString();
|
||||
mConversationId = in.readString();
|
||||
mDemoted = in.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -317,6 +320,7 @@ public final class NotificationChannel implements Parcelable {
|
||||
dest.writeInt(mOriginalImportance);
|
||||
dest.writeString(mParentId);
|
||||
dest.writeString(mConversationId);
|
||||
dest.writeBoolean(mDemoted);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,8 +390,6 @@ public final class NotificationChannel implements Parcelable {
|
||||
return input;
|
||||
}
|
||||
|
||||
// Modifiable by apps on channel creation.
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -395,6 +397,8 @@ public final class NotificationChannel implements Parcelable {
|
||||
mId = id;
|
||||
}
|
||||
|
||||
// Modifiable by apps on channel creation.
|
||||
|
||||
/**
|
||||
* Sets what group this channel belongs to.
|
||||
*
|
||||
@@ -766,6 +770,20 @@ public final class NotificationChannel implements Parcelable {
|
||||
mOriginalImportance = importance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void setDemoted(boolean demoted) {
|
||||
mDemoted = demoted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public boolean isDemoted() {
|
||||
return mDemoted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the user has chosen the importance of this channel, either to affirm the
|
||||
* initial selection from the app, or changed it to be higher or lower.
|
||||
@@ -829,6 +847,7 @@ public final class NotificationChannel implements Parcelable {
|
||||
setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE));
|
||||
setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL),
|
||||
parser.getAttributeValue(null, ATT_CONVERSATION_ID));
|
||||
setDemoted(safeBool(parser, ATT_DEMOTE, false));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -959,6 +978,9 @@ public final class NotificationChannel implements Parcelable {
|
||||
if (getConversationId() != null) {
|
||||
out.attribute(null, ATT_CONVERSATION_ID, getConversationId());
|
||||
}
|
||||
if (isDemoted()) {
|
||||
out.attribute(null, ATT_DEMOTE, Boolean.toString(isDemoted()));
|
||||
}
|
||||
|
||||
// mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
|
||||
// truth and so aren't written to this xml file
|
||||
@@ -1118,7 +1140,8 @@ public final class NotificationChannel implements Parcelable {
|
||||
&& mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
|
||||
&& mOriginalImportance == that.mOriginalImportance
|
||||
&& Objects.equals(getParentChannelId(), that.getParentChannelId())
|
||||
&& Objects.equals(getConversationId(), that.getConversationId());
|
||||
&& Objects.equals(getConversationId(), that.getConversationId())
|
||||
&& isDemoted() == that.isDemoted();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1129,7 +1152,7 @@ public final class NotificationChannel implements Parcelable {
|
||||
isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
|
||||
getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
|
||||
mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance,
|
||||
mParentId, mConversationId);
|
||||
mParentId, mConversationId, mDemoted);
|
||||
result = 31 * result + Arrays.hashCode(mVibration);
|
||||
return result;
|
||||
}
|
||||
@@ -1176,7 +1199,8 @@ public final class NotificationChannel implements Parcelable {
|
||||
+ ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
|
||||
+ ", mOriginalImp=" + mOriginalImportance
|
||||
+ ", mParent=" + mParentId
|
||||
+ ", mConversationId=" + mConversationId;
|
||||
+ ", mConversationId=" + mConversationId
|
||||
+ ", mDemoted=" + mDemoted;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
|
||||
@@ -61,7 +61,7 @@ public class FeatureFlagUtils {
|
||||
DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "false");
|
||||
DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false");
|
||||
DEFAULT_FLAGS.put("settings_conditionals", "false");
|
||||
DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
|
||||
DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.collection
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager.IMPORTANCE_HIGH
|
||||
import android.app.NotificationManager.IMPORTANCE_MIN
|
||||
import android.service.notification.NotificationListenerService.Ranking
|
||||
@@ -191,9 +192,9 @@ open class NotificationRankingManager @Inject constructor(
|
||||
}
|
||||
|
||||
private fun NotificationEntry.isPeopleNotification() =
|
||||
sbn.isPeopleNotification()
|
||||
private fun StatusBarNotification.isPeopleNotification() =
|
||||
peopleNotificationIdentifier.isPeopleNotification(this)
|
||||
sbn.isPeopleNotification(channel)
|
||||
private fun StatusBarNotification.isPeopleNotification(channel: NotificationChannel) =
|
||||
peopleNotificationIdentifier.isPeopleNotification(this, channel)
|
||||
|
||||
private fun NotificationEntry.isHighPriority() =
|
||||
highPriorityProvider.isHighPriority(this)
|
||||
|
||||
@@ -99,7 +99,8 @@ public class HighPriorityProvider {
|
||||
}
|
||||
|
||||
private boolean isPeopleNotification(NotificationEntry entry) {
|
||||
return mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn());
|
||||
return mPeopleNotificationIdentifier.isPeopleNotification(
|
||||
entry.getSbn(), entry.getChannel());
|
||||
}
|
||||
|
||||
private boolean hasUserSetImportance(NotificationEntry entry) {
|
||||
|
||||
@@ -18,13 +18,14 @@ package com.android.systemui.statusbar.notification.people
|
||||
|
||||
import android.app.Notification
|
||||
import android.content.Context
|
||||
import android.app.NotificationChannel
|
||||
import android.service.notification.StatusBarNotification
|
||||
import android.util.FeatureFlagUtils
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
interface PeopleNotificationIdentifier {
|
||||
fun isPeopleNotification(sbn: StatusBarNotification): Boolean
|
||||
fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel): Boolean
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@@ -33,12 +34,13 @@ class PeopleNotificationIdentifierImpl @Inject constructor(
|
||||
private val context: Context
|
||||
) : PeopleNotificationIdentifier {
|
||||
|
||||
override fun isPeopleNotification(sbn: StatusBarNotification) =
|
||||
(sbn.notification.notificationStyle == Notification.MessagingStyle::class.java &&
|
||||
override fun isPeopleNotification(sbn: StatusBarNotification, channel: NotificationChannel) =
|
||||
((sbn.notification.notificationStyle == Notification.MessagingStyle::class.java &&
|
||||
(sbn.notification.shortcutId != null ||
|
||||
FeatureFlagUtils.isEnabled(
|
||||
context,
|
||||
FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ
|
||||
))) ||
|
||||
personExtractor.isPersonNotification(sbn)
|
||||
personExtractor.isPersonNotification(sbn)) &&
|
||||
!channel.isDemoted
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
|
||||
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
|
||||
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_BUBBLE;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_DEMOTE;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_FAVORITE;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_HOME;
|
||||
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_MUTE;
|
||||
@@ -61,6 +62,7 @@ import android.util.Slog;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
@@ -151,6 +153,11 @@ public class NotificationConversationInfo extends LinearLayout implements
|
||||
closeControls(v, true);
|
||||
};
|
||||
|
||||
private OnClickListener mOnDemoteClick = v -> {
|
||||
mSelectedAction = ACTION_DEMOTE;
|
||||
closeControls(v, true);
|
||||
};
|
||||
|
||||
public NotificationConversationInfo(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
@@ -295,6 +302,8 @@ public class NotificationConversationInfo extends LinearLayout implements
|
||||
mContext.getDrawable(R.drawable.ic_notifications_alert), null, null, null);
|
||||
}
|
||||
|
||||
ImageButton demote = findViewById(R.id.demote);
|
||||
demote.setOnClickListener(mOnDemoteClick);
|
||||
}
|
||||
|
||||
private void bindHeader() {
|
||||
@@ -609,7 +618,7 @@ public class NotificationConversationInfo extends LinearLayout implements
|
||||
}
|
||||
break;
|
||||
case ACTION_DEMOTE:
|
||||
// TODO: when demotion status field exists on notificationchannel
|
||||
mChannelToUpdate.setDemoted(!mChannelToUpdate.isDemoted());
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
@@ -571,7 +571,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
private NotificationEntry createNotification() {
|
||||
Notification.Builder n = new Notification.Builder(mContext, "")
|
||||
Notification.Builder n = new Notification.Builder(mContext, "id")
|
||||
.setSmallIcon(R.drawable.ic_person)
|
||||
.setContentTitle("Title")
|
||||
.setContentText("Text");
|
||||
@@ -582,6 +582,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
|
||||
.setUid(TEST_UID)
|
||||
.setId(mId++)
|
||||
.setNotification(n.build())
|
||||
.setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
|
||||
.setUser(new UserHandle(ActivityManager.getCurrentUser()))
|
||||
.build();
|
||||
}
|
||||
@@ -616,7 +617,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications() {
|
||||
Assert.sMainLooper = TestableLooper.get(this).getLooper();
|
||||
Notification.Builder n = new Notification.Builder(mContext, "")
|
||||
Notification.Builder n = new Notification.Builder(mContext, "di")
|
||||
.setSmallIcon(R.drawable.ic_person)
|
||||
.setContentTitle("Title")
|
||||
.setContentText("Text");
|
||||
@@ -628,6 +629,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
|
||||
.setId(mId++)
|
||||
.setNotification(n.build())
|
||||
.setUser(new UserHandle(ActivityManager.getCurrentUser()))
|
||||
.setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
|
||||
.build();
|
||||
|
||||
mEntryManager.addActiveNotificationForTest(mEntry);
|
||||
|
||||
@@ -60,7 +60,8 @@ public class HighPriorityProviderTest extends SysuiTestCase {
|
||||
final NotificationEntry entry = new NotificationEntryBuilder()
|
||||
.setImportance(IMPORTANCE_HIGH)
|
||||
.build();
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
|
||||
.thenReturn(false);
|
||||
|
||||
// THEN it has high priority
|
||||
assertTrue(mHighPriorityProvider.isHighPriority(entry));
|
||||
@@ -75,7 +76,8 @@ public class HighPriorityProviderTest extends SysuiTestCase {
|
||||
.setNotification(notification)
|
||||
.setImportance(IMPORTANCE_LOW)
|
||||
.build();
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
|
||||
.thenReturn(true);
|
||||
|
||||
// THEN it has high priority
|
||||
assertTrue(mHighPriorityProvider.isHighPriority(entry));
|
||||
@@ -90,7 +92,8 @@ public class HighPriorityProviderTest extends SysuiTestCase {
|
||||
final NotificationEntry entry = new NotificationEntryBuilder()
|
||||
.setNotification(notification)
|
||||
.build();
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
|
||||
.thenReturn(false);
|
||||
|
||||
// THEN it has high priority
|
||||
assertTrue(mHighPriorityProvider.isHighPriority(entry));
|
||||
@@ -106,7 +109,8 @@ public class HighPriorityProviderTest extends SysuiTestCase {
|
||||
.setNotification(notification)
|
||||
.setImportance(IMPORTANCE_LOW)
|
||||
.build();
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
|
||||
.thenReturn(false);
|
||||
|
||||
// THEN it has high priority
|
||||
assertTrue(mHighPriorityProvider.isHighPriority(entry));
|
||||
@@ -122,7 +126,8 @@ public class HighPriorityProviderTest extends SysuiTestCase {
|
||||
.setNotification(notification)
|
||||
.setImportance(IMPORTANCE_MIN)
|
||||
.build();
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(false);
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
|
||||
.thenReturn(false);
|
||||
|
||||
// THEN it does NOT have high priority
|
||||
assertFalse(mHighPriorityProvider.isHighPriority(entry));
|
||||
@@ -144,7 +149,8 @@ public class HighPriorityProviderTest extends SysuiTestCase {
|
||||
.setNotification(notification)
|
||||
.setChannel(channel)
|
||||
.build();
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn())).thenReturn(true);
|
||||
when(mPeopleNotificationIdentifier.isPeopleNotification(entry.getSbn(), entry.getChannel()))
|
||||
.thenReturn(true);
|
||||
|
||||
// THEN it does NOT have high priority
|
||||
assertFalse(mHighPriorityProvider.isHighPriority(entry));
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.statusbar.notification.collection
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager.IMPORTANCE_DEFAULT
|
||||
import android.app.NotificationManager.IMPORTANCE_HIGH
|
||||
import android.app.NotificationManager.IMPORTANCE_LOW
|
||||
@@ -81,6 +82,7 @@ class NotificationRankingManagerTest : SysuiTestCase() {
|
||||
.setNotification(
|
||||
Notification.Builder(mContext, "test")
|
||||
.build())
|
||||
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
|
||||
.setUser(mContext.getUser())
|
||||
.setOverrideGroupKey("")
|
||||
.build()
|
||||
@@ -94,6 +96,7 @@ class NotificationRankingManagerTest : SysuiTestCase() {
|
||||
.setNotification(
|
||||
Notification.Builder(mContext, "test")
|
||||
.build())
|
||||
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
|
||||
.setUser(mContext.getUser())
|
||||
.setOverrideGroupKey("")
|
||||
.build()
|
||||
@@ -116,6 +119,7 @@ class NotificationRankingManagerTest : SysuiTestCase() {
|
||||
.setOpPkg("pkg")
|
||||
.setTag("tag")
|
||||
.setNotification(aN)
|
||||
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
|
||||
.setUser(mContext.getUser())
|
||||
.setOverrideGroupKey("")
|
||||
.build()
|
||||
@@ -130,6 +134,7 @@ class NotificationRankingManagerTest : SysuiTestCase() {
|
||||
.setOpPkg("pkg2")
|
||||
.setTag("tag")
|
||||
.setNotification(bN)
|
||||
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
|
||||
.setUser(mContext.getUser())
|
||||
.setOverrideGroupKey("")
|
||||
.build()
|
||||
@@ -149,6 +154,7 @@ class NotificationRankingManagerTest : SysuiTestCase() {
|
||||
.setTag("tag")
|
||||
.setNotification(notif)
|
||||
.setUser(mContext.user)
|
||||
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
|
||||
.setOverrideGroupKey("")
|
||||
.build()
|
||||
|
||||
@@ -168,6 +174,7 @@ class NotificationRankingManagerTest : SysuiTestCase() {
|
||||
.setTag("tag")
|
||||
.setNotification(notif)
|
||||
.setUser(mContext.user)
|
||||
.setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
|
||||
.setOverrideGroupKey("")
|
||||
.build()
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ import android.util.Slog;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -683,6 +684,34 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
|
||||
assertFalse(captor.getValue().canBypassDnd());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDemote() throws Exception {
|
||||
mNotificationInfo.bindNotification(
|
||||
mShortcutManager,
|
||||
mLauncherApps,
|
||||
mMockPackageManager,
|
||||
mMockINotificationManager,
|
||||
mVisualStabilityManager,
|
||||
TEST_PACKAGE_NAME,
|
||||
mNotificationChannel,
|
||||
mEntry,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true);
|
||||
|
||||
|
||||
ImageButton demote = mNotificationInfo.findViewById(R.id.demote);
|
||||
demote.performClick();
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
ArgumentCaptor<NotificationChannel> captor =
|
||||
ArgumentCaptor.forClass(NotificationChannel.class);
|
||||
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
|
||||
anyString(), anyInt(), captor.capture());
|
||||
assertTrue(captor.getValue().isDemoted());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMute_mute() throws Exception {
|
||||
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
|
||||
|
||||
@@ -227,6 +227,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
|
||||
assertEquals(expected.getLightColor(), actual.getLightColor());
|
||||
assertEquals(expected.getParentChannelId(), actual.getParentChannelId());
|
||||
assertEquals(expected.getConversationId(), actual.getConversationId());
|
||||
assertEquals(expected.isDemoted(), actual.isDemoted());
|
||||
}
|
||||
|
||||
private void compareChannelsParentChild(NotificationChannel parent,
|
||||
@@ -355,6 +356,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
|
||||
channel2.setVibrationPattern(new long[]{100, 67, 145, 156});
|
||||
channel2.setLightColor(Color.BLUE);
|
||||
channel2.setConversationId("id1", "conversation");
|
||||
channel2.setDemoted(true);
|
||||
|
||||
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
|
||||
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg2, true);
|
||||
|
||||
Reference in New Issue
Block a user