Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 1 | /* |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 2 | * Copyright (C) 2020 The Android Open Source Project |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 14 | * limitations under the License. |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 15 | */ |
| 16 | |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 17 | package com.android.systemui.statusbar.notification.row; |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 18 | |
Mady Mellor | fc02cc3 | 2019-04-01 14:47:55 -0700 | [diff] [blame] | 19 | import static android.app.Notification.FLAG_BUBBLE; |
Julia Reynolds | 0abae11 | 2018-06-08 10:36:57 -0400 | [diff] [blame] | 20 | import static android.app.NotificationManager.IMPORTANCE_DEFAULT; |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 21 | import static android.app.NotificationManager.IMPORTANCE_HIGH; |
Julia Reynolds | 0abae11 | 2018-06-08 10:36:57 -0400 | [diff] [blame] | 22 | |
Ned Burns | 47c98f1 | 2019-09-06 17:12:07 -0400 | [diff] [blame] | 23 | import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; |
| 24 | |
Kevin Han | ce7451c | 2019-12-11 14:31:53 -0800 | [diff] [blame] | 25 | import static org.junit.Assert.assertTrue; |
Selim Cinek | c3fec68 | 2019-06-06 18:11:07 -0700 | [diff] [blame] | 26 | import static org.mockito.Mockito.mock; |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 27 | import static org.mockito.Mockito.verify; |
Selim Cinek | c3fec68 | 2019-06-06 18:11:07 -0700 | [diff] [blame] | 28 | |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 29 | import android.annotation.Nullable; |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 30 | import android.app.ActivityManager; |
| 31 | import android.app.Notification; |
Mark Renouf | 08bc42a | 2019-03-07 13:01:59 -0500 | [diff] [blame] | 32 | import android.app.Notification.BubbleMetadata; |
Julia Reynolds | 0abae11 | 2018-06-08 10:36:57 -0400 | [diff] [blame] | 33 | import android.app.NotificationChannel; |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 34 | import android.app.PendingIntent; |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 35 | import android.content.Context; |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 36 | import android.content.Intent; |
Ned Burns | d8b5154 | 2020-03-13 20:52:43 -0400 | [diff] [blame] | 37 | import android.content.pm.LauncherApps; |
Mady Mellor | c39b4ae | 2019-01-09 17:11:37 -0800 | [diff] [blame] | 38 | import android.graphics.drawable.Icon; |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 39 | import android.os.UserHandle; |
| 40 | import android.service.notification.StatusBarNotification; |
Kevin Han | a7c21be | 2020-04-01 17:58:35 -0700 | [diff] [blame] | 41 | import android.testing.TestableLooper; |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 42 | import android.text.TextUtils; |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 43 | import android.view.LayoutInflater; |
| 44 | import android.widget.RemoteViews; |
| 45 | |
Lucas Dupin | d236ee3 | 2019-10-08 15:33:59 -0700 | [diff] [blame] | 46 | import com.android.systemui.TestableDependency; |
| 47 | import com.android.systemui.bubbles.BubbleController; |
Mark Renouf | a9fbb6e | 2019-02-11 12:48:22 -0500 | [diff] [blame] | 48 | import com.android.systemui.bubbles.BubblesTestActivity; |
Dave Mankoff | c0211ff | 2020-02-07 15:36:12 -0500 | [diff] [blame] | 49 | import com.android.systemui.plugins.FalsingManager; |
Selim Cinek | c3fec68 | 2019-06-06 18:11:07 -0700 | [diff] [blame] | 50 | import com.android.systemui.plugins.statusbar.StatusBarStateController; |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 51 | import com.android.systemui.statusbar.NotificationMediaManager; |
| 52 | import com.android.systemui.statusbar.NotificationRemoteInputManager; |
| 53 | import com.android.systemui.statusbar.SmartReplyController; |
Steve Elliott | 46bb2a1 | 2020-03-17 11:04:09 -0400 | [diff] [blame] | 54 | import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; |
Ned Burns | f81c4c4 | 2019-01-07 14:10:43 -0500 | [diff] [blame] | 55 | import com.android.systemui.statusbar.notification.collection.NotificationEntry; |
Beverly | 79c89ec | 2019-12-13 10:33:01 -0500 | [diff] [blame] | 56 | import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; |
Kevin Han | 65c38b3 | 2020-02-05 16:42:40 -0800 | [diff] [blame] | 57 | import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; |
| 58 | import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; |
Ned Burns | d8b5154 | 2020-03-13 20:52:43 -0400 | [diff] [blame] | 59 | import com.android.systemui.statusbar.notification.icon.IconBuilder; |
| 60 | import com.android.systemui.statusbar.notification.icon.IconManager; |
Steve Elliott | 2304e4a | 2020-04-01 17:03:41 -0400 | [diff] [blame] | 61 | import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; |
Kevin Han | 5182c9f | 2020-01-07 17:54:43 -0800 | [diff] [blame] | 62 | import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger; |
| 63 | import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener; |
Kevin Han | ec09309 | 2019-12-11 16:47:59 -0800 | [diff] [blame] | 64 | import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; |
Beverly | 95a0802ac | 2020-02-10 15:27:40 -0500 | [diff] [blame] | 65 | import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; |
yoshiki iguchi | 4e30e76 | 2018-02-06 12:09:23 +0900 | [diff] [blame] | 66 | import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; |
Selim Cinek | d21232e | 2019-06-20 14:15:59 -0700 | [diff] [blame] | 67 | import com.android.systemui.statusbar.phone.KeyguardBypassController; |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 68 | import com.android.systemui.statusbar.phone.NotificationGroupManager; |
wilsonshih | e832194 | 2019-10-18 18:39:46 +0800 | [diff] [blame] | 69 | import com.android.systemui.statusbar.phone.NotificationShadeWindowController; |
Sergey Nikolaienkov | f6ad632 | 2020-02-10 15:46:32 +0100 | [diff] [blame] | 70 | import com.android.systemui.statusbar.policy.SmartReplyConstants; |
Hyunyoung Song | 8f9d34c | 2019-08-30 14:47:43 -0700 | [diff] [blame] | 71 | import com.android.systemui.tests.R; |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 72 | |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 73 | import org.mockito.ArgumentCaptor; |
| 74 | |
Kevin Han | ce7451c | 2019-12-11 14:31:53 -0800 | [diff] [blame] | 75 | import java.util.concurrent.CountDownLatch; |
Kevin Han | a6d5bb4 | 2020-03-17 13:55:49 -0700 | [diff] [blame] | 76 | import java.util.concurrent.Executor; |
Kevin Han | ce7451c | 2019-12-11 14:31:53 -0800 | [diff] [blame] | 77 | import java.util.concurrent.TimeUnit; |
| 78 | |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 79 | /** |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 80 | * A helper class to create {@link ExpandableNotificationRow} (for both individual and group |
| 81 | * notifications). |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 82 | */ |
| 83 | public class NotificationTestHelper { |
| 84 | |
Rohan Shah | 20790b8 | 2018-07-02 17:21:04 -0700 | [diff] [blame] | 85 | /** Package name for testing purposes. */ |
| 86 | public static final String PKG = "com.android.systemui"; |
| 87 | /** System UI id for testing purposes. */ |
| 88 | public static final int UID = 1000; |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 89 | /** Current {@link UserHandle} of the system. */ |
| 90 | public static final UserHandle USER_HANDLE = UserHandle.of(ActivityManager.getCurrentUser()); |
Rohan Shah | 20790b8 | 2018-07-02 17:21:04 -0700 | [diff] [blame] | 91 | |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 92 | private static final String GROUP_KEY = "gruKey"; |
Kevin Han | 5182c9f | 2020-01-07 17:54:43 -0800 | [diff] [blame] | 93 | private static final String APP_NAME = "appName"; |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 94 | |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 95 | private final Context mContext; |
Kevin Han | a7c21be | 2020-04-01 17:58:35 -0700 | [diff] [blame] | 96 | private final TestableLooper mTestLooper; |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 97 | private int mId; |
Selim Cinek | c3fec68 | 2019-06-06 18:11:07 -0700 | [diff] [blame] | 98 | private final NotificationGroupManager mGroupManager; |
Selim Cinek | 5ba2254 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 99 | private ExpandableNotificationRow mRow; |
Selim Cinek | c3fec68 | 2019-06-06 18:11:07 -0700 | [diff] [blame] | 100 | private HeadsUpManagerPhone mHeadsUpManager; |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 101 | private final NotifBindPipeline mBindPipeline; |
Kevin Han | 65c38b3 | 2020-02-05 16:42:40 -0800 | [diff] [blame] | 102 | private final NotifCollectionListener mBindPipelineEntryListener; |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 103 | private final RowContentBindStage mBindStage; |
Ned Burns | d8b5154 | 2020-03-13 20:52:43 -0400 | [diff] [blame] | 104 | private final IconManager mIconManager; |
Dave Mankoff | c0211ff | 2020-02-07 15:36:12 -0500 | [diff] [blame] | 105 | private StatusBarStateController mStatusBarStateController; |
Steve Elliott | 2304e4a | 2020-04-01 17:03:41 -0400 | [diff] [blame] | 106 | private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 107 | |
Kevin Han | a7c21be | 2020-04-01 17:58:35 -0700 | [diff] [blame] | 108 | public NotificationTestHelper( |
| 109 | Context context, |
| 110 | TestableDependency dependency, |
| 111 | TestableLooper testLooper) { |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 112 | mContext = context; |
Kevin Han | a7c21be | 2020-04-01 17:58:35 -0700 | [diff] [blame] | 113 | mTestLooper = testLooper; |
Lucas Dupin | d236ee3 | 2019-10-08 15:33:59 -0700 | [diff] [blame] | 114 | dependency.injectMockDependency(NotificationMediaManager.class); |
| 115 | dependency.injectMockDependency(BubbleController.class); |
wilsonshih | e832194 | 2019-10-18 18:39:46 +0800 | [diff] [blame] | 116 | dependency.injectMockDependency(NotificationShadeWindowController.class); |
Dave Mankoff | c0211ff | 2020-02-07 15:36:12 -0500 | [diff] [blame] | 117 | mStatusBarStateController = mock(StatusBarStateController.class); |
Steve Elliott | d38a70f | 2020-05-11 14:01:08 -0400 | [diff] [blame] | 118 | mGroupManager = new NotificationGroupManager( |
| 119 | mStatusBarStateController, |
| 120 | () -> mock(PeopleNotificationIdentifier.class)); |
Dave Mankoff | c0211ff | 2020-02-07 15:36:12 -0500 | [diff] [blame] | 121 | mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController, |
Beverly | 95a0802ac | 2020-02-10 15:27:40 -0500 | [diff] [blame] | 122 | mock(KeyguardBypassController.class), mock(NotificationGroupManager.class), |
| 123 | mock(ConfigurationControllerImpl.class)); |
Kevin | a97ea05 | 2018-09-11 13:53:18 -0700 | [diff] [blame] | 124 | mGroupManager.setHeadsUpManager(mHeadsUpManager); |
Ned Burns | d8b5154 | 2020-03-13 20:52:43 -0400 | [diff] [blame] | 125 | mIconManager = new IconManager( |
| 126 | mock(CommonNotifCollection.class), |
| 127 | mock(LauncherApps.class), |
| 128 | new IconBuilder(mContext)); |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 129 | |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 130 | NotificationContentInflater contentBinder = new NotificationContentInflater( |
| 131 | mock(NotifRemoteViewCache.class), |
Sergey Nikolaienkov | f6ad632 | 2020-02-10 15:46:32 +0100 | [diff] [blame] | 132 | mock(NotificationRemoteInputManager.class), |
| 133 | () -> mock(SmartReplyConstants.class), |
Steve Elliott | 46bb2a1 | 2020-03-17 11:04:09 -0400 | [diff] [blame] | 134 | () -> mock(SmartReplyController.class), |
Kevin Han | a6d5bb4 | 2020-03-17 13:55:49 -0700 | [diff] [blame] | 135 | mock(ConversationNotificationProcessor.class), |
| 136 | mock(Executor.class)); |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 137 | contentBinder.setInflateSynchronously(true); |
Kevin Han | c1f0448 | 2020-02-07 12:23:52 -0800 | [diff] [blame] | 138 | mBindStage = new RowContentBindStage(contentBinder, |
| 139 | mock(NotifInflationErrorManager.class), |
| 140 | mock(RowContentBindStageLogger.class)); |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 141 | |
Kevin Han | 65c38b3 | 2020-02-05 16:42:40 -0800 | [diff] [blame] | 142 | CommonNotifCollection collection = mock(CommonNotifCollection.class); |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 143 | |
Kevin Han | a7c21be | 2020-04-01 17:58:35 -0700 | [diff] [blame] | 144 | mBindPipeline = new NotifBindPipeline( |
| 145 | collection, |
| 146 | mock(NotifBindPipelineLogger.class), |
| 147 | mTestLooper.getLooper()); |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 148 | mBindPipeline.setStage(mBindStage); |
| 149 | |
Kevin Han | 65c38b3 | 2020-02-05 16:42:40 -0800 | [diff] [blame] | 150 | ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor = |
| 151 | ArgumentCaptor.forClass(NotifCollectionListener.class); |
| 152 | verify(collection).addCollectionListener(collectionListenerCaptor.capture()); |
| 153 | mBindPipelineEntryListener = collectionListenerCaptor.getValue(); |
Steve Elliott | 2304e4a | 2020-04-01 17:03:41 -0400 | [diff] [blame] | 154 | mPeopleNotificationIdentifier = mock(PeopleNotificationIdentifier.class); |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 155 | } |
| 156 | |
Kevin | d4660b2 | 2018-09-27 10:57:35 -0700 | [diff] [blame] | 157 | /** |
| 158 | * Creates a generic row. |
| 159 | * |
| 160 | * @return a generic row with no special properties. |
| 161 | * @throws Exception |
| 162 | */ |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 163 | public ExpandableNotificationRow createRow() throws Exception { |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 164 | return createRow(PKG, UID, USER_HANDLE); |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 165 | } |
| 166 | |
Kevin | d4660b2 | 2018-09-27 10:57:35 -0700 | [diff] [blame] | 167 | /** |
| 168 | * Create a row with the package and user id specified. |
| 169 | * |
| 170 | * @param pkg package |
| 171 | * @param uid user id |
| 172 | * @return a row with a notification using the package and user id |
| 173 | * @throws Exception |
| 174 | */ |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 175 | public ExpandableNotificationRow createRow(String pkg, int uid, UserHandle userHandle) |
| 176 | throws Exception { |
| 177 | return createRow(pkg, uid, userHandle, false /* isGroupSummary */, null /* groupKey */); |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 178 | } |
| 179 | |
Kevin | d4660b2 | 2018-09-27 10:57:35 -0700 | [diff] [blame] | 180 | /** |
| 181 | * Creates a row based off the notification given. |
| 182 | * |
| 183 | * @param notification the notification |
| 184 | * @return a row built off the notification |
| 185 | * @throws Exception |
| 186 | */ |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 187 | public ExpandableNotificationRow createRow(Notification notification) throws Exception { |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 188 | return generateRow(notification, PKG, UID, USER_HANDLE, 0 /* extraInflationFlags */); |
Kevin | d4660b2 | 2018-09-27 10:57:35 -0700 | [diff] [blame] | 189 | } |
| 190 | |
| 191 | /** |
| 192 | * Create a row with the specified content views inflated in addition to the default. |
| 193 | * |
| 194 | * @param extraInflationFlags the flags corresponding to the additional content views that |
| 195 | * should be inflated |
| 196 | * @return a row with the specified content views inflated in addition to the default |
| 197 | * @throws Exception |
| 198 | */ |
| 199 | public ExpandableNotificationRow createRow(@InflationFlag int extraInflationFlags) |
| 200 | throws Exception { |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 201 | return generateRow(createNotification(), PKG, UID, USER_HANDLE, extraInflationFlags); |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 202 | } |
| 203 | |
| 204 | /** |
| 205 | * Returns an {@link ExpandableNotificationRow} group with the given number of child |
| 206 | * notifications. |
| 207 | */ |
| 208 | public ExpandableNotificationRow createGroup(int numChildren) throws Exception { |
| 209 | ExpandableNotificationRow row = createGroupSummary(GROUP_KEY); |
| 210 | for (int i = 0; i < numChildren; i++) { |
| 211 | ExpandableNotificationRow childRow = createGroupChild(GROUP_KEY); |
| 212 | row.addChildNotification(childRow); |
| 213 | } |
| 214 | return row; |
| 215 | } |
| 216 | |
| 217 | /** Returns a group notification with 2 child notifications. */ |
| 218 | public ExpandableNotificationRow createGroup() throws Exception { |
| 219 | return createGroup(2); |
| 220 | } |
| 221 | |
| 222 | private ExpandableNotificationRow createGroupSummary(String groupkey) throws Exception { |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 223 | return createRow(PKG, UID, USER_HANDLE, true /* isGroupSummary */, groupkey); |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 224 | } |
| 225 | |
| 226 | private ExpandableNotificationRow createGroupChild(String groupkey) throws Exception { |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 227 | return createRow(PKG, UID, USER_HANDLE, false /* isGroupSummary */, groupkey); |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 228 | } |
| 229 | |
| 230 | /** |
| 231 | * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble. |
Mark Renouf | fec45da | 2019-03-13 13:24:27 -0400 | [diff] [blame] | 232 | */ |
Beverly | a53fb0d | 2020-01-29 15:26:13 -0500 | [diff] [blame] | 233 | public ExpandableNotificationRow createBubbleInGroup() |
| 234 | throws Exception { |
| 235 | return createBubble(makeBubbleMetadata(null), PKG, true); |
| 236 | } |
| 237 | |
| 238 | /** |
| 239 | * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble. |
| 240 | */ |
Mark Renouf | fec45da | 2019-03-13 13:24:27 -0400 | [diff] [blame] | 241 | public ExpandableNotificationRow createBubble() |
| 242 | throws Exception { |
Beverly | a53fb0d | 2020-01-29 15:26:13 -0500 | [diff] [blame] | 243 | return createBubble(makeBubbleMetadata(null), PKG, false); |
Mark Renouf | fec45da | 2019-03-13 13:24:27 -0400 | [diff] [blame] | 244 | } |
| 245 | |
| 246 | /** |
| 247 | * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble. |
Mark Renouf | 08bc42a | 2019-03-07 13:01:59 -0500 | [diff] [blame] | 248 | * |
| 249 | * @param deleteIntent the intent to assign to {@link BubbleMetadata#deleteIntent} |
| 250 | */ |
| 251 | public ExpandableNotificationRow createBubble(@Nullable PendingIntent deleteIntent) |
| 252 | throws Exception { |
Beverly | a53fb0d | 2020-01-29 15:26:13 -0500 | [diff] [blame] | 253 | return createBubble(makeBubbleMetadata(deleteIntent), PKG, false); |
Mady Mellor | e80930e | 2019-03-21 16:00:45 -0700 | [diff] [blame] | 254 | } |
| 255 | |
| 256 | /** |
| 257 | * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble. |
| 258 | * |
| 259 | * @param bubbleMetadata the {@link BubbleMetadata} to use |
| 260 | */ |
| 261 | public ExpandableNotificationRow createBubble(BubbleMetadata bubbleMetadata, String pkg) |
| 262 | throws Exception { |
Beverly | a53fb0d | 2020-01-29 15:26:13 -0500 | [diff] [blame] | 263 | return createBubble(bubbleMetadata, pkg, false); |
| 264 | } |
| 265 | |
| 266 | private ExpandableNotificationRow createBubble(BubbleMetadata bubbleMetadata, String pkg, |
| 267 | boolean inGroup) |
| 268 | throws Exception { |
Mady Mellor | e80930e | 2019-03-21 16:00:45 -0700 | [diff] [blame] | 269 | Notification n = createNotification(false /* isGroupSummary */, |
Beverly | a53fb0d | 2020-01-29 15:26:13 -0500 | [diff] [blame] | 270 | inGroup ? GROUP_KEY : null /* groupKey */, bubbleMetadata); |
Mady Mellor | fc02cc3 | 2019-04-01 14:47:55 -0700 | [diff] [blame] | 271 | n.flags |= FLAG_BUBBLE; |
Mady Mellor | aa8fef2 | 2019-04-11 13:36:40 -0700 | [diff] [blame] | 272 | ExpandableNotificationRow row = generateRow(n, pkg, UID, USER_HANDLE, |
| 273 | 0 /* extraInflationFlags */, IMPORTANCE_HIGH); |
Ned Burns | 47c98f1 | 2019-09-06 17:12:07 -0400 | [diff] [blame] | 274 | modifyRanking(row.getEntry()) |
| 275 | .setCanBubble(true) |
| 276 | .build(); |
Mady Mellor | aa8fef2 | 2019-04-11 13:36:40 -0700 | [diff] [blame] | 277 | return row; |
Mady Mellor | e80930e | 2019-03-21 16:00:45 -0700 | [diff] [blame] | 278 | } |
| 279 | |
| 280 | /** |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 281 | * Creates a notification row with the given details. |
| 282 | * |
| 283 | * @param pkg package used for creating a {@link StatusBarNotification} |
| 284 | * @param uid uid used for creating a {@link StatusBarNotification} |
| 285 | * @param isGroupSummary whether the notification row is a group summary |
| 286 | * @param groupKey the group key for the notification group used across notifications |
| 287 | * @return a row with that's either a standalone notification or a group notification if the |
| 288 | * groupKey is non-null |
| 289 | * @throws Exception |
| 290 | */ |
| 291 | private ExpandableNotificationRow createRow( |
| 292 | String pkg, |
| 293 | int uid, |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 294 | UserHandle userHandle, |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 295 | boolean isGroupSummary, |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 296 | @Nullable String groupKey) |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 297 | throws Exception { |
Kevin | d4660b2 | 2018-09-27 10:57:35 -0700 | [diff] [blame] | 298 | Notification notif = createNotification(isGroupSummary, groupKey); |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 299 | return generateRow(notif, pkg, uid, userHandle, 0 /* inflationFlags */); |
Kevin | d4660b2 | 2018-09-27 10:57:35 -0700 | [diff] [blame] | 300 | } |
| 301 | |
| 302 | /** |
| 303 | * Creates a generic notification. |
| 304 | * |
| 305 | * @return a notification with no special properties |
| 306 | */ |
Julia Reynolds | 0c24500 | 2019-03-27 16:10:11 -0400 | [diff] [blame] | 307 | public Notification createNotification() { |
Kevin | d4660b2 | 2018-09-27 10:57:35 -0700 | [diff] [blame] | 308 | return createNotification(false /* isGroupSummary */, null /* groupKey */); |
| 309 | } |
| 310 | |
| 311 | /** |
| 312 | * Creates a notification with the given parameters. |
| 313 | * |
| 314 | * @param isGroupSummary whether the notification is a group summary |
| 315 | * @param groupKey the group key for the notification group used across notifications |
| 316 | * @return a notification that is in the group specified or standalone if unspecified |
| 317 | */ |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 318 | private Notification createNotification(boolean isGroupSummary, @Nullable String groupKey) { |
Mady Mellor | e80930e | 2019-03-21 16:00:45 -0700 | [diff] [blame] | 319 | return createNotification(isGroupSummary, groupKey, null /* bubble metadata */); |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 320 | } |
| 321 | |
| 322 | /** |
| 323 | * Creates a notification with the given parameters. |
| 324 | * |
| 325 | * @param isGroupSummary whether the notification is a group summary |
| 326 | * @param groupKey the group key for the notification group used across notifications |
Mady Mellor | e80930e | 2019-03-21 16:00:45 -0700 | [diff] [blame] | 327 | * @param bubbleMetadata the bubble metadata to use for this notification if it exists. |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 328 | * @return a notification that is in the group specified or standalone if unspecified |
| 329 | */ |
Mark Renouf | 9ba6cea | 2019-04-17 11:53:50 -0400 | [diff] [blame] | 330 | public Notification createNotification(boolean isGroupSummary, |
Mady Mellor | e80930e | 2019-03-21 16:00:45 -0700 | [diff] [blame] | 331 | @Nullable String groupKey, @Nullable BubbleMetadata bubbleMetadata) { |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 332 | Notification publicVersion = new Notification.Builder(mContext).setSmallIcon( |
| 333 | R.drawable.ic_person) |
| 334 | .setCustomContentView(new RemoteViews(mContext.getPackageName(), |
| 335 | R.layout.custom_view_dark)) |
| 336 | .build(); |
Kevin | d4660b2 | 2018-09-27 10:57:35 -0700 | [diff] [blame] | 337 | Notification.Builder notificationBuilder = new Notification.Builder(mContext, "channelId") |
| 338 | .setSmallIcon(R.drawable.ic_person) |
| 339 | .setContentTitle("Title") |
| 340 | .setContentText("Text") |
Kevin | d5022f9 | 2018-10-08 18:30:26 -0700 | [diff] [blame] | 341 | .setPublicVersion(publicVersion) |
| 342 | .setStyle(new Notification.BigTextStyle().bigText("Big Text")); |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 343 | if (isGroupSummary) { |
| 344 | notificationBuilder.setGroupSummary(true); |
| 345 | } |
| 346 | if (!TextUtils.isEmpty(groupKey)) { |
| 347 | notificationBuilder.setGroup(groupKey); |
| 348 | } |
Mady Mellor | e80930e | 2019-03-21 16:00:45 -0700 | [diff] [blame] | 349 | if (bubbleMetadata != null) { |
| 350 | notificationBuilder.setBubbleMetadata(bubbleMetadata); |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 351 | } |
Kevin | d4660b2 | 2018-09-27 10:57:35 -0700 | [diff] [blame] | 352 | return notificationBuilder.build(); |
Julia Reynolds | fc64001 | 2018-02-21 12:25:27 -0500 | [diff] [blame] | 353 | } |
| 354 | |
Dave Mankoff | c0211ff | 2020-02-07 15:36:12 -0500 | [diff] [blame] | 355 | public StatusBarStateController getStatusBarStateController() { |
| 356 | return mStatusBarStateController; |
| 357 | } |
| 358 | |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 359 | private ExpandableNotificationRow generateRow( |
| 360 | Notification notification, |
| 361 | String pkg, |
| 362 | int uid, |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 363 | UserHandle userHandle, |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 364 | @InflationFlag int extraInflationFlags) |
| 365 | throws Exception { |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 366 | return generateRow(notification, pkg, uid, userHandle, extraInflationFlags, |
| 367 | IMPORTANCE_DEFAULT); |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 368 | } |
| 369 | |
| 370 | private ExpandableNotificationRow generateRow( |
| 371 | Notification notification, |
| 372 | String pkg, |
| 373 | int uid, |
Yohei Yukawa | 7276946 | 2019-01-20 09:28:08 -0800 | [diff] [blame] | 374 | UserHandle userHandle, |
Mady Mellor | b4991e6 | 2019-01-10 15:14:51 -0800 | [diff] [blame] | 375 | @InflationFlag int extraInflationFlags, |
| 376 | int importance) |
Julia Reynolds | fc64001 | 2018-02-21 12:25:27 -0500 | [diff] [blame] | 377 | throws Exception { |
Selim Cinek | 1079067 | 2017-03-08 16:33:05 -0800 | [diff] [blame] | 378 | LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( |
| 379 | mContext.LAYOUT_INFLATER_SERVICE); |
Jason Monk | 6dceace | 2018-05-15 20:24:07 -0400 | [diff] [blame] | 380 | mRow = (ExpandableNotificationRow) inflater.inflate( |
| 381 | R.layout.status_bar_notification_row, |
| 382 | null /* root */, |
| 383 | false /* attachToRoot */); |
Selim Cinek | 5ba2254 | 2017-04-20 15:16:10 -0700 | [diff] [blame] | 384 | ExpandableNotificationRow row = mRow; |
Ned Burns | 636c379 | 2019-09-11 16:59:07 -0400 | [diff] [blame] | 385 | |
Ned Burns | 296aec16 | 2019-09-04 17:30:59 -0400 | [diff] [blame] | 386 | final NotificationChannel channel = |
| 387 | new NotificationChannel( |
| 388 | notification.getChannelId(), |
| 389 | notification.getChannelId(), |
| 390 | importance); |
Hall Liu | 9866aa8 | 2020-03-12 12:55:50 -0700 | [diff] [blame] | 391 | channel.setBlockable(true); |
Ned Burns | 296aec16 | 2019-09-04 17:30:59 -0400 | [diff] [blame] | 392 | |
Ned Burns | 636c379 | 2019-09-11 16:59:07 -0400 | [diff] [blame] | 393 | NotificationEntry entry = new NotificationEntryBuilder() |
| 394 | .setPkg(pkg) |
| 395 | .setOpPkg(pkg) |
| 396 | .setId(mId++) |
| 397 | .setUid(uid) |
| 398 | .setInitialPid(2000) |
| 399 | .setNotification(notification) |
| 400 | .setUser(userHandle) |
| 401 | .setPostTime(System.currentTimeMillis()) |
| 402 | .setChannel(channel) |
| 403 | .build(); |
| 404 | |
Evan Laird | 9449285 | 2018-10-25 13:43:01 -0400 | [diff] [blame] | 405 | entry.setRow(row); |
Ned Burns | d8b5154 | 2020-03-13 20:52:43 -0400 | [diff] [blame] | 406 | mIconManager.createIcons(entry); |
Kevin | d4660b2 | 2018-09-27 10:57:35 -0700 | [diff] [blame] | 407 | row.setEntry(entry); |
Kevin Han | 5182c9f | 2020-01-07 17:54:43 -0800 | [diff] [blame] | 408 | |
Kevin Han | 65c38b3 | 2020-02-05 16:42:40 -0800 | [diff] [blame] | 409 | mBindPipelineEntryListener.onEntryInit(entry); |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 410 | mBindPipeline.manageRow(entry, row); |
Kevin Han | 5182c9f | 2020-01-07 17:54:43 -0800 | [diff] [blame] | 411 | |
| 412 | row.initialize( |
| 413 | APP_NAME, |
| 414 | entry.getKey(), |
| 415 | mock(ExpansionLogger.class), |
| 416 | mock(KeyguardBypassController.class), |
| 417 | mGroupManager, |
| 418 | mHeadsUpManager, |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 419 | mBindStage, |
Dave Mankoff | c0211ff | 2020-02-07 15:36:12 -0500 | [diff] [blame] | 420 | mock(OnExpandClickListener.class), |
| 421 | mock(NotificationMediaManager.class), |
| 422 | mock(ExpandableNotificationRow.OnAppOpsClickListener.class), |
| 423 | mock(FalsingManager.class), |
Steve Elliott | 2304e4a | 2020-04-01 17:03:41 -0400 | [diff] [blame] | 424 | mStatusBarStateController, |
| 425 | mPeopleNotificationIdentifier); |
Kevin Han | 5182c9f | 2020-01-07 17:54:43 -0800 | [diff] [blame] | 426 | row.setAboveShelfChangedListener(aboveShelf -> { }); |
Kevin Han | 933dc7c | 2020-01-29 11:17:46 -0800 | [diff] [blame] | 427 | mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); |
Kevin Han | a7c21be | 2020-04-01 17:58:35 -0700 | [diff] [blame] | 428 | inflateAndWait(entry); |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 429 | |
Rohan Shah | 524cf7b | 2018-03-15 14:40:02 -0700 | [diff] [blame] | 430 | // This would be done as part of onAsyncInflationFinished, but we skip large amounts of |
| 431 | // the callback chain, so we need to make up for not adding it to the group manager |
| 432 | // here. |
| 433 | mGroupManager.onEntryAdded(entry); |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 434 | return row; |
| 435 | } |
Mady Mellor | c39b4ae | 2019-01-09 17:11:37 -0800 | [diff] [blame] | 436 | |
Kevin Han | a7c21be | 2020-04-01 17:58:35 -0700 | [diff] [blame] | 437 | private void inflateAndWait(NotificationEntry entry) throws Exception { |
Kevin Han | ce7451c | 2019-12-11 14:31:53 -0800 | [diff] [blame] | 438 | CountDownLatch countDownLatch = new CountDownLatch(1); |
Kevin Han | a7c21be | 2020-04-01 17:58:35 -0700 | [diff] [blame] | 439 | mBindStage.requestRebind(entry, en -> countDownLatch.countDown()); |
| 440 | mTestLooper.processAllMessages(); |
Kevin Han | ce7451c | 2019-12-11 14:31:53 -0800 | [diff] [blame] | 441 | assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS)); |
| 442 | } |
| 443 | |
Mark Renouf | 08bc42a | 2019-03-07 13:01:59 -0500 | [diff] [blame] | 444 | private BubbleMetadata makeBubbleMetadata(PendingIntent deleteIntent) { |
Mark Renouf | a9fbb6e | 2019-02-11 12:48:22 -0500 | [diff] [blame] | 445 | Intent target = new Intent(mContext, BubblesTestActivity.class); |
| 446 | PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, 0); |
Mark Renouf | 08bc42a | 2019-03-07 13:01:59 -0500 | [diff] [blame] | 447 | |
Mady Mellor | aa9ce17 | 2020-03-17 10:34:20 -0700 | [diff] [blame] | 448 | return new BubbleMetadata.Builder(bubbleIntent, |
Mady Mellor | 2ac2d3a | 2020-01-08 17:18:54 -0800 | [diff] [blame] | 449 | Icon.createWithResource(mContext, R.drawable.android)) |
Mark Renouf | 08bc42a | 2019-03-07 13:01:59 -0500 | [diff] [blame] | 450 | .setDeleteIntent(deleteIntent) |
Mady Mellor | c39b4ae | 2019-01-09 17:11:37 -0800 | [diff] [blame] | 451 | .setDesiredHeight(314) |
| 452 | .build(); |
| 453 | } |
Selim Cinek | 6fd06b5 | 2017-03-07 15:54:10 -0800 | [diff] [blame] | 454 | } |