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