From 8582df5cc7515ede0dad38c687788f395677bb5b Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Fri, 24 Apr 2020 18:30:59 -0400 Subject: Add experience for shortcut-less convos - no conversation specific controls - no 'bubble this' button - turn off interim placeholder channels - fix msg tracking - only shortcut-less notifs matter Test: atest Bug: 154814754 Change-Id: Ia04ce496d4e7b2f353c71865f338b5229192c395 --- .../notification/StatusBarNotification.java | 11 +- .../res/layout/partial_conversation_info.xml | 200 +++++++++++ packages/SystemUI/res/values/strings.xml | 3 + .../notification/NotificationChannelHelper.java | 38 +- .../row/ExpandableNotificationRow.java | 1 + .../notification/row/NotificationContentView.java | 6 +- .../row/NotificationConversationInfo.java | 37 +- .../notification/row/NotificationGutsManager.java | 45 ++- .../notification/row/NotificationMenuRow.java | 14 +- .../notification/row/PartialConversationInfo.java | 376 +++++++++++++++++++ .../row/NotificationConversationInfoTest.java | 5 +- .../row/PartialConversationInfoTest.java | 397 +++++++++++++++++++++ .../notification/NotificationChannelExtractor.java | 2 +- .../notification/NotificationManagerService.java | 8 +- .../server/notification/NotificationRecord.java | 4 - .../server/notification/PreferencesHelper.java | 8 +- .../server/notification/ShortcutHelper.java | 6 +- .../NotificationChannelExtractorTest.java | 6 +- .../NotificationManagerServiceTest.java | 35 +- .../notification/NotificationRecordTest.java | 15 +- .../server/notification/PreferencesHelperTest.java | 41 +-- .../server/notification/ShortcutHelperTest.java | 7 +- 22 files changed, 1134 insertions(+), 131 deletions(-) create mode 100644 packages/SystemUI/res/layout/partial_conversation_info.xml create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 5c43f8f829b0..08d990581390 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -486,15 +486,8 @@ public class StatusBarNotification implements Parcelable { /** * @hide */ - public String getShortcutId(Context context) { - String conversationId = getNotification().getShortcutId(); - if (TextUtils.isEmpty(conversationId) - && (Settings.Global.getInt(context.getContentResolver(), - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 0) - && getNotification().getNotificationStyle() == Notification.MessagingStyle.class) { - conversationId = getId() + getTag() + PLACEHOLDER_CONVERSATION_ID; - } - return conversationId; + public String getShortcutId() { + return getNotification().getShortcutId(); } /** diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml new file mode 100644 index 000000000000..2401dfbc2435 --- /dev/null +++ b/packages/SystemUI/res/layout/partial_conversation_info.xml @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 9f0f48233458..43ebb4082650 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1847,6 +1847,9 @@ Priority + + %1$s does not support conversation specific settings + No recent bubbles diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java index ff945d15a4ed..1c2a00ed601a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationChannelHelper.java @@ -26,7 +26,6 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.Slog; -import com.android.systemui.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** @@ -44,32 +43,18 @@ public class NotificationChannelHelper { if (!TextUtils.isEmpty(channel.getConversationId())) { return channel; } - final String conversationId = entry.getSbn().getShortcutId(context); + final String conversationId = entry.getSbn().getShortcutId(); final String pkg = entry.getSbn().getPackageName(); final int appUid = entry.getSbn().getUid(); - if (TextUtils.isEmpty(conversationId) || TextUtils.isEmpty(pkg)) { + if (TextUtils.isEmpty(conversationId) || TextUtils.isEmpty(pkg) + || entry.getRanking().getShortcutInfo() == null) { return channel; } - String name; - if (entry.getRanking().getShortcutInfo() != null) { - name = entry.getRanking().getShortcutInfo().getShortLabel().toString(); - } else { - Bundle extras = entry.getSbn().getNotification().extras; - String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE); - if (TextUtils.isEmpty(nameString)) { - nameString = extras.getString(Notification.EXTRA_TITLE); - } - name = nameString; - } - // If this channel is not already a customized conversation channel, create // a custom channel try { - // TODO: When shortcuts are enforced remove this and use the shortcut label for naming - channel.setName(context.getString( - R.string.notification_summary_message_format, - name, channel.getName())); + channel.setName(getName(entry)); notificationManager.createConversationNotificationChannelForPackage( pkg, appUid, entry.getSbn().getKey(), channel, conversationId); @@ -81,4 +66,19 @@ public class NotificationChannelHelper { } return channel; } + + private static String getName(NotificationEntry entry) { + if (entry.getRanking().getShortcutInfo().getShortLabel() != null) { + return entry.getRanking().getShortcutInfo().getShortLabel().toString(); + } + Bundle extras = entry.getSbn().getNotification().extras; + String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE); + if (TextUtils.isEmpty(nameString)) { + nameString = extras.getString(Notification.EXTRA_TITLE); + } + if (TextUtils.isEmpty(nameString)) { + nameString = "fallback"; + } + return nameString; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index a9bb416cbebb..66b2ca633ca1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -1141,6 +1141,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (mMenuRow.shouldUseDefaultMenuItems()) { ArrayList items = new ArrayList<>(); items.add(NotificationMenuRow.createConversationItem(mContext)); + items.add(NotificationMenuRow.createPartialConversationItem(mContext)); items.add(NotificationMenuRow.createInfoItem(mContext)); items.add(NotificationMenuRow.createSnoozeItem(mContext)); items.add(NotificationMenuRow.createAppOpsItem(mContext)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index f23f3bf28312..e9849ec84987 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -1347,11 +1347,11 @@ public class NotificationContentView extends FrameLayout { if (bubbleButton == null || actionContainer == null) { return; } - boolean isPerson = + boolean isPersonWithShortcut = mPeopleIdentifier.getPeopleNotificationType(entry.getSbn(), entry.getRanking()) - != PeopleNotificationIdentifier.TYPE_NON_PERSON; + >= PeopleNotificationIdentifier.TYPE_FULL_PERSON; boolean showButton = isBubblesEnabled() - && isPerson + && isPersonWithShortcut && entry.getBubbleMetadata() != null; if (showButton) { Drawable d = mContext.getResources().getDrawable(entry.isBubble() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index 23b911b6f687..863951e655e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -97,7 +97,6 @@ public class NotificationConversationInfo extends LinearLayout implements private String mDelegatePkg; private NotificationChannel mNotificationChannel; private ShortcutInfo mShortcutInfo; - private String mConversationId; private StatusBarNotification mSbn; @Nullable private Notification.BubbleMetadata mBubbleMetadata; private Context mUserContext; @@ -233,14 +232,10 @@ public class NotificationConversationInfo extends LinearLayout implements mBuilderProvider = builderProvider; mShortcutManager = shortcutManager; - mConversationId = mNotificationChannel.getConversationId(); - if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) { - mConversationId = mSbn.getShortcutId(mContext); - } - if (TextUtils.isEmpty(mConversationId)) { + mShortcutInfo = entry.getRanking().getShortcutInfo(); + if (mShortcutInfo == null) { throw new IllegalArgumentException("Does not have required information"); } - mShortcutInfo = entry.getRanking().getShortcutInfo(); mNotificationChannel = NotificationChannelHelper.createConversationChannelIfNeeded( getContext(), mINotificationManager, entry, mNotificationChannel); @@ -319,31 +314,9 @@ public class NotificationConversationInfo extends LinearLayout implements private void bindIcon(boolean important) { ImageView image = findViewById(R.id.conversation_icon); - if (mShortcutInfo != null) { - image.setImageDrawable(mIconFactory.getConversationDrawable( - mShortcutInfo, mPackageName, mAppUid, - important)); - } else { - if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) { - // TODO: maybe use a generic group icon, or a composite of recent senders - image.setImageDrawable(mPm.getDefaultActivityIcon()); - } else { - final List messages = - Notification.MessagingStyle.Message.getMessagesFromBundleArray( - (Parcelable[]) mSbn.getNotification().extras.get( - Notification.EXTRA_MESSAGES)); - - final Notification.MessagingStyle.Message latestMessage = - Notification.MessagingStyle.findLatestIncomingMessage(messages); - Icon personIcon = latestMessage.getSenderPerson().getIcon(); - if (personIcon != null) { - image.setImageIcon(latestMessage.getSenderPerson().getIcon()); - } else { - // TODO: choose something better - image.setImageDrawable(mPm.getDefaultActivityIcon()); - } - } - } + image.setImageDrawable(mIconFactory.getConversationDrawable( + mShortcutInfo, mPackageName, mAppUid, important)); + } private void bindPackage() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 1c808cf90321..9c7de2bbf2ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -252,6 +252,9 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx } else if (gutsView instanceof NotificationConversationInfo) { initializeConversationNotificationInfo( row, (NotificationConversationInfo) gutsView); + } else if (gutsView instanceof PartialConversationInfo) { + initializePartialConversationNotificationInfo(row, + (PartialConversationInfo) gutsView); } return true; } catch (Exception e) { @@ -357,7 +360,47 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx } /** - * Sets up the {@link NotificationConversationInfo} inside the notification row's guts. + * Sets up the {@link PartialConversationInfo} inside the notification row's guts. + * @param row view to set up the guts for + * @param notificationInfoView view to set up/bind within {@code row} + */ + @VisibleForTesting + void initializePartialConversationNotificationInfo( + final ExpandableNotificationRow row, + PartialConversationInfo notificationInfoView) throws Exception { + NotificationGuts guts = row.getGuts(); + StatusBarNotification sbn = row.getEntry().getSbn(); + String packageName = sbn.getPackageName(); + // Settings link is only valid for notifications that specify a non-system user + NotificationInfo.OnSettingsClickListener onSettingsClick = null; + UserHandle userHandle = sbn.getUser(); + PackageManager pmUser = StatusBar.getPackageManagerForUser( + mContext, userHandle.getIdentifier()); + + if (!userHandle.equals(UserHandle.ALL) + || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) { + onSettingsClick = (View v, NotificationChannel channel, int appUid) -> { + mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO); + guts.resetFalsingCheck(); + mOnSettingsClickListener.onSettingsClick(sbn.getKey()); + startAppNotificationSettingsActivity(packageName, appUid, channel, row); + }; + } + + notificationInfoView.bindNotification( + pmUser, + mNotificationManager, + packageName, + row.getEntry().getChannel(), + row.getUniqueChannels(), + row.getEntry(), + onSettingsClick, + mDeviceProvisionedController.isDeviceProvisioned(), + row.getIsNonblockable()); + } + + /** + * Sets up the {@link ConversationInfo} inside the notification row's guts. * @param row view to set up the guts for * @param notificationInfoView view to set up/bind within {@code row} */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index 83a6eb297ab3..5e1e3b255867 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -268,7 +268,9 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl NotificationEntry entry = mParent.getEntry(); int personNotifType = mPeopleNotificationIdentifier .getPeopleNotificationType(entry.getSbn(), entry.getRanking()); - if (personNotifType != PeopleNotificationIdentifier.TYPE_NON_PERSON) { + if (personNotifType == PeopleNotificationIdentifier.TYPE_PERSON) { + mInfoItem = createPartialConversationItem(mContext); + } else if (personNotifType >= PeopleNotificationIdentifier.TYPE_FULL_PERSON) { mInfoItem = createConversationItem(mContext); } else { mInfoItem = createInfoItem(mContext); @@ -667,6 +669,16 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl R.drawable.ic_settings); } + static NotificationMenuItem createPartialConversationItem(Context context) { + Resources res = context.getResources(); + String infoDescription = res.getString(R.string.notification_menu_gear_description); + PartialConversationInfo infoContent = + (PartialConversationInfo) LayoutInflater.from(context).inflate( + R.layout.partial_conversation_info, null, false); + return new NotificationMenuItem(context, infoDescription, infoContent, + R.drawable.ic_settings); + } + static NotificationMenuItem createInfoItem(Context context) { Resources res = context.getResources(); String infoDescription = res.getString(R.string.notification_menu_gear_description); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java new file mode 100644 index 000000000000..2189b872da43 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java @@ -0,0 +1,376 @@ +/* + * 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.systemui.statusbar.notification.row; + +import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION; +import static android.app.NotificationManager.IMPORTANCE_LOW; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; + +import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.app.INotificationManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationChannelGroup; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.os.Parcelable; +import android.os.RemoteException; +import android.service.notification.StatusBarNotification; +import android.text.TextUtils; +import android.transition.ChangeBounds; +import android.transition.Fade; +import android.transition.TransitionManager; +import android.transition.TransitionSet; +import android.util.AttributeSet; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +import java.lang.annotation.Retention; +import java.util.List; +import java.util.Set; + +/** + * The guts of a conversation notification that doesn't use valid shortcuts that is revealed when + * performing a long press. + */ +public class PartialConversationInfo extends LinearLayout implements + NotificationGuts.GutsContent { + private static final String TAG = "PartialConvoGuts"; + + private INotificationManager mINotificationManager; + private PackageManager mPm; + private String mPackageName; + private String mAppName; + private int mAppUid; + private String mDelegatePkg; + private NotificationChannel mNotificationChannel; + private StatusBarNotification mSbn; + private boolean mIsDeviceProvisioned; + private boolean mIsNonBlockable; + private Set mUniqueChannelsInRow; + private Drawable mPkgIcon; + + private @Action int mSelectedAction = -1; + private boolean mPressedApply; + private boolean mPresentingChannelEditorDialog = false; + + private NotificationInfo.OnSettingsClickListener mOnSettingsClickListener; + private NotificationGuts mGutsContainer; + private ChannelEditorDialogController mChannelEditorDialogController; + + @VisibleForTesting + boolean mSkipPost = false; + + @Retention(SOURCE) + @IntDef({ACTION_SETTINGS}) + private @interface Action {} + static final int ACTION_SETTINGS = 5; + + private OnClickListener mOnDone = v -> { + mPressedApply = true; + closeControls(v, true); + }; + + public PartialConversationInfo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void bindNotification( + PackageManager pm, + INotificationManager iNotificationManager, + String pkg, + NotificationChannel notificationChannel, + Set uniqueChannelsInRow, + NotificationEntry entry, + NotificationInfo.OnSettingsClickListener onSettingsClick, + boolean isDeviceProvisioned, + boolean isNonBlockable) { + mSelectedAction = -1; + mINotificationManager = iNotificationManager; + mPackageName = pkg; + mSbn = entry.getSbn(); + mPm = pm; + mAppName = mPackageName; + mOnSettingsClickListener = onSettingsClick; + mNotificationChannel = notificationChannel; + mAppUid = mSbn.getUid(); + mDelegatePkg = mSbn.getOpPkg(); + mIsDeviceProvisioned = isDeviceProvisioned; + mIsNonBlockable = isNonBlockable; + mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class); + mUniqueChannelsInRow = uniqueChannelsInRow; + + bindHeader(); + bindActions(); + + View turnOffButton = findViewById(R.id.turn_off_notifications); + turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener()); + turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonBlockable + ? VISIBLE : GONE); + + View done = findViewById(R.id.done); + done.setOnClickListener(mOnDone); + } + + private void bindActions() { + final View settingsButton = findViewById(R.id.info); + settingsButton.setOnClickListener(getSettingsOnClickListener()); + settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE); + + TextView msg = findViewById(R.id.non_configurable_text); + msg.setText(getResources().getString(R.string.no_shortcut, mAppName)); + } + + private void bindHeader() { + bindConversationDetails(); + + // Delegate + bindDelegate(); + } + + private OnClickListener getSettingsOnClickListener() { + if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) { + final int appUidF = mAppUid; + return ((View view) -> { + mOnSettingsClickListener.onClick(view, mNotificationChannel, appUidF); + }); + } + return null; + } + + private OnClickListener getTurnOffNotificationsClickListener() { + return ((View view) -> { + if (!mPresentingChannelEditorDialog && mChannelEditorDialogController != null) { + mPresentingChannelEditorDialog = true; + + mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid, + mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener); + mChannelEditorDialogController.setOnFinishListener(() -> { + mPresentingChannelEditorDialog = false; + closeControls(this, false); + }); + mChannelEditorDialogController.show(); + } + }); + } + + private void bindConversationDetails() { + final TextView channelName = findViewById(R.id.parent_channel_name); + channelName.setText(mNotificationChannel.getName()); + + bindGroup(); + bindName(); + bindPackage(); + bindIcon(); + } + + private void bindName() { + TextView name = findViewById(R.id.name); + Bundle extras = mSbn.getNotification().extras; + String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE); + if (TextUtils.isEmpty(nameString)) { + nameString = extras.getString(Notification.EXTRA_TITLE); + } + name.setText(nameString); + } + + private void bindIcon() { + ImageView image = findViewById(R.id.conversation_icon); + if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) { + // TODO: maybe use a generic group icon, or a composite of recent senders + image.setImageDrawable(mPkgIcon); + } else { + final List messages = + Notification.MessagingStyle.Message.getMessagesFromBundleArray( + (Parcelable[]) mSbn.getNotification().extras.get( + Notification.EXTRA_MESSAGES)); + + final Notification.MessagingStyle.Message latestMessage = + Notification.MessagingStyle.findLatestIncomingMessage(messages); + Icon personIcon = null; + if (latestMessage != null && latestMessage.getSenderPerson() != null) { + personIcon = latestMessage.getSenderPerson().getIcon(); + } + if (personIcon != null) { + image.setImageIcon(latestMessage.getSenderPerson().getIcon()); + } else { + image.setImageDrawable(mPkgIcon); + } + } + } + + private void bindPackage() { + ApplicationInfo info; + try { + info = mPm.getApplicationInfo( + mPackageName, + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE); + if (info != null) { + mAppName = String.valueOf(mPm.getApplicationLabel(info)); + mPkgIcon = mPm.getApplicationIcon(info); + } + } catch (PackageManager.NameNotFoundException e) { + mPkgIcon = mPm.getDefaultActivityIcon(); + } + ((TextView) findViewById(R.id.pkg_name)).setText(mAppName); + } + + private void bindDelegate() { + TextView delegateView = findViewById(R.id.delegate_name); + + if (!TextUtils.equals(mPackageName, mDelegatePkg)) { + // this notification was posted by a delegate! + delegateView.setVisibility(View.VISIBLE); + } else { + delegateView.setVisibility(View.GONE); + } + } + + private void bindGroup() { + // Set group information if this channel has an associated group. + CharSequence groupName = null; + if (mNotificationChannel != null && mNotificationChannel.getGroup() != null) { + try { + final NotificationChannelGroup notificationChannelGroup = + mINotificationManager.getNotificationChannelGroupForPackage( + mNotificationChannel.getGroup(), mPackageName, mAppUid); + if (notificationChannelGroup != null) { + groupName = notificationChannelGroup.getName(); + } + } catch (RemoteException e) { + } + } + TextView groupNameView = findViewById(R.id.group_name); + View groupDivider = findViewById(R.id.group_divider); + if (groupName != null) { + groupNameView.setText(groupName); + groupNameView.setVisibility(VISIBLE); + groupDivider.setVisibility(VISIBLE); + } else { + groupNameView.setVisibility(GONE); + groupDivider.setVisibility(GONE); + } + } + + @Override + public boolean post(Runnable action) { + if (mSkipPost) { + action.run(); + return true; + } else { + return super.post(action); + } + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + } + + @Override + public void onFinishedClosing() { + // TODO: do we need to do anything here? + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (mGutsContainer != null && + event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { + if (mGutsContainer.isExposed()) { + event.getText().add(mContext.getString( + R.string.notification_channel_controls_opened_accessibility, mAppName)); + } else { + event.getText().add(mContext.getString( + R.string.notification_channel_controls_closed_accessibility, mAppName)); + } + } + } + + /** + * Closes the controls and commits the updated importance values (indirectly). + * + *

Note, this will only get called once the view is dismissing. This means that the + * user does not have the ability to undo the action anymore. + */ + @VisibleForTesting + void closeControls(View v, boolean save) { + int[] parentLoc = new int[2]; + int[] targetLoc = new int[2]; + mGutsContainer.getLocationOnScreen(parentLoc); + v.getLocationOnScreen(targetLoc); + final int centerX = v.getWidth() / 2; + final int centerY = v.getHeight() / 2; + final int x = targetLoc[0] - parentLoc[0] + centerX; + final int y = targetLoc[1] - parentLoc[1] + centerY; + mGutsContainer.closeControls(x, y, save, false /* force */); + } + + @Override + public void setGutsParent(NotificationGuts guts) { + mGutsContainer = guts; + } + + @Override + public boolean willBeRemoved() { + return false; + } + + @Override + public boolean shouldBeSaved() { + return mPressedApply; + } + + @Override + public View getContentView() { + return this; + } + + @Override + public boolean handleCloseControls(boolean save, boolean force) { + return false; + } + + @Override + public int getActualHeight() { + return getHeight(); + } + + @VisibleForTesting + public boolean isAnimating() { + return false; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 3847028a25df..dbf40e467c95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -386,7 +386,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase { applicationInfo); when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other"); - NotificationEntry entry = new NotificationEntryBuilder().setSbn(mSbn).build(); + NotificationEntry entry = new NotificationEntryBuilder() + .setSbn(mSbn) + .setShortcutInfo(mShortcutInfo) + .build(); mNotificationInfo.bindNotification( mShortcutManager, mMockPackageManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java new file mode 100644 index 000000000000..c390e3933d7a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java @@ -0,0 +1,397 @@ +/* + * 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.systemui.statusbar.notification.row; + +import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION; +import static android.app.NotificationManager.IMPORTANCE_LOW; +import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.app.INotificationManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationChannelGroup; +import android.app.Person; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CountDownLatch; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class PartialConversationInfoTest extends SysuiTestCase { + private static final String TEST_PACKAGE_NAME = "test_package"; + private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME; + private static final int TEST_UID = 1; + private static final String TEST_CHANNEL = "test_channel"; + private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME"; + + private TestableLooper mTestableLooper; + private PartialConversationInfo mInfo; + private NotificationChannel mNotificationChannel; + private NotificationChannel mDefaultNotificationChannel; + private Set mNotificationChannelSet = new HashSet<>(); + private Set mDefaultNotificationChannelSet = new HashSet<>(); + private StatusBarNotification mSbn; + private NotificationEntry mEntry; + + @Rule + public MockitoRule mockito = MockitoJUnit.rule(); + @Mock + private MetricsLogger mMetricsLogger; + @Mock + private INotificationManager mMockINotificationManager; + @Mock + private PackageManager mMockPackageManager; + + @Mock + private Icon mIcon; + @Mock + private Drawable mDrawable; + + @Before + public void setUp() throws Exception { + mTestableLooper = TestableLooper.get(this); + + mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); + mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); + // Inflate the layout + final LayoutInflater layoutInflater = LayoutInflater.from(mContext); + mInfo = (PartialConversationInfo) layoutInflater.inflate(R.layout.partial_conversation_info, + null); + mInfo.setGutsParent(mock(NotificationGuts.class)); + // Our view is never attached to a window so the View#post methods in NotificationInfo never + // get called. Setting this will skip the post and do the action immediately. + mInfo.mSkipPost = true; + + // PackageManager must return a packageInfo and applicationInfo. + final PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = TEST_PACKAGE_NAME; + when(mMockPackageManager.getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt())) + .thenReturn(packageInfo); + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.uid = TEST_UID; // non-zero + when(mMockPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME), anyInt())).thenReturn( + applicationInfo); + final PackageInfo systemPackageInfo = new PackageInfo(); + systemPackageInfo.packageName = TEST_SYSTEM_PACKAGE_NAME; + when(mMockPackageManager.getPackageInfo(eq(TEST_SYSTEM_PACKAGE_NAME), anyInt())) + .thenReturn(systemPackageInfo); + when(mMockPackageManager.getPackageInfo(eq("android"), anyInt())) + .thenReturn(packageInfo); + + // Package has one channel by default. + when(mMockINotificationManager.getNumNotificationChannelsForPackage( + eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(1); + + when(mIcon.loadDrawable(any())).thenReturn(mDrawable); + + // Some test channels. + mNotificationChannel = new NotificationChannel( + TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW); + mNotificationChannelSet.add(mNotificationChannel); + mDefaultNotificationChannel = new NotificationChannel( + NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, + IMPORTANCE_LOW); + mDefaultNotificationChannelSet.add(mDefaultNotificationChannel); + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, + new Notification(), UserHandle.CURRENT, null, 0); + mEntry = new NotificationEntryBuilder().setSbn(mSbn).build(); + } + + @Test + public void testBindNotification_SetsTextApplicationName() throws Exception { + when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + true, + false); + final TextView textView = mInfo.findViewById(R.id.pkg_name); + assertTrue(textView.getText().toString().contains("App Name")); + assertEquals(VISIBLE, mInfo.findViewById(R.id.header).getVisibility()); + } + + @Test + public void testBindNotification_groupSetsPackageIcon() { + mEntry.getSbn().getNotification().extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, true); + final Drawable iconDrawable = mock(Drawable.class); + when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) + .thenReturn(iconDrawable); + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + true, + false); + final ImageView iconView = mInfo.findViewById(R.id.conversation_icon); + assertEquals(iconDrawable, iconView.getDrawable()); + } + + @Test + public void testBindNotification_notGroupSetsMessageIcon() { + Notification n = new Notification.Builder(mContext, TEST_CHANNEL_NAME) + .setStyle(new Notification.MessagingStyle( + new Person.Builder().setName("me").build()) + .addMessage(new Notification.MessagingStyle.Message("hello", 0, + new Person.Builder().setName("friend").setIcon(mIcon).build()))) + .build(); + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, + n, UserHandle.CURRENT, null, 0); + mEntry.setSbn(mSbn); + mEntry.getSbn().getNotification().extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, false); + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + true, + false); + final ImageView iconView = mInfo.findViewById(R.id.conversation_icon); + assertEquals(mDrawable.hashCode() + "", mDrawable, iconView.getDrawable()); + } + + @Test + public void testBindNotification_noDelegate() { + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + true, + false); + final TextView nameView = mInfo.findViewById(R.id.delegate_name); + assertEquals(GONE, nameView.getVisibility()); + final TextView dividerView = mInfo.findViewById(R.id.group_divider); + assertEquals(GONE, dividerView.getVisibility()); + } + + @Test + public void testBindNotification_delegate() throws Exception { + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, "other", 0, null, TEST_UID, 0, + new Notification(), UserHandle.CURRENT, null, 0); + final ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.uid = 7; // non-zero + when(mMockPackageManager.getApplicationInfo(eq("other"), anyInt())).thenReturn( + applicationInfo); + when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other"); + + NotificationEntry entry = new NotificationEntryBuilder().setSbn(mSbn).build(); + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + entry, + null, + true, + false); + final TextView nameView = mInfo.findViewById(R.id.delegate_name); + assertEquals(VISIBLE, nameView.getVisibility()); + assertTrue(nameView.getText().toString().contains("Proxied")); + } + + @Test + public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + true, + false); + final TextView groupNameView = mInfo.findViewById(R.id.group_name); + assertEquals(GONE, groupNameView.getVisibility()); + final TextView dividerView = mInfo.findViewById(R.id.group_divider); + assertEquals(GONE, dividerView.getVisibility()); + } + + @Test + public void testBindNotification_SetsGroupNameIfNonNull() throws Exception { + mNotificationChannel.setGroup("test_group_id"); + final NotificationChannelGroup notificationChannelGroup = + new NotificationChannelGroup("test_group_id", "Test Group Name"); + when(mMockINotificationManager.getNotificationChannelGroupForPackage( + eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID))) + .thenReturn(notificationChannelGroup); + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + true, + false); + final TextView groupNameView = mInfo.findViewById(R.id.group_name); + assertEquals(View.VISIBLE, groupNameView.getVisibility()); + assertEquals("Test Group Name", groupNameView.getText()); + final TextView dividerView = mInfo.findViewById(R.id.group_divider); + assertEquals(View.VISIBLE, dividerView.getVisibility()); + } + + @Test + public void testBindNotification_SetsTextChannelName() { + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + true, + false); + final TextView textView = mInfo.findViewById(R.id.parent_channel_name); + assertEquals(TEST_CHANNEL_NAME, textView.getText()); + } + + @Test + public void testBindNotification_SetsOnClickListenerForSettings() { + final CountDownLatch latch = new CountDownLatch(1); + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + (View v, NotificationChannel c, int appUid) -> { + assertEquals(mNotificationChannel, c); + latch.countDown(); + }, + true, + false); + + final View settingsButton = mInfo.findViewById(R.id.info); + settingsButton.performClick(); + // Verify that listener was triggered. + assertEquals(0, latch.getCount()); + } + + @Test + public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() { + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + true, + false); + final View settingsButton = mInfo.findViewById(R.id.info); + assertTrue(settingsButton.getVisibility() != View.VISIBLE); + } + + @Test + public void testBindNotification_SettingsButtonInvisibleWhenDeviceUnprovisioned() { + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + (View v, NotificationChannel c, int appUid) -> { + assertEquals(mNotificationChannel, c); + }, + false, + false); + final View settingsButton = mInfo.findViewById(R.id.info); + assertTrue(settingsButton.getVisibility() != View.VISIBLE); + } + + @Test + public void testBindNotification_whenAppUnblockable() { + mInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + true, + true); + + assertEquals(GONE, + mInfo.findViewById(R.id.turn_off_notifications).getVisibility()); + } +} diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java index 83ca69956033..2f60e426245d 100644 --- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java +++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java @@ -47,7 +47,7 @@ public class NotificationChannelExtractor implements NotificationSignalExtractor NotificationChannel updatedChannel = mConfig.getConversationNotificationChannel( record.getSbn().getPackageName(), record.getSbn().getUid(), record.getChannel().getId(), - record.getSbn().getShortcutId(mContext), true, false); + record.getSbn().getShortcutId(), true, false); record.updateNotificationChannel(updatedChannel); return null; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6c3177fe253a..2f76a1f9e246 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2718,12 +2718,12 @@ public class NotificationManagerService extends SystemService { } return text == null ? null : String.valueOf(text); } - + protected void maybeRegisterMessageSent(NotificationRecord r) { Context appContext = r.getSbn().getPackageContext(getContext()); - Notification.Builder nb = + Notification.Builder nb = Notification.Builder.recoverBuilder(appContext, r.getNotification()); - if (nb.getStyle() instanceof Notification.MessagingStyle) { + if (nb.getStyle() instanceof Notification.MessagingStyle && r.getShortcutInfo() == null) { mPreferencesHelper.setMessageSent(r.getSbn().getPackageName(), r.getUid()); handleSavePolicyFile(); } @@ -5627,7 +5627,7 @@ public class NotificationManagerService extends SystemService { if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) { channelId = (new Notification.TvExtender(notification)).getChannelId(); } - String shortcutId = n.getShortcutId(getContext()); + String shortcutId = n.getShortcutId(); final NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel( pkg, notificationUid, channelId, shortcutId, true /* parent ok */, false /* includeDeleted */); diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index e45b41df38b2..8e3de1598275 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -1386,10 +1386,6 @@ public final class NotificationRecord { || !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) { return false; } - if (mShortcutInfo == null && Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 1) { - return false; - } if (mIsNotConversationOverride) { return false; } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 6d7b410b0b99..a4b99b376b30 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -116,7 +116,7 @@ public class PreferencesHelper implements RankingConfig { private static final String ATT_ENABLED = "enabled"; private static final String ATT_USER_ALLOWED = "allowed"; private static final String ATT_HIDE_SILENT = "hide_gentle"; - private static final String ATT_SENT_MESSAGE = "sent_msg"; + private static final String ATT_SENT_MESSAGE = "sent_invalid_msg"; private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT; private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE; @@ -194,8 +194,6 @@ public class PreferencesHelper implements RankingConfig { updateBadgingEnabled(); updateBubblesEnabled(); syncChannelsBypassingDnd(mContext.getUserId()); - mAllowInvalidShortcuts = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 0; } public void readXml(XmlPullParser parser, boolean forRestore, int userId) @@ -1313,7 +1311,9 @@ public class PreferencesHelper implements RankingConfig { int N = r.channels.size(); for (int i = 0; i < N; i++) { final NotificationChannel nc = r.channels.valueAt(i); - if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()) { + if (!TextUtils.isEmpty(nc.getConversationId()) + && !nc.isDeleted() + && !nc.isDemoted()) { ConversationChannelWrapper conversation = new ConversationChannelWrapper(); conversation.setPkg(r.pkg); conversation.setUid(r.uid); diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java index 13892ba0e480..94f69e9c73b3 100644 --- a/services/core/java/com/android/server/notification/ShortcutHelper.java +++ b/services/core/java/com/android/server/notification/ShortcutHelper.java @@ -152,9 +152,13 @@ public class ShortcutHelper { if (shortcutInfo == null || !shortcutInfo.isLongLived() || !shortcutInfo.isEnabled()) { return false; } - return mShortcutServiceInternal.isSharingShortcut(callingUserId, "android", + // TODO (b/155016294) uncomment when sharing shortcuts are required + /* + mShortcutServiceInternal.isSharingShortcut(callingUserId, "android", shortcutInfo.getPackage(), shortcutInfo.getId(), shortcutInfo.getUserId(), SHARING_FILTER); + */ + return true; } /** diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java index a23ade68b344..77ce2f032a09 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java @@ -76,8 +76,6 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { @Test public void testInvalidShortcutFlagEnabled_looksUpCorrectChannel() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0); NotificationChannelExtractor extractor = new NotificationChannelExtractor(); extractor.setConfig(mConfig); @@ -96,7 +94,7 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { NotificationChannel updatedChannel = new NotificationChannel("a", "", IMPORTANCE_HIGH); when(mConfig.getConversationNotificationChannel( - any(), anyInt(), eq("a"), eq(r.getSbn().getShortcutId(mContext)), + any(), anyInt(), eq("a"), eq(r.getSbn().getShortcutId()), eq(true), eq(false))) .thenReturn(updatedChannel); @@ -106,8 +104,6 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { @Test public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); NotificationChannelExtractor extractor = new NotificationChannelExtractor(); extractor.setConfig(mConfig); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 41748b8a893b..d5ecfeb55e95 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -6589,14 +6589,45 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testRecordMessages() throws RemoteException { + public void testRecordMessages_invalidMsg() throws RemoteException { NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel, - "testRecordMessages"); + "testRecordMessages_invalidMsg"); mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); waitForIdle(); assertTrue(mBinderService.hasSentMessage(PKG, mUid)); } + + @Test + public void testRecordMessages_validMsg() throws RemoteException { + // Messaging notification with shortcut info + Notification.BubbleMetadata metadata = + new Notification.BubbleMetadata.Builder("id").build(); + Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */, + null /* groupKey */, false /* isSummary */); + nb.setShortcutId("id"); + nb.setBubbleMetadata(metadata); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, + "testRecordMessages_validMsg", mUid, 0, nb.build(), new UserHandle(mUid), null, 0); + NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + // Pretend the shortcut exists + List shortcutInfos = new ArrayList<>(); + ShortcutInfo info = mock(ShortcutInfo.class); + when(info.getPackage()).thenReturn(PKG); + when(info.getId()).thenReturn("id"); + when(info.getUserId()).thenReturn(USER_SYSTEM); + when(info.isLongLived()).thenReturn(true); + when(info.isEnabled()).thenReturn(true); + shortcutInfos.add(info); + when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(), + nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); + waitForIdle(); + + assertFalse(mBinderService.hasSentMessage(PKG, mUid)); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index 3139bfaaf1f5..9f593ce42741 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -123,8 +123,6 @@ public class NotificationRecordTest extends UiServiceTestCase { when(mMockContext.getResources()).thenReturn(getContext().getResources()); when(mMockContext.getPackageManager()).thenReturn(mPm); when(mMockContext.getContentResolver()).thenReturn(mContentResolver); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); ApplicationInfo appInfo = new ApplicationInfo(); appInfo.targetSdkVersion = Build.VERSION_CODES.O; when(mMockContext.getApplicationInfo()).thenReturn(appInfo); @@ -1127,18 +1125,7 @@ public class NotificationRecordTest extends UiServiceTestCase { } @Test - public void testIsConversation_nullShortcut() { - StatusBarNotification sbn = getMessagingStyleNotification(); - NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); - record.setShortcutInfo(null); - - assertFalse(record.isConversation()); - } - - @Test - public void testIsConversation_bypassShortcutFlagEnabled() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0); + public void testIsConversation_noShortcut() { StatusBarNotification sbn = getMessagingStyleNotification(); NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); record.setShortcutInfo(null); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index e11392bde993..4320f1c3c896 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -3022,32 +3022,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { } } - @Test - public void testPlaceholderConversationId_shortcutNotRequired() throws Exception { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0); - - mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, - mAppOpsManager); - - final String xml = "\n" - + "\n" - + "" - + "" - + ""; - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), - null); - parser.nextTag(); - mHelper.readXml(parser, false, UserHandle.USER_ALL); - - assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true)); - } - @Test public void testPlaceholderConversationId_shortcutRequired() throws Exception { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager); @@ -3067,8 +3043,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testNormalConversationId_shortcutRequired() throws Exception { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager); @@ -3088,8 +3062,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testNoConversationId_shortcutRequired() throws Exception { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger, mAppOpsManager); @@ -3275,6 +3247,19 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertThat(mHelper.getConversations(PKG_O, UID_O)).isEmpty(); } + @Test + public void testGetConversations_noDemoted() { + NotificationChannel parent = new NotificationChannel("parent", "p", 1); + mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false); + NotificationChannel channel = + new NotificationChannel("convo", "convo", IMPORTANCE_DEFAULT); + channel.setConversationId("parent", "convo"); + channel.setDemoted(true); + mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false); + + assertThat(mHelper.getConversations(PKG_O, UID_O)).isEmpty(); + } + @Test public void testGetConversations() { NotificationChannelGroup group = new NotificationChannelGroup("acct", "account_name"); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java index 3095c87ba2f6..eb2d9fed197f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java @@ -40,6 +40,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.UiServiceTestCase; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -184,6 +185,7 @@ public class ShortcutHelperTest extends UiServiceTestCase { assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isNull(); } + @Ignore("b/155016294") @Test public void testGetValidShortcutInfo_notSharingShortcut() { ShortcutInfo si = mock(ShortcutInfo.class); @@ -229,8 +231,9 @@ public class ShortcutHelperTest extends UiServiceTestCase { ArrayList shortcuts = new ArrayList<>(); shortcuts.add(si); when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts); - when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), - anyString(), anyInt(), any())).thenReturn(true); + // TODO: b/155016294 + //when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), + // anyString(), anyInt(), any())).thenReturn(true); assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isSameAs(si); } -- cgit v1.2.3-59-g8ed1b