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.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          */
     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      */
     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      */
     84     public void setPresetListExitListener(PresetListExitListener listener) {
     85         mPresetListExitListener = listener;
     86     }
     87 
     88     @Override
     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
    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
    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
    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
    225     public void onDestroy() {
    226         mRadioStorage.removePresetsChangeListener(this);
    227         mPresetListExitListener = null;
    228         super.onDestroy();
    229     }
    230 
    231     @Override
    232     public void onExitAnimationComplete() {
    233         if (mPresetListExitListener != null) {
    234             mPresetListExitListener.OnPresetListExit();
    235         }
    236     }
    237 
    238     @Override
    239     public void onPresetItemClicked(RadioStation station) {
    240         mRadioController.tuneToRadioChannel(station);
    241     }
    242 
    243     @Override
    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
    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      */
    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 
    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
    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      */
    323     public static RadioPresetsFragment newInstance(RadioController radioController) {
    324         RadioPresetsFragment fragment = new RadioPresetsFragment();
    325         fragment.setRadioController(radioController);
    326 
    327         return fragment;
    328     }
    329 }
    330