Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 45bbc06c authored by George Mount's avatar George Mount Committed by Android (Google) Code Review
Browse files

Merge "Support Activity Transitions when activity stopped."

parents e2337588 62ab9b78
Loading
Loading
Loading
Loading
+12 −18
Original line number Diff line number Diff line
@@ -3257,6 +3257,7 @@ package android.app {
    method public boolean navigateUpToFromChild(android.app.Activity, android.content.Intent);
    method public void onActionModeFinished(android.view.ActionMode);
    method public void onActionModeStarted(android.view.ActionMode);
    method protected void onActivityReenter(int, android.content.Intent);
    method protected void onActivityResult(int, int, android.content.Intent);
    method public void onAttachFragment(android.app.Fragment);
    method public void onAttachedToWindow();
@@ -3335,7 +3336,6 @@ package android.app {
    method public final boolean requestWindowFeature(int);
    method public final void runOnUiThread(java.lang.Runnable);
    method public void setActionBar(android.widget.Toolbar);
    method public void setActivityTransitionListener(android.app.ActivityOptions.ActivityTransitionListener);
    method public void setContentTransitionManager(android.transition.TransitionManager);
    method public void setContentView(int);
    method public void setContentView(android.view.View);
@@ -3357,6 +3357,7 @@ package android.app {
    method public final void setResult(int);
    method public final void setResult(int, android.content.Intent);
    method public final void setSecondaryProgress(int);
    method public void setSharedElementListener(android.app.SharedElementListener);
    method public void setTitle(java.lang.CharSequence);
    method public void setTitle(int);
    method public deprecated void setTitleColor(int);
@@ -3576,28 +3577,13 @@ package android.app {
  public class ActivityOptions {
    method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
    method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int);
    method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.view.Window, android.view.View, java.lang.String);
    method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.view.Window, android.app.ActivityOptions.ActivityTransitionListener);
    method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String);
    method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.util.Pair<android.view.View, java.lang.String>...);
    method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
    method public android.os.Bundle toBundle();
    method public void update(android.app.ActivityOptions);
  }
  public static class ActivityOptions.ActivityTransitionListener {
    ctor public ActivityOptions.ActivityTransitionListener();
    method public android.util.Pair<android.view.View, java.lang.String>[] getSharedElementsMapping();
    method public boolean handleRejectedSharedElements(java.util.List<android.view.View>);
    method public void onCaptureSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
    method public void onCaptureSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
    method public void onEnterReady();
    method public void onExitTransitionComplete();
    method public void onRemoteExitComplete();
    method public void onSharedElementExitTransitionComplete();
    method public void onSharedElementTransferred(java.util.List<java.lang.String>, java.util.List<android.view.View>);
    method public void onStartEnterTransition(java.util.List<java.lang.String>, java.util.List<android.view.View>);
    method public void onStartExitTransition(java.util.List<java.lang.String>, java.util.List<android.view.View>);
  }
  public class AlarmManager {
    method public void cancel(android.app.PendingIntent);
    method public void set(int, long, android.app.PendingIntent);
@@ -4821,6 +4807,14 @@ package android.app {
    field public static final int START_STICKY_COMPATIBILITY = 0; // 0x0
  }
  public class SharedElementListener {
    ctor public SharedElementListener();
    method public void handleRejectedSharedElements(java.util.List<android.view.View>);
    method public void remapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>);
    method public void setSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
    method public void setSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
  }
  public deprecated class TabActivity extends android.app.ActivityGroup {
    ctor public TabActivity();
    method public android.widget.TabHost getTabHost();
+46 −35
Original line number Diff line number Diff line
@@ -777,8 +777,9 @@ public class Activity extends ContextThemeWrapper

    private Thread mUiThread;
    final Handler mHandler = new Handler();
    private ActivityOptions mCalledActivityOptions;
    private EnterTransitionCoordinator mEnterTransitionCoordinator;

    private ActivityTransitionState mActivityTransitionState = new ActivityTransitionState();
    SharedElementListener mTransitionListener = new SharedElementListener();

    /** Return the intent that started this activity. */
    public Intent getIntent() {
@@ -1100,9 +1101,6 @@ public class Activity extends ContextThemeWrapper
            mTitleReady = true;
            onTitleChanged(getTitle(), getTitleColor());
        }
        if (mEnterTransitionCoordinator != null) {
            mEnterTransitionCoordinator.readyToEnter();
        }
        mCalled = true;
    }

@@ -1149,12 +1147,6 @@ public class Activity extends ContextThemeWrapper
        }

        getApplication().dispatchActivityStarted(this);

        final ActivityOptions activityOptions = getActivityOptions();
        if (activityOptions != null &&
                activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
            mEnterTransitionCoordinator = activityOptions.createEnterActivityTransition(this);
        }
    }

    /**
@@ -1204,7 +1196,6 @@ public class Activity extends ContextThemeWrapper
    protected void onResume() {
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
        getApplication().dispatchActivityResumed(this);
        mCalledActivityOptions = null;
        mCalled = true;
    }

@@ -1279,6 +1270,7 @@ public class Activity extends ContextThemeWrapper
    final void performSaveInstanceState(Bundle outState) {
        onSaveInstanceState(outState);
        saveManagedDialogs(outState);
        mActivityTransitionState.saveState(outState);
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
    }

@@ -1549,10 +1541,7 @@ public class Activity extends ContextThemeWrapper
    protected void onStop() {
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
        if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
        if (mCalledActivityOptions != null) {
            mCalledActivityOptions.dispatchActivityStopped();
            mCalledActivityOptions = null;
        }
        mActivityTransitionState.onStop();
        getApplication().dispatchActivityStopped(this);
        mTranslucentCallback = null;
        mCalled = true;
@@ -3650,7 +3639,7 @@ public class Activity extends ContextThemeWrapper
    public void startActivityForResult(Intent intent, int requestCode) {
        Bundle options = null;
        if (mWindow.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
            options = ActivityOptions.makeSceneTransitionAnimation(mWindow, null).toBundle();
            options = ActivityOptions.makeSceneTransitionAnimation(this).toBundle();
        }
        startActivityForResult(intent, requestCode, options);
    }
@@ -3691,9 +3680,7 @@ public class Activity extends ContextThemeWrapper
     */
    public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        if (options != null) {
            ActivityOptions activityOptions = new ActivityOptions(options);
            activityOptions.dispatchStartExit();
            mCalledActivityOptions = activityOptions;
            mActivityTransitionState.startExitOutTransition(this, options);
        }
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
@@ -4559,13 +4546,10 @@ public class Activity extends ContextThemeWrapper
     * to reverse its exit Transition. When the exit Transition completes,
     * {@link #finish()} is called. If no entry Transition was used, finish() is called
     * immediately and the Activity exit Transition is run.
     * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
     * android.app.ActivityOptions.ActivityTransitionListener)
     * @see android.app.ActivityOptions#makeSceneTransitionAnimation(Activity, android.util.Pair[])
     */
    public void finishWithTransition() {
        if (mEnterTransitionCoordinator != null) {
            mEnterTransitionCoordinator.startExit();
        } else {
        if (!mActivityTransitionState.startExitBackTransition(this)) {
            finish();
        }
    }
@@ -4642,6 +4626,27 @@ public class Activity extends ContextThemeWrapper
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    }

    /**
     * Called when an activity you launched with an activity transition exposes this
     * Activity through a returning activity transition, giving you the resultCode
     * and any additional data from it. This method will only be called if the activity
     * set a result code other than {@link #RESULT_CANCELED} and it supports activity
     * transitions with {@link Window#FEATURE_CONTENT_TRANSITIONS}.
     *
     * <p>The purpose of this function is to let the called Activity send a hint about
     * its state so that this underlying Activity can prepare to be exposed. A call to
     * this method does not guarantee that the called Activity has or will be exiting soon.
     * It only indicates that it will expose this Activity's Window and it has
     * some data to pass to prepare it.</p>
     *
     * @param resultCode The integer result code returned by the child activity
     *                   through its setResult().
     * @param data An Intent, which can return result data to the caller
     *               (various data can be attached to Intent "extras").
     */
    protected void onActivityReenter(int resultCode, Intent data) {
    }

    /**
     * Create a new PendingIntent object which you can hand to others 
     * for them to use to send result data back to your 
@@ -5246,7 +5251,8 @@ public class Activity extends ContextThemeWrapper
     * This call has no effect on non-translucent activities or on activities with the
     * {@link android.R.attr#windowIsFloating} attribute.
     *
     * @see #convertToTranslucent(TranslucentConversionListener)
     * @see #convertToTranslucent(android.app.Activity.TranslucentConversionListener,
     * ActivityOptions)
     * @see TranslucentConversionListener
     *
     * @hide
@@ -5544,18 +5550,18 @@ public class Activity extends ContextThemeWrapper
    }

    /**
     * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
     * android.app.ActivityOptions.ActivityTransitionListener)} was used to start an Activity,
     * the Window will be triggered to enter with a Transition. <code>listener</code> allows
     * The Activity to listen to events of the entering transition and control the mapping of
     * shared elements. This requires {@link Window#FEATURE_CONTENT_TRANSITIONS}.
     * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
     * android.view.View, String)} was used to start an Activity, <var>listener</var>
     * will be called to handle shared elements. This requires
     * {@link Window#FEATURE_CONTENT_TRANSITIONS}.
     *
     * @param listener Used to listen to events in the entering transition.
     * @param listener Used to manipulate how shared element transitions function.
     */
    public void setActivityTransitionListener(ActivityOptions.ActivityTransitionListener listener) {
        if (mEnterTransitionCoordinator != null) {
            mEnterTransitionCoordinator.setActivityTransitionListener(listener);
    public void setSharedElementListener(SharedElementListener listener) {
        if (listener == null) {
            listener = new SharedElementListener();
        }
        mTransitionListener = listener;
    }

    // ------------------ Internal API ------------------
@@ -5621,19 +5627,23 @@ 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) {
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        onCreate(icicle, persistentState);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

    final void performStart() {
        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
        mFragments.noteStateNotSaved();
        mCalled = false;
        mFragments.execPendingActions();
@@ -5656,6 +5666,7 @@ public class Activity extends ContextThemeWrapper
                lm.doReportStart();
            }
        }
        mActivityTransitionState.enterReady(this);
    }
    
    final void performRestart() {
+113 −156

File changed.

Preview size limit exceeded, changes collapsed.

+122 −702

File changed.

Preview size limit exceeded, changes collapsed.

+204 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.app;

import android.os.Bundle;
import android.os.ResultReceiver;
import android.util.ArrayMap;
import android.view.View;
import android.view.Window;

import java.util.ArrayList;

/**
 * This class contains all persistence-related functionality for Activity Transitions.
 * Activities start exit and enter Activity Transitions through this class.
 */
class ActivityTransitionState {

    private static final String ENTERING_SHARED_ELEMENTS = "android:enteringSharedElements";

    private static final String ENTERING_MAPPED_FROM = "android:enteringMappedFrom";

    private static final String ENTERING_MAPPED_TO = "android:enteringMappedTo";

    private static final String EXITING_MAPPED_FROM = "android:exitingMappedFrom";

    private static final String EXITING_MAPPED_TO = "android:exitingMappedTo";

    /**
     * The shared elements that the calling Activity has said that they transferred to this
     * Activity.
     */
    private ArrayList<String> mEnteringNames;

    /**
     * The shared elements that this Activity as accepted and mapped to local Views.
     */
    private ArrayList<String> mEnteringFrom;

    /**
     * The names of local Views that are mapped to those elements in mEnteringFrom.
     */
    private ArrayList<String> mEnteringTo;

    /**
     * The names of shared elements that were shared to the called Activity.
     */
    private ArrayList<String> mExitingFrom;

    /**
     * The names of local Views that were shared out, mapped to those elements in mExitingFrom.
     */
    private ArrayList<String> mExitingTo;

    /**
     * The ActivityOptions used to call an Activity. Used to make the elements restore
     * Visibility of exited Views.
     */
    private ActivityOptions mCalledActivityOptions;

    /**
     * We must be able to cancel entering transitions to stop changing the Window to
     * opaque when we exit before making the Window opaque.
     */
    private EnterTransitionCoordinator mEnterTransitionCoordinator;

    /**
     * ActivityOptions used on entering this Activity.
     */
    private ActivityOptions mEnterActivityOptions;

    /**
     * Has an exit transition been started? If so, we don't want to double-exit.
     */
    private boolean mHasExited;

    public ActivityTransitionState() {
    }

    public void readState(Bundle bundle) {
        if (bundle != null) {
            if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isReturning()) {
                mEnteringNames = bundle.getStringArrayList(ENTERING_SHARED_ELEMENTS);
                mEnteringFrom = bundle.getStringArrayList(ENTERING_MAPPED_FROM);
                mEnteringTo = bundle.getStringArrayList(ENTERING_MAPPED_TO);
            }
            if (mEnterTransitionCoordinator == null) {
                mExitingFrom = bundle.getStringArrayList(EXITING_MAPPED_FROM);
                mExitingTo = bundle.getStringArrayList(EXITING_MAPPED_TO);
            }
        }
    }

    public void saveState(Bundle bundle) {
        if (mEnteringNames != null) {
            bundle.putStringArrayList(ENTERING_SHARED_ELEMENTS, mEnteringNames);
            bundle.putStringArrayList(ENTERING_MAPPED_FROM, mEnteringFrom);
            bundle.putStringArrayList(ENTERING_MAPPED_TO, mEnteringTo);
        }
        if (mExitingFrom != null) {
            bundle.putStringArrayList(EXITING_MAPPED_FROM, mExitingFrom);
            bundle.putStringArrayList(EXITING_MAPPED_TO, mExitingTo);
        }
    }

    public void setEnterActivityOptions(Activity activity, ActivityOptions options) {
        if (activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)
                && options != null && mEnterActivityOptions == null
                && options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
            mEnterActivityOptions = options;
            if (mEnterActivityOptions.isReturning()) {
                int result = mEnterActivityOptions.getResultCode();
                if (result != 0) {
                    activity.onActivityReenter(result, mEnterActivityOptions.getResultData());
                }
            }
        }
    }

    public void enterReady(Activity activity) {
        if (mEnterActivityOptions == null) {
            return;
        }
        mHasExited = false;
        ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
        ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
        if (mEnterActivityOptions.isReturning()) {
            if (mCalledActivityOptions != null) {
                mCalledActivityOptions.dispatchActivityStopped();
                mCalledActivityOptions = null;
            }
            activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
            mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
                    resultReceiver, sharedElementNames, mExitingFrom, mExitingTo);
        } else {
            mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
                    resultReceiver, sharedElementNames, null, null);
            mEnteringNames = sharedElementNames;
            mEnteringFrom = mEnterTransitionCoordinator.getAcceptedNames();
            mEnteringTo = mEnterTransitionCoordinator.getMappedNames();
        }
        mExitingFrom = null;
        mExitingTo = null;
        mEnterActivityOptions = null;
    }

    public void onStop() {
        if (mCalledActivityOptions != null) {
            mCalledActivityOptions.dispatchActivityStopped();
            mCalledActivityOptions = null;
        }
        if (mEnterTransitionCoordinator != null) {
            mEnterTransitionCoordinator.stop();
            mEnterTransitionCoordinator = null;
        }
    }

    public boolean startExitBackTransition(Activity activity) {
        if (mEnteringNames == null) {
            return false;
        } else {
            if (!mHasExited) {
                mHasExited = true;
                if (mEnterTransitionCoordinator != null) {
                    mEnterTransitionCoordinator.stop();
                    mEnterTransitionCoordinator = null;
                }
                ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
                activity.getWindow().getDecorView().findNamedViews(sharedElements);

                ExitTransitionCoordinator exitCoordinator =
                        new ExitTransitionCoordinator(activity, mEnteringNames, mEnteringFrom,
                                mEnteringTo, true);
                exitCoordinator.startExit(activity.mResultCode, activity.mResultData);
            }
            return true;
        }
    }

    public void startExitOutTransition(Activity activity, Bundle options) {
        if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
            return;
        }
        mCalledActivityOptions = new ActivityOptions(options);
        if (mCalledActivityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
            mExitingFrom = mCalledActivityOptions.getSharedElementNames();
            mExitingTo = mCalledActivityOptions.getLocalSharedElementNames();
            mCalledActivityOptions.dispatchStartExit();
        }
    }
}
Loading