From 06ca2744298933cd89ddc30c9d38e6d6b19447f5 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 29 Jun 2018 12:26:49 -0700 Subject: [PATCH] Allow share/edit screenshot actions from lockscreen - Dismiss keyguard when routing the actions through the proxy receiver - Fix issue with edit activity also disallowing auto-enter PiP - Minor cleanup of some unused/shared code Bug: 72459081 Test: Launch share/edit screenshot from lockscreen (and normally) Test: Launch screenshot share action with auto-enter PiP activity and ensure it does not trigger PiP Change-Id: Ibae2de51bc3fe10f439d7506ab7d0d9243142c94 --- packages/SystemUI/AndroidManifest.xml | 8 +- .../systemui/OverviewProxyService.java | 4 +- .../android/systemui/recents/RecentsImpl.java | 3 +- .../recents/RecentsSystemUserService.java | 4 +- .../systemui/screenshot/GlobalScreenshot.java | 147 +++++++++--------- 5 files changed, 79 insertions(+), 87 deletions(-) diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index eab4b9713b610..a5616d5e80ae5 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -339,19 +339,17 @@ android:exported="false"> - - + diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index 1e5b77ee3aa43..70ca055677e75 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -99,7 +99,7 @@ public class OverviewProxyService implements CallbackController { - StatusBar statusBar = ((SystemUIApplication) mContext).getComponent( + StatusBar statusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class); if (statusBar != null) { statusBar.showScreenPinningRequest(taskId, false /* allowCancel */); @@ -152,7 +152,7 @@ public class OverviewProxyService implements CallbackController { values.put(MediaStore.Images.ImageColumns.SIZE, new File(mImageFilePath).length()); Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); - // Create a share intent + // Note: Both the share and edit actions are proxied through ActionProxyReceiver in + // order to do some common work like dismissing the keyguard and sending + // closeSystemWindows + + // Create a share intent, this will always go through the chooser activity first which + // should not trigger auto-enter PiP String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime)); String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate); Intent sharingIntent = new Intent(Intent.ACTION_SEND); @@ -286,37 +293,47 @@ class SaveImageInBackgroundTask extends AsyncTask { sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject); sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - // Create a share action for the notification. Note, we proxy the call to - // ScreenshotActionReceiver because RemoteViews currently forces an activity options - // on the PendingIntent being launched, and since we don't want to trigger the share - // sheet in this case, we start the chooser activity directly in - // ScreenshotActionReceiver. + PendingIntent chooserAction = PendingIntent.getBroadcast(context, 0, + new Intent(context, GlobalScreenshot.TargetChosenReceiver.class), + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); + Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null, + chooserAction.getIntentSender()) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); + + // Create a share action for the notification PendingIntent shareAction = PendingIntent.getBroadcast(context, 0, - new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class) - .putExtra(SHARING_INTENT, sharingIntent), + new Intent(context, GlobalScreenshot.ActionProxyReceiver.class) + .putExtra(EXTRA_ACTION_INTENT, sharingChooserIntent) + .putExtra(EXTRA_DISALLOW_ENTER_PIP, true), PendingIntent.FLAG_CANCEL_CURRENT); Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder( R.drawable.ic_screenshot_share, r.getString(com.android.internal.R.string.share), shareAction); mNotificationBuilder.addAction(shareActionBuilder.build()); + // Create an edit intent, if a specific package is provided as the editor, then launch + // that directly + String editorPackage = context.getString(R.string.config_screenshotEditor); Intent editIntent = new Intent(Intent.ACTION_EDIT); + if (!TextUtils.isEmpty(editorPackage)) { + editIntent.setComponent(ComponentName.unflattenFromString(editorPackage)); + } editIntent.setType("image/png"); editIntent.setData(uri); editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - // Create a edit action for the notification the same way. + // Create a edit action PendingIntent editAction = PendingIntent.getBroadcast(context, 1, - new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class) - .putExtra(SHARING_INTENT, editIntent), + new Intent(context, GlobalScreenshot.ActionProxyReceiver.class) + .putExtra(EXTRA_ACTION_INTENT, editIntent) + .putExtra(EXTRA_CANCEL_NOTIFICATION, editIntent.getComponent() != null), PendingIntent.FLAG_CANCEL_CURRENT); Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( R.drawable.ic_screenshot_edit, r.getString(com.android.internal.R.string.screenshot_edit), editAction); mNotificationBuilder.addAction(editActionBuilder.build()); - // Create a delete action for the notification PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0, new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class) @@ -429,7 +446,9 @@ class DeleteImageInBackgroundTask extends AsyncTask { class GlobalScreenshot { static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id"; - static final String SHARING_INTENT = "android:screenshot_sharing_intent"; + static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent"; + static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification"; + static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip"; private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130; private static final int SCREENSHOT_DROP_IN_DURATION = 430; @@ -452,7 +471,6 @@ class GlobalScreenshot { private NotificationManager mNotificationManager; private Display mDisplay; private DisplayMetrics mDisplayMetrics; - private Matrix mDisplayMatrix; private Bitmap mScreenBitmap; private View mScreenshotLayout; @@ -482,7 +500,6 @@ class GlobalScreenshot { context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); // Inflate the screenshot layout - mDisplayMatrix = new Matrix(); mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null); mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background); mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot); @@ -512,7 +529,7 @@ class GlobalScreenshot { mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mNotificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); mDisplay = mWindowManager.getDefaultDisplay(); mDisplayMetrics = new DisplayMetrics(); mDisplay.getRealMetrics(mDisplayMetrics); @@ -561,21 +578,6 @@ class GlobalScreenshot { .execute(); } - /** - * @return the current display rotation in degrees - */ - private float getDegreesForRotation(int value) { - switch (value) { - case Surface.ROTATION_90: - return 360f - 90f; - case Surface.ROTATION_180: - return 360f - 180f; - case Surface.ROTATION_270: - return 360f - 270f; - } - return 0f; - } - /** * Takes a screenshot of the current display and shows an animation. */ @@ -891,52 +893,39 @@ class GlobalScreenshot { } /** - * Receiver to proxy the share or edit intent. + * Receiver to proxy the share or edit intent, used to clean up the notification and send + * appropriate signals to the system (ie. to dismiss the keyguard if necessary). */ - public static class ScreenshotActionReceiver extends BroadcastReceiver { + public static class ActionProxyReceiver extends BroadcastReceiver { @Override - public void onReceive(Context context, Intent intent) { - try { - ActivityManager.getService().closeSystemDialogs(SYSTEM_DIALOG_REASON_SCREENSHOT); - } catch (RemoteException e) { - } + public void onReceive(Context context, final Intent intent) { + Runnable startActivityRunnable = () -> { + ActivityManagerWrapper.getInstance().closeSystemWindows( + SYSTEM_DIALOG_REASON_SCREENSHOT); - Intent actionIntent = intent.getParcelableExtra(SHARING_INTENT); - - // If this is an edit & default editor exists, route straight there. - String editorPackage = context.getResources().getString(R.string.config_screenshotEditor); - if (actionIntent.getAction() == Intent.ACTION_EDIT && - editorPackage != null && editorPackage.length() > 0) { - actionIntent.setComponent(ComponentName.unflattenFromString(editorPackage)); - final NotificationManager nm = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT); - } else { - PendingIntent chooseAction = PendingIntent.getBroadcast(context, 0, - new Intent(context, GlobalScreenshot.TargetChosenReceiver.class), - PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); - actionIntent = Intent.createChooser(actionIntent, null, - chooseAction.getIntentSender()) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); - } - - ActivityOptions opts = ActivityOptions.makeBasic(); - opts.setDisallowEnterPictureInPictureWhileLaunching(true); - - context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT); + Intent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT); + if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) { + cancelScreenshotNotification(context); + } + ActivityOptions opts = ActivityOptions.makeBasic(); + opts.setDisallowEnterPictureInPictureWhileLaunching( + intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false)); + context.startActivityAsUser(actionIntent, opts.toBundle(),UserHandle.CURRENT); + }; + StatusBar statusBar = SysUiServiceProvider.getComponent(context, StatusBar.class); + statusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null, + true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */); } } /** - * Removes the notification for a screenshot after a share or edit target is chosen. + * Removes the notification for a screenshot after a share target is chosen. */ public static class TargetChosenReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - // Clear the notification - final NotificationManager nm = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT); + // Clear the notification only after the user has chosen a share action + cancelScreenshotNotification(context); } } @@ -950,14 +939,18 @@ class GlobalScreenshot { return; } - // Clear the notification - final NotificationManager nm = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID)); - nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT); + // Clear the notification when the image is deleted + cancelScreenshotNotification(context); // And delete the image from the media store + final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID)); new DeleteImageInBackgroundTask(context).execute(uri); } } + + private static void cancelScreenshotNotification(Context context) { + final NotificationManager nm = + (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); + nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT); + } }