OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / core / java / android / app / ExitTransitionCoordinator.java
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.animation.Animator;
19 import android.animation.AnimatorListenerAdapter;
20 import android.animation.ObjectAnimator;
21 import android.app.SharedElementCallback.OnSharedElementsReadyListener;
22 import android.content.Intent;
23 import android.graphics.Color;
24 import android.graphics.Matrix;
25 import android.graphics.RectF;
26 import android.graphics.drawable.ColorDrawable;
27 import android.graphics.drawable.Drawable;
28 import android.os.Build.VERSION_CODES;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.os.ResultReceiver;
33 import android.transition.Transition;
34 import android.transition.TransitionManager;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.view.ViewTreeObserver;
38
39 import java.util.ArrayList;
40
41 /**
42  * This ActivityTransitionCoordinator is created in ActivityOptions#makeSceneTransitionAnimation
43  * to govern the exit of the Scene and the shared elements when calling an Activity as well as
44  * the reentry of the Scene when coming back from the called Activity.
45  */
46 class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
47     private static final String TAG = "ExitTransitionCoordinator";
48     private static final long MAX_WAIT_MS = 1000;
49
50     private Bundle mSharedElementBundle;
51     private boolean mExitNotified;
52     private boolean mSharedElementNotified;
53     private Activity mActivity;
54     private boolean mIsBackgroundReady;
55     private boolean mIsCanceled;
56     private Handler mHandler;
57     private ObjectAnimator mBackgroundAnimator;
58     private boolean mIsHidden;
59     private Bundle mExitSharedElementBundle;
60     private boolean mIsExitStarted;
61     private boolean mSharedElementsHidden;
62
63     public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
64             ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
65         super(activity.getWindow(), names, getListener(activity, isReturning), isReturning);
66         viewsReady(mapSharedElements(accepted, mapped));
67         stripOffscreenViews();
68         mIsBackgroundReady = !isReturning;
69         mActivity = activity;
70     }
71
72     private static SharedElementCallback getListener(Activity activity, boolean isReturning) {
73         return isReturning ? activity.mEnterTransitionListener : activity.mExitTransitionListener;
74     }
75
76     @Override
77     protected void onReceiveResult(int resultCode, Bundle resultData) {
78         switch (resultCode) {
79             case MSG_SET_REMOTE_RECEIVER:
80                 stopCancel();
81                 mResultReceiver = resultData.getParcelable(KEY_REMOTE_RECEIVER);
82                 if (mIsCanceled) {
83                     mResultReceiver.send(MSG_CANCEL, null);
84                     mResultReceiver = null;
85                 } else {
86                     notifyComplete();
87                 }
88                 break;
89             case MSG_HIDE_SHARED_ELEMENTS:
90                 stopCancel();
91                 if (!mIsCanceled) {
92                     hideSharedElements();
93                 }
94                 break;
95             case MSG_START_EXIT_TRANSITION:
96                 mHandler.removeMessages(MSG_CANCEL);
97                 startExit();
98                 break;
99             case MSG_SHARED_ELEMENT_DESTINATION:
100                 mExitSharedElementBundle = resultData;
101                 sharedElementExitBack();
102                 break;
103             case MSG_CANCEL:
104                 mIsCanceled = true;
105                 finish();
106                 break;
107         }
108     }
109
110     private void stopCancel() {
111         if (mHandler != null) {
112             mHandler.removeMessages(MSG_CANCEL);
113         }
114     }
115
116     private void delayCancel() {
117         if (mHandler != null) {
118             mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
119         }
120     }
121
122     public void resetViews() {
123         if (mTransitioningViews != null) {
124             showViews(mTransitioningViews, true);
125             setTransitioningViewsVisiblity(View.VISIBLE, true);
126         }
127         showViews(mSharedElements, true);
128         mIsHidden = true;
129         ViewGroup decorView = getDecor();
130         if (!mIsReturning && decorView != null) {
131             decorView.suppressLayout(false);
132         }
133         moveSharedElementsFromOverlay();
134         clearState();
135     }
136
137     private void sharedElementExitBack() {
138         final ViewGroup decorView = getDecor();
139         if (decorView != null) {
140             decorView.suppressLayout(true);
141         }
142         if (decorView != null && mExitSharedElementBundle != null &&
143                 !mExitSharedElementBundle.isEmpty() &&
144                 !mSharedElements.isEmpty() && getSharedElementTransition() != null) {
145             startTransition(new Runnable() {
146                 public void run() {
147                     startSharedElementExit(decorView);
148                 }
149             });
150         } else {
151             sharedElementTransitionComplete();
152         }
153     }
154
155     private void startSharedElementExit(final ViewGroup decorView) {
156         Transition transition = getSharedElementExitTransition();
157         transition.addListener(new Transition.TransitionListenerAdapter() {
158             @Override
159             public void onTransitionEnd(Transition transition) {
160                 transition.removeListener(this);
161                 if (isViewsTransitionComplete()) {
162                     delayCancel();
163                 }
164             }
165         });
166         final ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
167                 mSharedElementNames);
168         decorView.getViewTreeObserver()
169                 .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
170                     @Override
171                     public boolean onPreDraw() {
172                         decorView.getViewTreeObserver().removeOnPreDrawListener(this);
173                         setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
174                         return true;
175                     }
176                 });
177         setGhostVisibility(View.INVISIBLE);
178         scheduleGhostVisibilityChange(View.INVISIBLE);
179         if (mListener != null) {
180             mListener.onSharedElementEnd(mSharedElementNames, mSharedElements,
181                     sharedElementSnapshots);
182         }
183         TransitionManager.beginDelayedTransition(decorView, transition);
184         scheduleGhostVisibilityChange(View.VISIBLE);
185         setGhostVisibility(View.VISIBLE);
186         decorView.invalidate();
187     }
188
189     private void hideSharedElements() {
190         moveSharedElementsFromOverlay();
191         if (!mIsHidden) {
192             hideViews(mSharedElements);
193         }
194         mSharedElementsHidden = true;
195         finishIfNecessary();
196     }
197
198     public void startExit() {
199         if (!mIsExitStarted) {
200             mIsExitStarted = true;
201             pauseInput();
202             ViewGroup decorView = getDecor();
203             if (decorView != null) {
204                 decorView.suppressLayout(true);
205             }
206             moveSharedElementsToOverlay();
207             startTransition(new Runnable() {
208                 @Override
209                 public void run() {
210                     beginTransitions();
211                 }
212             });
213         }
214     }
215
216     public void startExit(int resultCode, Intent data) {
217         if (!mIsExitStarted) {
218             mIsExitStarted = true;
219             pauseInput();
220             ViewGroup decorView = getDecor();
221             if (decorView != null) {
222                 decorView.suppressLayout(true);
223             }
224             mHandler = new Handler() {
225                 @Override
226                 public void handleMessage(Message msg) {
227                     mIsCanceled = true;
228                     finish();
229                 }
230             };
231             delayCancel();
232             moveSharedElementsToOverlay();
233             if (decorView != null && decorView.getBackground() == null) {
234                 getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
235             }
236             final boolean targetsM = decorView == null || decorView.getContext()
237                     .getApplicationInfo().targetSdkVersion >= VERSION_CODES.M;
238             ArrayList<String> sharedElementNames = targetsM ? mSharedElementNames :
239                     mAllSharedElementNames;
240             ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
241                     sharedElementNames, resultCode, data);
242             mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
243                 @Override
244                 public void onTranslucentConversionComplete(boolean drawComplete) {
245                     if (!mIsCanceled) {
246                         fadeOutBackground();
247                     }
248                 }
249             }, options);
250             startTransition(new Runnable() {
251                 @Override
252                 public void run() {
253                     startExitTransition();
254                 }
255             });
256         }
257     }
258
259     public void stop() {
260         if (mIsReturning && mActivity != null) {
261             // Override the previous ActivityOptions. We don't want the
262             // activity to have options since we're essentially canceling the
263             // transition and finishing right now.
264             mActivity.convertToTranslucent(null, null);
265             finish();
266         }
267     }
268
269     private void startExitTransition() {
270         Transition transition = getExitTransition();
271         ViewGroup decorView = getDecor();
272         if (transition != null && decorView != null && mTransitioningViews != null) {
273             setTransitioningViewsVisiblity(View.VISIBLE, false);
274             TransitionManager.beginDelayedTransition(decorView, transition);
275             setTransitioningViewsVisiblity(View.INVISIBLE, false);
276             decorView.invalidate();
277         } else {
278             transitionStarted();
279         }
280     }
281
282     private void fadeOutBackground() {
283         if (mBackgroundAnimator == null) {
284             ViewGroup decor = getDecor();
285             Drawable background;
286             if (decor != null && (background = decor.getBackground()) != null) {
287                 background = background.mutate();
288                 getWindow().setBackgroundDrawable(background);
289                 mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 0);
290                 mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
291                     @Override
292                     public void onAnimationEnd(Animator animation) {
293                         mBackgroundAnimator = null;
294                         if (!mIsCanceled) {
295                             mIsBackgroundReady = true;
296                             notifyComplete();
297                         }
298                     }
299                 });
300                 mBackgroundAnimator.setDuration(getFadeDuration());
301                 mBackgroundAnimator.start();
302             } else {
303                 mIsBackgroundReady = true;
304             }
305         }
306     }
307
308     private Transition getExitTransition() {
309         Transition viewsTransition = null;
310         if (mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
311             viewsTransition = configureTransition(getViewsTransition(), true);
312         }
313         if (viewsTransition == null) {
314             viewsTransitionComplete();
315         } else {
316             final ArrayList<View> transitioningViews = mTransitioningViews;
317             viewsTransition.addListener(new ContinueTransitionListener() {
318                 @Override
319                 public void onTransitionEnd(Transition transition) {
320                     transition.removeListener(this);
321                     viewsTransitionComplete();
322                     if (mIsHidden && transitioningViews != null) {
323                         showViews(transitioningViews, true);
324                         setTransitioningViewsVisiblity(View.VISIBLE, true);
325                     }
326                     if (mSharedElementBundle != null) {
327                         delayCancel();
328                     }
329                     super.onTransitionEnd(transition);
330                 }
331             });
332         }
333         return viewsTransition;
334     }
335
336     private Transition getSharedElementExitTransition() {
337         Transition sharedElementTransition = null;
338         if (!mSharedElements.isEmpty()) {
339             sharedElementTransition = configureTransition(getSharedElementTransition(), false);
340         }
341         if (sharedElementTransition == null) {
342             sharedElementTransitionComplete();
343         } else {
344             sharedElementTransition.addListener(new ContinueTransitionListener() {
345                 @Override
346                 public void onTransitionEnd(Transition transition) {
347                     transition.removeListener(this);
348                     sharedElementTransitionComplete();
349                     if (mIsHidden) {
350                         showViews(mSharedElements, true);
351                     }
352                 }
353             });
354             mSharedElements.get(0).invalidate();
355         }
356         return sharedElementTransition;
357     }
358
359     private void beginTransitions() {
360         Transition sharedElementTransition = getSharedElementExitTransition();
361         Transition viewsTransition = getExitTransition();
362
363         Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
364         ViewGroup decorView = getDecor();
365         if (transition != null && decorView != null) {
366             setGhostVisibility(View.INVISIBLE);
367             scheduleGhostVisibilityChange(View.INVISIBLE);
368             if (viewsTransition != null) {
369                 setTransitioningViewsVisiblity(View.VISIBLE, false);
370             }
371             TransitionManager.beginDelayedTransition(decorView, transition);
372             scheduleGhostVisibilityChange(View.VISIBLE);
373             setGhostVisibility(View.VISIBLE);
374             if (viewsTransition != null) {
375                 setTransitioningViewsVisiblity(View.INVISIBLE, false);
376             }
377             decorView.invalidate();
378         } else {
379             transitionStarted();
380         }
381     }
382
383     protected boolean isReadyToNotify() {
384         return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
385     }
386
387     @Override
388     protected void sharedElementTransitionComplete() {
389         mSharedElementBundle = mExitSharedElementBundle == null
390                 ? captureSharedElementState() : captureExitSharedElementsState();
391         super.sharedElementTransitionComplete();
392     }
393
394     private Bundle captureExitSharedElementsState() {
395         Bundle bundle = new Bundle();
396         RectF bounds = new RectF();
397         Matrix matrix = new Matrix();
398         for (int i = 0; i < mSharedElements.size(); i++) {
399             String name = mSharedElementNames.get(i);
400             Bundle sharedElementState = mExitSharedElementBundle.getBundle(name);
401             if (sharedElementState != null) {
402                 bundle.putBundle(name, sharedElementState);
403             } else {
404                 View view = mSharedElements.get(i);
405                 captureSharedElementState(view, name, bundle, matrix, bounds);
406             }
407         }
408         return bundle;
409     }
410
411     @Override
412     protected void onTransitionsComplete() {
413         notifyComplete();
414     }
415
416     protected void notifyComplete() {
417         if (isReadyToNotify()) {
418             if (!mSharedElementNotified) {
419                 mSharedElementNotified = true;
420                 delayCancel();
421                 if (mListener == null) {
422                     mResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, mSharedElementBundle);
423                     notifyExitComplete();
424                 } else {
425                     final ResultReceiver resultReceiver = mResultReceiver;
426                     final Bundle sharedElementBundle = mSharedElementBundle;
427                     mListener.onSharedElementsArrived(mSharedElementNames, mSharedElements,
428                             new OnSharedElementsReadyListener() {
429                                 @Override
430                                 public void onSharedElementsReady() {
431                                     resultReceiver.send(MSG_TAKE_SHARED_ELEMENTS,
432                                             sharedElementBundle);
433                                     notifyExitComplete();
434                                 }
435                             });
436                 }
437             } else {
438                 notifyExitComplete();
439             }
440         }
441     }
442
443     private void notifyExitComplete() {
444         if (!mExitNotified && isViewsTransitionComplete()) {
445             mExitNotified = true;
446             mResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null);
447             mResultReceiver = null; // done talking
448             ViewGroup decorView = getDecor();
449             if (!mIsReturning && decorView != null) {
450                 decorView.suppressLayout(false);
451             }
452             finishIfNecessary();
453         }
454     }
455
456     private void finishIfNecessary() {
457         if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty() ||
458                 mSharedElementsHidden)) {
459             finish();
460         }
461         if (!mIsReturning && mExitNotified) {
462             mActivity = null; // don't need it anymore
463         }
464     }
465
466     private void finish() {
467         stopCancel();
468         if (mActivity != null) {
469             mActivity.mActivityTransitionState.clear();
470             mActivity.finish();
471             mActivity.overridePendingTransition(0, 0);
472             mActivity = null;
473         }
474         // Clear the state so that we can't hold any references accidentally and leak memory.
475         clearState();
476     }
477
478     @Override
479     protected void clearState() {
480         mHandler = null;
481         mSharedElementBundle = null;
482         if (mBackgroundAnimator != null) {
483             mBackgroundAnimator.cancel();
484             mBackgroundAnimator = null;
485         }
486         mExitSharedElementBundle = null;
487         super.clearState();
488     }
489
490     @Override
491     protected boolean moveSharedElementWithParent() {
492         return !mIsReturning;
493     }
494
495     @Override
496     protected Transition getViewsTransition() {
497         if (mIsReturning) {
498             return getWindow().getReturnTransition();
499         } else {
500             return getWindow().getExitTransition();
501         }
502     }
503
504     protected Transition getSharedElementTransition() {
505         if (mIsReturning) {
506             return getWindow().getSharedElementReturnTransition();
507         } else {
508             return getWindow().getSharedElementExitTransition();
509         }
510     }
511 }