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.LogConfig.DEBUG_ACTIONS;
20 import static com.android.systemui.screenshot.LogConfig.logTag;
21 import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType;
22 
23 import android.app.ActivityManager;
24 import android.app.Notification;
25 import android.content.ComponentName;
26 import android.content.Intent;
27 import android.graphics.Bitmap;
28 import android.net.Uri;
29 import android.os.SystemClock;
30 import android.os.UserHandle;
31 import android.util.Log;
32 
33 import com.android.systemui.dagger.SysUISingleton;
34 import com.android.systemui.shared.system.ActivityManagerWrapper;
35 
36 import java.util.Collections;
37 import java.util.List;
38 import java.util.concurrent.CompletableFuture;
39 import java.util.concurrent.TimeUnit;
40 import java.util.concurrent.TimeoutException;
41 
42 import javax.inject.Inject;
43 import javax.inject.Provider;
44 
45 /**
46  * Collects the static functions for retrieving and acting on smart actions.
47  */
48 @SysUISingleton
49 public class ScreenshotSmartActions {
50     private static final String TAG = logTag(ScreenshotSmartActions.class);
51     private final Provider<ScreenshotNotificationSmartActionsProvider>
52             mScreenshotNotificationSmartActionsProviderProvider;
53 
54     @Inject
ScreenshotSmartActions( Provider<ScreenshotNotificationSmartActionsProvider> screenshotNotificationSmartActionsProviderProvider )55     public ScreenshotSmartActions(
56             Provider<ScreenshotNotificationSmartActionsProvider>
57                     screenshotNotificationSmartActionsProviderProvider
58     ) {
59         mScreenshotNotificationSmartActionsProviderProvider =
60                 screenshotNotificationSmartActionsProviderProvider;
61     }
62 
getSmartActionsFuture( String screenshotId, Uri screenshotUri, Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotSmartActionType actionType, boolean smartActionsEnabled, UserHandle userHandle)63     CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
64             String screenshotId, Uri screenshotUri, Bitmap image,
65             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
66             ScreenshotSmartActionType actionType,
67             boolean smartActionsEnabled, UserHandle userHandle) {
68         if (DEBUG_ACTIONS) {
69             Log.d(TAG, String.format(
70                     "getSmartActionsFuture id=%s, uri=%s, provider=%s, actionType=%s, "
71                             + "smartActionsEnabled=%b, userHandle=%s",
72                     screenshotId, screenshotUri, smartActionsProvider.getClass(), actionType,
73                     smartActionsEnabled, userHandle));
74         }
75         if (!smartActionsEnabled) {
76             if (DEBUG_ACTIONS) {
77                 Log.d(TAG, "Screenshot Intelligence not enabled, returning empty list.");
78             }
79             return CompletableFuture.completedFuture(Collections.emptyList());
80         }
81         if (image.getConfig() != Bitmap.Config.HARDWARE) {
82             if (DEBUG_ACTIONS) {
83                 Log.d(TAG, String.format("Bitmap expected: Hardware, Bitmap found: %s. "
84                         + "Returning empty list.", image.getConfig()));
85             }
86             return CompletableFuture.completedFuture(Collections.emptyList());
87         }
88         CompletableFuture<List<Notification.Action>> smartActionsFuture;
89         long startTimeMs = SystemClock.uptimeMillis();
90         try {
91             ActivityManager.RunningTaskInfo runningTask =
92                     ActivityManagerWrapper.getInstance().getRunningTask();
93             ComponentName componentName =
94                     (runningTask != null && runningTask.topActivity != null)
95                             ? runningTask.topActivity
96                             : new ComponentName("", "");
97             smartActionsFuture = smartActionsProvider.getActions(screenshotId, screenshotUri, image,
98                     componentName, actionType, userHandle);
99         } catch (Throwable e) {
100             long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
101             smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList());
102             if (DEBUG_ACTIONS) {
103                 Log.e(TAG, "Failed to get future for screenshot notification smart actions.", e);
104             }
105             notifyScreenshotOp(screenshotId, smartActionsProvider,
106                     ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS,
107                     ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR,
108                     waitTimeMs);
109         }
110         return smartActionsFuture;
111     }
112 
getSmartActions(String screenshotId, CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs, ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotSmartActionType actionType)113     List<Notification.Action> getSmartActions(String screenshotId,
114             CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
115             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
116             ScreenshotSmartActionType actionType) {
117         long startTimeMs = SystemClock.uptimeMillis();
118         if (DEBUG_ACTIONS) {
119             Log.d(TAG,
120                     String.format("getSmartActions id=%s, timeoutMs=%d, actionType=%s, provider=%s",
121                             screenshotId, timeoutMs, actionType, smartActionsProvider.getClass()));
122         }
123         try {
124             List<Notification.Action> actions = smartActionsFuture.get(timeoutMs,
125                     TimeUnit.MILLISECONDS);
126             long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
127             if (DEBUG_ACTIONS) {
128                 Log.d(TAG, String.format("Got %d smart actions. Wait time: %d ms, actionType=%s",
129                         actions.size(), waitTimeMs, actionType));
130             }
131             notifyScreenshotOp(screenshotId, smartActionsProvider,
132                     ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
133                     ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS,
134                     waitTimeMs);
135             return actions;
136         } catch (Throwable e) {
137             long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
138             if (DEBUG_ACTIONS) {
139                 Log.e(TAG, String.format(
140                         "Error getting smart actions. Wait time: %d ms, actionType=%s",
141                         waitTimeMs, actionType), e);
142             }
143             ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status =
144                     (e instanceof TimeoutException)
145                             ? ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT
146                             : ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR;
147             notifyScreenshotOp(screenshotId, smartActionsProvider,
148                     ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
149                     status, waitTimeMs);
150             return Collections.emptyList();
151         }
152     }
153 
notifyScreenshotOp(String screenshotId, ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotNotificationSmartActionsProvider.ScreenshotOp op, ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs)154     void notifyScreenshotOp(String screenshotId,
155             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
156             ScreenshotNotificationSmartActionsProvider.ScreenshotOp op,
157             ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) {
158         if (DEBUG_ACTIONS) {
159             Log.d(TAG, String.format("%s notifyOp: %s id=%s, status=%s, durationMs=%d",
160                     smartActionsProvider.getClass(), op, screenshotId, status, durationMs));
161         }
162         try {
163             smartActionsProvider.notifyOp(screenshotId, op, status, durationMs);
164         } catch (Throwable e) {
165             Log.e(TAG, "Error in notifyScreenshotOp: ", e);
166         }
167     }
168 
notifyScreenshotAction(String screenshotId, String action, boolean isSmartAction, Intent intent)169     void notifyScreenshotAction(String screenshotId, String action,
170             boolean isSmartAction, Intent intent) {
171         try {
172             ScreenshotNotificationSmartActionsProvider provider =
173                     mScreenshotNotificationSmartActionsProviderProvider.get();
174             if (DEBUG_ACTIONS) {
175                 Log.d(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b",
176                         provider.getClass(), action, screenshotId, isSmartAction));
177             }
178             provider.notifyAction(screenshotId, action, isSmartAction, intent);
179         } catch (Throwable e) {
180             Log.e(TAG, "Error in notifyScreenshotAction: ", e);
181         }
182     }
183 }
184