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
17package com.android.tv.settings.dialog.old;
18
19import android.animation.Animator;
20import android.os.Bundle;
21import android.view.LayoutInflater;
22import android.view.View;
23import android.view.ViewGroup;
24import android.view.ViewGroup.LayoutParams;
25import android.view.ViewPropertyAnimator;
26import android.view.animation.DecelerateInterpolator;
27
28import com.android.tv.settings.R;
29import com.android.tv.settings.widget.ScrollAdapter;
30import com.android.tv.settings.widget.ScrollAdapterView;
31import 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 */
40public 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
51    public BaseScrollAdapterFragment(LiteFragment fragment) {
52        mFragment = fragment;
53    }
54
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
62    public void onSaveInstanceState(Bundle outState) {
63        if (mScrollAdapterView != null) {
64            outState.putInt(STATE_SELECTION, getSelectedItemPosition());
65        }
66    }
67
68    public void onViewCreated(View view, Bundle savedInstanceState) {
69        ensureList();
70        if (savedInstanceState != null) {
71            setSelection(savedInstanceState.getInt(STATE_SELECTION, 0));
72        }
73    }
74
75    public boolean hasCreatedView() {
76        return mFragment != null && mFragment.getView() != null;
77    }
78
79    public ScrollAdapterView getScrollAdapterView() {
80        ensureList();
81        return mScrollAdapterView;
82    }
83
84    public ScrollAdapter getAdapter() {
85        return mAdapter;
86    }
87
88    public void setAdapter(ScrollAdapter adapter) {
89        mAdapter = adapter;
90        if (mScrollAdapterView != null) {
91            mScrollAdapterView.setAdapter(mAdapter);
92        }
93    }
94
95    public void setSelection(int position) {
96        mScrollAdapterView.setSelection(position);
97    }
98
99    public void setSelectionSmooth(int position) {
100        mScrollAdapterView.setSelectionSmooth(position);
101    }
102
103    public int getSelectedItemPosition() {
104        return mScrollAdapterView.getSelectedItemPosition();
105    }
106
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
150        public Listener(boolean fadingOut) {
151            mFadingOut = fadingOut;
152        }
153
154        @Override
155        public void onAnimationStart(Animator animation) {
156            if(!mFadingOut) {
157                mFadedOut = false;
158            }
159        }
160
161        @Override
162        public void onAnimationEnd(Animator animation) {
163            if (!mCanceled && mFadingOut) {
164                mFadedOut = true;
165            }
166        }
167
168        @Override
169        public void onAnimationCancel(Animator animation) {
170            mCanceled = true;
171        }
172
173        @Override
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
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