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