Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89cf7e02 authored by Miranda Kephart's avatar Miranda Kephart Committed by Automerger Merge Worker
Browse files

Merge "Implement "shared element" SS->LSS transition in screenshots" into sc-dev am: 070852ed

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15005300

Change-Id: I997b2b89fefbe2aea3f8818ed7cd01df4cb2a515
parents ca0a0662 070852ed
Loading
Loading
Loading
Loading
+17 −23
Original line number Diff line number Diff line
@@ -32,8 +32,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.text.TextUtils;
import android.transition.Transition;
import android.transition.TransitionListenerAdapter;
import android.util.Log;
import android.view.ScrollCaptureResponse;
import android.view.View;
@@ -74,7 +72,7 @@ public class LongScreenshotActivity extends Activity {
    private final Executor mUiExecutor;
    private final Executor mBackgroundExecutor;
    private final ImageExporter mImageExporter;
    private final LongScreenshotHolder mLongScreenshotHolder;
    private final LongScreenshotData mLongScreenshotHolder;

    private ImageView mPreview;
    private ImageView mTransitionView;
@@ -103,7 +101,7 @@ public class LongScreenshotActivity extends Activity {
    @Inject
    public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
            @Main Executor mainExecutor, @Background Executor bgExecutor,
            LongScreenshotHolder longScreenshotHolder) {
            LongScreenshotData longScreenshotHolder) {
        mUiEventLogger = uiEventLogger;
        mUiExecutor = mainExecutor;
        mBackgroundExecutor = bgExecutor;
@@ -116,7 +114,6 @@ public class LongScreenshotActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
        super.onCreate(savedInstanceState);
        postponeEnterTransition();
        setContentView(R.layout.long_screenshot);

        mPreview = requireViewById(R.id.preview);
@@ -210,13 +207,11 @@ public class LongScreenshotActivity extends Activity {
                    public boolean onPreDraw() {
                        mEnterTransitionView.getViewTreeObserver().removeOnPreDrawListener(this);
                        updateImageDimensions();
                        startPostponedEnterTransition();
                        if (isActivityTransitionRunning()) {
                            getWindow().getSharedElementEnterTransition().addListener(
                                    new TransitionListenerAdapter() {
                                        @Override
                                        public void onTransitionEnd(Transition transition) {
                                            super.onTransitionEnd(transition);
                        mEnterTransitionView.post(() -> {
                            Rect dest = new Rect();
                            mEnterTransitionView.getBoundsOnScreen(dest);
                            mLongScreenshotHolder.takeTransitionDestinationCallback()
                                    .setTransitionDestination(dest, () -> {
                                        mPreview.animate().alpha(1f);
                                        mCropView.setBoundaryPosition(
                                                CropView.CropBoundary.TOP, topFraction);
@@ -226,9 +221,8 @@ public class LongScreenshotActivity extends Activity {
                                        mCropView.setVisibility(View.VISIBLE);
                                        setButtonsEnabled(true);
                                        mEnterTransitionView.setVisibility(View.GONE);
                                        }
                                    });
                        }
                        });
                        return true;
                    }
                });
+21 −4
Original line number Diff line number Diff line
@@ -23,16 +23,19 @@ import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject;

/**
 * LongScreenshotHolder holds on to 1 LongScreenshot reference to facilitate indirect in-process
 * passing.
 * LongScreenshotData holds on to 1 LongScreenshot reference and 1 TransitionDestination
 * reference, to facilitate indirect in-process passing.
 */
@SysUISingleton
public class LongScreenshotHolder {
public class LongScreenshotData {
    private final AtomicReference<ScrollCaptureController.LongScreenshot> mLongScreenshot;
    private final AtomicReference<ScreenshotController.TransitionDestination>
            mTransitionDestinationCallback;

    @Inject
    public LongScreenshotHolder() {
    public LongScreenshotData() {
        mLongScreenshot = new AtomicReference<>();
        mTransitionDestinationCallback = new AtomicReference<>();
    }

    /**
@@ -56,4 +59,18 @@ public class LongScreenshotHolder {
        return mLongScreenshot.getAndSet(null);
    }

    /**
     * Set the holder's TransitionDestination callback.
     */
    public void setTransitionDestinationCallback(
            ScreenshotController.TransitionDestination destination) {
        mTransitionDestinationCallback.set(destination);
    }

    /**
     * Return the current TransitionDestination callback and clear.
     */
    public ScreenshotController.TransitionDestination takeTransitionDestinationCallback() {
        return mTransitionDestinationCallback.getAndSet(null);
    }
}
+54 −17
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.ScrollCaptureResponse;
import android.view.SurfaceControl;
@@ -72,6 +73,7 @@ import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
@@ -203,6 +205,15 @@ public class ScreenshotController {
        void onActionsReady(ScreenshotController.QuickShareData quickShareData);
    }

    interface TransitionDestination {
        /**
         * Allows the long screenshot activity to call back with a destination location (the bounds
         * on screen of the destination for the transitioning view) and a Runnable to be run once
         * the transition animation is complete.
         */
        void setTransitionDestination(Rect transitionDestination, Runnable onTransitionEnd);
    }

    // These strings are used for communicating the action invoked to
    // ScreenshotNotificationSmartActionsProvider.
    static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
@@ -241,7 +252,7 @@ public class ScreenshotController {
    private final PhoneWindow mWindow;
    private final DisplayManager mDisplayManager;
    private final ScrollCaptureController mScrollCaptureController;
    private final LongScreenshotHolder mLongScreenshotHolder;
    private final LongScreenshotData mLongScreenshotHolder;

    private ScreenshotView mScreenshotView;
    private Bitmap mScreenBitmap;
@@ -286,7 +297,7 @@ public class ScreenshotController {
            ImageExporter imageExporter,
            @Main Executor mainExecutor,
            ScrollCaptureController scrollCaptureController,
            LongScreenshotHolder longScreenshotHolder) {
            LongScreenshotData longScreenshotHolder) {
        mScreenshotSmartActions = screenshotSmartActions;
        mNotificationsController = screenshotNotificationsController;
        mScrollCaptureClient = scrollCaptureClient;
@@ -648,17 +659,29 @@ public class ScreenshotController {
                    }

                    mLongScreenshotHolder.setLongScreenshot(longScreenshot);
                    mLongScreenshotHolder.setTransitionDestinationCallback(
                            (transitionDestination, onTransitionEnd) ->
                                    mScreenshotView.startLongScreenshotTransition(
                                            transitionDestination, onTransitionEnd,
                                            longScreenshot));

                    final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);

                    Pair<ActivityOptions, ExitTransitionCoordinator> transition =
                            ActivityOptions.startSharedElementAnimation(
                                    mWindow, new ScreenshotExitTransitionCallbacks(), null,
                                    Pair.create(mScreenshotView.getScrollablePreview(),
                                            ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
                            ActivityOptions.startSharedElementAnimation(mWindow,
                                    new ScreenshotExitTransitionCallbacksSupplier(false).get(),
                                    null);
                    transition.second.startExit();
                    mContext.startActivity(intent, transition.first.toBundle());
                    RemoteAnimationAdapter runner = new RemoteAnimationAdapter(
                            SCREENSHOT_REMOTE_RUNNER, 0, 0);
                    try {
                        WindowManagerGlobal.getWindowManagerService()
                                .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY);
                    } catch (Exception e) {
                        Log.e(TAG, "Error overriding screenshot app transition", e);
                    }
                }, mMainExecutor);
            });
        } catch (CancellationException e) {
@@ -883,8 +906,8 @@ public class ScreenshotController {
        return () -> {
            Pair<ActivityOptions, ExitTransitionCoordinator> transition =
                    ActivityOptions.startSharedElementAnimation(
                            mWindow, new ScreenshotExitTransitionCallbacks(), null,
                            Pair.create(mScreenshotView.getScreenshotPreview(),
                            mWindow, new ScreenshotExitTransitionCallbacksSupplier(true).get(),
                            null, Pair.create(mScreenshotView.getScreenshotPreview(),
                                    ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
            transition.second.startExit();

@@ -970,7 +993,17 @@ public class ScreenshotController {
        return matchWithinTolerance;
    }

    private class ScreenshotExitTransitionCallbacks implements ExitTransitionCallbacks {
    private class ScreenshotExitTransitionCallbacksSupplier implements
            Supplier<ExitTransitionCallbacks> {
        final boolean mDismissOnHideSharedElements;

        ScreenshotExitTransitionCallbacksSupplier(boolean dismissOnHideSharedElements) {
            mDismissOnHideSharedElements = dismissOnHideSharedElements;
        }

        @Override
        public ExitTransitionCallbacks get() {
            return new ExitTransitionCallbacks() {
                @Override
                public boolean isReturnTransitionAllowed() {
                    return false;
@@ -978,11 +1011,15 @@ public class ScreenshotController {

                @Override
                public void hideSharedElements() {
                    if (mDismissOnHideSharedElements) {
                        finishDismiss();
                    }
                }

                @Override
                public void onFinish() {
                }
            };
        }
    }
}
+47 −3
Original line number Diff line number Diff line
@@ -764,7 +764,53 @@ public class ScreenshotView extends FrameLayout implements
        return r;
    }

    void startLongScreenshotTransition(Rect destination, Runnable onTransitionEnd,
            ScrollCaptureController.LongScreenshot longScreenshot) {
        mScrollablePreview.setImageBitmap(longScreenshot.toBitmap());
        ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
        float startX = mScrollablePreview.getX();
        float startY = mScrollablePreview.getY();
        int[] locInScreen = mScrollablePreview.getLocationOnScreen();
        destination.offset((int) startX - locInScreen[0], (int) startY - locInScreen[1]);
        mScrollablePreview.setPivotX(0);
        mScrollablePreview.setPivotY(0);
        mScrollablePreview.setAlpha(1f);
        float currentScale = mScrollablePreview.getWidth() / (float) longScreenshot.getWidth();
        Matrix matrix = new Matrix();
        matrix.setScale(currentScale, currentScale);
        matrix.postTranslate(
                longScreenshot.getLeft() * currentScale, longScreenshot.getTop() * currentScale);
        mScrollablePreview.setImageMatrix(matrix);
        float destinationScale = destination.width() / (float) mScrollablePreview.getWidth();
        anim.addUpdateListener(animation -> {
            float t = animation.getAnimatedFraction();
            mScrollingScrim.setAlpha(1 - t);
            float currScale = MathUtils.lerp(1, destinationScale, t);
            mScrollablePreview.setScaleX(currScale);
            mScrollablePreview.setScaleY(currScale);
            mScrollablePreview.setX(MathUtils.lerp(startX, destination.left, t));
            mScrollablePreview.setY(MathUtils.lerp(startY, destination.top, t));
        });
        anim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                onTransitionEnd.run();
                mScrollablePreview.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        mCallbacks.onDismiss();
                    }
                });
            }
        });
        anim.start();
    }

    void prepareScrollingTransition(ScrollCaptureResponse response, Bitmap screenBitmap) {
        mScrollingScrim.setImageBitmap(screenBitmap);
        mScrollingScrim.setVisibility(View.VISIBLE);
        Rect scrollableArea = scrollableAreaOnScreen(response);
        float scale = mCornerSizeX
                / (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight());
@@ -775,14 +821,12 @@ public class ScreenshotView extends FrameLayout implements
        params.height = (int) (scale * scrollableArea.height());
        Matrix matrix = new Matrix();
        matrix.setScale(scale, scale);
        matrix.postTranslate(0, -scrollableArea.top * scale);
        matrix.postTranslate(-scrollableArea.left * scale, -scrollableArea.top * scale);

        mScrollablePreview.setTranslationX(scale * scrollableArea.left);
        mScrollablePreview.setTranslationY(scale * scrollableArea.top);
        mScrollablePreview.setImageMatrix(matrix);

        mScrollingScrim.setImageBitmap(screenBitmap);
        mScrollingScrim.setVisibility(View.VISIBLE);
        mScrollablePreview.setImageBitmap(screenBitmap);
        mScrollablePreview.setVisibility(View.VISIBLE);
        createScreenshotFadeDismissAnimation(true).start();