1 /*
2  * Copyright (C) 2019 The Android Open Source Project
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
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.screenshot;
18 
19 import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType.REGULAR_SMART_ACTIONS;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.assertTrue;
25 import static org.mockito.ArgumentMatchers.any;
26 import static org.mockito.ArgumentMatchers.anyLong;
27 import static org.mockito.Mockito.doThrow;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.times;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 
34 import android.app.Notification;
35 import android.content.Intent;
36 import android.graphics.Bitmap;
37 import android.net.Uri;
38 import android.os.Bundle;
39 import android.os.Handler;
40 import android.os.Looper;
41 import android.os.UserHandle;
42 import android.testing.AndroidTestingRunner;
43 
44 import androidx.test.filters.SmallTest;
45 
46 import com.android.systemui.SysuiTestCase;
47 import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
48 
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 
53 import java.util.Collections;
54 import java.util.List;
55 import java.util.concurrent.CompletableFuture;
56 import java.util.concurrent.TimeUnit;
57 
58 @SmallTest
59 @RunWith(AndroidTestingRunner.class)
60 /**
61  * Tests for exception handling and  bitmap configuration in adding smart actions to Screenshot
62  * Notification.
63  */
64 public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
65     private ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
66     private ScreenshotSmartActions mScreenshotSmartActions;
67     private Handler mHandler;
68 
69     @Before
setup()70     public void setup() {
71         mSmartActionsProvider = mock(
72                 ScreenshotNotificationSmartActionsProvider.class);
73         mScreenshotSmartActions = new ScreenshotSmartActions(() -> mSmartActionsProvider);
74         mHandler = mock(Handler.class);
75     }
76 
77     // Tests any exception thrown in getting smart actions future does not affect regular
78     // screenshot flow.
79     @Test
testExceptionHandlingInGetSmartActionsFuture()80     public void testExceptionHandlingInGetSmartActionsFuture()
81             throws Exception {
82         Bitmap bitmap = mock(Bitmap.class);
83         when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
84         ScreenshotNotificationSmartActionsProvider smartActionsProvider = mock(
85                 ScreenshotNotificationSmartActionsProvider.class);
86         when(smartActionsProvider.getActions(any(), any(), any(), any(), any(), any()))
87             .thenThrow(RuntimeException.class);
88         CompletableFuture<List<Notification.Action>> smartActionsFuture =
89                 mScreenshotSmartActions.getSmartActionsFuture(
90                         "", Uri.parse("content://authority/data"), bitmap, smartActionsProvider,
91                         REGULAR_SMART_ACTIONS,
92                         true, UserHandle.of(UserHandle.myUserId()));
93         assertNotNull(smartActionsFuture);
94         List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
95         assertEquals(Collections.emptyList(), smartActions);
96     }
97 
98     // Tests any exception thrown in waiting for smart actions future to complete does
99     // not affect regular screenshot flow.
100     @Test
testExceptionHandlingInGetSmartActions()101     public void testExceptionHandlingInGetSmartActions()
102             throws Exception {
103         CompletableFuture<List<Notification.Action>> smartActionsFuture = mock(
104                 CompletableFuture.class);
105         int timeoutMs = 1000;
106         when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow(
107                 RuntimeException.class);
108         List<Notification.Action> actions = mScreenshotSmartActions.getSmartActions(
109                 "", smartActionsFuture, timeoutMs, mSmartActionsProvider, REGULAR_SMART_ACTIONS);
110         assertEquals(Collections.emptyList(), actions);
111     }
112 
113     // Tests any exception thrown in notifying feedback does not affect regular screenshot flow.
114     @Test
testExceptionHandlingInNotifyingFeedback()115     public void testExceptionHandlingInNotifyingFeedback()
116             throws Exception {
117         doThrow(RuntimeException.class).when(mSmartActionsProvider).notifyOp(any(), any(), any(),
118                 anyLong());
119         mScreenshotSmartActions.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
120     }
121 
122     // Tests for a non-hardware bitmap, ScreenshotNotificationSmartActionsProvider is never invoked
123     // and a completed future is returned.
124     @Test
testUnsupportedBitmapConfiguration()125     public void testUnsupportedBitmapConfiguration()
126             throws Exception {
127         Bitmap bitmap = mock(Bitmap.class);
128         when(bitmap.getConfig()).thenReturn(Bitmap.Config.RGB_565);
129         CompletableFuture<List<Notification.Action>> smartActionsFuture =
130                 mScreenshotSmartActions.getSmartActionsFuture(
131                         "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
132                         REGULAR_SMART_ACTIONS,
133                         true, UserHandle.of(UserHandle.myUserId()));
134         verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any(), any());
135         assertNotNull(smartActionsFuture);
136         List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
137         assertEquals(Collections.emptyList(), smartActions);
138     }
139 
140     // Tests for a hardware bitmap, ScreenshotNotificationSmartActionsProvider is invoked once.
141     @Test
testScreenshotNotificationSmartActionsProviderInvokedOnce()142     public void testScreenshotNotificationSmartActionsProviderInvokedOnce() {
143         Bitmap bitmap = mock(Bitmap.class);
144         when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
145         mScreenshotSmartActions.getSmartActionsFuture(
146                 "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
147                 REGULAR_SMART_ACTIONS, true,
148                 UserHandle.of(UserHandle.myUserId()));
149         verify(mSmartActionsProvider, times(1)).getActions(
150                 any(), any(), any(), any(), any(), any());
151     }
152 
153     // Tests for a hardware bitmap, a completed future is returned.
154     @Test
testSupportedBitmapConfiguration()155     public void testSupportedBitmapConfiguration()
156             throws Exception {
157         Bitmap bitmap = mock(Bitmap.class);
158         when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
159         ScreenshotNotificationSmartActionsProvider actionsProvider =
160                 new ScreenshotNotificationSmartActionsProvider();
161         CompletableFuture<List<Notification.Action>> smartActionsFuture =
162                 mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
163                         actionsProvider, REGULAR_SMART_ACTIONS,
164                         true, UserHandle.of(UserHandle.myUserId()));
165         assertNotNull(smartActionsFuture);
166         List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
167         assertEquals(smartActions.size(), 0);
168     }
169 
170     // Tests for share action extras
171     @Test
testShareActionExtras()172     public void testShareActionExtras() {
173         if (Looper.myLooper() == null) {
174             Looper.prepare();
175         }
176 
177         ScreenshotController.SaveImageInBackgroundData
178                 data = new ScreenshotController.SaveImageInBackgroundData();
179         data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
180         data.finisher = null;
181         data.mActionsReadyListener = null;
182         SaveImageInBackgroundTask task =
183                 new SaveImageInBackgroundTask(mContext, null, null, mScreenshotSmartActions, data,
184                         ActionTransition::new, mSmartActionsProvider);
185 
186         Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(),
187                 Uri.parse("Screenshot_123.png"), true).get().action;
188 
189         Intent intent = shareAction.actionIntent.getIntent();
190         assertNotNull(intent);
191         Bundle bundle = intent.getExtras();
192         assertTrue(bundle.containsKey(ScreenshotController.EXTRA_ID));
193         assertTrue(bundle.containsKey(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED));
194         assertEquals(ScreenshotController.ACTION_TYPE_SHARE, shareAction.title);
195         assertEquals(Intent.ACTION_SEND, intent.getAction());
196     }
197 
198     // Tests for edit action extras
199     @Test
testEditActionExtras()200     public void testEditActionExtras() {
201         if (Looper.myLooper() == null) {
202             Looper.prepare();
203         }
204 
205         ScreenshotController.SaveImageInBackgroundData
206                 data = new ScreenshotController.SaveImageInBackgroundData();
207         data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
208         data.finisher = null;
209         data.mActionsReadyListener = null;
210         SaveImageInBackgroundTask task =
211                 new SaveImageInBackgroundTask(mContext, null, null, mScreenshotSmartActions, data,
212                         ActionTransition::new, mSmartActionsProvider);
213 
214         Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(),
215                 Uri.parse("Screenshot_123.png"), true).get().action;
216 
217         Intent intent = editAction.actionIntent.getIntent();
218         assertNotNull(intent);
219         Bundle bundle = intent.getExtras();
220         assertTrue(bundle.containsKey(ScreenshotController.EXTRA_ID));
221         assertTrue(bundle.containsKey(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED));
222         assertEquals(ScreenshotController.ACTION_TYPE_EDIT, editAction.title);
223         assertEquals(Intent.ACTION_EDIT, intent.getAction());
224     }
225 
226     // Tests for share action extras
227     @Test
testDeleteActionExtras()228     public void testDeleteActionExtras() {
229         if (Looper.myLooper() == null) {
230             Looper.prepare();
231         }
232 
233         ScreenshotController.SaveImageInBackgroundData
234                 data = new ScreenshotController.SaveImageInBackgroundData();
235         data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
236         data.finisher = null;
237         data.mActionsReadyListener = null;
238         SaveImageInBackgroundTask task =
239                 new SaveImageInBackgroundTask(mContext, null, null, mScreenshotSmartActions, data,
240                         ActionTransition::new, mSmartActionsProvider);
241 
242         Notification.Action deleteAction = task.createDeleteAction(mContext,
243                 mContext.getResources(),
244                 Uri.parse("Screenshot_123.png"), true);
245 
246         Intent intent = deleteAction.actionIntent.getIntent();
247         assertNotNull(intent);
248         Bundle bundle = intent.getExtras();
249         assertTrue(bundle.containsKey(ScreenshotController.EXTRA_ID));
250         assertTrue(bundle.containsKey(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED));
251         assertEquals(deleteAction.title, ScreenshotController.ACTION_TYPE_DELETE);
252         assertNull(intent.getAction());
253     }
254 }
255