1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui.statusbar;
16 
17 import static junit.framework.Assert.assertFalse;
18 import static junit.framework.Assert.assertTrue;
19 
20 import static org.mockito.ArgumentMatchers.anyString;
21 import static org.mockito.ArgumentMatchers.argThat;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.verify;
24 import static org.mockito.Mockito.when;
25 
26 import android.app.ActivityManager;
27 import android.app.Notification;
28 import android.os.Handler;
29 import android.os.RemoteException;
30 import android.os.UserHandle;
31 import android.service.notification.StatusBarNotification;
32 import android.testing.TestableLooper;
33 
34 import androidx.test.ext.junit.runners.AndroidJUnit4;
35 import androidx.test.filters.SmallTest;
36 
37 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
38 import com.android.internal.statusbar.IStatusBarService;
39 import com.android.systemui.SysuiTestCase;
40 import com.android.systemui.dump.DumpManager;
41 import com.android.systemui.res.R;
42 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
43 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
44 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
45 import com.android.systemui.statusbar.notification.collection.coordinator.RemoteInputCoordinator;
46 import com.android.systemui.statusbar.notification.collection.notifcollection.InternalNotifUpdater;
47 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
48 
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.mockito.Mock;
53 import org.mockito.MockitoAnnotations;
54 
55 @RunWith(AndroidJUnit4.class)
56 @TestableLooper.RunWithLooper
57 @SmallTest
58 public class SmartReplyControllerTest extends SysuiTestCase {
59     private static final String TEST_PACKAGE_NAME = "test";
60     private static final int TEST_UID = 0;
61     private static final String TEST_CHOICE_TEXT = "A Reply";
62     private static final int TEST_CHOICE_INDEX = 2;
63     private static final int TEST_CHOICE_COUNT = 4;
64     private static final int TEST_ACTION_COUNT = 3;
65 
66     private NotificationEntry mEntry;
67     private SmartReplyController mSmartReplyController;
68 
69     @Mock private NotificationVisibilityProvider mVisibilityProvider;
70     @Mock private StatusBarNotification mSbn;
71     @Mock private IStatusBarService mIStatusBarService;
72     @Mock private NotificationClickNotifier mClickNotifier;
73     @Mock private InternalNotifUpdater mInternalNotifUpdater;
74 
75     @Before
setUp()76     public void setUp() {
77         MockitoAnnotations.initMocks(this);
78 
79         mSmartReplyController = new SmartReplyController(
80                 mock(DumpManager.class),
81                 mVisibilityProvider,
82                 mIStatusBarService,
83                 mClickNotifier);
84         RemoteInputCoordinator remoteInputCoordinator = new RemoteInputCoordinator(
85                 mock(DumpManager.class),
86                 new RemoteInputNotificationRebuilder(mContext),
87                 mock(NotificationRemoteInputManager.class),
88                 mock(Handler.class),
89                 mSmartReplyController);
90         remoteInputCoordinator.setRemoteInputController(mock(RemoteInputController.class));
91         NotifPipeline notifPipeline = mock(NotifPipeline.class);
92         when(notifPipeline.getInternalNotifUpdater(anyString())).thenReturn(mInternalNotifUpdater);
93         remoteInputCoordinator.attach(notifPipeline);
94 
95         Notification notification = new Notification.Builder(mContext, "")
96                 .setSmallIcon(R.drawable.ic_person)
97                 .setContentTitle("Title")
98                 .setContentText("Text").build();
99         mSbn = new StatusBarNotification(
100                 TEST_PACKAGE_NAME,
101                 TEST_PACKAGE_NAME,
102                 0,
103                 null,
104                 TEST_UID,
105                 0,
106                 notification,
107                 new UserHandle(ActivityManager.getCurrentUser()),
108                 null,
109                 0);
110         mEntry = new NotificationEntryBuilder()
111                 .setSbn(mSbn)
112                 .build();
113     }
114 
115     @Test
testSendSmartReply_updatesRemoteInput()116     public void testSendSmartReply_updatesRemoteInput() {
117         mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT,
118                 MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */);
119 
120         // Sending smart reply should update the notification with reply and spinner.
121         verify(mInternalNotifUpdater).onInternalNotificationUpdate(
122                 argThat(sbn -> sbn.getKey().equals(mSbn.getKey())), anyString());
123     }
124 
125     @Test
testSendSmartReply_logsToStatusBar()126     public void testSendSmartReply_logsToStatusBar() throws RemoteException {
127         mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT,
128                 MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */);
129 
130         // Check we log the result to the status bar service.
131         verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
132                 TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, MetricsEvent.LOCATION_UNKNOWN, false);
133     }
134 
135 
136     @Test
testSendSmartReply_logsToStatusBar_modifiedBeforeSending()137     public void testSendSmartReply_logsToStatusBar_modifiedBeforeSending() throws RemoteException {
138         mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT,
139                 MetricsEvent.LOCATION_UNKNOWN, true /* modifiedBeforeSending */);
140 
141         // Check we log the result to the status bar service.
142         verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
143                 TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, MetricsEvent.LOCATION_UNKNOWN, true);
144     }
145 
146     @Test
testShowSmartSuggestions_logsToStatusBar()147     public void testShowSmartSuggestions_logsToStatusBar() throws RemoteException {
148         final boolean generatedByAsssistant = true;
149         final boolean editBeforeSending = true;
150         mSmartReplyController.smartSuggestionsAdded(mEntry, TEST_CHOICE_COUNT, TEST_ACTION_COUNT,
151                 generatedByAsssistant, editBeforeSending);
152 
153         // Check we log the result to the status bar service.
154         verify(mIStatusBarService).onNotificationSmartSuggestionsAdded(mSbn.getKey(),
155                 TEST_CHOICE_COUNT, TEST_ACTION_COUNT, generatedByAsssistant, editBeforeSending);
156     }
157 
158     @Test
testSendSmartReply_reportsSending()159     public void testSendSmartReply_reportsSending() {
160         mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT,
161                 MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */);
162 
163         assertTrue(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
164     }
165 
166     @Test
testSendingSmartReply_afterRemove_shouldReturnFalse()167     public void testSendingSmartReply_afterRemove_shouldReturnFalse() {
168         mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT,
169                 MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */);
170         mSmartReplyController.stopSending(mEntry);
171 
172         assertFalse(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
173     }
174 }
175