Merge "Add experience for shortcut-less convos" into rvc-dev am: bc78548368

Change-Id: I5082e8c7b34c956c8fb06d621dadc647f3841f0d
This commit is contained in:
TreeHugger Robot
2020-04-28 14:02:27 +00:00
committed by Automerger Merge Worker
22 changed files with 1133 additions and 130 deletions

View File

@@ -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();
}
/**

View File

@@ -0,0 +1,200 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 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.
-->
<com.android.systemui.statusbar.notification.row.PartialConversationInfo
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notification_guts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:clipChildren="false"
android:clipToPadding="true"
android:orientation="vertical"
android:paddingStart="@*android:dimen/notification_content_margin_start">
<!-- Package Info -->
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_guts_conversation_header_height"
android:gravity="center_vertical"
android:clipChildren="false"
android:clipToPadding="false">
<ImageView
android:id="@+id/conversation_icon"
android:layout_width="@dimen/notification_guts_conversation_icon_size"
android:layout_height="@dimen/notification_guts_conversation_icon_size"
android:layout_centerVertical="true"
android:layout_alignParentStart="true"
android:layout_marginEnd="15dp" />
<LinearLayout
android:id="@+id/names"
android:layout_weight="1"
android:layout_width="0dp"
android:orientation="vertical"
android:layout_height="wrap_content"
android:minHeight="@dimen/notification_guts_conversation_icon_size"
android:layout_centerVertical="true"
android:gravity="center_vertical"
android:layout_alignEnd="@id/conversation_icon"
android:layout_toEndOf="@id/conversation_icon">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:orientation="horizontal">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance.NotificationImportanceChannel"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
style="@style/TextAppearance.NotificationImportanceHeader"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:text="@*android:string/notification_header_divider_symbol" />
<TextView
android:id="@+id/parent_channel_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance.NotificationImportanceChannel"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:orientation="horizontal">
<TextView
android:id="@+id/pkg_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance.NotificationImportanceChannelGroup"
android:ellipsize="end"
android:maxLines="1"/>
<TextView
android:id="@+id/group_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
style="@style/TextAppearance.NotificationImportanceHeader"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:text="@*android:string/notification_header_divider_symbol" />
<TextView
android:id="@+id/group_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
style="@style/TextAppearance.NotificationImportanceChannelGroup"/>
</LinearLayout>
<TextView
android:id="@+id/delegate_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
style="@style/TextAppearance.NotificationImportanceHeader"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:ellipsize="end"
android:text="@string/notification_delegate_header"
android:maxLines="1" />
</LinearLayout>
<!-- end aligned fields -->
<ImageButton
android:id="@+id/info"
android:layout_width="@dimen/notification_importance_toggle_size"
android:layout_height="@dimen/notification_importance_toggle_size"
android:layout_centerVertical="true"
android:background="@drawable/ripple_drawable"
android:contentDescription="@string/notification_more_settings"
android:src="@drawable/ic_settings"
android:layout_alignParentEnd="true"
android:tint="@color/notification_guts_link_icon_tint"/>
</LinearLayout>
<LinearLayout
android:id="@+id/inline_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="@*android:dimen/notification_content_margin_end"
android:layout_marginTop="@dimen/notification_guts_option_vertical_padding"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="horizontal">
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_info"
android:tint="?android:attr/textColorPrimary"
android:layout_marginEnd="8dp"/>
<TextView
android:id="@+id/non_configurable_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextAppearance.NotificationImportanceChannelGroup" />
</LinearLayout>
<RelativeLayout
android:id="@+id/bottom_buttons"
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center_vertical"
android:paddingStart="4dp"
android:paddingEnd="4dp"
>
<TextView
android:id="@+id/turn_off_notifications"
android:text="@string/inline_turn_off_notifications"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:gravity="start|center_vertical"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="200dp"
style="@style/TextAppearance.NotificationInfo.Button"/>
<TextView
android:id="@+id/done"
android:text="@string/inline_done_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:gravity="end|center_vertical"
android:minWidth="@dimen/notification_importance_toggle_size"
android:minHeight="@dimen/notification_importance_toggle_size"
android:maxWidth="125dp"
style="@style/TextAppearance.NotificationInfo.Button"/>
</RelativeLayout>
</LinearLayout>
</com.android.systemui.statusbar.notification.row.PartialConversationInfo>

View File

@@ -1847,6 +1847,9 @@
<!-- [CHAR LIMIT=150] Notification Importance title: important conversation level -->
<string name="notification_priority_title">Priority</string>
<!-- Text shown in notification guts for conversation notifications that don't implement the full feature -->
<string name="no_shortcut"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> does not support conversation specific settings</string>
<!-- [CHAR LIMIT=NONE] Empty overflow title -->
<string name="bubble_overflow_empty_title">No recent bubbles</string>

View File

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

View File

@@ -1141,6 +1141,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (mMenuRow.shouldUseDefaultMenuItems()) {
ArrayList<MenuItem> 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));

View File

@@ -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()

View File

@@ -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<Notification.MessagingStyle.Message> messages =
Notification.MessagingStyle.Message.getMessagesFromBundleArray(
(Parcelable[]) mSbn.getNotification().extras.get(
Notification.EXTRA_MESSAGES));
image.setImageDrawable(mIconFactory.getConversationDrawable(
mShortcutInfo, mPackageName, mAppUid, important));
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());
}
}
}
}
private void bindPackage() {

View File

@@ -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}
*/

View File

@@ -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);

View File

@@ -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<NotificationChannel> 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<NotificationChannel> 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<Notification.MessagingStyle.Message> 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).
*
* <p><b>Note,</b> 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;
}
}

View File

@@ -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,

View File

@@ -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<NotificationChannel> mNotificationChannelSet = new HashSet<>();
private Set<NotificationChannel> 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());
}
}

View File

@@ -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;

View File

@@ -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 */);

View File

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

View File

@@ -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);

View File

@@ -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;
}
/**

View File

@@ -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);

View File

@@ -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<ShortcutInfo> 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));
}
}

View File

@@ -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);

View File

@@ -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 = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>"
+ "</package>"
+ "</ranking>";
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");

View File

@@ -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<ShortcutInfo> 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);
}