• 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.annotation.NonNull;
23 import android.content.Context;
24 import android.hardware.radio.RadioManager.ProgramInfo;
25 import android.os.Bundle;
26 import android.support.v4.app.Fragment;
27 import android.util.Log;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.view.ViewGroup;
31 
32 import androidx.car.widget.DayNightStyle;
33 import androidx.car.widget.PagedListView;
34 
35 import com.android.car.broadcastradio.support.Program;
36 import com.android.car.radio.storage.RadioStorage;
37 
38 import java.util.List;
39 
40 /**
41  * A fragment that is responsible for displaying a list of the user's saved radio stations.
42  */
43 public class RadioPresetsFragment extends Fragment implements
44         FragmentWithFade,
45         RadioAnimationManager.OnExitCompleteListener,
46         RadioController.ProgramInfoChangeListener,
47         RadioStorage.PresetsChangeListener {
48     private static final String TAG = "PresetsFragment";
49     private static final int ANIM_DURATION_MS = 200;
50 
51     private View mCurrentRadioCard;
52 
53     private RadioStorage mRadioStorage;
54     private RadioController mRadioController;
55     private RadioAnimationManager mAnimManager;
56 
57     private PresetListExitListener mPresetListExitListener;
58 
59     private PagedListView mPresetsList;
60     private final PresetsAdapter mPresetsAdapter = new PresetsAdapter();
61 
62     /**
63      * An interface that will be notified when the user has indicated that they would like to
64      * exit the preset list.
65      */
66     public interface PresetListExitListener {
67         /**
68          * Method to be called when the preset list should be closed and the main radio display
69          * should be shown.
70          */
OnPresetListExit()71         void OnPresetListExit();
72     }
73 
74     /**
75      * Sets the {@link RadioController} that is responsible for updating the UI of this fragment
76      * with the information of the current radio station.
77      */
setRadioController(RadioController radioController)78     private void setRadioController(RadioController radioController) {
79         mRadioController = radioController;
80     }
81 
82     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)83     public View onCreateView(LayoutInflater inflater, ViewGroup container,
84             Bundle savedInstanceState) {
85         return inflater.inflate(R.layout.radio_presets_list, container, false);
86     }
87 
88     @Override
onViewCreated(View view, Bundle savedInstanceState)89     public void onViewCreated(View view, Bundle savedInstanceState) {
90         Context context = getContext();
91 
92         mPresetsAdapter.setOnPresetItemClickListener(mRadioController::tune);
93         mPresetsAdapter.setOnPresetItemFavoriteListener(this::handlePresetItemFavoriteChanged);
94 
95         mCurrentRadioCard = view.findViewById(R.id.current_radio_station_card);
96 
97         mAnimManager = new RadioAnimationManager(getContext(), view);
98         mAnimManager.playEnterAnimation();
99 
100         // Clicking on the current radio station card will close the activity and return to the
101         // main radio screen.
102         mCurrentRadioCard.setOnClickListener(
103                 v -> mAnimManager.playExitAnimation(RadioPresetsFragment.this /* listener */));
104 
105         mPresetsList = view.findViewById(R.id.presets_list);
106         mPresetsList.setDayNightStyle(DayNightStyle.ALWAYS_LIGHT);
107         mPresetsList.setAdapter(mPresetsAdapter);
108         mPresetsList.getRecyclerView().addOnScrollListener(new PresetListScrollListener(
109                 context, view, mCurrentRadioCard, mPresetsList));
110 
111         mRadioStorage = RadioStorage.getInstance(context);
112         mRadioStorage.addPresetsChangeListener(this);
113         setPresetsOnList(mRadioStorage.getPresets());
114     }
115 
116     @Override
fadeOutContent()117     public void fadeOutContent() {
118         ObjectAnimator containerAlphaAnimator =
119                 ObjectAnimator.ofFloat(mCurrentRadioCard, View.ALPHA, 0f);
120         containerAlphaAnimator.setDuration(ANIM_DURATION_MS);
121         containerAlphaAnimator.addListener(new Animator.AnimatorListener() {
122             @Override
123             public void onAnimationStart(Animator animation) {}
124 
125             @Override
126             public void onAnimationEnd(Animator animation) {
127                 mCurrentRadioCard.setVisibility(View.GONE);
128             }
129 
130             @Override
131             public void onAnimationCancel(Animator animation) {}
132 
133             @Override
134             public void onAnimationRepeat(Animator animation) {}
135         });
136 
137         ObjectAnimator presetListAlphaAnimator =
138                 ObjectAnimator.ofFloat(mPresetsList, View.ALPHA, 0f);
139         presetListAlphaAnimator.setDuration(ANIM_DURATION_MS);
140         presetListAlphaAnimator.addListener(new Animator.AnimatorListener() {
141             @Override
142             public void onAnimationStart(Animator animation) {}
143 
144             @Override
145             public void onAnimationEnd(Animator animation) {
146                 mPresetsList.setVisibility(View.GONE);
147             }
148 
149             @Override
150             public void onAnimationCancel(Animator animation) {}
151 
152             @Override
153             public void onAnimationRepeat(Animator animation) {}
154         });
155 
156         AnimatorSet animatorSet = new AnimatorSet();
157         animatorSet.playTogether(containerAlphaAnimator, presetListAlphaAnimator);
158         animatorSet.start();
159     }
160 
161     @Override
fadeInContent()162     public void fadeInContent() {
163         // Only the current radio card needs to be faded in as that is the only part
164         // of the fragment that will peek over the manual tuner.
165         ObjectAnimator containerAlphaAnimator =
166                 ObjectAnimator.ofFloat(mCurrentRadioCard, View.ALPHA, 1f);
167         containerAlphaAnimator.setDuration(ANIM_DURATION_MS);
168         containerAlphaAnimator.addListener(new Animator.AnimatorListener() {
169             @Override
170             public void onAnimationStart(Animator animation) {
171                 mCurrentRadioCard.setVisibility(View.VISIBLE);
172             }
173 
174             @Override
175             public void onAnimationEnd(Animator animation) {}
176 
177             @Override
178             public void onAnimationCancel(Animator animation) {}
179 
180             @Override
181             public void onAnimationRepeat(Animator animation) {}
182         });
183 
184         ObjectAnimator presetListAlphaAnimator =
185                 ObjectAnimator.ofFloat(mPresetsList, View.ALPHA, 1f);
186         presetListAlphaAnimator.setDuration(ANIM_DURATION_MS);
187         presetListAlphaAnimator.addListener(new Animator.AnimatorListener() {
188             @Override
189             public void onAnimationStart(Animator animation) {
190                 mPresetsList.setVisibility(View.VISIBLE);
191             }
192 
193             @Override
194             public void onAnimationEnd(Animator animation) {}
195 
196             @Override
197             public void onAnimationCancel(Animator animation) {}
198 
199             @Override
200             public void onAnimationRepeat(Animator animation) {}
201         });
202 
203         AnimatorSet animatorSet = new AnimatorSet();
204         animatorSet.playTogether(containerAlphaAnimator, presetListAlphaAnimator);
205         animatorSet.start();
206     }
207 
208     @Override
onStart()209     public void onStart() {
210         super.onStart();
211         mRadioController.initialize(getView());
212         mRadioController.setShouldColorStatusBar(true);
213         mRadioController.addProgramInfoChangeListener(this);
214 
215         // TODO(b/73950974): use callback only
216         ProgramInfo info = mRadioController.getCurrentProgramInfo();
217         if (info != null) {
218             mPresetsAdapter.setActiveProgram(Program.fromProgramInfo(info));
219         }
220     }
221 
222     @Override
onDestroy()223     public void onDestroy() {
224         mRadioStorage.removePresetsChangeListener(this);
225         mRadioController.removeProgramInfoChangeListener(this);
226         mPresetListExitListener = null;
227         super.onDestroy();
228     }
229 
230     @Override
onExitAnimationComplete()231     public void onExitAnimationComplete() {
232         if (mPresetListExitListener != null) {
233             mPresetListExitListener.OnPresetListExit();
234         }
235     }
236 
237 
238     @Override
onProgramInfoChanged(@onNull ProgramInfo info)239     public void onProgramInfoChanged(@NonNull ProgramInfo info) {
240         mPresetsAdapter.setPresets(mRadioStorage.getPresets());
241         mPresetsAdapter.setActiveProgram(Program.fromProgramInfo(info));
242     }
243 
244     @Override
onPresetsRefreshed()245     public void onPresetsRefreshed() {
246         if (Log.isLoggable(TAG, Log.DEBUG)) {
247             Log.d(TAG, "onPresetsRefreshed()");
248         }
249     }
250 
handlePresetItemFavoriteChanged(Program program, boolean saveAsFavorite)251     private void handlePresetItemFavoriteChanged(Program program, boolean saveAsFavorite) {
252         if (saveAsFavorite) {
253             mRadioStorage.storePreset(program);
254         } else {
255             mRadioStorage.removePreset(program.getSelector());
256         }
257     }
258 
259     /**
260      * Sets the given list of presets into the PagedListView {@link #mPresetsList}.
261      */
setPresetsOnList(List<Program> presets)262     private void setPresetsOnList(List<Program> presets) {
263         if (Log.isLoggable(TAG, Log.DEBUG)) {
264             Log.d(TAG, "setPresetsOnList(). # of presets: " + presets.size());
265         }
266 
267         mPresetsAdapter.setPresets(presets);
268     }
269 
270     /**
271      * Returns a new instance of the {@link RadioPresetsFragment}.
272      *
273      * @param radioController The {@link RadioController} that is responsible for updating the UI
274      *                        of the returned fragment.
275      */
newInstance(RadioController radioController, PresetListExitListener existListener)276     public static RadioPresetsFragment newInstance(RadioController radioController,
277             PresetListExitListener existListener) {
278         RadioPresetsFragment fragment = new RadioPresetsFragment();
279         fragment.setRadioController(radioController);
280         fragment.mPresetListExitListener = existListener;
281 
282         return fragment;
283     }
284 }
285