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.app; 15 16 import android.app.Fragment; 17 import android.os.Bundle; 18 import android.support.v17.leanback.R; 19 import android.support.v17.leanback.transition.TransitionHelper; 20 import android.support.v17.leanback.transition.TransitionListener; 21 import android.view.View; 22 import android.view.ViewTreeObserver; 23 24 /** 25 * @hide 26 */ 27 class 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 onViewCreated(View view, Bundle savedInstanceState)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 */ prepareEntranceTransition()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 */ isEntranceTransitionEnabled()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 */ createEntranceTransition()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 */ runEntranceTransition(Object entranceTransition)99 protected void runEntranceTransition(Object entranceTransition) { 100 } 101 102 /** 103 * Callback when entrance transition is started. 104 */ onEntranceTransitionStart()105 protected void onEntranceTransitionStart() { 106 } 107 108 /** 109 * Callback when entrance transition is ended. 110 */ onEntranceTransitionEnd()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 */ startEntranceTransition()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 internalCreateEntranceTransition()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