FullWidthDetailsOverviewSharedElementHelper.java revision 0246318f27a905a31df5a8af445cfe67d31dfb68
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 */
14package android.support.v17.leanback.widget;
15
16import android.os.Handler;
17import android.support.v4.app.ActivityCompat;
18import android.support.v4.app.SharedElementCallback;
19import android.support.v4.view.ViewCompat;
20import android.support.v17.leanback.R;
21import android.support.v17.leanback.transition.TransitionListener;
22import android.support.v17.leanback.transition.TransitionHelper;
23import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder;
24import android.app.Activity;
25import android.content.Intent;
26import android.graphics.Matrix;
27import android.text.TextUtils;
28import android.util.Log;
29import android.view.View;
30import android.view.ViewGroup;
31import android.view.View.MeasureSpec;
32import android.widget.ImageView;
33import android.widget.ImageView.ScaleType;
34
35import 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 */
46public 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
60    public void setSharedElementEnterTransition(Activity activity, String sharedElementName) {
61        setSharedElementEnterTransition(activity, sharedElementName, DEFAULT_TIMEOUT);
62    }
63
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.getInstance()
80                .getSharedElementEnterTransition(activity.getWindow());
81        setAutoStartSharedElementTransition(transition != null);
82        ActivityCompat.postponeEnterTransition(mActivityToRunTransition);
83        if (timeoutMs > 0) {
84            new Handler().postDelayed(new Runnable() {
85                @Override
86                public void run() {
87                    if (DEBUG) {
88                        Log.d(TAG, "timeout " + mActivityToRunTransition);
89                    }
90                    startPostponedEnterTransitionInternal();
91                }
92            }, timeoutMs);
93        }
94    }
95
96    /**
97     * Enable or disable auto startPostponedEnterTransition() when bound to logo. When it's
98     * disabled, app must call {@link #startPostponedEnterTransition()} to kick off
99     * windowEnterTransition. By default, it is disabled when there is no
100     * windowEnterSharedElementTransition set on the activity.
101     */
102    public void setAutoStartSharedElementTransition(boolean enabled) {
103        mAutoStartSharedElementTransition = enabled;
104    }
105
106    /**
107     * Returns true if auto startPostponedEnterTransition() when bound to logo. When it's
108     * disabled, app must call {@link #startPostponedEnterTransition()} to kick off
109     * windowEnterTransition. By default, it is disabled when there is no
110     * windowEnterSharedElementTransition set on the activity.
111     */
112    public boolean getAutoStartSharedElementTransition() {
113        return mAutoStartSharedElementTransition;
114    }
115
116    @Override
117    public void onBindLogo(ViewHolder vh) {
118        if (DEBUG) {
119            Log.d(TAG, "onBindLogo, could start transition of " + mActivityToRunTransition);
120        }
121        mViewHolder = vh;
122        if (!mAutoStartSharedElementTransition) {
123            return;
124        }
125        if (mViewHolder != null) {
126            if (DEBUG) {
127                Log.d(TAG, "rebind? clear transitionName on current viewHolder "
128                        + mViewHolder.getOverviewView());
129            }
130            ViewCompat.setTransitionName(mViewHolder.getLogoViewHolder().view, null);
131        }
132        // After we got a image drawable,  we can determine size of right panel.
133        // We want right panel to have fixed size so that the right panel don't change size
134        // when the overview is layout as a small bounds in transition.
135        mViewHolder.getDetailsDescriptionFrame().postOnAnimation(new Runnable() {
136            @Override
137            public void run() {
138                if (DEBUG) {
139                    Log.d(TAG, "setTransitionName "+mViewHolder.getOverviewView());
140                }
141                ViewCompat.setTransitionName(mViewHolder.getLogoViewHolder().view,
142                        mSharedElementName);
143                final TransitionHelper transitionHelper = TransitionHelper.getInstance();
144                Object transition = transitionHelper.getSharedElementEnterTransition(
145                        mActivityToRunTransition.getWindow());
146                if (transition != null) {
147                    transitionHelper.setTransitionListener(transition, new TransitionListener() {
148                        @Override
149                        public void onTransitionEnd(Object transition) {
150                            if (DEBUG) {
151                                Log.d(TAG, "onTransitionEnd " + mActivityToRunTransition);
152                            }
153                            // after transition if the action row still focused, transfer
154                            // focus to its children
155                            if (mViewHolder.getActionsRow().isFocused()) {
156                                mViewHolder.getActionsRow().requestFocus();
157                            }
158                            transitionHelper.setTransitionListener(transition, null);
159                        }
160                    });
161                }
162                startPostponedEnterTransitionInternal();
163            }
164        });
165    }
166
167    /**
168     * Manually start postponed enter transition.
169     */
170    public void startPostponedEnterTransition() {
171        new Handler().post(new Runnable(){
172            @Override
173            public void run() {
174                startPostponedEnterTransitionInternal();
175            }
176        });
177    }
178
179    private void startPostponedEnterTransitionInternal() {
180        if (!mStartedPostpone && mViewHolder != null) {
181            if (DEBUG) {
182                Log.d(TAG, "startPostponedEnterTransition " + mActivityToRunTransition);
183            }
184            ActivityCompat.startPostponedEnterTransition(mActivityToRunTransition);
185            mStartedPostpone = true;
186        }
187    }
188}
189