Home | History | Annotate | Download | only in radio
      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
     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
     91     protected CarDrawerAdapter getRootAdapter() {
     92         return new RadioDrawerAdapter();
     93     }
     94 
     95     @Override
     96     public void onPresetListClicked() {
     97         setContentFragment(
     98                 RadioPresetsFragment.newInstance(mRadioController, this /* existListener */));
     99     }
    100 
    101     @Override
    102     public void OnPresetListExit() {
    103         setContentFragment(
    104                 MainRadioFragment.newInstance(mRadioController, this /* clickListener */));
    105     }
    106 
    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
    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
    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
    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
    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
    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      */
    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 
    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
    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 
    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
    245         protected int getActualItemCount() {
    246             return mDrawerOptions.size();
    247         }
    248 
    249         @Override
    250         public void populateViewHolder(DrawerItemViewHolder holder, int position) {
    251             holder.getTitle().setText(mDrawerOptions.get(position));
    252         }
    253 
    254         @Override
    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