FullWidthDetailsOverviewSharedElementHelper.java revision 99ec8b0cb375f7e5577ea3ec9f09e6ff7a95de0d
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    static final String TAG = "FullWidthDetailsOverviewSharedElementHelper";
50    static final boolean DEBUG = false;
51
52    private static final long DEFAULT_TIMEOUT = 5000;
53
54    ViewHolder mViewHolder;
55    Activity mActivityToRunTransition;
56    private boolean mStartedPostpone;
57    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.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     */
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     */
111    public boolean getAutoStartSharedElementTransition() {
112        return mAutoStartSharedElementTransition;
113    }
114
115    @Override
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     */
168    public void startPostponedEnterTransition() {
169        new Handler().post(new Runnable(){
170            @Override
171            public void run() {
172                startPostponedEnterTransitionInternal();
173            }
174        });
175    }
176
177    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