Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.app;
     17 
     18 import android.os.Bundle;
     19 import android.os.ResultReceiver;
     20 import android.transition.Transition;
     21 import android.util.ArrayMap;
     22 import android.util.SparseArray;
     23 import android.view.View;
     24 import android.view.ViewGroup;
     25 import android.view.ViewTreeObserver;
     26 import android.view.Window;
     27 
     28 import java.lang.ref.WeakReference;
     29 import java.util.ArrayList;
     30 
     31 /**
     32  * This class contains all persistence-related functionality for Activity Transitions.
     33  * Activities start exit and enter Activity Transitions through this class.
     34  */
     35 class ActivityTransitionState {
     36 
     37     private static final String ENTERING_SHARED_ELEMENTS = "android:enteringSharedElements";
     38 
     39     private static final String EXITING_MAPPED_FROM = "android:exitingMappedFrom";
     40 
     41     private static final String EXITING_MAPPED_TO = "android:exitingMappedTo";
     42 
     43     /**
     44      * The shared elements that the calling Activity has said that they transferred to this
     45      * Activity.
     46      */
     47     private ArrayList<String> mEnteringNames;
     48 
     49     /**
     50      * The names of shared elements that were shared to the called Activity.
     51      */
     52     private ArrayList<String> mExitingFrom;
     53 
     54     /**
     55      * The names of local Views that were shared out, mapped to those elements in mExitingFrom.
     56      */
     57     private ArrayList<String> mExitingTo;
     58 
     59     /**
     60      * The local Views that were shared out, mapped to those elements in mExitingFrom.
     61      */
     62     private ArrayList<View> mExitingToView;
     63 
     64     /**
     65      * The ExitTransitionCoordinator used to start an Activity. Used to make the elements restore
     66      * Visibility of exited Views.
     67      */
     68     private ExitTransitionCoordinator mCalledExitCoordinator;
     69 
     70     /**
     71      * The ExitTransitionCoordinator used to return to a previous Activity when called with
     72      * {@link android.app.Activity#finishAfterTransition()}.
     73      */
     74     private ExitTransitionCoordinator mReturnExitCoordinator;
     75 
     76     /**
     77      * We must be able to cancel entering transitions to stop changing the Window to
     78      * opaque when we exit before making the Window opaque.
     79      */
     80     private EnterTransitionCoordinator mEnterTransitionCoordinator;
     81 
     82     /**
     83      * ActivityOptions used on entering this Activity.
     84      */
     85     private ActivityOptions mEnterActivityOptions;
     86 
     87     /**
     88      * Has an exit transition been started? If so, we don't want to double-exit.
     89      */
     90     private boolean mHasExited;
     91 
     92     /**
     93      * Postpone painting and starting the enter transition until this is false.
     94      */
     95     private boolean mIsEnterPostponed;
     96 
     97     /**
     98      * Potential exit transition coordinators.
     99      */
    100     private SparseArray<WeakReference<ExitTransitionCoordinator>> mExitTransitionCoordinators;
    101 
    102     /**
    103      * Next key for mExitTransitionCoordinator.
    104      */
    105     private int mExitTransitionCoordinatorsKey = 1;
    106 
    107     private boolean mIsEnterTriggered;
    108 
    109     public ActivityTransitionState() {
    110     }
    111 
    112     public int addExitTransitionCoordinator(ExitTransitionCoordinator exitTransitionCoordinator) {
    113         if (mExitTransitionCoordinators == null) {
    114             mExitTransitionCoordinators =
    115                     new SparseArray<WeakReference<ExitTransitionCoordinator>>();
    116         }
    117         WeakReference<ExitTransitionCoordinator> ref = new WeakReference(exitTransitionCoordinator);
    118         // clean up old references:
    119         for (int i = mExitTransitionCoordinators.size() - 1; i >= 0; i--) {
    120             WeakReference<ExitTransitionCoordinator> oldRef
    121                     = mExitTransitionCoordinators.valueAt(i);
    122             if (oldRef.get() == null) {
    123                 mExitTransitionCoordinators.removeAt(i);
    124             }
    125         }
    126         int newKey = mExitTransitionCoordinatorsKey++;
    127         mExitTransitionCoordinators.append(newKey, ref);
    128         return newKey;
    129     }
    130 
    131     public void readState(Bundle bundle) {
    132         if (bundle != null) {
    133             if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isReturning()) {
    134                 mEnteringNames = bundle.getStringArrayList(ENTERING_SHARED_ELEMENTS);
    135             }
    136             if (mEnterTransitionCoordinator == null) {
    137                 mExitingFrom = bundle.getStringArrayList(EXITING_MAPPED_FROM);
    138                 mExitingTo = bundle.getStringArrayList(EXITING_MAPPED_TO);
    139             }
    140         }
    141     }
    142 
    143     public void saveState(Bundle bundle) {
    144         if (mEnteringNames != null) {
    145             bundle.putStringArrayList(ENTERING_SHARED_ELEMENTS, mEnteringNames);
    146         }
    147         if (mExitingFrom != null) {
    148             bundle.putStringArrayList(EXITING_MAPPED_FROM, mExitingFrom);
    149             bundle.putStringArrayList(EXITING_MAPPED_TO, mExitingTo);
    150         }
    151     }
    152 
    153     public void setEnterActivityOptions(Activity activity, ActivityOptions options) {
    154         if (activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
    155                 && options != null && mEnterActivityOptions == null
    156                 && mEnterTransitionCoordinator == null
    157                 && options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
    158             mEnterActivityOptions = options;
    159             mIsEnterTriggered = false;
    160             if (mEnterActivityOptions.isReturning()) {
    161                 restoreExitedViews();
    162                 int result = mEnterActivityOptions.getResultCode();
    163                 if (result != 0) {
    164                     activity.onActivityReenter(result, mEnterActivityOptions.getResultData());
    165                 }
    166             }
    167         }
    168     }
    169 
    170     public void enterReady(Activity activity) {
    171         if (mEnterActivityOptions == null || mIsEnterTriggered) {
    172             return;
    173         }
    174         mIsEnterTriggered = true;
    175         mHasExited = false;
    176         ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
    177         ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
    178         if (mEnterActivityOptions.isReturning()) {
    179             restoreExitedViews();
    180             activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
    181         }
    182         mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
    183                 resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning());
    184 
    185         if (!mIsEnterPostponed) {
    186             startEnter();
    187         }
    188     }
    189 
    190     public void postponeEnterTransition() {
    191         mIsEnterPostponed = true;
    192     }
    193 
    194     public void startPostponedEnterTransition() {
    195         if (mIsEnterPostponed) {
    196             mIsEnterPostponed = false;
    197             if (mEnterTransitionCoordinator != null) {
    198                 startEnter();
    199             }
    200         }
    201     }
    202 
    203     private void startEnter() {
    204         if (mEnterActivityOptions.isReturning()) {
    205             if (mExitingToView != null) {
    206                 mEnterTransitionCoordinator.viewInstancesReady(mExitingFrom, mExitingTo,
    207                         mExitingToView);
    208             } else {
    209                 mEnterTransitionCoordinator.namedViewsReady(mExitingFrom, mExitingTo);
    210             }
    211         } else {
    212             mEnterTransitionCoordinator.namedViewsReady(null, null);
    213             mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames();
    214         }
    215 
    216         mExitingFrom = null;
    217         mExitingTo = null;
    218         mExitingToView = null;
    219         mEnterActivityOptions = null;
    220     }
    221 
    222     public void onStop() {
    223         restoreExitedViews();
    224         if (mEnterTransitionCoordinator != null) {
    225             mEnterTransitionCoordinator.stop();
    226             mEnterTransitionCoordinator = null;
    227         }
    228         if (mReturnExitCoordinator != null) {
    229             mReturnExitCoordinator.stop();
    230             mReturnExitCoordinator = null;
    231         }
    232     }
    233 
    234     public void onResume() {
    235         restoreExitedViews();
    236     }
    237 
    238     public void clear() {
    239         mEnteringNames = null;
    240         mExitingFrom = null;
    241         mExitingTo = null;
    242         mExitingToView = null;
    243         mCalledExitCoordinator = null;
    244         mEnterTransitionCoordinator = null;
    245         mEnterActivityOptions = null;
    246         mExitTransitionCoordinators = null;
    247     }
    248 
    249     private void restoreExitedViews() {
    250         if (mCalledExitCoordinator != null) {
    251             mCalledExitCoordinator.resetViews();
    252             mCalledExitCoordinator = null;
    253         }
    254     }
    255 
    256     public boolean startExitBackTransition(final Activity activity) {
    257         if (mEnteringNames == null) {
    258             return false;
    259         } else {
    260             if (!mHasExited) {
    261                 mHasExited = true;
    262                 Transition enterViewsTransition = null;
    263                 ViewGroup decor = null;
    264                 boolean delayExitBack = false;
    265                 if (mEnterTransitionCoordinator != null) {
    266                     enterViewsTransition = mEnterTransitionCoordinator.getEnterViewsTransition();
    267                     decor = mEnterTransitionCoordinator.getDecor();
    268                     delayExitBack = mEnterTransitionCoordinator.cancelEnter();
    269                     mEnterTransitionCoordinator = null;
    270                     if (enterViewsTransition != null && decor != null) {
    271                         enterViewsTransition.pause(decor);
    272                     }
    273                 }
    274 
    275                 mReturnExitCoordinator =
    276                         new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true);
    277                 if (enterViewsTransition != null && decor != null) {
    278                     enterViewsTransition.resume(decor);
    279                 }
    280                 if (delayExitBack && decor != null) {
    281                     final ViewGroup finalDecor = decor;
    282                     decor.getViewTreeObserver().addOnPreDrawListener(
    283                             new ViewTreeObserver.OnPreDrawListener() {
    284                                 @Override
    285                                 public boolean onPreDraw() {
    286                                     finalDecor.getViewTreeObserver().removeOnPreDrawListener(this);
    287                                     if (mReturnExitCoordinator != null) {
    288                                         mReturnExitCoordinator.startExit(activity.mResultCode,
    289                                                 activity.mResultData);
    290                                     }
    291                                     return true;
    292                                 }
    293                             });
    294                 } else {
    295                     mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
    296                 }
    297             }
    298             return true;
    299         }
    300     }
    301 
    302     public void startExitOutTransition(Activity activity, Bundle options) {
    303         if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
    304             return;
    305         }
    306         ActivityOptions activityOptions = new ActivityOptions(options);
    307         mEnterTransitionCoordinator = null;
    308         if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
    309             int key = activityOptions.getExitCoordinatorKey();
    310             int index = mExitTransitionCoordinators.indexOfKey(key);
    311             if (index >= 0) {
    312                 mCalledExitCoordinator = mExitTransitionCoordinators.valueAt(index).get();
    313                 mExitTransitionCoordinators.removeAt(index);
    314                 if (mCalledExitCoordinator != null) {
    315                     mExitingFrom = mCalledExitCoordinator.getAcceptedNames();
    316                     mExitingTo = mCalledExitCoordinator.getMappedNames();
    317                     mExitingToView = mCalledExitCoordinator.copyMappedViews();
    318                     mCalledExitCoordinator.startExit();
    319                 }
    320             }
    321         }
    322     }
    323 }
    324