blob: 1780fb1848e7a068bd95499f5ef2c87e671d871e [file] [log] [blame]
Winson Chung9112ec32011-06-27 13:15:32 -07001/*
Miranda Kephart43c02962020-01-10 17:50:48 -05002 * Copyright (C) 2020 The Android Open Source Project
Winson Chung9112ec32011-06-27 13:15:32 -07003 *
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
17package com.android.systemui.screenshot;
18
Miranda Kephartdf62ee62020-01-10 18:21:44 -050019import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
Miranda Kephart0148b282019-10-31 15:36:21 -040020import static android.view.View.VISIBLE;
Adrian Roosd1229772018-03-21 14:46:10 +010021import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
Alison Cichowlas3e340502018-08-07 17:15:01 -040022
Miranda Kephartdf62ee62020-01-10 18:21:44 -050023import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED;
Winson Chunged376a32017-09-07 14:05:42 -070024import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
25
Winson Chung9112ec32011-06-27 13:15:32 -070026import android.animation.Animator;
27import android.animation.AnimatorListenerAdapter;
28import android.animation.AnimatorSet;
Winson Chung9112ec32011-06-27 13:15:32 -070029import android.animation.ValueAnimator;
Miranda Kephartea2e67a2019-10-29 11:52:56 -040030import android.annotation.Nullable;
Zak Cohenebd3b8b2020-01-17 11:16:00 -080031import android.annotation.SuppressLint;
Miranda Kephart43c02962020-01-10 17:50:48 -050032import android.app.ActivityManager;
Winson Chunged376a32017-09-07 14:05:42 -070033import android.app.ActivityOptions;
Winson Chungc57ccf02011-10-13 15:04:59 -070034import android.app.Notification;
Winson Chungc57ccf02011-10-13 15:04:59 -070035import android.app.PendingIntent;
Adam Powelld4d34152015-04-17 12:13:40 -070036import android.content.BroadcastReceiver;
Winson Chung9112ec32011-06-27 13:15:32 -070037import android.content.Context;
Winson Chungc57ccf02011-10-13 15:04:59 -070038import android.content.Intent;
39import android.content.res.Resources;
Winson Chung9112ec32011-06-27 13:15:32 -070040import android.graphics.Bitmap;
Zak Cohenebd3b8b2020-01-17 11:16:00 -080041import android.graphics.Insets;
Miranda Kephart43c02962020-01-10 17:50:48 -050042import android.graphics.Outline;
Winson Chung9112ec32011-06-27 13:15:32 -070043import android.graphics.PixelFormat;
Winson Chunga63bb842011-10-17 10:26:28 -070044import android.graphics.PointF;
Muyuan Li6ca619f2016-03-08 13:30:42 -080045import android.graphics.Rect;
Miranda Kephart43c02962020-01-10 17:50:48 -050046import android.graphics.Region;
47import android.graphics.drawable.Icon;
Eino-Ville Talvalae6909582012-03-01 11:01:38 -080048import android.media.MediaActionSound;
Winson Chung9112ec32011-06-27 13:15:32 -070049import android.net.Uri;
50import android.os.AsyncTask;
Miranda Kephartea2e67a2019-10-29 11:52:56 -040051import android.os.Handler;
52import android.os.Looper;
53import android.os.Message;
Geoffrey Pitsched5de312017-06-27 11:42:12 -040054import android.os.PowerManager;
Miranda Kephart43c02962020-01-10 17:50:48 -050055import android.os.RemoteException;
phweissec3d9ca2017-02-09 16:57:39 +010056import android.os.UserHandle;
Miranda Kephartdf62ee62020-01-10 18:21:44 -050057import android.provider.DeviceConfig;
Winson Chung9112ec32011-06-27 13:15:32 -070058import android.util.DisplayMetrics;
Miranda Kephartea2e67a2019-10-29 11:52:56 -040059import android.util.Log;
Miranda Kephart4890f3e2020-02-05 15:55:11 -050060import android.util.MathUtils;
Dan Sandler150651b2017-06-14 10:43:07 -040061import android.util.Slog;
Winson Chung9112ec32011-06-27 13:15:32 -070062import android.view.Display;
Winson Chung9112ec32011-06-27 13:15:32 -070063import android.view.LayoutInflater;
64import android.view.MotionEvent;
Mathias Agopian3866f0d2013-02-11 22:08:48 -080065import android.view.SurfaceControl;
Winson Chung9112ec32011-06-27 13:15:32 -070066import android.view.View;
67import android.view.ViewGroup;
Miranda Kephart43c02962020-01-10 17:50:48 -050068import android.view.ViewOutlineProvider;
69import android.view.ViewTreeObserver;
Winson Chung9112ec32011-06-27 13:15:32 -070070import android.view.WindowManager;
Miranda Kephart4890f3e2020-02-05 15:55:11 -050071import android.view.animation.AnimationUtils;
Winson Chung22ca0952011-10-20 19:44:32 -070072import android.view.animation.Interpolator;
Miranda Kepharta2ebd762020-02-06 10:23:40 -050073import android.widget.FrameLayout;
Miranda Kephart43c02962020-01-10 17:50:48 -050074import android.widget.HorizontalScrollView;
Winson Chung9112ec32011-06-27 13:15:32 -070075import android.widget.ImageView;
Miranda Kephart0148b282019-10-31 15:36:21 -040076import android.widget.LinearLayout;
Geoffrey Pitsched5de312017-06-27 11:42:12 -040077import android.widget.Toast;
Alison Cichowlas3e340502018-08-07 17:15:01 -040078
Winson Chung9112ec32011-06-27 13:15:32 -070079import com.android.systemui.R;
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050080import com.android.systemui.dagger.qualifiers.Main;
Winson Chung06ca2742018-06-29 12:26:49 -070081import com.android.systemui.shared.system.ActivityManagerWrapper;
82import com.android.systemui.statusbar.phone.StatusBar;
Alison Cichowlas3e340502018-08-07 17:15:01 -040083
Satakshid2010f22019-10-29 10:57:43 -070084import java.util.List;
Garfield Tana22423972019-11-04 17:40:26 -080085import java.util.Optional;
Matt Pietal777c82d2019-06-11 15:47:10 -040086import java.util.concurrent.ExecutionException;
87import java.util.concurrent.TimeUnit;
88import java.util.concurrent.TimeoutException;
Aran Inkeb565fc2019-11-15 15:52:31 -050089import java.util.function.Consumer;
Matt Pietal777c82d2019-06-11 15:47:10 -040090
Dave Mankoffaefb3462019-11-01 14:21:45 -040091import javax.inject.Inject;
92import javax.inject.Singleton;
Matt Pietal777c82d2019-06-11 15:47:10 -040093
Garfield Tana22423972019-11-04 17:40:26 -080094import dagger.Lazy;
Winson Chung9112ec32011-06-27 13:15:32 -070095
96/**
Sunny Goyalaf7cddd2019-11-05 17:49:17 -080097 * Class for handling device screen shots
Winson Chung9112ec32011-06-27 13:15:32 -070098 */
Sunny Goyalaf7cddd2019-11-05 17:49:17 -080099@Singleton
Miranda Kephart43c02962020-01-10 17:50:48 -0500100public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInsetsListener {
Winson Chung0e6232c2013-01-22 14:11:46 -0800101
Sunny Goyalaf7cddd2019-11-05 17:49:17 -0800102 /**
103 * POD used in the AsyncTask which saves an image in the background.
104 */
Miranda Kephartca6c5eb2019-11-14 12:44:11 -0500105 static class SaveImageInBackgroundData {
Sunny Goyalaf7cddd2019-11-05 17:49:17 -0800106 public Bitmap image;
107 public Uri imageUri;
Aran Inkeb565fc2019-11-15 15:52:31 -0500108 public Consumer<Uri> finisher;
Miranda Kephart0148b282019-10-31 15:36:21 -0400109 public GlobalScreenshot.ActionsReadyListener mActionsReadyListener;
Sunny Goyalaf7cddd2019-11-05 17:49:17 -0800110 public int errorMsgResId;
Miranda Kephart43c02962020-01-10 17:50:48 -0500111 public boolean createDeleteAction;
Winson Chung9112ec32011-06-27 13:15:32 -0700112
Sunny Goyalaf7cddd2019-11-05 17:49:17 -0800113 void clearImage() {
114 image = null;
115 imageUri = null;
Sunny Goyalaf7cddd2019-11-05 17:49:17 -0800116 }
Winson Chungc57ccf02011-10-13 15:04:59 -0700117 }
118
Miranda Kephart0148b282019-10-31 15:36:21 -0400119 abstract static class ActionsReadyListener {
Miranda Kephart43c02962020-01-10 17:50:48 -0500120 abstract void onActionsReady(Uri imageUri, List<Notification.Action> smartActions,
121 List<Notification.Action> actions);
Miranda Kephart0148b282019-10-31 15:36:21 -0400122 }
123
Satakshiaaf69532019-11-07 17:54:24 -0800124 // These strings are used for communicating the action invoked to
125 // ScreenshotNotificationSmartActionsProvider.
126 static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
127 static final String EXTRA_ID = "android:screenshot_id";
128 static final String ACTION_TYPE_DELETE = "Delete";
129 static final String ACTION_TYPE_SHARE = "Share";
130 static final String ACTION_TYPE_EDIT = "Edit";
131 static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
Winson Chung06ca2742018-06-29 12:26:49 -0700132 static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
Satakshiaaf69532019-11-07 17:54:24 -0800133
134 static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
Winson Chung06ca2742018-06-29 12:26:49 -0700135 static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
136 static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
Adam Powelld4d34152015-04-17 12:13:40 -0700137
Matt Pietal777c82d2019-06-11 15:47:10 -0400138 private static final String TAG = "GlobalScreenshot";
139
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500140 private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
141 private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217;
142 private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
143 private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
144 private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
Miranda Kephart43c02962020-01-10 17:50:48 -0500145 private static final float ROUNDED_CORNER_RADIUS = .05f;
Miranda Kepharta2ebd762020-02-06 10:23:40 -0500146 private static final long SCREENSHOT_CORNER_TIMEOUT_MILLIS = 6000;
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400147 private static final int MESSAGE_CORNER_TIMEOUT = 2;
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500148
149 private final ScreenshotNotificationsController mNotificationsController;
Winson Chung9112ec32011-06-27 13:15:32 -0700150
Miranda Kephart43c02962020-01-10 17:50:48 -0500151 private final Context mContext;
152 private final WindowManager mWindowManager;
153 private final WindowManager.LayoutParams mWindowLayoutParams;
154 private final Display mDisplay;
155 private final DisplayMetrics mDisplayMetrics;
156
157 private final View mScreenshotLayout;
158 private final ScreenshotSelectorView mScreenshotSelectorView;
159 private final ImageView mBackgroundView;
160 private final ImageView mScreenshotView;
161 private final ImageView mScreenshotFlash;
162 private final HorizontalScrollView mActionsContainer;
163 private final LinearLayout mActionsView;
164 private final ImageView mBackgroundProtection;
Miranda Kepharta2ebd762020-02-06 10:23:40 -0500165 private final FrameLayout mDismissButton;
Winson Chung9112ec32011-06-27 13:15:32 -0700166
167 private Bitmap mScreenBitmap;
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500168 private Animator mScreenshotAnimation;
Winson Chung9112ec32011-06-27 13:15:32 -0700169
Miranda Kephart43c02962020-01-10 17:50:48 -0500170 private float mScreenshotOffsetXPx;
171 private float mScreenshotOffsetYPx;
172 private float mScreenshotHeightPx;
Miranda Kepharta2ebd762020-02-06 10:23:40 -0500173 private float mDismissButtonSize;
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500174 private float mCornerSizeX;
Winson Chunga63bb842011-10-17 10:26:28 -0700175
Winsonc7aa65b2016-03-10 12:52:27 -0800176 private AsyncTask<Void, Void, Void> mSaveInBgTask;
Winson Chung0e6232c2013-01-22 14:11:46 -0800177
Eino-Ville Talvalae6909582012-03-01 11:01:38 -0800178 private MediaActionSound mCameraSound;
Winson Chung8d513ea2011-12-01 14:39:12 -0800179
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500180 // standard material ease
181 private final Interpolator mFastOutSlowIn;
182
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400183 private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
184 @Override
185 public void handleMessage(Message msg) {
186 switch (msg.what) {
187 case MESSAGE_CORNER_TIMEOUT:
Miranda Kephart43c02962020-01-10 17:50:48 -0500188 GlobalScreenshot.this.clearScreenshot("timeout");
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400189 break;
190 default:
191 break;
192 }
193 }
194 };
195
Winson Chung9112ec32011-06-27 13:15:32 -0700196 /**
197 * @param context everything needs a context :(
198 */
Dave Mankoffaefb3462019-11-01 14:21:45 -0400199 @Inject
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500200 public GlobalScreenshot(
Dave Mankoff00e8a2f2019-12-18 16:59:49 -0500201 Context context, @Main Resources resources, LayoutInflater layoutInflater,
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500202 ScreenshotNotificationsController screenshotNotificationsController) {
Winson Chung9112ec32011-06-27 13:15:32 -0700203 mContext = context;
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500204 mNotificationsController = screenshotNotificationsController;
Winson Chung9112ec32011-06-27 13:15:32 -0700205
206 // Inflate the screenshot layout
Romain Guy8279acb2011-11-29 13:56:25 -0800207 mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null);
Dave Mankoffaefb3462019-11-01 14:21:45 -0400208 mBackgroundView = mScreenshotLayout.findViewById(R.id.global_screenshot_background);
209 mScreenshotView = mScreenshotLayout.findViewById(R.id.global_screenshot);
Miranda Kephart43c02962020-01-10 17:50:48 -0500210 mScreenshotView.setClipToOutline(true);
211 mScreenshotView.setOutlineProvider(new ViewOutlineProvider() {
212 @Override
213 public void getOutline(View view, Outline outline) {
214 outline.setRoundRect(new Rect(0, 0, view.getWidth(), view.getHeight()),
215 ROUNDED_CORNER_RADIUS * view.getWidth());
216 }
217 });
218
219 mActionsContainer = mScreenshotLayout.findViewById(
220 R.id.global_screenshot_actions_container);
Miranda Kephart0148b282019-10-31 15:36:21 -0400221 mActionsView = mScreenshotLayout.findViewById(R.id.global_screenshot_actions);
Miranda Kephart43c02962020-01-10 17:50:48 -0500222 mBackgroundProtection = mScreenshotLayout.findViewById(
223 R.id.global_screenshot_actions_background);
Miranda Kepharta2ebd762020-02-06 10:23:40 -0500224 mDismissButton = mScreenshotLayout.findViewById(R.id.global_screenshot_dismiss_button);
225 mDismissButton.setOnClickListener(view -> clearScreenshot("dismiss_button"));
Miranda Kephart0148b282019-10-31 15:36:21 -0400226
Dave Mankoffaefb3462019-11-01 14:21:45 -0400227 mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
228 mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector);
Winson Chung9112ec32011-06-27 13:15:32 -0700229 mScreenshotLayout.setFocusable(true);
Muyuan Li6ca619f2016-03-08 13:30:42 -0800230 mScreenshotSelectorView.setFocusable(true);
231 mScreenshotSelectorView.setFocusableInTouchMode(true);
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500232 mScreenshotView.setPivotX(0);
233 mScreenshotView.setPivotY(0);
Winson Chung9112ec32011-06-27 13:15:32 -0700234
235 // Setup the window that we are going to use
Winson Chung9112ec32011-06-27 13:15:32 -0700236 mWindowLayoutParams = new WindowManager.LayoutParams(
237 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
Muyuan Li6ca619f2016-03-08 13:30:42 -0800238 WindowManager.LayoutParams.TYPE_SCREENSHOT,
Winson Chung9112ec32011-06-27 13:15:32 -0700239 WindowManager.LayoutParams.FLAG_FULLSCREEN
Miranda Kephartca6c5eb2019-11-14 12:44:11 -0500240 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
Miranda Kephart43c02962020-01-10 17:50:48 -0500241 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
242 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
Miranda Kephartca6c5eb2019-11-14 12:44:11 -0500243 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
Winson Chung9112ec32011-06-27 13:15:32 -0700244 PixelFormat.TRANSLUCENT);
Winson Chung9112ec32011-06-27 13:15:32 -0700245 mWindowLayoutParams.setTitle("ScreenshotAnimation");
Adrian Roosd1229772018-03-21 14:46:10 +0100246 mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
Tiger Huang52724442020-01-20 21:38:42 +0800247 mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
Winson Chung9112ec32011-06-27 13:15:32 -0700248 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
249 mDisplay = mWindowManager.getDefaultDisplay();
Winson Chunga63bb842011-10-17 10:26:28 -0700250 mDisplayMetrics = new DisplayMetrics();
251 mDisplay.getRealMetrics(mDisplayMetrics);
252
Miranda Kephart43c02962020-01-10 17:50:48 -0500253 mScreenshotOffsetXPx = resources.getDimensionPixelSize(R.dimen.screenshot_offset_x);
254 mScreenshotOffsetYPx = resources.getDimensionPixelSize(R.dimen.screenshot_offset_y);
255 mScreenshotHeightPx =
256 resources.getDimensionPixelSize(R.dimen.screenshot_action_container_offset_y);
Miranda Kepharta2ebd762020-02-06 10:23:40 -0500257 mDismissButtonSize = resources.getDimensionPixelSize(
258 R.dimen.screenshot_dismiss_button_tappable_size);
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500259 mCornerSizeX = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale);
260
261 mFastOutSlowIn =
262 AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
Chris Wrenf63185e2014-07-31 13:52:39 +0100263
Winson Chung8d513ea2011-12-01 14:39:12 -0800264 // Setup the Camera shutter sound
Eino-Ville Talvalae6909582012-03-01 11:01:38 -0800265 mCameraSound = new MediaActionSound();
266 mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
Winson Chung9112ec32011-06-27 13:15:32 -0700267 }
268
Miranda Kephart43c02962020-01-10 17:50:48 -0500269 @Override // ViewTreeObserver.OnComputeInternalInsetsListener
270 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
271 inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
272 Region touchRegion = new Region();
273
274 Rect screenshotRect = new Rect();
275 mScreenshotView.getBoundsOnScreen(screenshotRect);
276 touchRegion.op(screenshotRect, Region.Op.UNION);
277 Rect actionsRect = new Rect();
278 mActionsContainer.getBoundsOnScreen(actionsRect);
279 touchRegion.op(actionsRect, Region.Op.UNION);
Miranda Kepharta2ebd762020-02-06 10:23:40 -0500280 Rect dismissRect = new Rect();
281 mDismissButton.getBoundsOnScreen(dismissRect);
282 touchRegion.op(dismissRect, Region.Op.UNION);
Miranda Kephart43c02962020-01-10 17:50:48 -0500283
284 inoutInfo.touchableRegion.set(touchRegion);
285 }
286
Winson Chung9112ec32011-06-27 13:15:32 -0700287 /**
288 * Creates a new worker thread and saves the screenshot to the media store.
289 */
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400290 private void saveScreenshotInWorkerThread(
Miranda Kephart0148b282019-10-31 15:36:21 -0400291 Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
Winson Chung9112ec32011-06-27 13:15:32 -0700292 SaveImageInBackgroundData data = new SaveImageInBackgroundData();
Winson Chung9112ec32011-06-27 13:15:32 -0700293 data.image = mScreenBitmap;
Dianne Hackbornfc8fa632011-08-17 16:20:47 -0700294 data.finisher = finisher;
Miranda Kephart0148b282019-10-31 15:36:21 -0400295 data.mActionsReadyListener = actionsReadyListener;
Miranda Kephart43c02962020-01-10 17:50:48 -0500296 data.createDeleteAction = false;
Winson Chung0e6232c2013-01-22 14:11:46 -0800297 if (mSaveInBgTask != null) {
298 mSaveInBgTask.cancel(false);
299 }
Winson Chung9112ec32011-06-27 13:15:32 -0700300
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500301 mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data).execute();
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400302 }
303
Winson Chung9112ec32011-06-27 13:15:32 -0700304 /**
Winson Chung9112ec32011-06-27 13:15:32 -0700305 * Takes a screenshot of the current display and shows an animation.
306 */
Miranda Kephart63e65a92020-01-10 11:18:51 -0500307 private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
Miranda Kephart43c02962020-01-10 17:50:48 -0500308 clearScreenshot("new screenshot requested");
309
chaviwa69e0a72017-11-29 17:55:12 -0800310 int rot = mDisplay.getRotation();
311 int width = crop.width();
312 int height = crop.height();
Winson Chunga46d7782012-01-04 16:43:10 -0800313
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500314 Rect screenRect = new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels);
315
316 takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect);
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800317 }
318
319 private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) {
320 mScreenBitmap = screenshot;
Winson Chunga46d7782012-01-04 16:43:10 -0800321 if (mScreenBitmap == null) {
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500322 mNotificationsController.notifyScreenshotError(
Winsone5591712016-02-18 13:45:54 -0800323 R.string.screenshot_failed_to_capture_text);
Aran Inkeb565fc2019-11-15 15:52:31 -0500324 finisher.accept(null);
Winson Chunga46d7782012-01-04 16:43:10 -0800325 return;
326 }
327
Winson Chunga63bb842011-10-17 10:26:28 -0700328 // Optimizations
329 mScreenBitmap.setHasAlpha(false);
330 mScreenBitmap.prepareToDraw();
331
Miranda Kephart43c02962020-01-10 17:50:48 -0500332 mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
333 mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
334
Winson Chung9112ec32011-06-27 13:15:32 -0700335 // Start the post-screenshot animation
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500336 startAnimation(finisher, screenRect.width(), screenRect.height(),
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800337 screenRect);
Winson Chung9112ec32011-06-27 13:15:32 -0700338 }
339
Miranda Kephart63e65a92020-01-10 11:18:51 -0500340 void takeScreenshot(Consumer<Uri> finisher) {
Muyuan Li6ca619f2016-03-08 13:30:42 -0800341 mDisplay.getRealMetrics(mDisplayMetrics);
Miranda Kephart63e65a92020-01-10 11:18:51 -0500342 takeScreenshot(
343 finisher,
chaviwa69e0a72017-11-29 17:55:12 -0800344 new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
Muyuan Li6ca619f2016-03-08 13:30:42 -0800345 }
346
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800347 void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
348 Insets visibleInsets, int taskId, Consumer<Uri> finisher) {
349 // TODO use taskId and visibleInsets
Miranda Kephartab359be2020-03-12 16:13:42 -0400350 clearScreenshot("new screenshot requested");
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800351 takeScreenshot(screenshot, finisher, screenshotScreenBounds);
352 }
353
Muyuan Li6ca619f2016-03-08 13:30:42 -0800354 /**
355 * Displays a screenshot selector
356 */
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800357 @SuppressLint("ClickableViewAccessibility")
Miranda Kephart63e65a92020-01-10 11:18:51 -0500358 void takeScreenshotPartial(final Consumer<Uri> finisher) {
Muyuan Li6ca619f2016-03-08 13:30:42 -0800359 mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
360 mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
361 @Override
362 public boolean onTouch(View v, MotionEvent event) {
363 ScreenshotSelectorView view = (ScreenshotSelectorView) v;
364 switch (event.getAction()) {
365 case MotionEvent.ACTION_DOWN:
366 view.startSelection((int) event.getX(), (int) event.getY());
367 return true;
368 case MotionEvent.ACTION_MOVE:
369 view.updateSelection((int) event.getX(), (int) event.getY());
370 return true;
371 case MotionEvent.ACTION_UP:
372 view.setVisibility(View.GONE);
373 mWindowManager.removeView(mScreenshotLayout);
374 final Rect rect = view.getSelectionRect();
375 if (rect != null) {
376 if (rect.width() != 0 && rect.height() != 0) {
377 // Need mScreenshotLayout to handle it after the view disappears
Miranda Kephart63e65a92020-01-10 11:18:51 -0500378 mScreenshotLayout.post(() -> takeScreenshot(finisher, rect));
Muyuan Li6ca619f2016-03-08 13:30:42 -0800379 }
380 }
381
382 view.stopSelection();
383 return true;
384 }
385
386 return false;
387 }
388 });
Miranda Kephart43c02962020-01-10 17:50:48 -0500389 mScreenshotLayout.post(() -> {
390 mScreenshotSelectorView.setVisibility(View.VISIBLE);
391 mScreenshotSelectorView.requestFocus();
Muyuan Li6ca619f2016-03-08 13:30:42 -0800392 });
393 }
394
395 /**
396 * Cancels screenshot request
397 */
398 void stopScreenshot() {
399 // If the selector layer still presents on screen, we remove it and resets its state.
400 if (mScreenshotSelectorView.getSelectionRect() != null) {
401 mWindowManager.removeView(mScreenshotLayout);
402 mScreenshotSelectorView.stopSelection();
403 }
404 }
Winson Chung9112ec32011-06-27 13:15:32 -0700405
406 /**
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400407 * Clears current screenshot
408 */
Miranda Kephart43c02962020-01-10 17:50:48 -0500409 private void clearScreenshot(String reason) {
410 Log.e(TAG, "clearing screenshot: " + reason);
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400411 if (mScreenshotLayout.isAttachedToWindow()) {
412 mWindowManager.removeView(mScreenshotLayout);
413 }
Miranda Kephart43c02962020-01-10 17:50:48 -0500414 mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
415 mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400416
417 // Clear any references to the bitmap
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400418 mScreenshotView.setImageBitmap(null);
Miranda Kephart43c02962020-01-10 17:50:48 -0500419 mActionsContainer.setVisibility(View.GONE);
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400420 mBackgroundView.setVisibility(View.GONE);
Miranda Kephart43c02962020-01-10 17:50:48 -0500421 mBackgroundProtection.setAlpha(0f);
Miranda Kepharta2ebd762020-02-06 10:23:40 -0500422 mDismissButton.setVisibility(View.GONE);
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400423 mScreenshotView.setVisibility(View.GONE);
424 mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
425 }
426
427 /**
Winson Chung9112ec32011-06-27 13:15:32 -0700428 * Starts the animation after taking the screenshot
429 */
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800430 private void startAnimation(final Consumer<Uri> finisher, int w, int h,
431 @Nullable Rect screenRect) {
Geoffrey Pitsched5de312017-06-27 11:42:12 -0400432 // If power save is on, show a toast so there is some visual indication that a screenshot
433 // has been taken.
434 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
435 if (powerManager.isPowerSaveMode()) {
436 Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
437 }
438
Winson Chung9112ec32011-06-27 13:15:32 -0700439 // Add the view for the animation
440 mScreenshotView.setImageBitmap(mScreenBitmap);
Winson Chung9112ec32011-06-27 13:15:32 -0700441
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500442 mScreenshotAnimation = createScreenshotDropInAnimation(w, h, screenRect);
Miranda Kephartf50fb5b2020-01-28 17:12:28 -0500443
444 saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
Winson Chung9112ec32011-06-27 13:15:32 -0700445 @Override
Miranda Kephartf50fb5b2020-01-28 17:12:28 -0500446 void onActionsReady(Uri uri, List<Notification.Action> smartActions,
447 List<Notification.Action> actions) {
448 if (uri == null) {
449 mNotificationsController.notifyScreenshotError(
450 R.string.screenshot_failed_to_capture_text);
451 } else {
452 mScreenshotHandler.post(() -> {
453 if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
454 mScreenshotAnimation.addListener(
455 new AnimatorListenerAdapter() {
456 @Override
457 public void onAnimationEnd(Animator animation) {
458 super.onAnimationEnd(animation);
459 createScreenshotActionsShadeAnimation(
460 smartActions, actions).start();
461 }
462 });
Miranda Kephart63e65a92020-01-10 11:18:51 -0500463 } else {
Miranda Kephartf50fb5b2020-01-28 17:12:28 -0500464 createScreenshotActionsShadeAnimation(smartActions,
465 actions).start();
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500466 }
Miranda Kephartf50fb5b2020-01-28 17:12:28 -0500467 });
468 }
Miranda Kephart43c02962020-01-10 17:50:48 -0500469 mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
Miranda Kephart63e65a92020-01-10 11:18:51 -0500470 mScreenshotHandler.sendMessageDelayed(
471 mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
472 SCREENSHOT_CORNER_TIMEOUT_MILLIS);
Winson Chung9112ec32011-06-27 13:15:32 -0700473 }
474 });
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400475 mScreenshotHandler.post(() -> {
476 // Play the shutter sound to notify that we've taken a screenshot
477 mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
Winson Chung8d513ea2011-12-01 14:39:12 -0800478
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400479 mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
480 mScreenshotView.buildLayer();
481 mScreenshotAnimation.start();
Winson Chunga63bb842011-10-17 10:26:28 -0700482 });
Winson Chung9112ec32011-06-27 13:15:32 -0700483 }
Miranda Kephartca6c5eb2019-11-14 12:44:11 -0500484
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500485 private AnimatorSet createScreenshotDropInAnimation(int width, int height, Rect bounds) {
486 float cornerScale = mCornerSizeX / (float) width;
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800487
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500488 AnimatorSet dropInAnimation = new AnimatorSet();
489 ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
490 flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
491 flashInAnimator.setInterpolator(mFastOutSlowIn);
492 flashInAnimator.addUpdateListener(animation ->
493 mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
494
495 ValueAnimator flashOutAnimator = ValueAnimator.ofFloat(1, 0);
496 flashOutAnimator.setDuration(SCREENSHOT_FLASH_OUT_DURATION_MS);
497 flashOutAnimator.setInterpolator(mFastOutSlowIn);
498 flashOutAnimator.addUpdateListener(animation ->
499 mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
500
Miranda Kephart4f150e02020-02-20 12:49:41 -0500501 final PointF startPos = new PointF(bounds.centerX(), bounds.centerY());
502 final PointF finalPos = new PointF(mScreenshotOffsetXPx + width * cornerScale / 2f,
503 mDisplayMetrics.heightPixels - mScreenshotOffsetYPx - height * cornerScale / 2f);
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500504
505 ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
506 toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
507 float xPositionPct =
508 SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
509 float scalePct =
510 SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
511 toCorner.addUpdateListener(animation -> {
512 float t = animation.getAnimatedFraction();
513 if (t < scalePct) {
514 float scale = MathUtils.lerp(
515 1, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct));
516 mScreenshotView.setScaleX(scale);
517 mScreenshotView.setScaleY(scale);
Miranda Kephart2750ad02020-02-24 13:12:41 -0500518 } else {
519 mScreenshotView.setScaleX(cornerScale);
520 mScreenshotView.setScaleY(cornerScale);
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800521 }
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800522
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500523 if (t < xPositionPct) {
Miranda Kephart4f150e02020-02-20 12:49:41 -0500524 float xCenter = MathUtils.lerp(startPos.x, finalPos.x,
525 mFastOutSlowIn.getInterpolation(t / xPositionPct));
526 mScreenshotView.setX(xCenter - width * mScreenshotView.getScaleX() / 2f);
Miranda Kephart2750ad02020-02-24 13:12:41 -0500527 } else {
528 mScreenshotView.setX(finalPos.x - width * mScreenshotView.getScaleX() / 2f);
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500529 }
Miranda Kephart4f150e02020-02-20 12:49:41 -0500530 float yCenter = MathUtils.lerp(startPos.y, finalPos.y,
531 mFastOutSlowIn.getInterpolation(t));
532 mScreenshotView.setY(yCenter - height * mScreenshotView.getScaleY() / 2f);
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500533 });
534
535 toCorner.addListener(new AnimatorListenerAdapter() {
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800536 @Override
537 public void onAnimationStart(Animator animation) {
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500538 super.onAnimationStart(animation);
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800539 mScreenshotView.setVisibility(View.VISIBLE);
540 }
541 });
Zak Cohenebd3b8b2020-01-17 11:16:00 -0800542
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500543 mScreenshotFlash.setAlpha(0f);
544 mScreenshotFlash.setVisibility(View.VISIBLE);
Beth Thibodeaud98a9452019-03-08 14:32:51 -0500545
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500546 dropInAnimation.play(flashOutAnimator).after(flashInAnimator);
547 dropInAnimation.play(flashOutAnimator).with(toCorner);
Miranda Kephartca6c5eb2019-11-14 12:44:11 -0500548
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500549 dropInAnimation.addListener(new AnimatorListenerAdapter() {
Miranda Kepharta2ebd762020-02-06 10:23:40 -0500550 @Override
551 public void onAnimationEnd(Animator animation) {
552 super.onAnimationEnd(animation);
Miranda Kephart2750ad02020-02-24 13:12:41 -0500553 mScreenshotView.setScaleX(cornerScale);
554 mScreenshotView.setScaleY(cornerScale);
Miranda Kephart5fc03172020-02-27 14:34:47 -0500555 mScreenshotView.setX(finalPos.x - width * cornerScale / 2f);
Miranda Kephart2750ad02020-02-24 13:12:41 -0500556 mScreenshotView.setY(finalPos.y - height * cornerScale / 2f);
Miranda Kepharta2ebd762020-02-06 10:23:40 -0500557 Rect bounds = new Rect();
558 mScreenshotView.getBoundsOnScreen(bounds);
559 mDismissButton.setX(bounds.right - mDismissButtonSize / 2f);
560 mDismissButton.setY(bounds.top - mDismissButtonSize / 2f);
561 mDismissButton.setVisibility(View.VISIBLE);
562 }
563 });
Miranda Kephart4890f3e2020-02-05 15:55:11 -0500564
565 return dropInAnimation;
Miranda Kephartea2e67a2019-10-29 11:52:56 -0400566 }
567
Miranda Kephart43c02962020-01-10 17:50:48 -0500568 private ValueAnimator createScreenshotActionsShadeAnimation(
569 List<Notification.Action> smartActions, List<Notification.Action> actions) {
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500570 LayoutInflater inflater = LayoutInflater.from(mContext);
571 mActionsView.removeAllViews();
Miranda Kephart43c02962020-01-10 17:50:48 -0500572 mActionsContainer.setScrollX(0);
573 mScreenshotLayout.invalidate();
574 mScreenshotLayout.requestLayout();
575 mScreenshotLayout.getViewTreeObserver().dispatchOnGlobalLayout();
576
577 // By default the activities won't be able to start immediately; override this to keep
578 // the same behavior as if started from a notification
579 try {
580 ActivityManager.getService().resumeAppSwitches();
581 } catch (RemoteException e) {
582 }
583
584 for (Notification.Action smartAction : smartActions) {
585 ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
586 R.layout.global_screenshot_action_chip, mActionsView, false);
587 actionChip.setText(smartAction.title);
588 actionChip.setIcon(smartAction.getIcon(), false);
589 actionChip.setPendingIntent(smartAction.actionIntent,
590 () -> clearScreenshot("chip tapped"));
591 mActionsView.addView(actionChip);
592 }
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500593
594 for (Notification.Action action : actions) {
Miranda Kephart6b655af2020-01-13 15:50:32 -0500595 ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500596 R.layout.global_screenshot_action_chip, mActionsView, false);
597 actionChip.setText(action.title);
Miranda Kephart6b655af2020-01-13 15:50:32 -0500598 actionChip.setIcon(action.getIcon(), true);
Miranda Kephart43c02962020-01-10 17:50:48 -0500599 actionChip.setPendingIntent(action.actionIntent, () -> clearScreenshot("chip tapped"));
600 if (action.actionIntent.getIntent().getAction().equals(Intent.ACTION_EDIT)) {
601 mScreenshotView.setOnClickListener(v -> {
602 try {
603 action.actionIntent.send();
604 clearScreenshot("screenshot preview tapped");
605 } catch (PendingIntent.CanceledException e) {
606 Log.e(TAG, "Intent cancelled", e);
607 }
608 });
609 }
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500610 mActionsView.addView(actionChip);
611 }
Miranda Kephartdf62ee62020-01-10 18:21:44 -0500612
613 if (DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, SCREENSHOT_SCROLLING_ENABLED, false)) {
Miranda Kephart6b655af2020-01-13 15:50:32 -0500614 ScreenshotActionChip scrollChip = (ScreenshotActionChip) inflater.inflate(
Miranda Kephartdf62ee62020-01-10 18:21:44 -0500615 R.layout.global_screenshot_action_chip, mActionsView, false);
616 Toast scrollNotImplemented = Toast.makeText(
617 mContext, "Not implemented", Toast.LENGTH_SHORT);
Miranda Kephart6b655af2020-01-13 15:50:32 -0500618 scrollChip.setText("Extend"); // TODO (mkephart): add resource and translate
Miranda Kephart43c02962020-01-10 17:50:48 -0500619 scrollChip.setIcon(
620 Icon.createWithResource(mContext, R.drawable.ic_arrow_downward), true);
Miranda Kephartdf62ee62020-01-10 18:21:44 -0500621 scrollChip.setOnClickListener(v -> scrollNotImplemented.show());
622 mActionsView.addView(scrollChip);
623 }
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500624
Miranda Kephart0148b282019-10-31 15:36:21 -0400625 ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
Miranda Kephart43c02962020-01-10 17:50:48 -0500626 mActionsContainer.setY(mDisplayMetrics.heightPixels);
627 mActionsContainer.setVisibility(VISIBLE);
628 mActionsContainer.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
629 float actionsViewHeight = mActionsContainer.getMeasuredHeight() + mScreenshotHeightPx;
Miranda Kephart0148b282019-10-31 15:36:21 -0400630
631 animator.addUpdateListener(animation -> {
632 float t = animation.getAnimatedFraction();
Miranda Kephart43c02962020-01-10 17:50:48 -0500633 mBackgroundProtection.setAlpha(t);
634 mActionsContainer.setY(mDisplayMetrics.heightPixels - actionsViewHeight * t);
Miranda Kephart0148b282019-10-31 15:36:21 -0400635 });
Miranda Kephart0148b282019-10-31 15:36:21 -0400636 return animator;
637 }
638
Adam Powelld4d34152015-04-17 12:13:40 -0700639 /**
Winson Chung06ca2742018-06-29 12:26:49 -0700640 * Receiver to proxy the share or edit intent, used to clean up the notification and send
641 * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
Winson Chunged376a32017-09-07 14:05:42 -0700642 */
Winson Chung06ca2742018-06-29 12:26:49 -0700643 public static class ActionProxyReceiver extends BroadcastReceiver {
Matt Pietal777c82d2019-06-11 15:47:10 -0400644 static final int CLOSE_WINDOWS_TIMEOUT_MILLIS = 3000;
Dave Mankoffaefb3462019-11-01 14:21:45 -0400645 private final StatusBar mStatusBar;
646
647 @Inject
Garfield Tana22423972019-11-04 17:40:26 -0800648 public ActionProxyReceiver(Optional<Lazy<StatusBar>> statusBarLazy) {
649 Lazy<StatusBar> statusBar = statusBarLazy.orElse(null);
650 mStatusBar = statusBar != null ? statusBar.get() : null;
Dave Mankoffaefb3462019-11-01 14:21:45 -0400651 }
Matt Pietal777c82d2019-06-11 15:47:10 -0400652
Winson Chunged376a32017-09-07 14:05:42 -0700653 @Override
Winson Chung06ca2742018-06-29 12:26:49 -0700654 public void onReceive(Context context, final Intent intent) {
655 Runnable startActivityRunnable = () -> {
Matt Pietal777c82d2019-06-11 15:47:10 -0400656 try {
657 ActivityManagerWrapper.getInstance().closeSystemWindows(
658 SYSTEM_DIALOG_REASON_SCREENSHOT).get(
659 CLOSE_WINDOWS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
660 } catch (TimeoutException | InterruptedException | ExecutionException e) {
661 Slog.e(TAG, "Unable to share screenshot", e);
662 return;
663 }
Winson Chunged376a32017-09-07 14:05:42 -0700664
Winson Chung06ca2742018-06-29 12:26:49 -0700665 Intent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
666 if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) {
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500667 ScreenshotNotificationsController.cancelScreenshotNotification(context);
Winson Chung06ca2742018-06-29 12:26:49 -0700668 }
669 ActivityOptions opts = ActivityOptions.makeBasic();
670 opts.setDisallowEnterPictureInPictureWhileLaunching(
671 intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
Winson Chung6c454f62018-07-09 15:55:56 -0700672 context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
Winson Chung06ca2742018-06-29 12:26:49 -0700673 };
Dave Mankoffaefb3462019-11-01 14:21:45 -0400674
Garfield Tana22423972019-11-04 17:40:26 -0800675 if (mStatusBar != null) {
676 mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
677 true /* dismissShade */, true /* afterKeyguardGone */,
678 true /* deferred */);
679 } else {
680 startActivityRunnable.run();
681 }
Satakshiaaf69532019-11-07 17:54:24 -0800682
683 if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
684 String actionType = Intent.ACTION_EDIT.equals(intent.getAction()) ? ACTION_TYPE_EDIT
685 : ACTION_TYPE_SHARE;
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500686 ScreenshotSmartActions.notifyScreenshotAction(
687 context, intent.getStringExtra(EXTRA_ID), actionType, false);
Satakshiaaf69532019-11-07 17:54:24 -0800688 }
Winson Chunged376a32017-09-07 14:05:42 -0700689 }
690 }
691
692 /**
Winson Chung06ca2742018-06-29 12:26:49 -0700693 * Removes the notification for a screenshot after a share target is chosen.
Adam Powelld4d34152015-04-17 12:13:40 -0700694 */
695 public static class TargetChosenReceiver extends BroadcastReceiver {
696 @Override
697 public void onReceive(Context context, Intent intent) {
Winson Chung06ca2742018-06-29 12:26:49 -0700698 // Clear the notification only after the user has chosen a share action
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500699 ScreenshotNotificationsController.cancelScreenshotNotification(context);
Adam Powelld4d34152015-04-17 12:13:40 -0700700 }
701 }
Winson Chung8858e6e2015-05-27 17:18:56 -0700702
703 /**
704 * Removes the last screenshot.
705 */
706 public static class DeleteScreenshotReceiver extends BroadcastReceiver {
707 @Override
708 public void onReceive(Context context, Intent intent) {
Winsone5591712016-02-18 13:45:54 -0800709 if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
Winson Chung8858e6e2015-05-27 17:18:56 -0700710 return;
711 }
712
Winson Chung06ca2742018-06-29 12:26:49 -0700713 // Clear the notification when the image is deleted
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500714 ScreenshotNotificationsController.cancelScreenshotNotification(context);
Winson Chung8858e6e2015-05-27 17:18:56 -0700715
716 // And delete the image from the media store
Winson Chung06ca2742018-06-29 12:26:49 -0700717 final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
Winson Chung8858e6e2015-05-27 17:18:56 -0700718 new DeleteImageInBackgroundTask(context).execute(uri);
Satakshiaaf69532019-11-07 17:54:24 -0800719 if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500720 ScreenshotSmartActions.notifyScreenshotAction(
721 context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false);
Satakshiaaf69532019-11-07 17:54:24 -0800722 }
723 }
724 }
725
726 /**
727 * Executes the smart action tapped by the user in the notification.
728 */
729 public static class SmartActionsReceiver extends BroadcastReceiver {
730 @Override
731 public void onReceive(Context context, Intent intent) {
Satakshi386efae2019-12-04 16:08:18 -0800732 PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
733 Intent actionIntent = pendingIntent.getIntent();
734 String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
735 Slog.d(TAG, "Executing smart action [" + actionType + "]:" + actionIntent);
Satakshiaaf69532019-11-07 17:54:24 -0800736 ActivityOptions opts = ActivityOptions.makeBasic();
Miranda Kephart63e65a92020-01-10 11:18:51 -0500737 context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
Satakshiaaf69532019-11-07 17:54:24 -0800738
Miranda Kephart9bbecf32019-12-06 10:54:58 -0500739 ScreenshotSmartActions.notifyScreenshotAction(
740 context, intent.getStringExtra(EXTRA_ID), actionType, true);
Winson Chung8858e6e2015-05-27 17:18:56 -0700741 }
742 }
Winson Chung9112ec32011-06-27 13:15:32 -0700743}