Merge "Fix Chooser-to-editor transition animation" into tm-qpr-dev am: 9b723322f1
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18304427 Change-Id: Ic3dc5abac382389e916325dda7a59c90a2dfa9b7 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -30,6 +30,7 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
|
import android.app.ActivityOptions;
|
||||||
import android.app.SharedElementCallback;
|
import android.app.SharedElementCallback;
|
||||||
import android.app.prediction.AppPredictionContext;
|
import android.app.prediction.AppPredictionContext;
|
||||||
import android.app.prediction.AppPredictionManager;
|
import android.app.prediction.AppPredictionManager;
|
||||||
@@ -101,7 +102,10 @@ import android.view.ViewGroup.LayoutParams;
|
|||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.view.animation.AccelerateInterpolator;
|
import android.view.animation.AccelerateInterpolator;
|
||||||
|
import android.view.animation.AlphaAnimation;
|
||||||
|
import android.view.animation.Animation;
|
||||||
import android.view.animation.DecelerateInterpolator;
|
import android.view.animation.DecelerateInterpolator;
|
||||||
|
import android.view.animation.LinearInterpolator;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.Space;
|
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_COUNT = "count";
|
||||||
private static final String PLURALS_FILE_NAME = "file_name";
|
private static final String PLURALS_FILE_NAME = "file_name";
|
||||||
|
|
||||||
|
private static final String IMAGE_EDITOR_SHARED_ELEMENT = "screenshot_preview_image";
|
||||||
|
|
||||||
private boolean mIsAppPredictorComponentAvailable;
|
private boolean mIsAppPredictorComponentAvailable;
|
||||||
private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
|
private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
|
||||||
private Map<ChooserTarget, ShortcutInfo> mDirectShareShortcutInfoCache;
|
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 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
|
@VisibleForTesting
|
||||||
int mListViewUpdateDelayMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
|
int mListViewUpdateDelayMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
SystemUiDeviceConfigFlags.SHARESHEET_LIST_VIEW_UPDATE_DELAY,
|
SystemUiDeviceConfigFlags.SHARESHEET_LIST_VIEW_UPDATE_DELAY,
|
||||||
@@ -305,6 +316,8 @@ public class ChooserActivity extends ResolverActivity implements
|
|||||||
|
|
||||||
private boolean mRemoveSharedElements = false;
|
private boolean mRemoveSharedElements = false;
|
||||||
|
|
||||||
|
private View mContentView = null;
|
||||||
|
|
||||||
private class ContentPreviewCoordinator {
|
private class ContentPreviewCoordinator {
|
||||||
private static final int IMAGE_FADE_IN_MILLIS = 150;
|
private static final int IMAGE_FADE_IN_MILLIS = 150;
|
||||||
private static final int IMAGE_LOAD_TIMEOUT = 1;
|
private static final int IMAGE_LOAD_TIMEOUT = 1;
|
||||||
@@ -990,6 +1003,7 @@ public class ChooserActivity extends ResolverActivity implements
|
|||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
Log.d(TAG, "onResume: " + getComponentName().flattenToShortString());
|
Log.d(TAG, "onResume: " + getComponentName().flattenToShortString());
|
||||||
|
maybeCancelFinishAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1085,6 +1099,10 @@ public class ChooserActivity extends ResolverActivity implements
|
|||||||
final ComponentName cn = getEditSharingComponent();
|
final ComponentName cn = getEditSharingComponent();
|
||||||
|
|
||||||
final Intent resolveIntent = new Intent(originalIntent);
|
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.setComponent(cn);
|
||||||
resolveIntent.setAction(Intent.ACTION_EDIT);
|
resolveIntent.setAction(Intent.ACTION_EDIT);
|
||||||
String originalAction = originalIntent.getAction();
|
String originalAction = originalIntent.getAction();
|
||||||
@@ -1114,7 +1132,6 @@ public class ChooserActivity extends ResolverActivity implements
|
|||||||
return dri;
|
return dri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
protected TargetInfo getNearbySharingTarget(Intent originalIntent) {
|
protected TargetInfo getNearbySharingTarget(Intent originalIntent) {
|
||||||
final ComponentName cn = getNearbySharingComponent();
|
final ComponentName cn = getNearbySharingComponent();
|
||||||
@@ -1217,15 +1234,30 @@ public class ChooserActivity extends ResolverActivity implements
|
|||||||
"",
|
"",
|
||||||
-1,
|
-1,
|
||||||
false);
|
false);
|
||||||
|
View firstImgView = getFirstVisibleImgPreviewView();
|
||||||
// Action bar is user-independent, always start as primary
|
// Action bar is user-independent, always start as primary
|
||||||
safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
|
if (firstImgView == null) {
|
||||||
finish();
|
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);
|
b.setId(R.id.chooser_edit_button);
|
||||||
return b;
|
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) {
|
private void addActionButton(ViewGroup parent, Button b) {
|
||||||
if (b == null) return;
|
if (b == null) return;
|
||||||
final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
|
final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
|
||||||
@@ -1572,6 +1604,14 @@ public class ChooserActivity extends ResolverActivity implements
|
|||||||
getNumSheetExpansions() + 1).apply();
|
getNumSheetExpansions() + 1).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
if (maybeCancelFinishAnimation()) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
@@ -2900,6 +2940,30 @@ public class ChooserActivity extends ResolverActivity implements
|
|||||||
.setSubtype(previewType));
|
.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 {
|
abstract static class ViewHolderBase extends RecyclerView.ViewHolder {
|
||||||
private int mViewType;
|
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
|
@Override
|
||||||
protected void maybeLogProfileChange() {
|
protected void maybeLogProfileChange() {
|
||||||
getChooserActivityLogger().logShareheetProfileChanged();
|
getChooserActivityLogger().logShareheetProfileChanged();
|
||||||
|
|||||||
@@ -1314,7 +1314,7 @@ public class ResolverActivity extends Activity implements
|
|||||||
StrictMode.disableDeathOnFileUriExposure();
|
StrictMode.disableDeathOnFileUriExposure();
|
||||||
try {
|
try {
|
||||||
UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle();
|
UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle();
|
||||||
safelyStartActivityInternal(cti, currentUserHandle);
|
safelyStartActivityInternal(cti, currentUserHandle, null);
|
||||||
} finally {
|
} finally {
|
||||||
StrictMode.enableDeathOnFileUriExposure();
|
StrictMode.enableDeathOnFileUriExposure();
|
||||||
}
|
}
|
||||||
@@ -1327,18 +1327,23 @@ public class ResolverActivity extends Activity implements
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void safelyStartActivityAsUser(TargetInfo cti, UserHandle user) {
|
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
|
// We're dispatching intents that might be coming from legacy apps, so
|
||||||
// don't kill ourselves.
|
// don't kill ourselves.
|
||||||
StrictMode.disableDeathOnFileUriExposure();
|
StrictMode.disableDeathOnFileUriExposure();
|
||||||
try {
|
try {
|
||||||
safelyStartActivityInternal(cti, user);
|
safelyStartActivityInternal(cti, user, options);
|
||||||
} finally {
|
} finally {
|
||||||
StrictMode.enableDeathOnFileUriExposure();
|
StrictMode.enableDeathOnFileUriExposure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void safelyStartActivityInternal(
|
||||||
private void safelyStartActivityInternal(TargetInfo cti, UserHandle user) {
|
TargetInfo cti, UserHandle user, @Nullable Bundle options) {
|
||||||
// If the target is suspended, the activity will not be successfully launched.
|
// If the target is suspended, the activity will not be successfully launched.
|
||||||
// Do not unregister from package manager updates in this case
|
// Do not unregister from package manager updates in this case
|
||||||
if (!cti.isSuspended() && mRegistered) {
|
if (!cti.isSuspended() && mRegistered) {
|
||||||
@@ -1356,14 +1361,14 @@ public class ResolverActivity extends Activity implements
|
|||||||
Toast.makeText(this, mProfileSwitchMessage, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, mProfileSwitchMessage, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
if (!mSafeForwardingMode) {
|
if (!mSafeForwardingMode) {
|
||||||
if (cti.startAsUser(this, null, user)) {
|
if (cti.startAsUser(this, options, user)) {
|
||||||
onActivityStarted(cti);
|
onActivityStarted(cti);
|
||||||
maybeLogCrossProfileTargetLaunch(cti, user);
|
maybeLogCrossProfileTargetLaunch(cti, user);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (cti.startAsCaller(this, null, user.getIdentifier())) {
|
if (cti.startAsCaller(this, options, user.getIdentifier())) {
|
||||||
onActivityStarted(cti);
|
onActivityStarted(cti);
|
||||||
maybeLogCrossProfileTargetLaunch(cti, user);
|
maybeLogCrossProfileTargetLaunch(cti, user);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user