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