• 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.annotation.Nullable;
20 import android.content.Intent;
21 import android.hardware.radio.ProgramSelector;
22 import android.hardware.radio.RadioManager;
23 import android.os.Bundle;
24 import android.support.v4.app.Fragment;
25 import android.support.v4.app.FragmentManager;
26 import android.util.Log;
27 import android.util.Pair;
28 
29 import androidx.car.drawer.CarDrawerActivity;
30 import androidx.car.drawer.CarDrawerAdapter;
31 import androidx.car.drawer.DrawerItemViewHolder;
32 
33 import java.util.ArrayList;
34 import java.util.List;
35 
36 /**
37  * The main activity for the radio. This activity initializes the radio controls and listener for
38  * radio changes.
39  */
40 public class CarRadioActivity extends CarDrawerActivity implements
41         RadioPresetsFragment.PresetListExitListener,
42         MainRadioFragment.RadioPresetListClickListener,
43         ManualTunerFragment.ManualTunerCompletionListener {
44     private static final String TAG = "Em.RadioActivity";
45     private static final String MANUAL_TUNER_BACKSTACK = "MANUAL_TUNER_BACKSTACK";
46     private static final String CONTENT_FRAGMENT_TAG = "CONTENT_FRAGMENT_TAG";
47 
48     private static final List<Pair<Integer, String>> SUPPORTED_RADIO_BANDS = new ArrayList<>();
49 
50     /**
51      * Intent action for notifying that the radio state has changed.
52      */
53     private static final String ACTION_RADIO_APP_STATE_CHANGE
54             = "android.intent.action.RADIO_APP_STATE_CHANGE";
55 
56     /**
57      * Boolean Intent extra indicating if the radio is the currently in the foreground.
58      */
59     private static final String EXTRA_RADIO_APP_FOREGROUND
60             = "android.intent.action.RADIO_APP_STATE";
61 
62     /**
63      * Whether or not it is safe to make transactions on the
64      * {@link android.support.v4.app.FragmentManager}. This variable prevents a possible exception
65      * when calling commit() on the FragmentManager.
66      *
67      * <p>The default value is {@code true} because it is only after
68      * {@link #onSaveInstanceState(Bundle)} has been called that fragment commits are not allowed.
69      */
70     private boolean mAllowFragmentCommits = true;
71 
72     private RadioController mRadioController;
73 
74     @Override
onCreate(Bundle savedInstanceState)75     protected void onCreate(Bundle savedInstanceState) {
76         SUPPORTED_RADIO_BANDS.add(
77                 new Pair<>(RadioManager.BAND_AM, getString(R.string.radio_am_text)));
78         SUPPORTED_RADIO_BANDS.add(
79                 new Pair<>(RadioManager.BAND_FM, getString(R.string.radio_fm_text)));
80 
81         super.onCreate(savedInstanceState);
82         setToolbarElevation(0f);
83 
84         mRadioController = new RadioController(this);
85         setContentFragment(
86                 MainRadioFragment.newInstance(mRadioController, this /* clickListener */));
87 
88     }
89 
90     @Override
getRootAdapter()91     protected CarDrawerAdapter getRootAdapter() {
92         return new RadioDrawerAdapter();
93     }
94 
95     @Override
onPresetListClicked()96     public void onPresetListClicked() {
97         setContentFragment(
98                 RadioPresetsFragment.newInstance(mRadioController, this /* existListener */));
99     }
100 
101     @Override
OnPresetListExit()102     public void OnPresetListExit() {
103         setContentFragment(
104                 MainRadioFragment.newInstance(mRadioController, this /* clickListener */));
105     }
106 
startManualTuner()107     private void startManualTuner() {
108         if (!mAllowFragmentCommits || getSupportFragmentManager().getBackStackEntryCount() > 0) {
109             return;
110         }
111 
112         Fragment currentFragment = getCurrentFragment();
113         if (currentFragment instanceof FragmentWithFade) {
114             ((FragmentWithFade) currentFragment).fadeOutContent();
115         }
116 
117         ManualTunerFragment tunerFragment =
118                 ManualTunerFragment.newInstance(mRadioController.getCurrentRadioBand());
119         tunerFragment.setManualTunerCompletionListener(this);
120 
121         getSupportFragmentManager().beginTransaction()
122                 .setCustomAnimations(R.anim.slide_up, R.anim.slide_down,
123                         R.anim.slide_up, R.anim.slide_down)
124                 .add(getContentContainerId(), tunerFragment)
125                 .addToBackStack(MANUAL_TUNER_BACKSTACK)
126                 .commit();
127     }
128 
129     @Override
onStationSelected(ProgramSelector sel)130     public void onStationSelected(ProgramSelector sel) {
131         maybeDismissManualTuner();
132 
133         Fragment fragment = getCurrentFragment();
134         if (fragment instanceof FragmentWithFade) {
135             ((FragmentWithFade) fragment).fadeInContent();
136         }
137 
138         if (sel != null) {
139             mRadioController.tune(sel);
140         }
141     }
142 
143     @Override
onStart()144     protected void onStart() {
145         super.onStart();
146 
147         if (Log.isLoggable(TAG, Log.DEBUG)) {
148             Log.d(TAG, "onStart");
149         }
150 
151         // Fragment commits are not allowed once the Activity's state has been saved. Once
152         // onStart() has been called, the FragmentManager should now allow commits.
153         mAllowFragmentCommits = true;
154 
155         mRadioController.start();
156 
157         Intent broadcast = new Intent(ACTION_RADIO_APP_STATE_CHANGE);
158         broadcast.putExtra(EXTRA_RADIO_APP_FOREGROUND, true);
159         sendBroadcast(broadcast);
160     }
161 
162     @Override
onStop()163     protected void onStop() {
164         super.onStop();
165 
166         if (Log.isLoggable(TAG, Log.DEBUG)) {
167             Log.d(TAG, "onStop");
168         }
169 
170         Intent broadcast = new Intent(ACTION_RADIO_APP_STATE_CHANGE);
171         broadcast.putExtra(EXTRA_RADIO_APP_FOREGROUND, false);
172         sendBroadcast(broadcast);
173     }
174 
175     @Override
onDestroy()176     protected void onDestroy() {
177         super.onDestroy();
178 
179         if (Log.isLoggable(TAG, Log.DEBUG)) {
180             Log.d(TAG, "onDestroy");
181         }
182 
183         mRadioController.shutdown();
184     }
185 
186     @Override
onSaveInstanceState(Bundle outState)187     public void onSaveInstanceState(Bundle outState) {
188         // A transaction can only be committed with this method prior to its containing activity
189         // saving its state.
190         mAllowFragmentCommits = false;
191         super.onSaveInstanceState(outState);
192     }
193 
194     /**
195      * Checks if the manual tuner is currently being displayed. If it is, then dismiss it.
196      */
maybeDismissManualTuner()197     private void maybeDismissManualTuner() {
198         FragmentManager fragmentManager = getSupportFragmentManager();
199         if (fragmentManager.getBackStackEntryCount() > 0) {
200             // A station can only be selected if the manual tuner fragment has been shown; so, remove
201             // that here.
202             getSupportFragmentManager().popBackStack();
203         }
204     }
205 
setContentFragment(Fragment fragment)206     private void setContentFragment(Fragment fragment) {
207         if (!mAllowFragmentCommits) {
208             return;
209         }
210 
211         getSupportFragmentManager().beginTransaction()
212                 .replace(getContentContainerId(), fragment, CONTENT_FRAGMENT_TAG)
213                 .commitNow();
214     }
215 
216     /**
217      * Returns the fragment that is currently being displayed as the content view. Note that this
218      * is not necessarily the fragment that is visible. The manual tuner fragment can be displayed
219      * on top of this content fragment.
220      */
221     @Nullable
getCurrentFragment()222     private Fragment getCurrentFragment() {
223         return getSupportFragmentManager().findFragmentByTag(CONTENT_FRAGMENT_TAG);
224     }
225 
226     /**
227      * An adapter that is responsible for populating the Radio drawer with the available bands to
228      * select, as well as the option for opening the manual tuner.
229      */
230     private class RadioDrawerAdapter extends CarDrawerAdapter {
231         private final List<String> mDrawerOptions =
232                 new ArrayList<>(SUPPORTED_RADIO_BANDS.size() + 1);
233 
RadioDrawerAdapter()234         RadioDrawerAdapter() {
235             super(CarRadioActivity.this, false /* showDisabledListOnEmpty */);
236             setTitle(getString(R.string.app_name));
237             // The ordering of options is hardcoded. The click handler below depends on it.
238             for (Pair<Integer, String> band : SUPPORTED_RADIO_BANDS) {
239                 mDrawerOptions.add(band.second);
240             }
241             mDrawerOptions.add(getString(R.string.manual_tuner_drawer_entry));
242         }
243 
244         @Override
getActualItemCount()245         protected int getActualItemCount() {
246             return mDrawerOptions.size();
247         }
248 
249         @Override
populateViewHolder(DrawerItemViewHolder holder, int position)250         public void populateViewHolder(DrawerItemViewHolder holder, int position) {
251             holder.getTitle().setText(mDrawerOptions.get(position));
252         }
253 
254         @Override
onItemClick(int position)255         public void onItemClick(int position) {
256             getDrawerController().closeDrawer();
257             if (position < SUPPORTED_RADIO_BANDS.size()) {
258                 mRadioController.switchBand(SUPPORTED_RADIO_BANDS.get(position).first);
259             } else if (position == SUPPORTED_RADIO_BANDS.size()) {
260                 startManualTuner();
261             } else {
262                 Log.w(TAG, "Unexpected position: " + position);
263             }
264         }
265     }
266 }
267