diff options
| -rw-r--r-- | core/java/android/app/Activity.java | 17 | ||||
| -rw-r--r-- | core/java/android/app/ActivityOptions.java | 157 | ||||
| -rw-r--r-- | core/java/android/app/ActivityTransitionState.java | 32 | ||||
| -rw-r--r-- | core/java/android/app/EnterTransitionCoordinator.java | 14 | ||||
| -rw-r--r-- | core/java/android/app/ExitTransitionCoordinator.java | 24 | ||||
| -rw-r--r-- | core/java/android/transition/Transition.java | 20 | ||||
| -rw-r--r-- | core/java/android/transition/TransitionManager.java | 2 | ||||
| -rw-r--r-- | core/java/android/transition/TransitionSet.java | 14 | ||||
| -rw-r--r-- | core/res/res/values/ids.xml | 4 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 3 | 
10 files changed, 261 insertions, 26 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index ac5f3effa1f9..f5ef703e39fa 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4219,6 +4219,7 @@ public class Activity extends ContextThemeWrapper      public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,              @Nullable Bundle options) {          if (mParent == null) { +            options = transferSpringboardActivityOptions(options);              Instrumentation.ActivityResult ar =                  mInstrumentation.execStartActivity(                      this, mMainThread.getApplicationThread(), mToken, this, @@ -4267,6 +4268,14 @@ public class Activity extends ContextThemeWrapper          }      } +    private Bundle transferSpringboardActivityOptions(Bundle options) { +        if (options == null && (mWindow != null && !mWindow.isActive())) { +            return mActivityTransitionState.transferEnterActivityOptions(); +        } else { +            return options; +        } +    } +      /**       * @hide Implement to provide correct calling token.       */ @@ -4282,6 +4291,7 @@ public class Activity extends ContextThemeWrapper          if (mParent != null) {              throw new RuntimeException("Can't be called from a child");          } +        options = transferSpringboardActivityOptions(options);          Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(                  this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode,                  options, user); @@ -4317,6 +4327,7 @@ public class Activity extends ContextThemeWrapper          if (mParent != null) {              throw new RuntimeException("Can't be called from a child");          } +        options = transferSpringboardActivityOptions(options);          Instrumentation.ActivityResult ar =                  mInstrumentation.execStartActivity(                          this, mMainThread.getApplicationThread(), mToken, this, @@ -4349,6 +4360,7 @@ public class Activity extends ContextThemeWrapper          if (mParent != null) {              throw new RuntimeException("Can't be called from a child");          } +        options = transferSpringboardActivityOptions(options);          Instrumentation.ActivityResult ar =                  mInstrumentation.execStartActivityAsCaller(                          this, mMainThread.getApplicationThread(), mToken, this, @@ -4788,6 +4800,7 @@ public class Activity extends ContextThemeWrapper       */      public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,              int requestCode, @Nullable Bundle options) { +        options = transferSpringboardActivityOptions(options);          Instrumentation.ActivityResult ar =              mInstrumentation.execStartActivity(                  this, mMainThread.getApplicationThread(), mToken, child, @@ -4853,6 +4866,7 @@ public class Activity extends ContextThemeWrapper          if (referrer != null) {              intent.putExtra(Intent.EXTRA_REFERRER, referrer);          } +        options = transferSpringboardActivityOptions(options);          Instrumentation.ActivityResult ar =              mInstrumentation.execStartActivity(                  this, mMainThread.getApplicationThread(), mToken, who, @@ -6650,11 +6664,11 @@ public class Activity extends ContextThemeWrapper          mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(                  com.android.internal.R.styleable.Window_windowNoDisplay, false);          mFragments.dispatchActivityCreated(); -        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());      }      final void performCreate(Bundle icicle) {          restoreHasCurrentPermissionRequest(icicle); +        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());          onCreate(icicle);          mActivityTransitionState.readState(icicle);          performCreateCommon(); @@ -6662,6 +6676,7 @@ public class Activity extends ContextThemeWrapper      final void performCreate(Bundle icicle, PersistableBundle persistentState) {          restoreHasCurrentPermissionRequest(icicle); +        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());          onCreate(icicle, persistentState);          mActivityTransitionState.readState(icicle);          performCreateCommon(); diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 4c8ddc7eb6b1..ccc37d72e846 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -31,10 +31,13 @@ import android.os.IRemoteCallback;  import android.os.Parcelable;  import android.os.RemoteException;  import android.os.ResultReceiver; +import android.transition.Transition; +import android.transition.TransitionManager;  import android.util.Pair;  import android.util.Slog;  import android.view.AppTransitionAnimationSpec;  import android.view.View; +import android.view.ViewGroup;  import android.view.Window;  import java.util.ArrayList; @@ -640,10 +643,71 @@ public class ActivityOptions {      public static ActivityOptions makeSceneTransitionAnimation(Activity activity,              Pair<View, String>... sharedElements) {          ActivityOptions opts = new ActivityOptions(); -        if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) { -            opts.mAnimationType = ANIM_DEFAULT; +        makeSceneTransitionAnimation(activity, activity.getWindow(), opts, +                activity.mExitTransitionListener, sharedElements); +        return opts; +    } + +    /** +     * Call this immediately prior to startActivity to begin a shared element transition +     * from a non-Activity. The window must support Window.FEATURE_ACTIVITY_TRANSITIONS. +     * The exit transition will start immediately and the shared element transition will +     * start once the launched Activity's shared element is ready. +     * <p> +     * When all transitions have completed and the shared element has been transfered, +     * the window's decor View will have its visibility set to View.GONE. +     * +     * @hide +     */ +    @SafeVarargs +    public static ActivityOptions startSharedElementAnimation(Window window, +            Pair<View, String>... sharedElements) { +        ActivityOptions opts = new ActivityOptions(); +        final View decorView = window.getDecorView(); +        if (decorView == null) {              return opts;          } +        final ExitTransitionCoordinator exit = +                makeSceneTransitionAnimation(null, window, opts, null, sharedElements); +        if (exit != null) { +            HideWindowListener listener = new HideWindowListener(window, exit); +            exit.setHideSharedElementsCallback(listener); +            exit.startExit(); +        } +        return opts; +    } + +    /** +     * This method should be called when the {@link #startSharedElementAnimation(Window, Pair[])} +     * animation must be stopped and the Views reset. This can happen if there was an error +     * from startActivity or a springboard activity and the animation should stop and reset. +     * +     * @hide +     */ +    public static void stopSharedElementAnimation(Window window) { +        final View decorView = window.getDecorView(); +        if (decorView == null) { +            return; +        } +        final ExitTransitionCoordinator exit = (ExitTransitionCoordinator) +                decorView.getTag(com.android.internal.R.id.cross_task_transition); +        if (exit != null) { +            exit.cancelPendingTransitions(); +            decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, null); +            TransitionManager.endTransitions((ViewGroup) decorView); +            exit.resetViews(); +            exit.clearState(); +            decorView.setVisibility(View.VISIBLE); +        } +    } + +    static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window, +            ActivityOptions opts, SharedElementCallback callback, +            Pair<View, String>[] sharedElements) { +        if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) { +            opts.mAnimationType = ANIM_DEFAULT; +            return null; +        }          opts.mAnimationType = ANIM_SCENE_TRANSITION;          ArrayList<String> names = new ArrayList<String>(); @@ -665,18 +729,22 @@ public class ActivityOptions {              }          } -        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, names, names, -                views, false); +        ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window, +                callback, names, names, views, false);          opts.mTransitionReceiver = exit;          opts.mSharedElementNames = names; -        opts.mIsReturning = false; -        opts.mExitCoordinatorIndex = -                activity.mActivityTransitionState.addExitTransitionCoordinator(exit); -        return opts; +        opts.mIsReturning = (activity == null); +        if (activity == null) { +            opts.mExitCoordinatorIndex = -1; +        } else { +            opts.mExitCoordinatorIndex = +                    activity.mActivityTransitionState.addExitTransitionCoordinator(exit); +        } +        return exit;      }      /** @hide */ -    public static ActivityOptions makeSceneTransitionAnimation(Activity activity, +    static ActivityOptions makeSceneTransitionAnimation(Activity activity,              ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames,              int resultCode, Intent resultData) {          ActivityOptions opts = new ActivityOptions(); @@ -900,6 +968,16 @@ public class ActivityOptions {          return mIsReturning;      } +    /** +     * Returns whether or not the ActivityOptions was created with +     * {@link #startSharedElementAnimation(Window, Pair[])}. +     * +     * @hide +     */ +    boolean isCrossTask() { +        return mExitCoordinatorIndex < 0; +    } +      /** @hide */      public ArrayList<String> getSharedElementNames() {          return mSharedElementNames; @@ -1191,4 +1269,65 @@ public class ActivityOptions {                  + ", mAnimationType=" + mAnimationType + ", mStartX=" + mStartX + ", mStartY="                  + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;      } + +    private static class HideWindowListener extends Transition.TransitionListenerAdapter +        implements ExitTransitionCoordinator.HideSharedElementsCallback { +        private final Window mWindow; +        private final ExitTransitionCoordinator mExit; +        private final boolean mWaitingForTransition; +        private boolean mTransitionEnded; +        private boolean mSharedElementHidden; +        private ArrayList<View> mSharedElements; + +        public HideWindowListener(Window window, ExitTransitionCoordinator exit) { +            mWindow = window; +            mExit = exit; +            mSharedElements = new ArrayList<>(exit.mSharedElements); +            Transition transition = mWindow.getExitTransition(); +            if (transition != null) { +                transition.addListener(this); +                mWaitingForTransition = true; +            } else { +                mWaitingForTransition = false; +            } +            View decorView = mWindow.getDecorView(); +            if (decorView != null) { +                if (decorView.getTag(com.android.internal.R.id.cross_task_transition) != null) { +                    throw new IllegalStateException( +                            "Cannot start a transition while one is running"); +                } +                decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit); +            } +        } + +        @Override +        public void onTransitionEnd(Transition transition) { +            mTransitionEnded = true; +            hideWhenDone(); +            transition.removeListener(this); +        } + +        @Override +        public void hideSharedElements() { +            mSharedElementHidden = true; +            hideWhenDone(); +        } + +        private void hideWhenDone() { +            if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) { +                mExit.resetViews(); +                int numSharedElements = mSharedElements.size(); +                for (int i = 0; i < numSharedElements; i++) { +                    View view = mSharedElements.get(i); +                    view.requestLayout(); +                } +                View decorView = mWindow.getDecorView(); +                if (decorView != null) { +                    decorView.setTagInternal( +                            com.android.internal.R.id.cross_task_transition, null); +                    decorView.setVisibility(View.GONE); +                } +            } +        } +    }  } diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java index 02eb4d3ded1c..88526fb65162 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -105,6 +105,12 @@ class ActivityTransitionState {      private boolean mIsEnterTriggered; +    /** +     * The ActivityOptions Bundle. This is used to transfer ActivityOptions through a +     * springboard Activity. +     */ +    private Bundle mEnterBundle; +      public ActivityTransitionState() {      } @@ -150,6 +156,10 @@ class ActivityTransitionState {      }      public void setEnterActivityOptions(Activity activity, ActivityOptions options) { +        if (options != null && mEnterBundle == null && +                options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { +            mEnterBundle = options.toBundle(); +        }          final Window window = activity.getWindow();          if (window == null) {              return; @@ -185,7 +195,12 @@ class ActivityTransitionState {              activity.getWindow().getDecorView().setVisibility(View.VISIBLE);          }          mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity, -                resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning()); +                resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(), +                mEnterActivityOptions.isCrossTask()); +        if (mEnterActivityOptions.isCrossTask()) { +            mExitingFrom = new ArrayList<>(mEnterActivityOptions.getSharedElementNames()); +            mExitingTo = new ArrayList<>(mEnterActivityOptions.getSharedElementNames()); +        }          if (!mIsEnterPostponed) {              startEnter(); @@ -224,6 +239,13 @@ class ActivityTransitionState {          mEnterActivityOptions = null;      } +    Bundle transferEnterActivityOptions() { +        mEnterActivityOptions = null; +        Bundle options = mEnterBundle; +        mEnterBundle = null; +        return options; +    } +      public void onStop() {          restoreExitedViews();          if (mEnterTransitionCoordinator != null) { @@ -275,7 +297,8 @@ class ActivityTransitionState {      }      private void restoreReenteringViews() { -        if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning()) { +        if (mEnterTransitionCoordinator != null && mEnterTransitionCoordinator.isReturning() && +                !mEnterTransitionCoordinator.isCrossTask()) {              mEnterTransitionCoordinator.forceViewsToAppear();              mExitingFrom = null;              mExitingTo = null; @@ -302,8 +325,9 @@ class ActivityTransitionState {                      }                  } -                mReturnExitCoordinator = -                        new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true); +                mReturnExitCoordinator = new ExitTransitionCoordinator(activity, +                        activity.getWindow(), activity.mEnterTransitionListener, mEnteringNames, +                        null, null, true);                  if (enterViewsTransition != null && decor != null) {                      enterViewsTransition.resume(decor);                  } diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 8bf1e9a97e48..5d12b0da6ab8 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -59,12 +59,14 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {      private boolean mIsViewsTransitionStarted;      private Transition mEnterViewsTransition;      private OnPreDrawListener mViewsReadyListener; +    private final boolean mIsCrossTask;      public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver, -            ArrayList<String> sharedElementNames, boolean isReturning) { +            ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) {          super(activity.getWindow(), sharedElementNames, -                getListener(activity, isReturning), isReturning); +                getListener(activity, isReturning && !isCrossTask), isReturning);          mActivity = activity; +        mIsCrossTask = isCrossTask;          setResultReceiver(resultReceiver);          prepareEnter();          Bundle resultReceiverBundle = new Bundle(); @@ -85,6 +87,10 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {          }      } +    boolean isCrossTask() { +        return mIsCrossTask; +    } +      public void viewInstancesReady(ArrayList<String> accepted, ArrayList<String> localNames,              ArrayList<View> localViews) {          boolean remap = false; @@ -325,7 +331,9 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator {          if (mActivity == null || decorView == null) {              return;          } -        mActivity.overridePendingTransition(0, 0); +        if (!isCrossTask()) { +            mActivity.overridePendingTransition(0, 0); +        }          if (!mIsReturning) {              mWasOpaque = mActivity.convertToTranslucent(null, null);              Drawable background = decorView.getBackground(); diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index 040428877371..160c28592582 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -35,6 +35,7 @@ import android.transition.TransitionManager;  import android.view.View;  import android.view.ViewGroup;  import android.view.ViewTreeObserver; +import android.view.Window;  import java.util.ArrayList; @@ -59,18 +60,20 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {      private Bundle mExitSharedElementBundle;      private boolean mIsExitStarted;      private boolean mSharedElementsHidden; +    private HideSharedElementsCallback mHideSharedElementsCallback; -    public ExitTransitionCoordinator(Activity activity, ArrayList<String> names, +    public ExitTransitionCoordinator(Activity activity, Window window, +            SharedElementCallback listener, ArrayList<String> names,              ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) { -        super(activity.getWindow(), names, getListener(activity, isReturning), isReturning); +        super(window, names, listener, isReturning);          viewsReady(mapSharedElements(accepted, mapped));          stripOffscreenViews();          mIsBackgroundReady = !isReturning;          mActivity = activity;      } -    private static SharedElementCallback getListener(Activity activity, boolean isReturning) { -        return isReturning ? activity.mEnterTransitionListener : activity.mExitTransitionListener; +    void setHideSharedElementsCallback(HideSharedElementsCallback callback) { +        mHideSharedElementsCallback = callback;      }      @Override @@ -188,6 +191,9 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {      private void hideSharedElements() {          moveSharedElementsFromOverlay(); +        if (mHideSharedElementsCallback != null) { +            mHideSharedElementsCallback.hideSharedElements(); +        }          if (!mIsHidden) {              hideViews(mSharedElements);          } @@ -207,7 +213,11 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {              startTransition(new Runnable() {                  @Override                  public void run() { -                    beginTransitions(); +                    if (mActivity != null) { +                        beginTransitions(); +                    } else { +                        startExitTransition(); +                    }                  }              });          } @@ -508,4 +518,8 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {              return getWindow().getSharedElementExitTransition();          }      } + +    interface HideSharedElementsCallback { +        void hideSharedElements(); +    }  } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 316c7e3f6084..8823605c52b6 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -1941,6 +1941,26 @@ public abstract class Transition implements Cloneable {      }      /** +     * Force the transition to move to its end state, ending all the animators. +     * +     * @hide +     */ +    void forceToEnd(ViewGroup sceneRoot) { +        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators(); +        int numOldAnims = runningAnimators.size(); +        if (sceneRoot != null) { +            WindowId windowId = sceneRoot.getWindowId(); +            for (int i = numOldAnims - 1; i >= 0; i--) { +                AnimationInfo info = runningAnimators.valueAt(i); +                if (info.view != null && windowId != null && windowId.equals(info.windowId)) { +                    Animator anim = runningAnimators.keyAt(i); +                    anim.end(); +                } +            } +        } +    } + +    /**       * This method cancels a transition that is currently running.       *       * @hide diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java index 71c80991bb19..f2c871e3c718 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -440,7 +440,7 @@ public class TransitionManager {              ArrayList<Transition> copy = new ArrayList(runningTransitions);              for (int i = copy.size() - 1; i >= 0; i--) {                  final Transition transition = copy.get(i); -                transition.end(); +                transition.forceToEnd(sceneRoot);              }          } diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java index 583dc0f1ef2c..a41fe64d0be1 100644 --- a/core/java/android/transition/TransitionSet.java +++ b/core/java/android/transition/TransitionSet.java @@ -16,8 +16,6 @@  package android.transition; -import com.android.internal.R; -  import android.animation.TimeInterpolator;  import android.content.Context;  import android.content.res.TypedArray; @@ -26,6 +24,8 @@ import android.util.AttributeSet;  import android.view.View;  import android.view.ViewGroup; +import com.android.internal.R; +  import java.util.ArrayList;  /** @@ -498,6 +498,16 @@ public class TransitionSet extends Transition {          }      } +    /** @hide */ +    @Override +    void forceToEnd(ViewGroup sceneRoot) { +        super.forceToEnd(sceneRoot); +        int numTransitions = mTransitions.size(); +        for (int i = 0; i < numTransitions; ++i) { +            mTransitions.get(i).forceToEnd(sceneRoot); +        } +    } +      @Override      TransitionSet setSceneRoot(ViewGroup sceneRoot) {          super.setSceneRoot(sceneRoot); diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 7f8acd3d61cc..5c165e6709ee 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -122,9 +122,11 @@    <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SET_PROGRESS}. -->    <item type="id" name="accessibilityActionSetProgress" /> -   +    <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_CONTEXT_CLICK}. -->    <item type="id" name="accessibilityActionContextClick" />    <item type="id" name="remote_input_tag" /> + +  <item type="id" name="cross_task_transition" />  </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 8edd9d1bb1d6..10092945e8f8 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2612,4 +2612,7 @@    <java-symbol type="array" name="config_defaultPinnerServiceFiles" />    <java-symbol type="string" name="suspended_widget_accessibility" /> + +  <!-- Used internally for assistant to launch activity transitions --> +  <java-symbol type="id" name="cross_task_transition" />  </resources>  |