CarAppFocusManager
位于android.car.jar中。
官方介绍
CarAppFocusManager allows applications to set and listen for the current application focus like active navigation or active voice command. Usually only one instance of such application should run in the system, and other app setting the flag for the matching app should lead into other app to stop.
主要是为了处理车机系统中音频焦点的管理和互斥操作。
使用
public class ECAudioFocusManager {
private Car mCar;
private CarAudioManager mCarAudioManager = null;
private AudioFocusListener audioFocusListener = null;
private CarAppFocusManager mAppFocusManager;
private AppFocusOwnershipCallback mAppFocusCallback;
ECAudioFocusManager() {
mCar = Car.createCar(ECManager.getInstance().getContext(), new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
} catch (CarNotConnectedException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
});
mCar.connect();
audioFocusListener = new AudioFocusListener();
if (mCar.isConnected()) {
AppFocusChangedListener mListener = new AppFocusChangedListener();
mAppFocusCallback = new AppFocusOwnershipCallback();
try {
mAppFocusManager = (CarAppFocusManager) mCar.getCarManager(Car.APP_FOCUS_SERVICE);
mAppFocusManager.addFocusListener(mListener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
mAppFocusManager.addFocusListener(mListener, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
} catch (CarNotConnectedException e) {
Log.e(" carAppFocusManager init error : " + e.getMessage());
}
}
}
/**
* 先申请APP焦点
* 最大重试次数十次
* 重试着抢焦点也是为了增加抢到的几率
*/
private void doRequestAppFocus(int appFocusType) {
Log.d("doRequestAppFocus type : " + appFocusType);
if (isOwningFocus(appFocusType)) {
doRequestFocus(player);
return;
}
int status = CarAppFocusManager.APP_FOCUS_REQUEST_FAILED;
int retryCnt = 10;
while (status == CarAppFocusManager.APP_FOCUS_REQUEST_FAILED && retryCnt-- > 0) {
try {
status = mAppFocusManager.requestAppFocus(appFocusType, mAppFocusCallback);
Log.d("doRequestAppFocus --- status : " + status);
if (status != CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED && retryCnt > 1) {
Thread.sleep(500);
}
} catch (CarNotConnectedException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 再申请声音焦点
* 最大重试次数十次
* 重试着抢焦点也是为了增加抢到的几率
*/
private void doRequestFocus(AudioPlayer player) {
Log.d("doRequestFocus name : " + player.getName());
if (player.getVrPlayFlag()) {
return;
}
int status = AudioManager.AUDIOFOCUS_REQUEST_FAILED;
int retryCnt = 10;
while (status != AudioManager.AUDIOFOCUS_REQUEST_GRANTED && retryCnt-- > 0) {
try {
status = mCarAudioManager.requestAudioFocus(audioFocusListener, player.getAudioAttributes(), player.getDurationHint(), 0);
Log.d("requestAudioFocus --- name : " + player.getName() + ", status : " + status);
if (status != AudioManager.AUDIOFOCUS_REQUEST_GRANTED && retryCnt > 1) {
Thread.sleep(500L);
}
} catch (CarNotConnectedException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
boolean result = status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
player.setVrPlayFlag(result);
}
/**
* 释放APP焦点
*/
private void doAbandonAppFocus(int appFocusType) {
Log.d("doAbandonAppFocus type : " + appFocusType);
try {
mAppFocusManager.abandonAppFocus(mAppFocusCallback, appFocusType);
} catch (SecurityException e) {
Log.e("doAbandonAppFocus error : " + e.getMessage());
}
}
/**
* 释放声音焦点
*/
private void doAbandonFocus(AudioPlayer player) {
Log.d("doAbandonFocus name : " + player.getName());
player.setVrPlayFlag(false);
mCarAudioManager.abandonAudioFocus(audioFocusListener, player.getAudioAttributes());
}
/**
* 声音焦点的监听回调
* 在这里可以根据自己所申请的焦点类型的变化进行监听
*/
class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener {
@Override
public void onAudioFocusChange(int focusChange) {
Log.d("onAudioFocusChange focusChange = " + focusChange);
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
break;
case AudioManager.AUDIOFOCUS_LOSS:
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
break;
}
}
}
/**
* APP焦点的监听回调
* 在这里可以根据自己所申请的焦点类型的变化进行监听
*/
private class AppFocusChangedListener
implements CarAppFocusManager.OnAppFocusChangedListener {
@Override
public void onAppFocusChanged(int appType, boolean active) {
Log.d(" onAppFocusChanged: " + appType + " appActive: " + active);
switch (appType) {
case CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION:
if (active) {
doRequestFocus(mTTSPlayer);
} else {
doAbandonFocus(mTTSPlayer);
}
break;
case CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND:
if (active) {
doRequestFocus(mVRPlayer);
} else {
doAbandonFocus(mVRPlayer);
}
break;
}
}
}
/**
* APP焦点获得或者丢失的监听回调
* onAppFocusOwnershipLost 该类型的APP焦点丢失,一般需要当前应用停止该类型音频的播放
* onAppFocusOwnershipGranted 该类型的APP焦点的授予获得,一般需要当前应用申请该类型APP焦点成功后回调
*/
private class AppFocusOwnershipCallback
implements CarAppFocusManager.OnAppFocusOwnershipCallback {
@Override
public void onAppFocusOwnershipLost(int appType) {
Log.d(" onAppFocusOwnershipLost: " + appType);
mAppFocusManager.abandonAppFocus(mAppFocusCallback, appType);
if (appType == CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION) {
doAbandonFocus(mTTSPlayer);
} else if (appType == CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND) {
doAbandonFocus(mVRPlayer);
}
}
@Override
public void onAppFocusOwnershipGranted(int appType) {
Log.d(" onAppFocusOwnershipGranted: " + appType);
if (appType == CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION) {
doRequestFocus(mTTSPlayer);
} else if (appType == CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND) {
doRequestFocus(mVRPlayer);
}
}
}
/**
* 判断该类型的APP焦点是否拥有
* 一般借用此方法可以避免重复申请APP焦点
*/
private boolean isOwningFocus(int appType) {
boolean owningFocus = false;
try {
owningFocus = mAppFocusManager.isOwningFocus(mAppFocusCallback, appType);
} catch (CarNotConnectedException e) {
Log.e("isOwningFocus error : " + e.getMessage());
}
return owningFocus;
}
}