diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index e06413783fe4f..666ee6e606a7b 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -182,7 +182,7 @@ public class ChooserActivity extends ResolverActivity implements * To be used for shared element transition into this activity. * @hide */ - public static final String FIRST_IMAGE_PREVIEW_TRANSITION_NAME = "chooser_preview_image_1"; + public static final String FIRST_IMAGE_PREVIEW_TRANSITION_NAME = "screenshot_preview_image"; private static final String PREF_NUM_SHEET_EXPANSIONS = "pref_num_sheet_expansions"; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index 334693589503e..57a41d9149109 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -57,7 +57,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import java.io.File; import java.io.IOException; @@ -98,11 +98,11 @@ class SaveImageInBackgroundTask extends AsyncTask { private final String mScreenshotId; private final boolean mSmartActionsEnabled; private final Random mRandom = new Random(); - private final Supplier mSharedElementTransition; + private final Supplier mSharedElementTransition; SaveImageInBackgroundTask(Context context, ScreenshotSmartActions screenshotSmartActions, ScreenshotController.SaveImageInBackgroundData data, - Supplier sharedElementTransition) { + Supplier sharedElementTransition) { mContext = context; mScreenshotSmartActions = screenshotSmartActions; mImageData = new ScreenshotController.SavedImageData(); @@ -239,7 +239,7 @@ class SaveImageInBackgroundTask extends AsyncTask { mImageData.uri = uri; mImageData.smartActions = smartActions; mImageData.shareTransition = createShareAction(mContext, mContext.getResources(), uri); - mImageData.editAction = createEditAction(mContext, mContext.getResources(), uri); + mImageData.editTransition = createEditAction(mContext, mContext.getResources(), uri); mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri); mParams.mActionsReadyListener.onActionsReady(mImageData); @@ -293,9 +293,9 @@ class SaveImageInBackgroundTask extends AsyncTask { * Assumes that the action intent is sent immediately after being supplied. */ @VisibleForTesting - Supplier createShareAction(Context context, Resources r, Uri uri) { + Supplier createShareAction(Context context, Resources r, Uri uri) { return () -> { - ShareTransition transition = mSharedElementTransition.get(); + ActionTransition transition = mSharedElementTransition.get(); // Note: Both the share and edit actions are proxied through ActionProxyReceiver in // order to do some common work like dismissing the keyguard and sending @@ -348,52 +348,57 @@ class SaveImageInBackgroundTask extends AsyncTask { Icon.createWithResource(r, R.drawable.ic_screenshot_share), r.getString(com.android.internal.R.string.share), shareAction); - transition.shareAction = shareActionBuilder.build(); + transition.action = shareActionBuilder.build(); return transition; }; } @VisibleForTesting - Notification.Action createEditAction(Context context, Resources r, Uri uri) { - // 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 + Supplier createEditAction(Context context, Resources r, Uri uri) { + return () -> { + ActionTransition transition = mSharedElementTransition.get(); + // 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 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.setDataAndType(uri, "image/png"); - editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + // 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.setDataAndType(uri, "image/png"); + editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0, - editIntent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT); + PendingIntent pendingIntent = PendingIntent.getActivityAsUser( + context, 0, editIntent, PendingIntent.FLAG_IMMUTABLE, + transition.bundle, UserHandle.CURRENT); - // Make sure pending intents for the system user are still unique across users - // by setting the (otherwise unused) request code to the current user id. - int requestCode = mContext.getUserId(); + // Make sure pending intents for the system user are still unique across users + // by setting the (otherwise unused) request code to the current user id. + int requestCode = mContext.getUserId(); - // Create a edit action - PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode, - new Intent(context, ActionProxyReceiver.class) - .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, pendingIntent) - .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId) - .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, - mSmartActionsEnabled) - .setAction(Intent.ACTION_EDIT) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), - PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, - UserHandle.SYSTEM); - Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( - Icon.createWithResource(r, R.drawable.ic_screenshot_edit), - r.getString(com.android.internal.R.string.screenshot_edit), editAction); + // Create a edit action + PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode, + new Intent(context, ActionProxyReceiver.class) + .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, pendingIntent) + .putExtra(ScreenshotController.EXTRA_ID, mScreenshotId) + .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, + mSmartActionsEnabled) + .setAction(Intent.ACTION_EDIT) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, + UserHandle.SYSTEM); + Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( + Icon.createWithResource(r, R.drawable.ic_screenshot_edit), + r.getString(com.android.internal.R.string.screenshot_edit), editAction); - return editActionBuilder.build(); + transition.action = editActionBuilder.build(); + return transition; + }; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index d2fe5d284a97a..68d7343ec144c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -78,7 +78,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.policy.PhoneWindow; import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.R; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.android.systemui.util.DeviceConfigProxy; import java.util.List; @@ -111,17 +111,17 @@ public class ScreenshotController { */ static class SavedImageData { public Uri uri; - public Supplier shareTransition; - public Notification.Action editAction; + public Supplier shareTransition; + public Supplier editTransition; public Notification.Action deleteAction; public List smartActions; /** - * POD for shared element transition to share sheet. + * POD for shared element transition. */ - static class ShareTransition { + static class ActionTransition { public Bundle bundle; - public Notification.Action shareAction; + public Notification.Action action; public Runnable onCancelRunnable; } @@ -131,7 +131,7 @@ public class ScreenshotController { public void reset() { uri = null; shareTransition = null; - editAction = null; + editTransition = null; deleteAction = null; smartActions = null; } @@ -339,6 +339,10 @@ public class ScreenshotController { } } + boolean isPendingSharedTransition() { + return mScreenshotView.isPendingSharedTransition(); + } + /** * Update resources on configuration change. Reinflate for theme/color changes. */ @@ -462,7 +466,7 @@ public class ScreenshotController { Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. " + "(dismissing=" + mScreenshotView.isDismissing() + ")"); } - mScreenshotView.reset(); + reloadAssets(); } mScreenBitmap = screenshot; @@ -605,7 +609,7 @@ public class ScreenshotController { } mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data, - getShareTransitionSupplier()); + getActionTransitionSupplier()); mSaveInBgTask.execute(); } @@ -659,7 +663,7 @@ public class ScreenshotController { * Supplies the necessary bits for the shared element transition to share sheet. * Note that once supplied, the action intent to share must be sent immediately after. */ - private Supplier getShareTransitionSupplier() { + private Supplier getActionTransitionSupplier() { return () -> { ExitTransitionCallbacks cb = new ExitTransitionCallbacks() { @Override @@ -668,7 +672,8 @@ public class ScreenshotController { } @Override - public void onFinish() { } + public void onFinish() { + } }; Pair transition = @@ -677,7 +682,7 @@ public class ScreenshotController { ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)); transition.second.startExit(); - ShareTransition supply = new ShareTransition(); + ActionTransition supply = new ActionTransition(); supply.bundle = transition.first.toBundle(); supply.onCancelRunnable = () -> ActivityOptions.stopSharedElementAnimation(mWindow); return supply; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 357702ada82b1..c6e0acead8b7a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -73,7 +73,7 @@ import android.widget.LinearLayout; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.android.systemui.shared.system.QuickStepContract; import java.util.ArrayList; @@ -105,7 +105,6 @@ public class ScreenshotView extends FrameLayout implements private static final long SCREENSHOT_DISMISS_Y_DURATION_MS = 350; private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 183; private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade - private static final long SCREENSHOT_DISMISS_SHARE_OFFSET_MS = 300; // delay after share clicked private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f; private static final float ROUNDED_CORNER_RADIUS = .05f; private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe @@ -140,7 +139,7 @@ public class ScreenshotView extends FrameLayout implements private UiEventLogger mUiEventLogger; private ScreenshotViewCallback mCallbacks; private Animator mDismissAnimation; - private boolean mIgnoreDismiss; + private boolean mPendingSharedTransition; private final ArrayList mSmartChips = new ArrayList<>(); private PendingInteraction mPendingInteraction; @@ -292,6 +291,10 @@ public class ScreenshotView extends FrameLayout implements requestFocus(); } + View getScreenshotPreview() { + return mScreenshotPreview; + } + /** * Set up the logger and callback on dismissal. * @@ -529,44 +532,22 @@ public class ScreenshotView extends FrameLayout implements }); return animator; } - protected View getScreenshotPreview() { - return mScreenshotPreview; - } void setChipIntents(ScreenshotController.SavedImageData imageData) { mShareChip.setOnClickListener(v -> { - ShareTransition transition = imageData.shareTransition.get(); - try { - mIgnoreDismiss = true; - transition.shareAction.actionIntent.send(); - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED); - - // Ensures that we delay dismissing until transition has started. - postDelayed(() -> { - mIgnoreDismiss = false; - animateDismissal(); - }, SCREENSHOT_DISMISS_SHARE_OFFSET_MS); - } catch (PendingIntent.CanceledException e) { - mIgnoreDismiss = false; - if (transition.onCancelRunnable != null) { - transition.onCancelRunnable.run(); - } - Log.e(TAG, "Share intent cancelled", e); - } + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED); + startSharedTransition( + imageData.shareTransition.get()); + }); + mEditChip.setOnClickListener(v -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED); + startSharedTransition( + imageData.editTransition.get()); }); - mEditChip.setPendingIntent(imageData.editAction.actionIntent, - () -> { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED); - animateDismissal(); - }); mScreenshotPreview.setOnClickListener(v -> { - try { - imageData.editAction.actionIntent.send(); - } catch (PendingIntent.CanceledException e) { - Log.e(TAG, "PendingIntent was cancelled", e); - } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED); - animateDismissal(); + startSharedTransition( + imageData.editTransition.get()); }); if (mPendingInteraction != null) { @@ -605,12 +586,16 @@ public class ScreenshotView extends FrameLayout implements return (mDismissAnimation != null && mDismissAnimation.isRunning()); } + boolean isPendingSharedTransition() { + return mPendingSharedTransition; + } + void animateDismissal() { - animateDismissal(createScreenshotDismissAnimation()); + animateDismissal(createScreenshotTranslateDismissAnimation()); } private void animateDismissal(Animator dismissAnimation) { - if (mIgnoreDismiss) { + if (mPendingSharedTransition) { return; } if (DEBUG_WINDOW) { @@ -665,6 +650,7 @@ public class ScreenshotView extends FrameLayout implements getViewTreeObserver().removeOnComputeInternalInsetsListener(this); // Clear any references to the bitmap mScreenshotPreview.setImageDrawable(null); + mPendingSharedTransition = false; mActionsContainerBackground.setVisibility(View.GONE); mActionsContainer.setVisibility(View.GONE); mBackgroundProtection.setAlpha(0f); @@ -692,7 +678,23 @@ public class ScreenshotView extends FrameLayout implements mScreenshotSelectorView.stop(); } - private AnimatorSet createScreenshotDismissAnimation() { + private void startSharedTransition(ActionTransition transition) { + try { + mPendingSharedTransition = true; + transition.action.actionIntent.send(); + + // fade out non-preview UI + createScreenshotFadeDismissAnimation().start(); + } catch (PendingIntent.CanceledException e) { + mPendingSharedTransition = false; + if (transition.onCancelRunnable != null) { + transition.onCancelRunnable.run(); + } + Log.e(TAG, "Intent cancelled", e); + } + } + + private AnimatorSet createScreenshotTranslateDismissAnimation() { ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS); alphaAnim.setDuration(SCREENSHOT_DISMISS_ALPHA_DURATION_MS); @@ -719,6 +721,19 @@ public class ScreenshotView extends FrameLayout implements return animSet; } + private ValueAnimator createScreenshotFadeDismissAnimation() { + ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); + alphaAnim.addUpdateListener(animation -> { + float alpha = 1 - animation.getAnimatedFraction(); + mDismissButton.setAlpha(alpha); + mActionsContainerBackground.setAlpha(alpha); + mActionsContainer.setAlpha(alpha); + mBackgroundProtection.setAlpha(alpha); + }); + alphaAnim.setDuration(600); + return alphaAnim; + } + /** * Create a drawable using the size of the bitmap and insets as the fractional inset parameters. */ diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index c2b20d37f3f39..7621587ac838e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -72,7 +72,9 @@ public class TakeScreenshotService extends Service { if (DEBUG_DISMISS) { Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS"); } - mScreenshot.dismissScreenshot(false); + if (!mScreenshot.isPendingSharedTransition()) { + mScreenshot.dismissScreenshot(false); + } } } }; diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java index 6759c90753567..c79416a76fb2e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java @@ -43,7 +43,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; -import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition; +import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import org.junit.Before; import org.junit.Test; @@ -177,10 +177,10 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data, - ShareTransition::new); + ActionTransition::new); Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(), - Uri.parse("Screenshot_123.png")).get().shareAction; + Uri.parse("Screenshot_123.png")).get().action; Intent intent = shareAction.actionIntent.getIntent(); assertNotNull(intent); @@ -205,10 +205,10 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data, - ShareTransition::new); + ActionTransition::new); Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(), - Uri.parse("Screenshot_123.png")); + Uri.parse("Screenshot_123.png")).get().action; Intent intent = editAction.actionIntent.getIntent(); assertNotNull(intent); @@ -233,7 +233,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data, - ShareTransition::new); + ActionTransition::new); Notification.Action deleteAction = task.createDeleteAction(mContext, mContext.getResources(),