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

Commit 2438f7ee authored by Andrey Epin's avatar Andrey Epin Committed by Automerger Merge Worker
Browse files

Merge "Fix Chooser-to-editor transition animation" into tm-qpr-dev am: 9b723322 am: 68242f1b

parents 39fc317d 68242f1b
Loading
Loading
Loading
Loading
+127 −3
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.SharedElementCallback;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionManager;
@@ -101,7 +102,10 @@ import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Space;
@@ -197,6 +201,8 @@ public class ChooserActivity extends ResolverActivity implements
    private static final String PLURALS_COUNT = "count";
    private static final String PLURALS_FILE_NAME = "file_name";

    private static final String IMAGE_EDITOR_SHARED_ELEMENT = "screenshot_preview_image";

    private boolean mIsAppPredictorComponentAvailable;
    private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
    private Map<ChooserTarget, ShortcutInfo> mDirectShareShortcutInfoCache;
@@ -250,6 +256,11 @@ public class ChooserActivity extends ResolverActivity implements

    private static final int DEFAULT_LIST_VIEW_UPDATE_DELAY_MS = 125;

    private static final int URI_PERMISSION_INTENT_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
            | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
            | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;

    @VisibleForTesting
    int mListViewUpdateDelayMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
            SystemUiDeviceConfigFlags.SHARESHEET_LIST_VIEW_UPDATE_DELAY,
@@ -305,6 +316,8 @@ public class ChooserActivity extends ResolverActivity implements

    private boolean mRemoveSharedElements = false;

    private View mContentView = null;

    private class ContentPreviewCoordinator {
        private static final int IMAGE_FADE_IN_MILLIS = 150;
        private static final int IMAGE_LOAD_TIMEOUT = 1;
@@ -990,6 +1003,7 @@ public class ChooserActivity extends ResolverActivity implements
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: " + getComponentName().flattenToShortString());
        maybeCancelFinishAnimation();
    }

    @Override
@@ -1085,6 +1099,10 @@ public class ChooserActivity extends ResolverActivity implements
        final ComponentName cn = getEditSharingComponent();

        final Intent resolveIntent = new Intent(originalIntent);
        // Retain only URI permission grant flags if present. Other flags may prevent the scene
        // transition animation from running (i.e FLAG_ACTIVITY_NO_ANIMATION,
        // FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_NEW_DOCUMENT) but also not needed.
        resolveIntent.setFlags(originalIntent.getFlags() & URI_PERMISSION_INTENT_FLAGS);
        resolveIntent.setComponent(cn);
        resolveIntent.setAction(Intent.ACTION_EDIT);
        String originalAction = originalIntent.getAction();
@@ -1114,7 +1132,6 @@ public class ChooserActivity extends ResolverActivity implements
        return dri;
    }


    @VisibleForTesting
    protected TargetInfo getNearbySharingTarget(Intent originalIntent) {
        final ComponentName cn = getNearbySharingComponent();
@@ -1217,15 +1234,30 @@ public class ChooserActivity extends ResolverActivity implements
                            "",
                            -1,
                            false);
                    View firstImgView = getFirstVisibleImgPreviewView();
                    // Action bar is user-independent, always start as primary
                    if (firstImgView == null) {
                        safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
                        finish();
                    } else {
                        ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(
                                this, firstImgView, IMAGE_EDITOR_SHARED_ELEMENT);
                        safelyStartActivityAsUser(
                                ti, getPersonalProfileUserHandle(), options.toBundle());
                        startFinishAnimation();
                    }
                }
        );
        b.setId(R.id.chooser_edit_button);
        return b;
    }

    @Nullable
    private View getFirstVisibleImgPreviewView() {
        View firstImage = findViewById(R.id.content_preview_image_1_large);
        return firstImage != null && firstImage.isVisibleToUser() ? firstImage : null;
    }

    private void addActionButton(ViewGroup parent, Button b) {
        if (b == null) return;
        final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
@@ -1572,6 +1604,14 @@ public class ChooserActivity extends ResolverActivity implements
                getNumSheetExpansions() + 1).apply();
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (maybeCancelFinishAnimation()) {
            finish();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
@@ -2900,6 +2940,30 @@ public class ChooserActivity extends ResolverActivity implements
                .setSubtype(previewType));
    }

    private void startFinishAnimation() {
        View rootView = findRootView();
        rootView.startAnimation(new FinishAnimation(this, rootView));
    }

    private boolean maybeCancelFinishAnimation() {
        View rootView = findRootView();
        Animation animation = rootView.getAnimation();
        if (animation instanceof FinishAnimation) {
            boolean hasEnded = animation.hasEnded();
            animation.cancel();
            rootView.clearAnimation();
            return !hasEnded;
        }
        return false;
    }

    private View findRootView() {
        if (mContentView == null) {
            mContentView = findViewById(android.R.id.content);
        }
        return mContentView;
    }

    abstract static class ViewHolderBase extends RecyclerView.ViewHolder {
        private int mViewType;

@@ -4000,6 +4064,66 @@ public class ChooserActivity extends ResolverActivity implements
        }
    }

    /**
     * Used in combination with the scene transition when launching the image editor
     */
    private static class FinishAnimation extends AlphaAnimation implements
            Animation.AnimationListener {
        private Activity mActivity;
        private View mRootView;
        private final float mFromAlpha;

        FinishAnimation(Activity activity, View rootView) {
            super(rootView.getAlpha(), 0.0f);
            mActivity = activity;
            mRootView = rootView;
            mFromAlpha = rootView.getAlpha();
            setInterpolator(new LinearInterpolator());
            long duration = activity.getWindow().getTransitionBackgroundFadeDuration();
            setDuration(duration);
            // The scene transition animation looks better when it's not overlapped with this
            // fade-out animation thus the delay.
            // It is most likely that the image editor will cause this activity to stop and this
            // animation will be cancelled in the background without running (i.e. we'll animate
            // only when this activity remains partially visible after the image editor launch).
            setStartOffset(duration);
            super.setAnimationListener(this);
        }

        @Override
        public void setAnimationListener(AnimationListener listener) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void cancel() {
            mRootView.setAlpha(mFromAlpha);
            cleanup();
            super.cancel();
        }

        @Override
        public void onAnimationStart(Animation animation) {
        }

        @Override
        public void onAnimationEnd(Animation animation) {
            if (mActivity != null) {
                mActivity.finish();
                cleanup();
            }
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
        }

        private void cleanup() {
            mActivity = null;
            mRootView = null;
        }
    }

    @Override
    protected void maybeLogProfileChange() {
        getChooserActivityLogger().logShareheetProfileChanged();
+11 −6
Original line number Diff line number Diff line
@@ -1314,7 +1314,7 @@ public class ResolverActivity extends Activity implements
        StrictMode.disableDeathOnFileUriExposure();
        try {
            UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle();
            safelyStartActivityInternal(cti, currentUserHandle);
            safelyStartActivityInternal(cti, currentUserHandle, null);
        } finally {
            StrictMode.enableDeathOnFileUriExposure();
        }
@@ -1327,18 +1327,23 @@ public class ResolverActivity extends Activity implements
     */
    @VisibleForTesting
    public void safelyStartActivityAsUser(TargetInfo cti, UserHandle user) {
        safelyStartActivityAsUser(cti, user, null);
    }

    protected void safelyStartActivityAsUser(
            TargetInfo cti, UserHandle user, @Nullable Bundle options) {
        // We're dispatching intents that might be coming from legacy apps, so
        // don't kill ourselves.
        StrictMode.disableDeathOnFileUriExposure();
        try {
            safelyStartActivityInternal(cti, user);
            safelyStartActivityInternal(cti, user, options);
        } finally {
            StrictMode.enableDeathOnFileUriExposure();
        }
    }


    private void safelyStartActivityInternal(TargetInfo cti, UserHandle user) {
    private void safelyStartActivityInternal(
            TargetInfo cti, UserHandle user, @Nullable Bundle options) {
        // If the target is suspended, the activity will not be successfully launched.
        // Do not unregister from package manager updates in this case
        if (!cti.isSuspended() && mRegistered) {
@@ -1356,14 +1361,14 @@ public class ResolverActivity extends Activity implements
            Toast.makeText(this, mProfileSwitchMessage, Toast.LENGTH_LONG).show();
        }
        if (!mSafeForwardingMode) {
            if (cti.startAsUser(this, null, user)) {
            if (cti.startAsUser(this, options, user)) {
                onActivityStarted(cti);
                maybeLogCrossProfileTargetLaunch(cti, user);
            }
            return;
        }
        try {
            if (cti.startAsCaller(this, null, user.getIdentifier())) {
            if (cti.startAsCaller(this, options, user.getIdentifier())) {
                onActivityStarted(cti);
                maybeLogCrossProfileTargetLaunch(cti, user);
            }