EnterTransitionCoordinator.java revision 1a5b294d399c95e656c9ad1aebbc09d6c384be41
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 onRemoteSceneExitComplete() { 137 super.onRemoteSceneExitComplete(); 138 } 139 140 @Override 141 protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) { 142 mEnteringSharedElementNames = new ArrayList<String>(); 143 mEnteringSharedElementNames.addAll(sharedElementNames); 144 super.onTakeSharedElements(sharedElementNames, state); 145 } 146 147 @Override 148 protected void sharedElementTransitionComplete(Bundle bundle) { 149 notifySharedElementTransitionComplete(bundle); 150 } 151 152 @Override 153 public boolean onPreDraw() { 154 getWindow().getDecorView().getViewTreeObserver().removeOnPreDrawListener(this); 155 setEnteringViews(readyEnteringViews()); 156 notifySetListener(); 157 onPrepareRestore(); 158 return false; 159 } 160 161 @Override 162 public void startExit() { 163 if (!mExitTransitionStarted) { 164 mExitTransitionStarted = true; 165 startExitTransition(mEnteringSharedElementNames); 166 } 167 } 168 169 @Override 170 protected Transition getViewsTransition() { 171 if (!mSupportsTransition) { 172 return null; 173 } 174 return getWindow().getEnterTransition(); 175 } 176 177 @Override 178 protected Transition getSharedElementTransition() { 179 if (!mSupportsTransition) { 180 return null; 181 } 182 return getWindow().getSharedElementEnterTransition(); 183 } 184 185 @Override 186 protected void onStartEnterTransition(Transition transition, ArrayList<View> enteringViews) { 187 Drawable background = getDecor().getBackground(); 188 if (background != null) { 189 ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 255); 190 animator.setDuration(FADE_BACKGROUND_DURATION_MS); 191 animator.addListener(new AnimatorListenerAdapter() { 192 @Override 193 public void onAnimationEnd(Animator animation) { 194 mMakeOpaque = true; 195 if (mWasOpaque) { 196 mActivity.convertFromTranslucent(); 197 } 198 } 199 }); 200 animator.start(); 201 } else if (mWasOpaque) { 202 transition.addListener(new Transition.TransitionListenerAdapter() { 203 @Override 204 public void onTransitionEnd(Transition transition) { 205 mMakeOpaque = true; 206 mActivity.convertFromTranslucent(); 207 } 208 }); 209 } 210 super.onStartEnterTransition(transition, enteringViews); 211 } 212 213 public ArrayList<View> readyEnteringViews() { 214 ArrayList<View> enteringViews = new ArrayList<View>(); 215 getDecor().captureTransitioningViews(enteringViews); 216 if (getViewsTransition() != null) { 217 setViewVisibility(enteringViews, View.INVISIBLE); 218 } 219 return enteringViews; 220 } 221 222 @Override 223 protected void startExitTransition(ArrayList<String> sharedElements) { 224 mMakeOpaque = false; 225 notifyPrepareRestore(); 226 227 if (getDecor().getBackground() == null) { 228 ColorDrawable black = new ColorDrawable(0xFF000000); 229 getWindow().setBackgroundDrawable(black); 230 } 231 if (mWasOpaque) { 232 mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() { 233 @Override 234 public void onTranslucentConversionComplete(boolean drawComplete) { 235 fadeOutBackground(); 236 } 237 }); 238 } else { 239 fadeOutBackground(); 240 } 241 242 super.startExitTransition(sharedElements); 243 } 244 245 private void fadeOutBackground() { 246 ObjectAnimator animator = ObjectAnimator.ofInt(getDecor().getBackground(), 247 "alpha", 0); 248 animator.addListener(new AnimatorListenerAdapter() { 249 @Override 250 public void onAnimationEnd(Animator animation) { 251 mBackgroundFadedOut = true; 252 if (mSharedElementTransitionComplete) { 253 EnterTransitionCoordinator.super.onSharedElementTransitionEnd(); 254 } 255 } 256 }); 257 animator.setDuration(FADE_BACKGROUND_DURATION_MS); 258 animator.start(); 259 } 260 261 @Override 262 protected void onExitTransitionEnd() { 263 mExitTransitionComplete = true; 264 exitAfterSharedElementTransition(); 265 super.onExitTransitionEnd(); 266 } 267 268 @Override 269 protected void onSharedElementTransitionEnd() { 270 mSharedElementTransitionComplete = true; 271 if (mBackgroundFadedOut) { 272 super.onSharedElementTransitionEnd(); 273 } 274 } 275 276 @Override 277 protected boolean allowOverlappingTransitions() { 278 return getWindow().getAllowEnterTransitionOverlap(); 279 } 280 281 private void exitAfterSharedElementTransition() { 282 if (mSharedElementTransitionComplete && mExitTransitionComplete) { 283 mActivity.finish(); 284 if (mSupportsTransition) { 285 mActivity.overridePendingTransition(0, 0); 286 } 287 notifyExitTransitionComplete(); 288 clearConnections(); 289 } 290 } 291} 292