1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.settings.dialog.old; 18 19 import android.animation.Animator; 20 import android.os.Bundle; 21 import android.view.LayoutInflater; 22 import android.view.View; 23 import android.view.ViewGroup; 24 import android.view.ViewGroup.LayoutParams; 25 import android.view.ViewPropertyAnimator; 26 import android.view.animation.DecelerateInterpolator; 27 28 import com.android.tv.settings.R; 29 import com.android.tv.settings.widget.ScrollAdapter; 30 import com.android.tv.settings.widget.ScrollAdapterView; 31 import com.android.tv.settings.widget.ScrollAdapterView.OnScrollListener; 32 33 /** 34 * Fragment which uses a ScrollAdapterView as its basic UI. 35 * <p> 36 * This fragment is meant to be inserted as the action fragment into a {@link DialogActivity}. 37 * <p> 38 * Users can either subclass this or just use listener objects to receive life cycle events. 39 */ 40 public class BaseScrollAdapterFragment implements OnScrollListener { 41 private static final String STATE_SELECTION = "BaseScrollAdapterFragment.selection"; 42 private int mAnimationDuration; 43 private final LiteFragment mFragment; 44 45 private ScrollAdapter mAdapter; 46 private ScrollAdapterView mScrollAdapterView; 47 private View mSelectorView; 48 private View mSelectedView = null; 49 private volatile boolean mFadedOut = true; 50 BaseScrollAdapterFragment(LiteFragment fragment)51 public BaseScrollAdapterFragment(LiteFragment fragment) { 52 mFragment = fragment; 53 } 54 onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)55 public View onCreateView(LayoutInflater inflater, ViewGroup container, 56 Bundle savedInstanceState) { 57 View v = inflater.inflate(R.layout.settings_list, container, false); 58 mScrollAdapterView = null; 59 return v; 60 } 61 onSaveInstanceState(Bundle outState)62 public void onSaveInstanceState(Bundle outState) { 63 if (mScrollAdapterView != null) { 64 outState.putInt(STATE_SELECTION, getSelectedItemPosition()); 65 } 66 } 67 onViewCreated(View view, Bundle savedInstanceState)68 public void onViewCreated(View view, Bundle savedInstanceState) { 69 ensureList(); 70 if (savedInstanceState != null) { 71 setSelection(savedInstanceState.getInt(STATE_SELECTION, 0)); 72 } 73 } 74 hasCreatedView()75 public boolean hasCreatedView() { 76 return mFragment != null && mFragment.getView() != null; 77 } 78 getScrollAdapterView()79 public ScrollAdapterView getScrollAdapterView() { 80 ensureList(); 81 return mScrollAdapterView; 82 } 83 getAdapter()84 public ScrollAdapter getAdapter() { 85 return mAdapter; 86 } 87 setAdapter(ScrollAdapter adapter)88 public void setAdapter(ScrollAdapter adapter) { 89 mAdapter = adapter; 90 if (mScrollAdapterView != null) { 91 mScrollAdapterView.setAdapter(mAdapter); 92 } 93 } 94 setSelection(int position)95 public void setSelection(int position) { 96 mScrollAdapterView.setSelection(position); 97 } 98 setSelectionSmooth(int position)99 public void setSelectionSmooth(int position) { 100 mScrollAdapterView.setSelectionSmooth(position); 101 } 102 getSelectedItemPosition()103 public int getSelectedItemPosition() { 104 return mScrollAdapterView.getSelectedItemPosition(); 105 } 106 ensureList()107 public void ensureList() { 108 if (mScrollAdapterView != null) { 109 return; 110 } 111 View root = mFragment.getView(); 112 if (root == null) { 113 throw new IllegalStateException("Content view not created yet."); 114 } 115 if (root instanceof ScrollAdapterView) { 116 mScrollAdapterView = (ScrollAdapterView) root; 117 mSelectorView = null; 118 } else { 119 mScrollAdapterView = (ScrollAdapterView) root.findViewById(R.id.list); 120 if (mScrollAdapterView == null) { 121 throw new IllegalStateException("No scroll adapter view exists."); 122 } 123 mSelectorView = root.findViewById(R.id.selector); 124 } 125 if (mScrollAdapterView != null) { 126 mScrollAdapterView.requestFocusFromTouch(); 127 if (mAdapter != null) { 128 mScrollAdapterView.setAdapter(mAdapter); 129 } 130 if (mSelectorView != null) { 131 mAnimationDuration = mFragment.getActivity() 132 .getResources().getInteger(R.integer.dialog_animation_duration); 133 mScrollAdapterView.addOnScrollListener(this); 134 } 135 } 136 } 137 138 /** 139 * Sets {@link BaseScrollAdapterFragment#mFadedOut} 140 * 141 * {@link BaseScrollAdapterFragment#mFadedOut} is true, iff 142 * {@link BaseScrollAdapterFragment#mSelectorView} has an alpha of 0 143 * (faded out). If false the view either has an alpha of 1 (visible) or 144 * is in the process of animating. 145 */ 146 private class Listener implements Animator.AnimatorListener { 147 private final boolean mFadingOut; 148 private boolean mCanceled; 149 Listener(boolean fadingOut)150 public Listener(boolean fadingOut) { 151 mFadingOut = fadingOut; 152 } 153 154 @Override onAnimationStart(Animator animation)155 public void onAnimationStart(Animator animation) { 156 if(!mFadingOut) { 157 mFadedOut = false; 158 } 159 } 160 161 @Override onAnimationEnd(Animator animation)162 public void onAnimationEnd(Animator animation) { 163 if (!mCanceled && mFadingOut) { 164 mFadedOut = true; 165 } 166 } 167 168 @Override onAnimationCancel(Animator animation)169 public void onAnimationCancel(Animator animation) { 170 mCanceled = true; 171 } 172 173 @Override onAnimationRepeat(Animator animation)174 public void onAnimationRepeat(Animator animation) { 175 } 176 } 177 178 // We want to fade in the selector if we've stopped scrolling on it (mainPosition = 0). 179 // If mainPosition is not 0, we want to ensure to dim the selector if we haven't already. 180 // we dim the last highlighted view so that while a user is scrolling, nothing is highlighted. 181 @Override onScrolled(View view, int position, float mainPosition, float secondPosition)182 public synchronized void onScrolled(View view, int position, float mainPosition, 183 float secondPosition) { 184 boolean hasFocus = (mainPosition == 0.0); 185 if (hasFocus) { 186 if (view != null) { 187 // The selector starts with a height of 0. In order to scale up 188 // from 189 // 0 we first need the set the height to 1 and scale form there. 190 int selectorHeight = mSelectorView.getHeight(); 191 if (selectorHeight == 0) { 192 LayoutParams lp = mSelectorView.getLayoutParams(); 193 lp.height = selectorHeight = mFragment.getActivity().getResources() 194 .getDimensionPixelSize(R.dimen.action_fragment_selector_min_height); 195 mSelectorView.setLayoutParams(lp); 196 } 197 float scaleY = (float) view.getHeight() / selectorHeight; 198 ViewPropertyAnimator animation = mSelectorView.animate() 199 .alpha(1f) 200 .setListener(new Listener(false)) 201 .setDuration(mAnimationDuration) 202 .setInterpolator(new DecelerateInterpolator(2f)); 203 if (mFadedOut) { 204 // selector is completely faded out, so we can just scale 205 // before fading in. 206 mSelectorView.setScaleY(scaleY); 207 } else { 208 // selector is not faded out, so we must animate the scale 209 // as we fade in. 210 animation.scaleY(scaleY); 211 } 212 animation.start(); 213 mSelectedView = view; 214 } else { 215 LayoutParams lp = mSelectorView.getLayoutParams(); 216 lp.height = 0; 217 mSelectorView.setLayoutParams(lp); 218 } 219 } else if (mSelectedView != null) { 220 mSelectorView.animate() 221 .alpha(0f) 222 .setDuration(mAnimationDuration) 223 .setInterpolator(new DecelerateInterpolator(2f)) 224 .setListener(new Listener(true)) 225 .start(); 226 mSelectedView = null; 227 } 228 } 229 } 230