144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer/* 244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Copyright (c) 2016, The Android Open Source Project 344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Licensed under the Apache License, Version 2.0 (the "License"); 544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * you may not use this file except in compliance with the License. 644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * You may obtain a copy of the License at 744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * http://www.apache.org/licenses/LICENSE-2.0 944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 1044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Unless required by applicable law or agreed to in writing, software 1144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * distributed under the License is distributed on an "AS IS" BASIS, 1244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * See the License for the specific language governing permissions and 1444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * limitations under the License. 1544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 1644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerpackage com.android.car.radio.demo; 1744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 1844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.car.hardware.radio.CarRadioManager; 1944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.content.Context; 2044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.content.pm.PackageManager; 2144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.hardware.radio.RadioManager; 2244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.media.AudioAttributes; 2344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.media.AudioManager; 2444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.os.RemoteException; 2544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.os.SystemProperties; 2644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.support.car.Car; 2744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.support.car.CarNotConnectedException; 289dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrupimport android.support.car.CarConnectionCallback; 2944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.support.car.media.CarAudioManager; 3044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport android.util.Log; 3144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport com.android.car.radio.service.IRadioCallback; 3244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport com.android.car.radio.service.IRadioManager; 3344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport com.android.car.radio.service.RadioStation; 3444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 3544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport java.util.ArrayList; 3644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerimport java.util.List; 3744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 3844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer/** 3944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * A demo {@link IRadiomanager} that has a fixed set of AM and FM stations. 4044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 4144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyerpublic class RadioDemo implements AudioManager.OnAudioFocusChangeListener { 4244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private static final String TAG = "RadioDemo"; 4344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 4444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 4544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * The property name to enable demo mode. 4644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 4744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public static final String DEMO_MODE_PROPERTY = "com.android.car.radio.demo"; 4844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 4944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 5044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * The property name to enable the radio in demo mode with dual tuners. 5144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 5244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public static final String DUAL_DEMO_MODE_PROPERTY = "com.android.car.radio.demo.dual"; 5344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 5444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private static RadioDemo sInstance; 5544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private List<IRadioCallback> mCallbacks = new ArrayList<>(); 5644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 5744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private List<RadioStation> mCurrentStations = new ArrayList<>(); 5844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private int mCurrentRadioBand = RadioManager.BAND_FM; 5944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 6044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private Car mCarApi; 6144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private CarAudioManager mCarAudioManager; 6244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private AudioAttributes mRadioAudioAttributes; 6344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 6444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private boolean mHasAudioFocus; 6544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 6644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private int mCurrentIndex; 6744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private boolean mIsMuted; 6844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 6944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private RadioDemo(Context context) { 7044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { 719dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup mCarApi = Car.createCar(context, mCarConnectionCallback); 7244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCarApi.connect(); 7344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 7444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 7544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 7644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 7744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Returns a mock {@link IRadioManager} to use for demo purposes. The returned class will have 7844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * a fixed list of AM and FM changegs and support all the IRadioManager's functionality. 7944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 8044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public IRadioManager.Stub createDemoManager() { 8144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return new IRadioManager.Stub() { 8244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 8344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void tune(RadioStation station) throws RemoteException { 8444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (station == null || !requestAudioFocus()) { 8544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 8644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 8744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 8844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (station.getRadioBand() != mCurrentRadioBand) { 8944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer switchRadioBand(station.getRadioBand()); 9044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 9144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 9244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer boolean found = false; 9344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 9444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (int i = 0, size = mCurrentStations.size(); i < size; i++) { 9544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer RadioStation storedStation = mCurrentStations.get(i); 9644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 9744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (storedStation.equals(station)) { 9844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer found = true; 9944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentIndex = i; 10044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 10144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 10244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 10344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 10444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // If not found, then insert it into the list, sorted by the channel. 10544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (!found) { 10644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer int indexToInsert = 0; 10744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 10844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (int i = 0, size = mCurrentStations.size(); i < size; i++) { 10944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer RadioStation storedStation = mCurrentStations.get(i); 11044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 11144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (station.getChannelNumber() >= storedStation.getChannelNumber()) { 11244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer indexToInsert = i + 1; 11344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 11444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 11544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 11644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 11744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer RadioStation stationToInsert = new RadioStation(station.getChannelNumber(), 11844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 0 /* subChannel */, station.getRadioBand(), null /* rds */); 11944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentStations.add(indexToInsert, stationToInsert); 12044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 12144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentIndex = indexToInsert; 12244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 12344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 12444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer notifyCallbacks(station); 12544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 12644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 12744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 12844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void seekForward() throws RemoteException { 12944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (!requestAudioFocus()) { 13044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 13144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 13244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 13344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (++mCurrentIndex >= mCurrentStations.size()) { 13444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentIndex = 0; 13544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 13644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 13744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer notifyCallbacks(mCurrentStations.get(mCurrentIndex)); 13844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 13944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 14044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 14144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void seekBackward() throws RemoteException { 14244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (!requestAudioFocus()) { 14344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 14444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 14544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 14644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (--mCurrentIndex < 0){ 14744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentIndex = mCurrentStations.size() - 1; 14844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 14944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 15044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer notifyCallbacks(mCurrentStations.get(mCurrentIndex)); 15144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 15244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 15344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 15444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean mute() throws RemoteException { 15544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mIsMuted = true; 15644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer notifyCallbacksMuteChanged(mIsMuted); 15744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mIsMuted; 15844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 15944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 16044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 16144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean unMute() throws RemoteException { 16244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer requestAudioFocus(); 16344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 16444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mHasAudioFocus) { 16544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mIsMuted = false; 16644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 16744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 16844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer notifyCallbacksMuteChanged(mIsMuted); 16944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return !mIsMuted; 17044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 17144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 17244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 17344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean isMuted() throws RemoteException { 17444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mIsMuted; 17544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 17644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 17744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 17844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public int openRadioBand(int radioBand) throws RemoteException { 17944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (!requestAudioFocus()) { 18044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return RadioManager.STATUS_ERROR; 18144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 18244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 18344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer switchRadioBand(radioBand); 18444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer notifyCallbacks(radioBand); 18544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return RadioManager.STATUS_OK; 18644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 18744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 18844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 18944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void addRadioTunerCallback(IRadioCallback callback) throws RemoteException { 19044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCallbacks.add(callback); 19144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 19244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 19344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 19444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void removeRadioTunerCallback(IRadioCallback callback) throws RemoteException { 19544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCallbacks.remove(callback); 19644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 19744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 19844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 19944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public RadioStation getCurrentRadioStation() throws RemoteException { 20044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mCurrentStations.get(mCurrentIndex); 20144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 20244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 20344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 20444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean isInitialized() throws RemoteException { 20544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return true; 20644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 20744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 20844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 20944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean hasFocus() { 21044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return mHasAudioFocus; 21144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 21244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 21344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 21444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public boolean hasDualTuners() throws RemoteException { 21544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return SystemProperties.getBoolean(RadioDemo.DUAL_DEMO_MODE_PROPERTY, false); 21644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 21744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer }; 21844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 21944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 22044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 22144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public void onAudioFocusChange(int focusChange) { 22244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 22344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "focus change: " + focusChange); 22444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 22544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 22644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer switch (focusChange) { 22744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_GAIN: 22844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHasAudioFocus = true; 22944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 23044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 23144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 23244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 23344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHasAudioFocus = false; 23444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 23544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 23644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case AudioManager.AUDIOFOCUS_LOSS: 23744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer abandonAudioFocus(); 23844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 23944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 24044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer default: 24144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Do nothing for all other cases. 24244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 24344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 24444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 24544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 24644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Requests audio focus for the current application. 24744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 24844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @return {@code true} if the request succeeded. 24944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 25044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private boolean requestAudioFocus() { 25144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mCarAudioManager == null) { 25244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return false; 25344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 25444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 25544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer int status = AudioManager.AUDIOFOCUS_REQUEST_FAILED; 25644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 25744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer status = mCarAudioManager.requestAudioFocus(this, mRadioAudioAttributes, 25844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer AudioManager.AUDIOFOCUS_GAIN, 0); 25944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (CarNotConnectedException e) { 26044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "requestAudioFocus() failed", e); 26144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 26244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 26344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 26444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "requestAudioFocus status: " + status); 26544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 26644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 26744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 26844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mHasAudioFocus = true; 26944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 27044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 27144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 27244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 27344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 27444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 27544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Abandons audio focus for the current application. 27644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * 27744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * @return {@code true} if the request succeeded. 27844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 27944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void abandonAudioFocus() { 28044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 28144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "abandonAudioFocus()"); 28244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 28344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 28444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mCarAudioManager == null) { 28544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return; 28644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 28744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 28844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCarAudioManager.abandonAudioFocus(this, mRadioAudioAttributes); 28944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 29044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 29144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 2929dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup * {@link CarConnectionCallback} that retrieves the {@link CarRadioManager}. 29344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 2949dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup private final CarConnectionCallback mCarConnectionCallback = 2959dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup new CarConnectionCallback() { 29644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 2979dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup public void onConnected(Car car) { 29844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 29944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "Car service connected."); 30044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 30144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 30244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // The CarAudioManager only needs to be retrieved once. 30344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (mCarAudioManager == null) { 30444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCarAudioManager = (CarAudioManager) mCarApi.getCarManager( 30544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer android.car.Car.AUDIO_SERVICE); 30644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 30744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mRadioAudioAttributes = mCarAudioManager.getAudioAttributesForCarUsage( 30844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer CarAudioManager.CAR_AUDIO_USAGE_RADIO); 30944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 31044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (CarNotConnectedException e) { 31144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer //TODO finish 31244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.e(TAG, "Car not connected"); 31344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 31444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 31544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 31644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer @Override 3179dc138b7446e68228e233839d75bd2cfb5736807Jason Tholstrup public void onDisconnected(Car car) { 31844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 31944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer Log.d(TAG, "Car service disconnected."); 32044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 32144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 32244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer }; 32344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 32444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 32544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Switches to the corresponding radio band. This will update the list of current stations 32644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * as well as notify any callbacks. 32744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 32844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void switchRadioBand(int radioBand) { 32944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer switch (radioBand) { 33044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case RadioManager.BAND_AM: 33144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentStations = DemoRadioStations.getAmStations(); 33244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 33344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer case RadioManager.BAND_FM: 33444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentStations = DemoRadioStations.getFmStations(); 33544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer break; 33644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer default: 33744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentStations = new ArrayList<>(); 33844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 33944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 34044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentRadioBand = radioBand; 34144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer mCurrentIndex = 0; 34244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 34344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer notifyCallbacks(mCurrentRadioBand); 34444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer notifyCallbacks(mCurrentStations.get(mCurrentIndex)); 34544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 34644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 34744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 34844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Notifies any {@link IRadioCallback} that the mute state of the radio has changed. 34944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 35044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void notifyCallbacksMuteChanged(boolean isMuted) { 35144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mCallbacks) { 35244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 35344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioMuteChanged(isMuted); 35444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 35544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Ignore. 35644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 35744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 35844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 35944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 36044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 36144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Notifies any {@link IRadioCallback}s that the radio band has changed. 36244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 36344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void notifyCallbacks(int radioBand) { 36444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mCallbacks) { 36544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 36644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioBandChanged(radioBand); 36744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 36844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Ignore. 36944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 37044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 37144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 37244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 37344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer /** 37444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * Notifies any {@link IRadioCallback}s that the radio station has been changed to the given 37544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer * {@link RadioStation}. 37644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer */ 37744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer private void notifyCallbacks(RadioStation station) { 37844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer for (IRadioCallback callback : mCallbacks) { 37944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer try { 38044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer callback.onRadioStationChanged(station); 38144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } catch (RemoteException e) { 38244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer // Ignore. 38344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 38444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 38544f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 38644f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 38744f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer public static RadioDemo getInstance(Context context) { 38844f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer if (sInstance == null) { 38944f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer sInstance = new RadioDemo(context); 39044f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 39144f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer 39244f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer return sInstance; 39344f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer } 39444f17dab6698b8c5d87672f5df71c471bd4b91a3Rakesh Iyer} 395