Loading core/java/com/android/internal/app/ChooserActivity.java +127 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -990,6 +1003,7 @@ public class ChooserActivity extends ResolverActivity implements protected void onResume() { super.onResume(); Log.d(TAG, "onResume: " + getComponentName().flattenToShortString()); maybeCancelFinishAnimation(); } @Override Loading Loading @@ -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( Loading @@ -1101,7 +1119,6 @@ public class ChooserActivity extends ResolverActivity implements return dri; } @VisibleForTesting protected TargetInfo getNearbySharingTarget(Intent originalIntent) { final ComponentName cn = getNearbySharingComponent(); Loading Loading @@ -1204,15 +1221,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( Loading Loading @@ -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(); Loading Loading @@ -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; Loading Loading @@ -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(); Loading core/java/com/android/internal/app/ResolverActivity.java +11 −6 Original line number Diff line number Diff line Loading @@ -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(); } Loading @@ -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) { Loading @@ -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); } Loading Loading
core/java/com/android/internal/app/ChooserActivity.java +127 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -990,6 +1003,7 @@ public class ChooserActivity extends ResolverActivity implements protected void onResume() { super.onResume(); Log.d(TAG, "onResume: " + getComponentName().flattenToShortString()); maybeCancelFinishAnimation(); } @Override Loading Loading @@ -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( Loading @@ -1101,7 +1119,6 @@ public class ChooserActivity extends ResolverActivity implements return dri; } @VisibleForTesting protected TargetInfo getNearbySharingTarget(Intent originalIntent) { final ComponentName cn = getNearbySharingComponent(); Loading Loading @@ -1204,15 +1221,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( Loading Loading @@ -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(); Loading Loading @@ -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; Loading Loading @@ -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(); Loading
core/java/com/android/internal/app/ResolverActivity.java +11 −6 Original line number Diff line number Diff line Loading @@ -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(); } Loading @@ -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) { Loading @@ -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); } Loading