/* * 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.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager.ProgramInfo; import android.hardware.radio.RadioMetadata; import android.media.session.PlaybackState; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.LiveData; import com.android.car.broadcastradio.support.Program; import com.android.car.broadcastradio.support.platform.ProgramInfoExt; import com.android.car.radio.bands.ProgramType; import com.android.car.radio.bands.RegionConfig; import com.android.car.radio.service.RadioAppService; import com.android.car.radio.service.RadioAppServiceWrapper; import com.android.car.radio.service.RadioAppServiceWrapper.ConnectionState; import com.android.car.radio.storage.RadioStorage; import com.android.car.radio.util.Log; import java.util.List; import java.util.Objects; /** * The main controller of the radio app. */ public class RadioController { private static final String TAG = "BcRadioApp.controller"; private final Object mLock = new Object(); private final RadioActivity mActivity; private final RadioAppServiceWrapper mAppService = new RadioAppServiceWrapper(); private final DisplayController mDisplayController; private final RadioStorage mRadioStorage; @Nullable private ProgramInfo mCurrentProgram; public RadioController(@NonNull RadioActivity activity) { mActivity = Objects.requireNonNull(activity); mDisplayController = new DisplayController(activity, this); mRadioStorage = RadioStorage.getInstance(activity); mRadioStorage.getFavorites().observe(activity, this::onFavoritesChanged); mAppService.getCurrentProgram().observe(activity, this::onCurrentProgramChanged); mAppService.getConnectionState().observe(activity, this::onConnectionStateChanged); mDisplayController.setBackwardSeekButtonListener(this::onBackwardSeekClick); mDisplayController.setForwardSeekButtonListener(this::onForwardSeekClick); mDisplayController.setPlayButtonCallback(this::onSwitchToPlayState); mDisplayController.setFavoriteToggleListener(this::onFavoriteToggled); } private void onConnectionStateChanged(@ConnectionState int state) { mDisplayController.setState(state); if (state == RadioAppServiceWrapper.STATE_CONNECTED) { mActivity.setProgramListSupported(mAppService.isProgramListSupported()); mActivity.setSupportedProgramTypes(getRegionConfig().getSupportedProgramTypes()); } } /** * Starts the controller and establishes connection with {@link RadioAppService}. */ public void start() { mAppService.bind(mActivity); } /** * Closes {@link RadioAppService} connection and cleans up the resources. */ public void shutdown() { mAppService.unbind(); } /** * See {@link RadioAppServiceWrapper#getPlaybackState}. */ @NonNull public LiveData getPlaybackState() { return mAppService.getPlaybackState(); } /** * See {@link RadioAppServiceWrapper#getCurrentProgram}. */ @NonNull public LiveData getCurrentProgram() { return mAppService.getCurrentProgram(); } /** * See {@link RadioAppServiceWrapper#getProgramList}. */ @NonNull public LiveData> getProgramList() { return mAppService.getProgramList(); } /** * Tunes the radio to the given channel. */ public void tune(ProgramSelector sel) { mAppService.tune(sel); } /** * Steps the radio tuner in the given direction, see {@link RadioAppServiceWrapper#step}. */ public void step(boolean forward) { mAppService.step(forward); } /** * Switch radio band. Currently, this only supports FM and AM bands. * * @param pt {@link ProgramType} to switch to. */ public void switchBand(@NonNull ProgramType pt) { mAppService.switchBand(pt); } @NonNull public RegionConfig getRegionConfig() { return mAppService.getRegionConfig(); } /** * Sets the service's {@link SkipMode}. */ public void setSkipMode(@NonNull SkipMode mode) { mAppService.setSkipMode(mode); } private void onFavoritesChanged(List favorites) { synchronized (mLock) { if (mCurrentProgram == null) return; boolean isFav = RadioStorage.isFavorite(favorites, mCurrentProgram.getSelector()); mDisplayController.setCurrentIsFavorite(isFav); } } private void onCurrentProgramChanged(@NonNull ProgramInfo info) { synchronized (mLock) { mCurrentProgram = Objects.requireNonNull(info); ProgramSelector sel = info.getSelector(); RadioMetadata meta = ProgramInfoExt.getMetadata(info); mDisplayController.setChannel(sel); mDisplayController.setStationName( ProgramInfoExt.getProgramName(info, ProgramInfoExt.NAME_NO_CHANNEL_FALLBACK)); if (meta.containsKey(RadioMetadata.METADATA_KEY_TITLE) || meta.containsKey(RadioMetadata.METADATA_KEY_ARTIST)) { mDisplayController.setDetails( meta.getString(RadioMetadata.METADATA_KEY_TITLE), meta.getString(RadioMetadata.METADATA_KEY_ARTIST)); } else { mDisplayController.setDetails(meta.getString(RadioMetadata.METADATA_KEY_RDS_RT)); } mDisplayController.setCurrentIsFavorite(mRadioStorage.isFavorite(sel)); } } private void onBackwardSeekClick(View v) { mDisplayController.startSeekAnimation(false); mAppService.skip(false); } private void onForwardSeekClick(View v) { mDisplayController.startSeekAnimation(true); mAppService.skip(true); } private void onSwitchToPlayState(@PlaybackState.State int newPlayState) { switch (newPlayState) { case PlaybackState.STATE_PLAYING: mAppService.setMuted(false); break; case PlaybackState.STATE_PAUSED: case PlaybackState.STATE_STOPPED: mAppService.setMuted(true); break; default: Log.e(TAG, "Invalid request to switch to play state " + newPlayState); } } private void onFavoriteToggled(boolean addFavorite) { synchronized (mLock) { if (mCurrentProgram == null) return; if (addFavorite) { mRadioStorage.addFavorite(Program.fromProgramInfo(mCurrentProgram)); } else { mRadioStorage.removeFavorite(mCurrentProgram.getSelector()); } } } }