blob: 455e7c58b5c908d097f49abdccba000cb89f19ad [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.radio;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.car.radio.service.RadioStation;
import com.android.car.view.PagedListView;
import java.util.List;
/**
* A fragment that is responsible for displaying a list of the user's saved radio stations.
*/
public class RadioPresetsFragment extends Fragment implements
FragmentWithFade,
PresetsAdapter.OnPresetItemClickListener,
RadioAnimationManager.OnExitCompleteListener,
RadioController.RadioStationChangeListener,
RadioStorage.PresetsChangeListener {
private static final String TAG = "PresetsFragment";
private static final int ANIM_DURATION_MS = 200;
private View mRootView;
private View mCurrentRadioCard;
private RadioStorage mRadioStorage;
private RadioController mRadioController;
private RadioAnimationManager mAnimManager;
private PresetListExitListener mPresetListExitListener;
private PagedListView mPresetsList;
private final PresetsAdapter mPresetsAdapter = new PresetsAdapter();
/**
* An interface that will be notified when the user has indicated that they would like to
* exit the preset list.
*/
public interface PresetListExitListener {
/**
* Method to be called when the preset list should be closed and the main radio display
* should be shown.
*/
void OnPresetListExit();
}
/**
* Sets the {@link RadioController} that is responsible for updating the UI of this fragment
* with the information of the current radio station.
*/
private void setRadioController(RadioController radioController) {
mRadioController = radioController;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.radio_presets_list, container, false);
Context context = getContext();
mPresetsAdapter.setOnPresetItemClickListener(this);
mCurrentRadioCard = mRootView.findViewById(R.id.current_radio_station_card);
mAnimManager = new RadioAnimationManager(getContext(), mRootView);
mAnimManager.playEnterAnimation();
// Clicking on the current radio station card will close the activity and return to the
// main radio screen.
mCurrentRadioCard.setOnClickListener(v -> {
mAnimManager.playExitAnimation(RadioPresetsFragment.this /* listener */);
});
mPresetsList = mRootView.findViewById(R.id.presets_list);
mPresetsList.setLightMode();
mPresetsList.setDefaultItemDecoration(new ItemSpacingDecoration(context));
mPresetsList.setAdapter(mPresetsAdapter);
mPresetsList.getLayoutManager().setOffsetRows(false);
mPresetsList.getRecyclerView().addOnScrollListener(new PresetListScrollListener(
context, mRootView, mCurrentRadioCard, mPresetsList));
mRadioStorage = RadioStorage.getInstance(context);
mRadioStorage.addPresetsChangeListener(this);
setPresetsOnList(mRadioStorage.getPresets());
return mRootView;
}
@Override
public void fadeOutContent() {
ObjectAnimator containerAlphaAnimator =
ObjectAnimator.ofFloat(mCurrentRadioCard, View.ALPHA, 0f);
containerAlphaAnimator.setDuration(ANIM_DURATION_MS);
containerAlphaAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {}
@Override
public void onAnimationEnd(Animator animation) {
mCurrentRadioCard.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
ObjectAnimator presetListAlphaAnimator =
ObjectAnimator.ofFloat(mPresetsList, View.ALPHA, 0f);
presetListAlphaAnimator.setDuration(ANIM_DURATION_MS);
presetListAlphaAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {}
@Override
public void onAnimationEnd(Animator animation) {
mPresetsList.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(containerAlphaAnimator, presetListAlphaAnimator);
animatorSet.start();
}
@Override
public void fadeInContent() {
// Only the current radio card needs to be faded in as that is the only part
// of the fragment that will peek over the manual tuner.
ObjectAnimator containerAlphaAnimator =
ObjectAnimator.ofFloat(mCurrentRadioCard, View.ALPHA, 1f);
containerAlphaAnimator.setDuration(ANIM_DURATION_MS);
containerAlphaAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mCurrentRadioCard.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
ObjectAnimator presetListAlphaAnimator =
ObjectAnimator.ofFloat(mPresetsList, View.ALPHA, 1f);
presetListAlphaAnimator.setDuration(ANIM_DURATION_MS);
presetListAlphaAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
mPresetsList.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(containerAlphaAnimator, presetListAlphaAnimator);
animatorSet.start();
}
@Override
public void onStart() {
super.onStart();
mRadioController.initialize(mRootView);
mRadioController.setShouldColorStatusBar(true);
mRadioController.setRadioStationChangeListener(this);
mPresetsAdapter.setActiveRadioStation(mRadioController.getCurrentRadioStation());
}
@Override
public void onDestroy() {
mRadioStorage.removePresetsChangeListener(this);
mPresetListExitListener = null;
super.onDestroy();
}
@Override
public void onExitAnimationComplete() {
if (mPresetListExitListener != null) {
mPresetListExitListener.OnPresetListExit();
}
}
@Override
public void onPresetItemClicked(RadioStation station) {
mRadioController.tuneToRadioChannel(station);
}
@Override
public void onRadioStationChanged(RadioStation station) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onRadioStationChanged(): " + station);
}
mPresetsAdapter.setActiveRadioStation(station);
}
@Override
public void onPresetsRefreshed() {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onPresetsRefreshed()");
}
setPresetsOnList(mRadioStorage.getPresets());
}
/**
* Sets the given list of presets into the PagedListView {@link #mPresetsList}.
*/
private void setPresetsOnList(List<RadioStation> presets) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "setPresetsOnList(). # of presets: " + presets.size());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
for (RadioStation radioStation : presets) {
Log.v(TAG, "\t" + radioStation);
}
}
mPresetsAdapter.setPresets(presets);
}
/**
* A {@link com.android.car.view.PagedListView.Decoration} that draws a line between
* the items.
*/
public static class ItemSpacingDecoration extends PagedListView.DividerDecoration {
private final int mLineStart;
public ItemSpacingDecoration(Context context) {
super(context);
Resources res = context.getResources();
mLineStart = res.getDimensionPixelSize(R.dimen.stream_card_keyline_3);
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
View presetCard = parent.findViewById(R.id.preset_card);
if (presetCard == null) {
return;
}
int left = mLineStart + presetCard.getLeft();
int right = presetCard.getRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
int bottom = child.getBottom();
int top = bottom - mDividerHeight;
// Draw a divider line between each item. No need to draw the line for the last
// item.
if (i != childCount - 1) {
c.drawRect(left, top, right, bottom, mPaint);
}
}
}
}
/**
* Returns a new instance of the {@link RadioPresetsFragment}.
*
* @param radioController The {@link RadioController} that is responsible for updating the UI
* of the returned fragment.
*/
public static RadioPresetsFragment newInstance(RadioController radioController,
PresetListExitListener existListener) {
RadioPresetsFragment fragment = new RadioPresetsFragment();
fragment.setRadioController(radioController);
fragment.mPresetListExitListener = existListener;
return fragment;
}
}