BaseFragment.java revision 572ed31d757c2635ea0a5cdd7ec8a33cd16f77b7
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.app;
15
16import android.app.Fragment;
17import android.os.Bundle;
18import android.support.v17.leanback.R;
19import android.support.v17.leanback.transition.TransitionHelper;
20import android.support.v17.leanback.transition.TransitionListener;
21import android.view.View;
22import android.view.ViewTreeObserver;
23
24/**
25 * @hide
26 */
27class BaseFragment extends Fragment {
28
29    private boolean mEntranceTransitionEnabled = false;
30    private boolean mStartEntranceTransitionPending = false;
31    private Object mEntranceTransition;
32
33    static TransitionHelper sTransitionHelper = TransitionHelper.getInstance();
34
35    @Override
36    public void onViewCreated(View view, Bundle savedInstanceState) {
37        super.onViewCreated(view, savedInstanceState);
38        if (mStartEntranceTransitionPending) {
39            mStartEntranceTransitionPending = false;
40            startEntranceTransition();
41        }
42    }
43
44    /**
45     * Enables entrance transition.<p>
46     * Entrance transition is the standard slide-in transition that shows rows of data in
47     * browse screen and details screen.
48     * <p>
49     * The method is ignored before LOLLIPOP (API21).
50     * <p>
51     * This method must be called in or
52     * before onCreate().  Typically entrance transition should be enabled when savedInstance is
53     * null so that fragment restored from instanceState does not run an extra entrance transition.
54     * When the entrance transition is enabled, the fragment will make headers and content
55     * hidden initially.
56     * When data of rows are ready, app must call {@link #startEntranceTransition()} to kick off
57     * the transition, otherwise the rows will be invisible forever.
58     * <p>
59     * It is similar to android:windowsEnterTransition and can be considered a late-executed
60     * android:windowsEnterTransition controlled by app.  There are two reasons that app needs it:
61     * <li> Workaround the problem that activity transition is not available between launcher and
62     * app.  Browse activity must programmatically start the slide-in transition.</li>
63     * <li> Separates DetailsOverviewRow transition from other rows transition.  So that
64     * the DetailsOverviewRow transition can be executed earlier without waiting for all rows
65     * to be loaded.</li>
66     * <p>
67     * Transition object is returned by createEntranceTransition().  Typically the app does not need
68     * override the default transition that browse and details provides.
69     */
70    public void prepareEntranceTransition() {
71        if (TransitionHelper.systemSupportsEntranceTransitions()) {
72            mEntranceTransitionEnabled = true;
73        }
74    }
75
76    /**
77     * Return true if entrance transition is enabled and not started yet.
78     * Entrance transition can only be executed once and isEntranceTransitionEnabled()
79     * is reset to false after entrance transition is started.
80     */
81    boolean isEntranceTransitionEnabled() {
82        return mEntranceTransitionEnabled;
83    }
84
85    /**
86     * Create entrance transition.  Subclass can override to load transition from
87     * resource or construct manually.  Typically app does not need to
88     * override the default transition that browse and details provides.
89     */
90    protected Object createEntranceTransition() {
91        return null;
92    }
93
94    /**
95     * Run entrance transition.  Subclass may use TransitionManager to perform
96     * go(Scene) or beginDelayedTransition().  App should not override the default
97     * implementation of browse and details fragment.
98     */
99    protected void runEntranceTransition(Object entranceTransition) {
100    }
101
102    /**
103     * Callback when entrance transition is started.
104     */
105    protected void onEntranceTransitionStart() {
106    }
107
108    /**
109     * Callback when entrance transition is ended.
110     */
111    protected void onEntranceTransitionEnd() {
112    }
113
114    /**
115     * When fragment finishes loading data, it should call startEntranceTransition()
116     * to execute the entrance transition.
117     * startEntranceTransition() will start transition only if both two conditions
118     * are satisfied:
119     * <li> prepareEntranceTransition() was called.</li>
120     * <li> has not executed entrance transition yet.</li>
121     * <p>
122     * If startEntranceTransition() is called before onViewCreated(), it will be pending
123     * and executed when view is created.
124     */
125    public void startEntranceTransition() {
126        if (!mEntranceTransitionEnabled || mEntranceTransition != null) {
127            return;
128        }
129        // if view is not created yet, delay until onViewCreated()
130        if (getView() == null) {
131            mStartEntranceTransitionPending = true;
132            return;
133        }
134        // wait till views get their initial position before start transition
135        final View view = getView();
136        view.getViewTreeObserver().addOnPreDrawListener(
137                new ViewTreeObserver.OnPreDrawListener() {
138            @Override
139            public boolean onPreDraw() {
140                view.getViewTreeObserver().removeOnPreDrawListener(this);
141                internalCreateEntranceTransition();
142                mEntranceTransitionEnabled = false;
143                runEntranceTransition(mEntranceTransition);
144                return false;
145            }
146        });
147        view.invalidate();
148    }
149
150    void internalCreateEntranceTransition() {
151        mEntranceTransition = createEntranceTransition();
152        if (mEntranceTransition == null) {
153            return;
154        }
155        sTransitionHelper.setTransitionListener(mEntranceTransition, new TransitionListener() {
156            @Override
157            public void onTransitionStart(Object transition) {
158                onEntranceTransitionStart();
159            }
160            @Override
161            public void onTransitionEnd(Object transition) {
162                mEntranceTransition = null;
163                onEntranceTransitionEnd();
164            }
165        });
166    }
167}
168