Merge "Fix Chooser-to-editor transition animation" into tm-qpr-dev
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user