EnterTransitionCoordinator.java revision 8df5a07d880691e420cb7c702e4d41260e85d1b8
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 */ 16package android.app; 17 18import android.animation.Animator; 19import android.animation.AnimatorListenerAdapter; 20import android.animation.ObjectAnimator; 21import android.graphics.drawable.ColorDrawable; 22import android.graphics.drawable.Drawable; 23import android.os.Bundle; 24import android.os.ResultReceiver; 25import android.transition.Transition; 26import android.view.View; 27import android.view.ViewTreeObserver; 28import android.view.Window; 29 30import java.util.ArrayList; 31 32/** 33 * This ActivityTransitionCoordinator is created by the Activity to manage 34 * the enter scene and shared element transfer as well as Activity#finishWithTransition 35 * exiting the Scene and transferring shared elements back to the called Activity. 36 */ 37class EnterTransitionCoordinator extends ActivityTransitionCoordinator 38 implements ViewTreeObserver.OnPreDrawListener { 39 private static final String TAG = "EnterTransitionCoordinator"; 40 41 // The background fade in/out duration. 150ms is pretty quick, but not abrupt. 42 private static final int FADE_BACKGROUND_DURATION_MS = 150; 43 44 /** 45 * The shared element names sent by the ExitTransitionCoordinator and may be 46 * shared when exiting back. 47 */ 48 private ArrayList<String> mEnteringSharedElementNames; 49 50 /** 51 * The Activity that has created this coordinator. This is used solely to make the 52 * Window translucent/opaque. 53 */ 54 private Activity mActivity; 55 56 /** 57 * True if the Window was opaque at the start and we should make it opaque again after 58 * enter transitions have completed. 59 */ 60 private boolean mWasOpaque; 61 62 /** 63 * During exit, is the background alpha == 0? 64 */ 65 private boolean mBackgroundFadedOut; 66 67 /** 68 * During exit, has the shared element transition completed? 69 */ 70 private boolean mSharedElementTransitionComplete; 71 72 /** 73 * Has the exit started? We don't want to accidentally exit multiple times. e.g. when 74 * back is hit twice during the exit animation. 75 */ 76 private boolean mExitTransitionStarted; 77 78 /** 79 * Has the exit transition ended? 80 */ 81 private boolean mExitTransitionComplete; 82 83 /** 84 * We only want to make the Window transparent and set the background alpha once. After that, 85 * the Activity won't want the same enter transition. 86 */ 87 private boolean mMadeReady; 88 89 /** 90 * True if Window.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS) -- this means that 91 * enter and exit transitions should be active. 92 */ 93 private boolean mSupportsTransition; 94 95 /** 96 * Background alpha animations may complete prior to receiving the callback for 97 * onTranslucentConversionComplete. If so, we need to immediately call to make the Window 98 * opaque. 99 */ 100 private boolean mMakeOpaque; 101 102 public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver) { 103 super(activity.getWindow()); 104 mActivity = activity; 105 setRemoteResultReceiver(resultReceiver); 106 } 107 108 public void readyToEnter() { 109 if (!mMadeReady) { 110 mMadeReady = true; 111 mSupportsTransition = getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS); 112 if (mSupportsTransition) { 113 Window window = getWindow(); 114 window.getDecorView().getViewTreeObserver().addOnPreDrawListener(this); 115 mActivity.overridePendingTransition(0, 0); 116 mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() { 117 @Override 118 public void onTranslucentConversionComplete(boolean drawComplete) { 119 mWasOpaque = true; 120 if (mMakeOpaque) { 121 mActivity.convertFromTranslucent(); 122 } 123 } 124 }); 125 Drawable background = getDecor().getBackground(); 126 if (background != null) { 127 window.setBackgroundDrawable(null); 128 background.setAlpha(0); 129 window.setBackgroundDrawable(background); 130 } 131 } 132 } 133 } 134 135 @Override 136 protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) { 137 mEnteringSharedElementNames = new ArrayList<String>(); 138 mEnteringSharedElementNames.addAll(sharedElementNames); 139 super.onTakeSharedElements(sharedElementNames, state); 140 } 141 142 @Override 143 protected void sharedElementTransitionComplete(Bundle bundle) { 144 notifySharedElementTransitionComplete(bundle); 145 exitAfterSharedElementTransition(); 146 } 147 148 @Override 149 public boolean onPreDraw() { 150 getWindow().getDecorView().getViewTreeObserver().removeOnPreDrawListener(this); 151 setEnteringViews(readyEnteringViews()); 152 notifySetListener(); 153 onPrepareRestore(); 154 return false; 155 } 156 157 @Override 158 public void startExit() { 159 if (!mExitTransitionStarted) { 160 mExitTransitionStarted = true; 161 startExitTransition(mEnteringSharedElementNames); 162 } 163 } 164 165 @Override 166 protected Transition getViewsTransition() { 167 if (!mSupportsTransition) { 168 return null; 169 } 170 return getWindow().getEnterTransition(); 171 } 172 173 @Override 174 protected Transition getSharedElementTransition() { 175 if (!mSupportsTransition) { 176 return null; 177 } 178 return getWindow().getSharedElementEnterTransition(); 179 } 180 181 @Override 182 protected void onStartEnterTransition(Transition transition, ArrayList<View> enteringViews) { 183 Drawable background = getDecor().getBackground(); 184 if (background != null) { 185 ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 255); 186 animator.setDuration(FADE_BACKGROUND_DURATION_MS); 187 animator.addListener(new AnimatorListenerAdapter() { 188 @Override 189 public void onAnimationEnd(Animator animation) { 190 mMakeOpaque = true; 191 if (mWasOpaque) { 192 mActivity.convertFromTranslucent(); 193 } 194 } 195 }); 196 animator.start(); 197 } else if (mWasOpaque) { 198 transition.addListener(new Transition.TransitionListenerAdapter() { 199 @Override 200 public void onTransitionEnd(Transition transition) { 201 mMakeOpaque = true; 202 mActivity.convertFromTranslucent(); 203 } 204 }); 205 } 206 super.onStartEnterTransition(transition, enteringViews); 207 } 208 209 public ArrayList<View> readyEnteringViews() { 210 ArrayList<View> enteringViews = new ArrayList<View>(); 211 getDecor().captureTransitioningViews(enteringViews); 212 if (getViewsTransition() != null) { 213 setViewVisibility(enteringViews, View.INVISIBLE); 214 } 215 return enteringViews; 216 } 217 218 @Override 219 protected void startExitTransition(ArrayList<String> sharedElements) { 220 mMakeOpaque = false; 221 notifyPrepareRestore(); 222 223 if (getDecor().getBackground() == null) { 224 ColorDrawable black = new ColorDrawable(0xFF000000); 225 getWindow().setBackgroundDrawable(black); 226 } 227 if (mWasOpaque) { 228 mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() { 229 @Override 230 public void onTranslucentConversionComplete(boolean drawComplete) { 231 fadeOutBackground(); 232 } 233 }); 234 } else { 235 fadeOutBackground(); 236 } 237 238 super.startExitTransition(sharedElements); 239 } 240 241 private void fadeOutBackground() { 242 ObjectAnimator animator = ObjectAnimator.ofInt(getDecor().getBackground(), 243 "alpha", 0); 244 animator.addListener(new AnimatorListenerAdapter() { 245 @Override 246 public void onAnimationEnd(Animator animation) { 247 mBackgroundFadedOut = true; 248 if (mSharedElementTransitionComplete) { 249 EnterTransitionCoordinator.super.onSharedElementTransitionEnd(); 250 } 251 } 252 }); 253 animator.setDuration(FADE_BACKGROUND_DURATION_MS); 254 animator.start(); 255 } 256 257 @Override 258 protected void onExitTransitionEnd() { 259 mExitTransitionComplete = true; 260 exitAfterSharedElementTransition(); 261 super.onExitTransitionEnd(); 262 } 263 264 @Override 265 protected void onSharedElementTransitionEnd() { 266 mSharedElementTransitionComplete = true; 267 if (mBackgroundFadedOut) { 268 super.onSharedElementTransitionEnd(); 269 } 270 } 271 272 @Override 273 protected boolean allowOverlappingTransitions() { 274 return getWindow().getAllowEnterTransitionOverlap(); 275 } 276 277 private void exitAfterSharedElementTransition() { 278 if (mSharedElementTransitionComplete && mExitTransitionComplete && mBackgroundFadedOut) { 279 mActivity.finish(); 280 if (mSupportsTransition) { 281 mActivity.overridePendingTransition(0, 0); 282 } 283 notifyExitTransitionComplete(); 284 clearConnections(); 285 } 286 } 287} 288