Merge "Fix Chooser-to-editor transition animation" into tm-qpr-dev

This commit is contained in:
Andrey Epin
2022-07-07 17:53:29 +00:00
committed by Android (Google) Code Review
2 changed files with 138 additions and 9 deletions

View File

@@ -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);
final ResolveInfo ri = getPackageManager().resolveActivity(
@@ -1101,7 +1119,6 @@ public class ChooserActivity extends ResolverActivity implements
return dri;
}
@VisibleForTesting
protected TargetInfo getNearbySharingTarget(Intent originalIntent) {
final ComponentName cn = getNearbySharingComponent();
@@ -1204,15 +1221,30 @@ public class ChooserActivity extends ResolverActivity implements
"",
-1,
false);
View firstImgView = getFirstVisibleImgPreviewView();
// Action bar is user-independent, always start as primary
safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
finish();
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(
@@ -1559,6 +1591,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();
@@ -2887,6 +2927,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;
@@ -3987,6 +4051,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();

View File

@@ -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);
}