• 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.hardware.radio.ProgramSelector;
20 import android.hardware.radio.RadioManager.ProgramInfo;
21 import android.hardware.radio.RadioMetadata;
22 import android.media.session.PlaybackState;
23 import android.view.View;
24 
25 import androidx.annotation.NonNull;
26 import androidx.annotation.Nullable;
27 import androidx.lifecycle.LiveData;
28 
29 import com.android.car.broadcastradio.support.Program;
30 import com.android.car.broadcastradio.support.platform.ProgramInfoExt;
31 import com.android.car.broadcastradio.support.platform.ProgramSelectorExt;
32 import com.android.car.radio.bands.ProgramType;
33 import com.android.car.radio.bands.RegionConfig;
34 import com.android.car.radio.media.TunerSession;
35 import com.android.car.radio.service.RadioAppService;
36 import com.android.car.radio.service.RadioAppServiceWrapper;
37 import com.android.car.radio.service.RadioAppServiceWrapper.ConnectionState;
38 import com.android.car.radio.storage.RadioStorage;
39 import com.android.car.radio.util.Log;
40 
41 import java.util.List;
42 import java.util.Objects;
43 
44 /**
45  * The main controller of the radio app.
46  */
47 public class RadioController {
48     private static final String TAG = "BcRadioApp.controller";
49 
50     private final Object mLock = new Object();
51     private final RadioActivity mActivity;
52 
53     private final RadioAppServiceWrapper mAppService = new RadioAppServiceWrapper();
54     private final DisplayController mDisplayController;
55     private final RadioStorage mRadioStorage;
56 
57     @Nullable private ProgramInfo mCurrentProgram;
58 
RadioController(@onNull RadioActivity activity)59     public RadioController(@NonNull RadioActivity activity) {
60         mActivity = Objects.requireNonNull(activity);
61 
62         mDisplayController = new DisplayController(activity, this);
63 
64         mRadioStorage = RadioStorage.getInstance(activity);
65         mRadioStorage.getFavorites().observe(activity, this::onFavoritesChanged);
66 
67         mAppService.getCurrentProgram().observe(activity, this::onCurrentProgramChanged);
68         mAppService.getConnectionState().observe(activity, this::onConnectionStateChanged);
69 
70         mDisplayController.setBackwardSeekButtonListener(this::onBackwardSeekClick);
71         mDisplayController.setForwardSeekButtonListener(this::onForwardSeekClick);
72         mDisplayController.setPlayButtonCallback(this::onSwitchToPlayState);
73         mDisplayController.setFavoriteToggleListener(this::onFavoriteToggled);
74     }
75 
onConnectionStateChanged(@onnectionState int state)76     private void onConnectionStateChanged(@ConnectionState int state) {
77         mDisplayController.setState(state);
78         if (state == RadioAppServiceWrapper.STATE_CONNECTED) {
79             mActivity.setProgramListSupported(mAppService.isProgramListSupported());
80             mActivity.setSupportedProgramTypes(getRegionConfig().getSupportedProgramTypes());
81         }
82     }
83 
84     /**
85      * Starts the controller and establishes connection with {@link RadioAppService}.
86      */
start()87     public void start() {
88         mAppService.bind(mActivity);
89     }
90 
91     /**
92      * Closes {@link RadioAppService} connection and cleans up the resources.
93      */
shutdown()94     public void shutdown() {
95         mAppService.unbind();
96     }
97 
98     /**
99      * See {@link RadioAppServiceWrapper#getPlaybackState}.
100      */
101     @NonNull
getPlaybackState()102     public LiveData<Integer> getPlaybackState() {
103         return mAppService.getPlaybackState();
104     }
105 
106     /**
107      * See {@link RadioAppServiceWrapper#getCurrentProgram}.
108      */
109     @NonNull
getCurrentProgram()110     public LiveData<ProgramInfo> getCurrentProgram() {
111         return mAppService.getCurrentProgram();
112     }
113 
114     /**
115      * See {@link RadioAppServiceWrapper#getProgramList}.
116      */
117     @NonNull
getProgramList()118     public LiveData<List<ProgramInfo>> getProgramList() {
119         return mAppService.getProgramList();
120     }
121 
122     /**
123      * Tunes the radio to the given channel.
124      */
tune(ProgramSelector sel)125     public void tune(ProgramSelector sel) {
126         mAppService.tune(sel);
127     }
128 
129     /**
130      * Steps the radio tuner in the given direction, see {@link RadioAppServiceWrapper#step}.
131      */
step(boolean forward)132     public void step(boolean forward) {
133         mAppService.step(forward);
134     }
135 
136     /**
137      * Switch radio band. Currently, this only supports FM and AM bands.
138      *
139      * @param pt {@link ProgramType} to switch to.
140      */
switchBand(@onNull ProgramType pt)141     public void switchBand(@NonNull ProgramType pt) {
142         mAppService.switchBand(pt);
143     }
144 
145     @NonNull
getRegionConfig()146     public RegionConfig getRegionConfig() {
147         return mAppService.getRegionConfig();
148     }
149 
150     /**
151      * Sets the service's {@link SkipMode}.
152      */
setSkipMode(@onNull SkipMode mode)153     public void setSkipMode(@NonNull SkipMode mode) {
154         mAppService.setSkipMode(mode);
155     }
156 
onFavoritesChanged(List<Program> favorites)157     private void onFavoritesChanged(List<Program> favorites) {
158         synchronized (mLock) {
159             if (mCurrentProgram == null) return;
160             boolean isFav = RadioStorage.isFavorite(favorites, mCurrentProgram.getSelector());
161             mDisplayController.setCurrentIsFavorite(isFav);
162         }
163     }
164 
onCurrentProgramChanged(@onNull ProgramInfo info)165     private void onCurrentProgramChanged(@NonNull ProgramInfo info) {
166         synchronized (mLock) {
167             mCurrentProgram = Objects.requireNonNull(info);
168             ProgramSelector sel = info.getSelector();
169             RadioMetadata meta = ProgramInfoExt.getMetadata(info);
170 
171             mDisplayController.setChannel(sel);
172 
173             mDisplayController.setStationName(
174                     ProgramSelectorExt.getDisplayName(sel, info.getChannel()));
175 
176             if (meta.containsKey(RadioMetadata.METADATA_KEY_TITLE)
177                     || meta.containsKey(RadioMetadata.METADATA_KEY_ARTIST)) {
178                 mDisplayController.setDetails(
179                         meta.getString(RadioMetadata.METADATA_KEY_TITLE),
180                         meta.getString(RadioMetadata.METADATA_KEY_ARTIST));
181             } else {
182                 mDisplayController.setDetails(ProgramInfoExt.getProgramName(info, /* flags= */ 0,
183                         TunerSession.PROGRAM_NAME_ORDER));
184             }
185 
186             mDisplayController.setCurrentIsFavorite(mRadioStorage.isFavorite(sel));
187         }
188     }
189 
onBackwardSeekClick(View v)190     private void onBackwardSeekClick(View v) {
191         mDisplayController.startSeekAnimation(false);
192         mAppService.skip(false);
193     }
194 
onForwardSeekClick(View v)195     private void onForwardSeekClick(View v) {
196         mDisplayController.startSeekAnimation(true);
197         mAppService.skip(true);
198     }
199 
onSwitchToPlayState(@laybackState.State int newPlayState)200     private void onSwitchToPlayState(@PlaybackState.State int newPlayState) {
201         switch (newPlayState) {
202             case PlaybackState.STATE_PLAYING:
203                 mAppService.setMuted(false);
204                 break;
205             case PlaybackState.STATE_PAUSED:
206             case PlaybackState.STATE_STOPPED:
207                 mAppService.setMuted(true);
208                 break;
209             default:
210                 Log.e(TAG, "Invalid request to switch to play state " + newPlayState);
211         }
212     }
213 
onFavoriteToggled(boolean addFavorite)214     private void onFavoriteToggled(boolean addFavorite) {
215         synchronized (mLock) {
216             if (mCurrentProgram == null) return;
217 
218             if (addFavorite) {
219                 mRadioStorage.addFavorite(Program.fromProgramInfo(mCurrentProgram));
220             } else {
221                 mRadioStorage.removeFavorite(mCurrentProgram.getSelector());
222             }
223         }
224     }
225 }
226