• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.car.radio;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorSet;
21 import android.animation.ObjectAnimator;
22 import android.content.Context;
23 import android.content.res.Resources;
24 import android.graphics.Canvas;
25 import android.os.Bundle;
26 import android.support.car.ui.PagedListView;
27 import android.support.v4.app.Fragment;
28 import android.support.v7.widget.RecyclerView;
29 import android.util.Log;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import com.android.car.radio.service.RadioStation;
34 
35 import java.util.List;
36 
37 /**
38  * A fragment that is responsible for displaying a list of the user's saved radio stations.
39  */
40 public class RadioPresetsFragment extends Fragment implements
41         FragmentWithFade,
42         PresetsAdapter.OnPresetItemClickListener,
43         RadioAnimationManager.OnExitCompleteListener,
44         RadioController.RadioStationChangeListener,
45         RadioStorage.PresetsChangeListener {
46     private static final String TAG = "PresetsFragment";
47     private static final int ANIM_DURATION_MS = 200;
48 
49     private View mRootView;
50     private View mCurrentRadioCard;
51 
52     private RadioStorage mRadioStorage;
53     private RadioController mRadioController;
54     private RadioAnimationManager mAnimManager;
55 
56     private PresetListExitListener mPresetListExitListener;
57 
58     private PagedListView mPresetsList;
59     private final PresetsAdapter mPresetsAdapter = new PresetsAdapter();
60 
61     /**
62      * An interface that will be notified when the user has indicated that they would like to
63      * exit the preset list.
64      */
65     public interface PresetListExitListener {
66         /**
67          * Method to be called when the preset list should be closed and the main radio display
68          * should be shown.
69          */
OnPresetListExit()70         void OnPresetListExit();
71     }
72 
73     /**
74      * Sets the {@link RadioController} that is responsible for updating the UI of this fragment
75      * with the information of the current radio station.
76      */
setRadioController(RadioController radioController)77     private void setRadioController(RadioController radioController) {
78         mRadioController = radioController;
79     }
80 
81     /**
82      * Sets the lsitener that will be notified when the preset list should be closed.
83      */
setPresetListExitListener(PresetListExitListener listener)84     public void setPresetListExitListener(PresetListExitListener listener) {
85         mPresetListExitListener = listener;
86     }
87 
88     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)89     public View onCreateView(LayoutInflater inflater, ViewGroup container,
90             Bundle savedInstanceState) {
91         mRootView = inflater.inflate(R.layout.radio_presets_list, container, false);
92         Context context = getContext();
93 
94         mPresetsAdapter.setOnPresetItemClickListener(this);
95 
96         mCurrentRadioCard = mRootView.findViewById(R.id.current_radio_station_card);
97 
98         mAnimManager = new RadioAnimationManager(getContext(), mRootView);
99         mAnimManager.playEnterAnimation();
100 
101         // Clicking on the current radio station card will close the activity and return to the
102         // main radio screen.
103         mCurrentRadioCard.setOnClickListener(v -> {
104             mAnimManager.playExitAnimation(RadioPresetsFragment.this /* listener */);
105         });
106 
107         mPresetsList = (PagedListView) mRootView.findViewById(R.id.presets_list);
108         mPresetsList.setLightMode();
109         mPresetsList.setDefaultItemDecoration(new ItemSpacingDecoration(context));
110         mPresetsList.setAdapter(mPresetsAdapter);
111         mPresetsList.getLayoutManager().setOffsetRows(false);
112         mPresetsList.getRecyclerView().addOnScrollListener(new PresetListScrollListener(
113                 context, mRootView, mCurrentRadioCard, mPresetsList));
114 
115         mRadioStorage = RadioStorage.getInstance(context);
116         mRadioStorage.addPresetsChangeListener(this);
117         setPresetsOnList(mRadioStorage.getPresets());
118 
119         return mRootView;
120     }
121 
122     @Override
fadeOutContent()123     public void fadeOutContent() {
124         ObjectAnimator containerAlphaAnimator =
125                 ObjectAnimator.ofFloat(mCurrentRadioCard, View.ALPHA, 0f);
126         containerAlphaAnimator.setDuration(ANIM_DURATION_MS);
127         containerAlphaAnimator.addListener(new Animator.AnimatorListener() {
128             @Override
129             public void onAnimationStart(Animator animation) {}
130 
131             @Override
132             public void onAnimationEnd(Animator animation) {
133                 mCurrentRadioCard.setVisibility(View.GONE);
134             }
135 
136             @Override
137             public void onAnimationCancel(Animator animation) {}
138 
139             @Override
140             public void onAnimationRepeat(Animator animation) {}
141         });
142 
143         ObjectAnimator presetListAlphaAnimator =
144                 ObjectAnimator.ofFloat(mPresetsList, View.ALPHA, 0f);
145         presetListAlphaAnimator.setDuration(ANIM_DURATION_MS);
146         presetListAlphaAnimator.addListener(new Animator.AnimatorListener() {
147             @Override
148             public void onAnimationStart(Animator animation) {}
149 
150             @Override
151             public void onAnimationEnd(Animator animation) {
152                 mPresetsList.setVisibility(View.GONE);
153             }
154 
155             @Override
156             public void onAnimationCancel(Animator animation) {}
157 
158             @Override
159             public void onAnimationRepeat(Animator animation) {}
160         });
161 
162         AnimatorSet animatorSet = new AnimatorSet();
163         animatorSet.playTogether(containerAlphaAnimator, presetListAlphaAnimator);
164         animatorSet.start();
165     }
166 
167     @Override
fadeInContent()168     public void fadeInContent() {
169         // Only the current radio card needs to be faded in as that is the only part
170         // of the fragment that will peek over the manual tuner.
171         ObjectAnimator containerAlphaAnimator =
172                 ObjectAnimator.ofFloat(mCurrentRadioCard, View.ALPHA, 1f);
173         containerAlphaAnimator.setDuration(ANIM_DURATION_MS);
174         containerAlphaAnimator.addListener(new Animator.AnimatorListener() {
175             @Override
176             public void onAnimationStart(Animator animation) {
177                 mCurrentRadioCard.setVisibility(View.VISIBLE);
178             }
179 
180             @Override
181             public void onAnimationEnd(Animator animation) {}
182 
183             @Override
184             public void onAnimationCancel(Animator animation) {}
185 
186             @Override
187             public void onAnimationRepeat(Animator animation) {}
188         });
189 
190         ObjectAnimator presetListAlphaAnimator =
191                 ObjectAnimator.ofFloat(mPresetsList, View.ALPHA, 1f);
192         presetListAlphaAnimator.setDuration(ANIM_DURATION_MS);
193         presetListAlphaAnimator.addListener(new Animator.AnimatorListener() {
194             @Override
195             public void onAnimationStart(Animator animation) {
196                 mPresetsList.setVisibility(View.VISIBLE);
197             }
198 
199             @Override
200             public void onAnimationEnd(Animator animation) {}
201 
202             @Override
203             public void onAnimationCancel(Animator animation) {}
204 
205             @Override
206             public void onAnimationRepeat(Animator animation) {}
207         });
208 
209         AnimatorSet animatorSet = new AnimatorSet();
210         animatorSet.playTogether(containerAlphaAnimator, presetListAlphaAnimator);
211         animatorSet.start();
212     }
213 
214     @Override
onStart()215     public void onStart() {
216         super.onStart();
217         mRadioController.initialize(mRootView);
218         mRadioController.setShouldColorStatusBar(true);
219         mRadioController.setRadioStationChangeListener(this);
220 
221         mPresetsAdapter.setActiveRadioStation(mRadioController.getCurrentRadioStation());
222     }
223 
224     @Override
onDestroy()225     public void onDestroy() {
226         mRadioStorage.removePresetsChangeListener(this);
227         mPresetListExitListener = null;
228         super.onDestroy();
229     }
230 
231     @Override
onExitAnimationComplete()232     public void onExitAnimationComplete() {
233         if (mPresetListExitListener != null) {
234             mPresetListExitListener.OnPresetListExit();
235         }
236     }
237 
238     @Override
onPresetItemClicked(RadioStation station)239     public void onPresetItemClicked(RadioStation station) {
240         mRadioController.tuneToRadioChannel(station);
241     }
242 
243     @Override
onRadioStationChanged(RadioStation station)244     public void onRadioStationChanged(RadioStation station) {
245         if (Log.isLoggable(TAG, Log.DEBUG)) {
246             Log.d(TAG, "onRadioStationChanged(): " + station);
247         }
248 
249         mPresetsAdapter.setActiveRadioStation(station);
250     }
251 
252     @Override
onPresetsRefreshed()253     public void onPresetsRefreshed() {
254         if (Log.isLoggable(TAG, Log.DEBUG)) {
255             Log.d(TAG, "onPresetsRefreshed()");
256         }
257 
258         setPresetsOnList(mRadioStorage.getPresets());
259     }
260 
261     /**
262      * Sets the given list of presets into the PagedListView {@link #mPresetsList}.
263      */
setPresetsOnList(List<RadioStation> presets)264     private void setPresetsOnList(List<RadioStation> presets) {
265         if (Log.isLoggable(TAG, Log.DEBUG)) {
266             Log.d(TAG, "setPresetsOnList(). # of presets: " + presets.size());
267         }
268 
269         if (Log.isLoggable(TAG, Log.VERBOSE)) {
270             for (RadioStation radioStation : presets) {
271                 Log.v(TAG, "\t" + radioStation);
272             }
273         }
274 
275         mPresetsAdapter.setPresets(presets);
276     }
277 
278     /**
279      * A {@link android.support.car.ui.PagedListView.Decoration} that draws a line between
280      * the items.
281      */
282     public static class ItemSpacingDecoration extends PagedListView.Decoration {
283         private final int mLineStart;
284 
ItemSpacingDecoration(Context context)285         public ItemSpacingDecoration(Context context) {
286             super(context);
287             Resources res = context.getResources();
288             mLineStart = res.getDimensionPixelSize(R.dimen.stream_card_keyline_3);
289         }
290 
291         @Override
onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state)292         public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
293             View presetCard = parent.findViewById(R.id.preset_card);
294 
295             if (presetCard == null) {
296                 return;
297             }
298 
299             int left = mLineStart + presetCard.getLeft();
300             int right = presetCard.getRight();
301             int childCount = parent.getChildCount();
302 
303             for (int i = 0; i < childCount; i++) {
304                 View child = parent.getChildAt(i);
305                 int bottom = child.getBottom();
306                 int top = bottom - mDividerHeight;
307 
308                 // Draw a divider line between each item. No need to draw the line for the last
309                 // item.
310                 if (i != childCount - 1) {
311                     c.drawRect(left, top, right, bottom, mPaint);
312                 }
313             }
314         }
315     }
316 
317     /**
318      * Returns a new instance of the {@link RadioPresetsFragment}.
319      *
320      * @param radioController The {@link RadioController} that is responsible for updating the UI
321      *                        of the returned fragment.
322      */
newInstance(RadioController radioController)323     public static RadioPresetsFragment newInstance(RadioController radioController) {
324         RadioPresetsFragment fragment = new RadioPresetsFragment();
325         fragment.setRadioController(radioController);
326 
327         return fragment;
328     }
329 }
330