• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 package android.support.v17.leanback.widget;
15 
16 import android.os.Handler;
17 import android.support.v4.app.ActivityCompat;
18 import android.support.v4.app.SharedElementCallback;
19 import android.support.v4.view.ViewCompat;
20 import android.support.v17.leanback.R;
21 import android.support.v17.leanback.transition.TransitionListener;
22 import android.support.v17.leanback.transition.TransitionHelper;
23 import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder;
24 import android.app.Activity;
25 import android.content.Intent;
26 import android.graphics.Matrix;
27 import android.text.TextUtils;
28 import android.util.Log;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.view.View.MeasureSpec;
32 import android.widget.ImageView;
33 import android.widget.ImageView.ScaleType;
34 
35 import java.util.List;
36 
37 /**
38  * Helper class to assist delayed shared element activity transition for view created by
39  * {@link FullWidthDetailsOverviewRowPresenter}. User must call
40  * {@link #setSharedElementEnterTransition(Activity, String, long)} during activity onCreate() and
41  * call {@link FullWidthDetailsOverviewRowPresenter#setListener(FullWidthDetailsOverviewRowPresenter.Listener)}.
42  * The helper implements {@link FullWidthDetailsOverviewRowPresenter.Listener} and starts delayed
43  * activity transition once {@link FullWidthDetailsOverviewRowPresenter.Listener#onBindLogo(ViewHolder)}
44  * is called.
45  */
46 public class FullWidthDetailsOverviewSharedElementHelper extends
47         FullWidthDetailsOverviewRowPresenter.Listener {
48 
49     private static final String TAG = "FullWidthDetailsOverviewSharedElementHelper";
50     private static final boolean DEBUG = false;
51 
52     private static final long DEFAULT_TIMEOUT = 5000;
53 
54     private ViewHolder mViewHolder;
55     private Activity mActivityToRunTransition;
56     private boolean mStartedPostpone;
57     private String mSharedElementName;
58     private boolean mAutoStartSharedElementTransition = true;
59 
setSharedElementEnterTransition(Activity activity, String sharedElementName)60     public void setSharedElementEnterTransition(Activity activity, String sharedElementName) {
61         setSharedElementEnterTransition(activity, sharedElementName, DEFAULT_TIMEOUT);
62     }
63 
setSharedElementEnterTransition(Activity activity, String sharedElementName, long timeoutMs)64     public void setSharedElementEnterTransition(Activity activity, String sharedElementName,
65             long timeoutMs) {
66         if (activity == null && !TextUtils.isEmpty(sharedElementName) ||
67                 activity != null && TextUtils.isEmpty(sharedElementName)) {
68             throw new IllegalArgumentException();
69         }
70         if (activity == mActivityToRunTransition &&
71                 TextUtils.equals(sharedElementName, mSharedElementName)) {
72             return;
73         }
74         mActivityToRunTransition = activity;
75         mSharedElementName = sharedElementName;
76         if (DEBUG) {
77             Log.d(TAG, "postponeEnterTransition " + mActivityToRunTransition);
78         }
79         Object transition = TransitionHelper.getSharedElementEnterTransition(activity.getWindow());
80         setAutoStartSharedElementTransition(transition != null);
81         ActivityCompat.postponeEnterTransition(mActivityToRunTransition);
82         if (timeoutMs > 0) {
83             new Handler().postDelayed(new Runnable() {
84                 @Override
85                 public void run() {
86                     if (DEBUG) {
87                         Log.d(TAG, "timeout " + mActivityToRunTransition);
88                     }
89                     startPostponedEnterTransitionInternal();
90                 }
91             }, timeoutMs);
92         }
93     }
94 
95     /**
96      * Enable or disable auto startPostponedEnterTransition() when bound to logo. When it's
97      * disabled, app must call {@link #startPostponedEnterTransition()} to kick off
98      * windowEnterTransition. By default, it is disabled when there is no
99      * windowEnterSharedElementTransition set on the activity.
100      */
setAutoStartSharedElementTransition(boolean enabled)101     public void setAutoStartSharedElementTransition(boolean enabled) {
102         mAutoStartSharedElementTransition = enabled;
103     }
104 
105     /**
106      * Returns true if auto startPostponedEnterTransition() when bound to logo. When it's
107      * disabled, app must call {@link #startPostponedEnterTransition()} to kick off
108      * windowEnterTransition. By default, it is disabled when there is no
109      * windowEnterSharedElementTransition set on the activity.
110      */
getAutoStartSharedElementTransition()111     public boolean getAutoStartSharedElementTransition() {
112         return mAutoStartSharedElementTransition;
113     }
114 
115     @Override
onBindLogo(ViewHolder vh)116     public void onBindLogo(ViewHolder vh) {
117         if (DEBUG) {
118             Log.d(TAG, "onBindLogo, could start transition of " + mActivityToRunTransition);
119         }
120         mViewHolder = vh;
121         if (!mAutoStartSharedElementTransition) {
122             return;
123         }
124         if (mViewHolder != null) {
125             if (DEBUG) {
126                 Log.d(TAG, "rebind? clear transitionName on current viewHolder "
127                         + mViewHolder.getOverviewView());
128             }
129             ViewCompat.setTransitionName(mViewHolder.getLogoViewHolder().view, null);
130         }
131         // After we got a image drawable,  we can determine size of right panel.
132         // We want right panel to have fixed size so that the right panel don't change size
133         // when the overview is layout as a small bounds in transition.
134         mViewHolder.getDetailsDescriptionFrame().postOnAnimation(new Runnable() {
135             @Override
136             public void run() {
137                 if (DEBUG) {
138                     Log.d(TAG, "setTransitionName "+mViewHolder.getOverviewView());
139                 }
140                 ViewCompat.setTransitionName(mViewHolder.getLogoViewHolder().view,
141                         mSharedElementName);
142                 Object transition = TransitionHelper.getSharedElementEnterTransition(
143                         mActivityToRunTransition.getWindow());
144                 if (transition != null) {
145                     TransitionHelper.addTransitionListener(transition, new TransitionListener() {
146                         @Override
147                         public void onTransitionEnd(Object transition) {
148                             if (DEBUG) {
149                                 Log.d(TAG, "onTransitionEnd " + mActivityToRunTransition);
150                             }
151                             // after transition if the action row still focused, transfer
152                             // focus to its children
153                             if (mViewHolder.getActionsRow().isFocused()) {
154                                 mViewHolder.getActionsRow().requestFocus();
155                             }
156                             TransitionHelper.removeTransitionListener(transition, this);
157                         }
158                     });
159                 }
160                 startPostponedEnterTransitionInternal();
161             }
162         });
163     }
164 
165     /**
166      * Manually start postponed enter transition.
167      */
startPostponedEnterTransition()168     public void startPostponedEnterTransition() {
169         new Handler().post(new Runnable(){
170             @Override
171             public void run() {
172                 startPostponedEnterTransitionInternal();
173             }
174         });
175     }
176 
startPostponedEnterTransitionInternal()177     private void startPostponedEnterTransitionInternal() {
178         if (!mStartedPostpone && mViewHolder != null) {
179             if (DEBUG) {
180                 Log.d(TAG, "startPostponedEnterTransition " + mActivityToRunTransition);
181             }
182             ActivityCompat.startPostponedEnterTransition(mActivityToRunTransition);
183             mStartedPostpone = true;
184         }
185     }
186 }
187