parent
9386769409
commit
bd2b9b414e
|
@ -15,42 +15,31 @@ import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.media.AudioDeviceInfo;
|
import android.media.AudioDeviceInfo;
|
||||||
import android.media.AudioFormat;
|
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.media.AudioRecord;
|
|
||||||
import android.media.MediaRecorder;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import org.webrtc.ThreadUtils;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.utils.AppRTCUtils;
|
import eu.siacs.conversations.utils.AppRTCUtils;
|
||||||
import eu.siacs.conversations.xmpp.jingle.Media;
|
|
||||||
|
|
||||||
/**
|
import org.webrtc.ThreadUtils;
|
||||||
* AppRTCAudioManager manages all audio related parts of the AppRTC demo.
|
|
||||||
*/
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** AppRTCAudioManager manages all audio related parts of the AppRTC demo. */
|
||||||
public class AppRTCAudioManager {
|
public class AppRTCAudioManager {
|
||||||
|
|
||||||
private static CountDownLatch microphoneLatch;
|
|
||||||
|
|
||||||
private final Context apprtcContext;
|
private final Context apprtcContext;
|
||||||
// Contains speakerphone setting: auto, true or false
|
// Contains speakerphone setting: auto, true or false
|
||||||
// Handles all tasks related to Bluetooth headset devices.
|
// Handles all tasks related to Bluetooth headset devices.
|
||||||
private final AppRTCBluetoothManager bluetoothManager;
|
private final AppRTCBluetoothManager bluetoothManager;
|
||||||
@Nullable
|
@Nullable private final AudioManager audioManager;
|
||||||
private final AudioManager audioManager;
|
@Nullable private AudioManagerEvents audioManagerEvents;
|
||||||
@Nullable
|
|
||||||
private AudioManagerEvents audioManagerEvents;
|
|
||||||
private AudioManagerState amState;
|
private AudioManagerState amState;
|
||||||
private boolean savedIsSpeakerPhoneOn;
|
private boolean savedIsSpeakerPhoneOn;
|
||||||
private boolean savedIsMicrophoneMute;
|
private boolean savedIsMicrophoneMute;
|
||||||
|
@ -76,8 +65,7 @@ public class AppRTCAudioManager {
|
||||||
// Broadcast receiver for wired headset intent broadcasts.
|
// Broadcast receiver for wired headset intent broadcasts.
|
||||||
private final BroadcastReceiver wiredHeadsetReceiver;
|
private final BroadcastReceiver wiredHeadsetReceiver;
|
||||||
// Callback method for changes in audio focus.
|
// Callback method for changes in audio focus.
|
||||||
@Nullable
|
@Nullable private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener;
|
||||||
private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener;
|
|
||||||
|
|
||||||
public AppRTCAudioManager(final Context context) {
|
public AppRTCAudioManager(final Context context) {
|
||||||
apprtcContext = context;
|
apprtcContext = context;
|
||||||
|
@ -95,7 +83,6 @@ public class AppRTCAudioManager {
|
||||||
AppRTCUtils.logDeviceInfo(Config.LOGTAG);
|
AppRTCUtils.logDeviceInfo(Config.LOGTAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public void start(final AudioManagerEvents audioManagerEvents) {
|
public void start(final AudioManagerEvents audioManagerEvents) {
|
||||||
Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".start()");
|
Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".start()");
|
||||||
|
@ -104,7 +91,6 @@ public class AppRTCAudioManager {
|
||||||
Log.e(Config.LOGTAG, "AudioManager is already active");
|
Log.e(Config.LOGTAG, "AudioManager is already active");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
awaitMicrophoneLatch();
|
|
||||||
this.audioManagerEvents = audioManagerEvents;
|
this.audioManagerEvents = audioManagerEvents;
|
||||||
amState = AudioManagerState.RUNNING;
|
amState = AudioManagerState.RUNNING;
|
||||||
// Store current audio state so we can restore it when stop() is called.
|
// Store current audio state so we can restore it when stop() is called.
|
||||||
|
@ -112,48 +98,45 @@ public class AppRTCAudioManager {
|
||||||
savedIsMicrophoneMute = audioManager.isMicrophoneMute();
|
savedIsMicrophoneMute = audioManager.isMicrophoneMute();
|
||||||
hasWiredHeadset = hasWiredHeadset();
|
hasWiredHeadset = hasWiredHeadset();
|
||||||
// Create an AudioManager.OnAudioFocusChangeListener instance.
|
// Create an AudioManager.OnAudioFocusChangeListener instance.
|
||||||
audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
|
audioFocusChangeListener =
|
||||||
// Called on the listener to notify if the audio focus for this listener has been changed.
|
new AudioManager.OnAudioFocusChangeListener() {
|
||||||
// The |focusChange| value indicates whether the focus was gained, whether the focus was lost,
|
// Called on the listener to notify if the audio focus for this listener has
|
||||||
// and whether that loss is transient, or whether the new focus holder will hold it for an
|
// been changed.
|
||||||
// unknown amount of time.
|
// The |focusChange| value indicates whether the focus was gained, whether the
|
||||||
// TODO(henrika): possibly extend support of handling audio-focus changes. Only contains
|
// focus was lost,
|
||||||
// logging for now.
|
// and whether that loss is transient, or whether the new focus holder will hold
|
||||||
@Override
|
// it for an
|
||||||
public void onAudioFocusChange(int focusChange) {
|
// unknown amount of time.
|
||||||
final String typeOfChange;
|
// TODO(henrika): possibly extend support of handling audio-focus changes. Only
|
||||||
switch (focusChange) {
|
// contains
|
||||||
case AudioManager.AUDIOFOCUS_GAIN:
|
// logging for now.
|
||||||
typeOfChange = "AUDIOFOCUS_GAIN";
|
@Override
|
||||||
break;
|
public void onAudioFocusChange(final int focusChange) {
|
||||||
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
|
final String typeOfChange =
|
||||||
typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT";
|
switch (focusChange) {
|
||||||
break;
|
case AudioManager.AUDIOFOCUS_GAIN -> "AUDIOFOCUS_GAIN";
|
||||||
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
|
case AudioManager
|
||||||
typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE";
|
.AUDIOFOCUS_GAIN_TRANSIENT -> "AUDIOFOCUS_GAIN_TRANSIENT";
|
||||||
break;
|
case AudioManager
|
||||||
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
|
.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE -> "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE";
|
||||||
typeOfChange = "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK";
|
case AudioManager
|
||||||
break;
|
.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK -> "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK";
|
||||||
case AudioManager.AUDIOFOCUS_LOSS:
|
case AudioManager.AUDIOFOCUS_LOSS -> "AUDIOFOCUS_LOSS";
|
||||||
typeOfChange = "AUDIOFOCUS_LOSS";
|
case AudioManager
|
||||||
break;
|
.AUDIOFOCUS_LOSS_TRANSIENT -> "AUDIOFOCUS_LOSS_TRANSIENT";
|
||||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
|
case AudioManager
|
||||||
typeOfChange = "AUDIOFOCUS_LOSS_TRANSIENT";
|
.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK";
|
||||||
break;
|
default -> "AUDIOFOCUS_INVALID";
|
||||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
|
};
|
||||||
typeOfChange = "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK";
|
Log.d(Config.LOGTAG, "onAudioFocusChange: " + typeOfChange);
|
||||||
break;
|
}
|
||||||
default:
|
};
|
||||||
typeOfChange = "AUDIOFOCUS_INVALID";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Log.d(Config.LOGTAG, "onAudioFocusChange: " + typeOfChange);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Request audio playout focus (without ducking) and install listener for changes in focus.
|
// Request audio playout focus (without ducking) and install listener for changes in focus.
|
||||||
int result = audioManager.requestAudioFocus(audioFocusChangeListener,
|
int result =
|
||||||
AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
|
audioManager.requestAudioFocus(
|
||||||
|
audioFocusChangeListener,
|
||||||
|
AudioManager.STREAM_VOICE_CALL,
|
||||||
|
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
|
||||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
||||||
Log.d(Config.LOGTAG, "Audio focus request granted for VOICE_CALL streams");
|
Log.d(Config.LOGTAG, "Audio focus request granted for VOICE_CALL streams");
|
||||||
} else {
|
} else {
|
||||||
|
@ -182,21 +165,9 @@ public class AppRTCAudioManager {
|
||||||
Log.d(Config.LOGTAG, "AudioManager started");
|
Log.d(Config.LOGTAG, "AudioManager started");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void awaitMicrophoneLatch() {
|
|
||||||
final CountDownLatch latch = microphoneLatch;
|
|
||||||
if (latch == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
latch.await();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
//ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public void stop() {
|
public void stop() {
|
||||||
Log.d(Config.LOGTAG,"appRtpAudioManager.stop()");
|
Log.d(Config.LOGTAG, "appRtpAudioManager.stop()");
|
||||||
Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".stop()");
|
Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".stop()");
|
||||||
ThreadUtils.checkIsOnMainThread();
|
ThreadUtils.checkIsOnMainThread();
|
||||||
if (amState != AudioManagerState.RUNNING) {
|
if (amState != AudioManagerState.RUNNING) {
|
||||||
|
@ -214,60 +185,44 @@ public class AppRTCAudioManager {
|
||||||
audioManager.abandonAudioFocus(audioFocusChangeListener);
|
audioManager.abandonAudioFocus(audioFocusChangeListener);
|
||||||
audioFocusChangeListener = null;
|
audioFocusChangeListener = null;
|
||||||
audioManagerEvents = null;
|
audioManagerEvents = null;
|
||||||
Log.d(Config.LOGTAG,"appRtpAudioManager.stopped()");
|
Log.d(Config.LOGTAG, "appRtpAudioManager.stopped()");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Changes selection of the currently active audio device. */
|
||||||
* Changes selection of the currently active audio device.
|
private void setAudioDeviceInternal(final CallIntegration.AudioDevice device) {
|
||||||
*/
|
|
||||||
private void setAudioDeviceInternal(CallIntegration.AudioDevice device) {
|
|
||||||
Log.d(Config.LOGTAG, "setAudioDeviceInternal(device=" + device + ")");
|
Log.d(Config.LOGTAG, "setAudioDeviceInternal(device=" + device + ")");
|
||||||
AppRTCUtils.assertIsTrue(audioDevices.contains(device));
|
AppRTCUtils.assertIsTrue(audioDevices.contains(device));
|
||||||
switch (device) {
|
switch (device) {
|
||||||
case SPEAKER_PHONE:
|
case SPEAKER_PHONE -> setSpeakerphoneOn(true);
|
||||||
setSpeakerphoneOn(true);
|
case EARPIECE, WIRED_HEADSET, BLUETOOTH -> setSpeakerphoneOn(false);
|
||||||
break;
|
default -> Log.e(Config.LOGTAG, "Invalid audio device selection");
|
||||||
case EARPIECE:
|
|
||||||
case WIRED_HEADSET:
|
|
||||||
case BLUETOOTH:
|
|
||||||
setSpeakerphoneOn(false);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log.e(Config.LOGTAG, "Invalid audio device selection");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
selectedAudioDevice = device;
|
selectedAudioDevice = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes default audio device.
|
* Changes default audio device. TODO(henrika): add usage of this method in the AppRTCMobile
|
||||||
* TODO(henrika): add usage of this method in the AppRTCMobile client.
|
* client.
|
||||||
*/
|
*/
|
||||||
public void setDefaultAudioDevice(CallIntegration.AudioDevice defaultDevice) {
|
public void setDefaultAudioDevice(final CallIntegration.AudioDevice defaultDevice) {
|
||||||
ThreadUtils.checkIsOnMainThread();
|
ThreadUtils.checkIsOnMainThread();
|
||||||
switch (defaultDevice) {
|
switch (defaultDevice) {
|
||||||
case SPEAKER_PHONE:
|
case SPEAKER_PHONE -> defaultAudioDevice = defaultDevice;
|
||||||
defaultAudioDevice = defaultDevice;
|
case EARPIECE -> {
|
||||||
break;
|
|
||||||
case EARPIECE:
|
|
||||||
if (hasEarpiece()) {
|
if (hasEarpiece()) {
|
||||||
defaultAudioDevice = defaultDevice;
|
defaultAudioDevice = defaultDevice;
|
||||||
} else {
|
} else {
|
||||||
defaultAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE;
|
defaultAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
default -> Log.e(Config.LOGTAG, "Invalid default audio device selection");
|
||||||
Log.e(Config.LOGTAG, "Invalid default audio device selection");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, "setDefaultAudioDevice(device=" + defaultAudioDevice + ")");
|
Log.d(Config.LOGTAG, "setDefaultAudioDevice(device=" + defaultAudioDevice + ")");
|
||||||
updateAudioDeviceState();
|
updateAudioDeviceState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Changes selection of the currently active audio device. */
|
||||||
* Changes selection of the currently active audio device.
|
public void selectAudioDevice(final CallIntegration.AudioDevice device) {
|
||||||
*/
|
|
||||||
public void selectAudioDevice(CallIntegration.AudioDevice device) {
|
|
||||||
ThreadUtils.checkIsOnMainThread();
|
ThreadUtils.checkIsOnMainThread();
|
||||||
if (!audioDevices.contains(device)) {
|
if (!audioDevices.contains(device)) {
|
||||||
Log.e(Config.LOGTAG, "Can not select " + device + " from available " + audioDevices);
|
Log.e(Config.LOGTAG, "Can not select " + device + " from available " + audioDevices);
|
||||||
|
@ -276,38 +231,27 @@ public class AppRTCAudioManager {
|
||||||
updateAudioDeviceState();
|
updateAudioDeviceState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns current set of available/selectable audio devices. */
|
||||||
* Returns current set of available/selectable audio devices.
|
|
||||||
*/
|
|
||||||
public Set<CallIntegration.AudioDevice> getAudioDevices() {
|
public Set<CallIntegration.AudioDevice> getAudioDevices() {
|
||||||
ThreadUtils.checkIsOnMainThread();
|
return ImmutableSet.copyOf(audioDevices);
|
||||||
return Collections.unmodifiableSet(new HashSet<>(audioDevices));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the currently selected audio device. */
|
||||||
* Returns the currently selected audio device.
|
|
||||||
*/
|
|
||||||
public CallIntegration.AudioDevice getSelectedAudioDevice() {
|
public CallIntegration.AudioDevice getSelectedAudioDevice() {
|
||||||
return selectedAudioDevice;
|
return selectedAudioDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Helper method for receiver registration. */
|
||||||
* Helper method for receiver registration.
|
|
||||||
*/
|
|
||||||
private void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
|
private void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
|
||||||
apprtcContext.registerReceiver(receiver, filter);
|
apprtcContext.registerReceiver(receiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Helper method for unregistration of an existing receiver. */
|
||||||
* Helper method for unregistration of an existing receiver.
|
|
||||||
*/
|
|
||||||
private void unregisterReceiver(BroadcastReceiver receiver) {
|
private void unregisterReceiver(BroadcastReceiver receiver) {
|
||||||
apprtcContext.unregisterReceiver(receiver);
|
apprtcContext.unregisterReceiver(receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Sets the speaker phone mode. */
|
||||||
* Sets the speaker phone mode.
|
|
||||||
*/
|
|
||||||
private void setSpeakerphoneOn(boolean on) {
|
private void setSpeakerphoneOn(boolean on) {
|
||||||
boolean wasOn = audioManager.isSpeakerphoneOn();
|
boolean wasOn = audioManager.isSpeakerphoneOn();
|
||||||
if (wasOn == on) {
|
if (wasOn == on) {
|
||||||
|
@ -316,9 +260,7 @@ public class AppRTCAudioManager {
|
||||||
audioManager.setSpeakerphoneOn(on);
|
audioManager.setSpeakerphoneOn(on);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Sets the microphone mute state. */
|
||||||
* Sets the microphone mute state.
|
|
||||||
*/
|
|
||||||
private void setMicrophoneMute(boolean on) {
|
private void setMicrophoneMute(boolean on) {
|
||||||
boolean wasMuted = audioManager.isMicrophoneMute();
|
boolean wasMuted = audioManager.isMicrophoneMute();
|
||||||
if (wasMuted == on) {
|
if (wasMuted == on) {
|
||||||
|
@ -327,53 +269,57 @@ public class AppRTCAudioManager {
|
||||||
audioManager.setMicrophoneMute(on);
|
audioManager.setMicrophoneMute(on);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Gets the current earpiece state. */
|
||||||
* Gets the current earpiece state.
|
|
||||||
*/
|
|
||||||
private boolean hasEarpiece() {
|
private boolean hasEarpiece() {
|
||||||
return apprtcContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
|
return apprtcContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a wired headset is connected or not.
|
* Checks whether a wired headset is connected or not. This is not a valid indication that audio
|
||||||
* This is not a valid indication that audio playback is actually over
|
* playback is actually over the wired headset as audio routing depends on other conditions. We
|
||||||
* the wired headset as audio routing depends on other conditions. We
|
* only use it as an early indicator (during initialization) of an attached wired headset.
|
||||||
* only use it as an early indicator (during initialization) of an attached
|
|
||||||
* wired headset.
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
private boolean hasWiredHeadset() {
|
private boolean hasWiredHeadset() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
|
||||||
return audioManager.isWiredHeadsetOn();
|
for (AudioDeviceInfo device : devices) {
|
||||||
} else {
|
final int type = device.getType();
|
||||||
final AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
|
if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
|
||||||
for (AudioDeviceInfo device : devices) {
|
Log.d(Config.LOGTAG, "hasWiredHeadset: found wired headset");
|
||||||
final int type = device.getType();
|
return true;
|
||||||
if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
|
} else if (type == AudioDeviceInfo.TYPE_USB_DEVICE) {
|
||||||
Log.d(Config.LOGTAG, "hasWiredHeadset: found wired headset");
|
Log.d(Config.LOGTAG, "hasWiredHeadset: found USB audio device");
|
||||||
return true;
|
return true;
|
||||||
} else if (type == AudioDeviceInfo.TYPE_USB_DEVICE) {
|
|
||||||
Log.d(Config.LOGTAG, "hasWiredHeadset: found USB audio device");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates list of possible audio devices and make new device selection.
|
* Updates list of possible audio devices and make new device selection. TODO(henrika): add unit
|
||||||
* TODO(henrika): add unit test to verify all state transitions.
|
* test to verify all state transitions.
|
||||||
*/
|
*/
|
||||||
public void updateAudioDeviceState() {
|
public void updateAudioDeviceState() {
|
||||||
ThreadUtils.checkIsOnMainThread();
|
ThreadUtils.checkIsOnMainThread();
|
||||||
Log.d(Config.LOGTAG, "--- updateAudioDeviceState: "
|
Log.d(
|
||||||
+ "wired headset=" + hasWiredHeadset + ", "
|
Config.LOGTAG,
|
||||||
+ "BT state=" + bluetoothManager.getState());
|
"--- updateAudioDeviceState: "
|
||||||
Log.d(Config.LOGTAG, "Device status: "
|
+ "wired headset="
|
||||||
+ "available=" + audioDevices + ", "
|
+ hasWiredHeadset
|
||||||
+ "selected=" + selectedAudioDevice + ", "
|
+ ", "
|
||||||
+ "user selected=" + userSelectedAudioDevice);
|
+ "BT state="
|
||||||
|
+ bluetoothManager.getState());
|
||||||
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
"Device status: "
|
||||||
|
+ "available="
|
||||||
|
+ audioDevices
|
||||||
|
+ ", "
|
||||||
|
+ "selected="
|
||||||
|
+ selectedAudioDevice
|
||||||
|
+ ", "
|
||||||
|
+ "user selected="
|
||||||
|
+ userSelectedAudioDevice);
|
||||||
// Check if any Bluetooth headset is connected. The internal BT state will
|
// Check if any Bluetooth headset is connected. The internal BT state will
|
||||||
// change accordingly.
|
// change accordingly.
|
||||||
// TODO(henrika): perhaps wrap required state into BT manager.
|
// TODO(henrika): perhaps wrap required state into BT manager.
|
||||||
|
@ -410,12 +356,14 @@ public class AppRTCAudioManager {
|
||||||
// If BT is not available, it can't be the user selection.
|
// If BT is not available, it can't be the user selection.
|
||||||
userSelectedAudioDevice = CallIntegration.AudioDevice.NONE;
|
userSelectedAudioDevice = CallIntegration.AudioDevice.NONE;
|
||||||
}
|
}
|
||||||
if (hasWiredHeadset && userSelectedAudioDevice == CallIntegration.AudioDevice.SPEAKER_PHONE) {
|
if (hasWiredHeadset
|
||||||
|
&& userSelectedAudioDevice == CallIntegration.AudioDevice.SPEAKER_PHONE) {
|
||||||
// If user selected speaker phone, but then plugged wired headset then make
|
// If user selected speaker phone, but then plugged wired headset then make
|
||||||
// wired headset as user selected device.
|
// wired headset as user selected device.
|
||||||
userSelectedAudioDevice = CallIntegration.AudioDevice.WIRED_HEADSET;
|
userSelectedAudioDevice = CallIntegration.AudioDevice.WIRED_HEADSET;
|
||||||
}
|
}
|
||||||
if (!hasWiredHeadset && userSelectedAudioDevice == CallIntegration.AudioDevice.WIRED_HEADSET) {
|
if (!hasWiredHeadset
|
||||||
|
&& userSelectedAudioDevice == CallIntegration.AudioDevice.WIRED_HEADSET) {
|
||||||
// If user selected wired headset, but then unplugged wired headset then make
|
// If user selected wired headset, but then unplugged wired headset then make
|
||||||
// speaker phone as user selected device.
|
// speaker phone as user selected device.
|
||||||
userSelectedAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE;
|
userSelectedAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE;
|
||||||
|
@ -425,20 +373,30 @@ public class AppRTCAudioManager {
|
||||||
boolean needBluetoothAudioStart =
|
boolean needBluetoothAudioStart =
|
||||||
bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE
|
bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE
|
||||||
&& (userSelectedAudioDevice == CallIntegration.AudioDevice.NONE
|
&& (userSelectedAudioDevice == CallIntegration.AudioDevice.NONE
|
||||||
|| userSelectedAudioDevice == CallIntegration.AudioDevice.BLUETOOTH);
|
|| userSelectedAudioDevice
|
||||||
|
== CallIntegration.AudioDevice.BLUETOOTH);
|
||||||
// Need to stop Bluetooth audio if user selected different device and
|
// Need to stop Bluetooth audio if user selected different device and
|
||||||
// Bluetooth SCO connection is established or in the process.
|
// Bluetooth SCO connection is established or in the process.
|
||||||
boolean needBluetoothAudioStop =
|
boolean needBluetoothAudioStop =
|
||||||
(bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED
|
(bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED
|
||||||
|| bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTING)
|
|| bluetoothManager.getState()
|
||||||
|
== AppRTCBluetoothManager.State.SCO_CONNECTING)
|
||||||
&& (userSelectedAudioDevice != CallIntegration.AudioDevice.NONE
|
&& (userSelectedAudioDevice != CallIntegration.AudioDevice.NONE
|
||||||
&& userSelectedAudioDevice != CallIntegration.AudioDevice.BLUETOOTH);
|
&& userSelectedAudioDevice
|
||||||
|
!= CallIntegration.AudioDevice.BLUETOOTH);
|
||||||
if (bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE
|
if (bluetoothManager.getState() == AppRTCBluetoothManager.State.HEADSET_AVAILABLE
|
||||||
|| bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTING
|
|| bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTING
|
||||||
|| bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED) {
|
|| bluetoothManager.getState() == AppRTCBluetoothManager.State.SCO_CONNECTED) {
|
||||||
Log.d(Config.LOGTAG, "Need BT audio: start=" + needBluetoothAudioStart + ", "
|
Log.d(
|
||||||
+ "stop=" + needBluetoothAudioStop + ", "
|
Config.LOGTAG,
|
||||||
+ "BT state=" + bluetoothManager.getState());
|
"Need BT audio: start="
|
||||||
|
+ needBluetoothAudioStart
|
||||||
|
+ ", "
|
||||||
|
+ "stop="
|
||||||
|
+ needBluetoothAudioStop
|
||||||
|
+ ", "
|
||||||
|
+ "BT state="
|
||||||
|
+ bluetoothManager.getState());
|
||||||
}
|
}
|
||||||
// Start or stop Bluetooth SCO connection given states set earlier.
|
// Start or stop Bluetooth SCO connection given states set earlier.
|
||||||
if (needBluetoothAudioStop) {
|
if (needBluetoothAudioStop) {
|
||||||
|
@ -467,7 +425,8 @@ public class AppRTCAudioManager {
|
||||||
} else {
|
} else {
|
||||||
// No wired headset and no Bluetooth, hence the audio-device list can contain speaker
|
// No wired headset and no Bluetooth, hence the audio-device list can contain speaker
|
||||||
// phone (on a tablet), or speaker phone and earpiece (on mobile phone).
|
// phone (on a tablet), or speaker phone and earpiece (on mobile phone).
|
||||||
// |defaultAudioDevice| contains either AudioDevice.SPEAKER_PHONE or AudioDevice.EARPIECE
|
// |defaultAudioDevice| contains either AudioDevice.SPEAKER_PHONE or
|
||||||
|
// AudioDevice.EARPIECE
|
||||||
// depending on the user's selection.
|
// depending on the user's selection.
|
||||||
newAudioDevice = defaultAudioDevice;
|
newAudioDevice = defaultAudioDevice;
|
||||||
}
|
}
|
||||||
|
@ -475,9 +434,14 @@ public class AppRTCAudioManager {
|
||||||
if (newAudioDevice != selectedAudioDevice || audioDeviceSetUpdated) {
|
if (newAudioDevice != selectedAudioDevice || audioDeviceSetUpdated) {
|
||||||
// Do the required device switch.
|
// Do the required device switch.
|
||||||
setAudioDeviceInternal(newAudioDevice);
|
setAudioDeviceInternal(newAudioDevice);
|
||||||
Log.d(Config.LOGTAG, "New device status: "
|
Log.d(
|
||||||
+ "available=" + audioDevices + ", "
|
Config.LOGTAG,
|
||||||
+ "selected=" + newAudioDevice);
|
"New device status: "
|
||||||
|
+ "available="
|
||||||
|
+ audioDevices
|
||||||
|
+ ", "
|
||||||
|
+ "selected="
|
||||||
|
+ newAudioDevice);
|
||||||
if (audioManagerEvents != null) {
|
if (audioManagerEvents != null) {
|
||||||
// Notify a listening client that audio device has been changed.
|
// Notify a listening client that audio device has been changed.
|
||||||
audioManagerEvents.onAudioDeviceChanged(selectedAudioDevice, audioDevices);
|
audioManagerEvents.onAudioDeviceChanged(selectedAudioDevice, audioDevices);
|
||||||
|
@ -490,22 +454,19 @@ public class AppRTCAudioManager {
|
||||||
ContextCompat.getMainExecutor(apprtcContext).execute(runnable);
|
ContextCompat.getMainExecutor(apprtcContext).execute(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** AudioManager state. */
|
||||||
* AudioManager state.
|
|
||||||
*/
|
|
||||||
public enum AudioManagerState {
|
public enum AudioManagerState {
|
||||||
UNINITIALIZED,
|
UNINITIALIZED,
|
||||||
PREINITIALIZED,
|
PREINITIALIZED,
|
||||||
RUNNING,
|
RUNNING,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Selected audio device change event. */
|
||||||
* Selected audio device change event.
|
|
||||||
*/
|
|
||||||
public interface AudioManagerEvents {
|
public interface AudioManagerEvents {
|
||||||
// Callback fired once audio device is changed or list of available audio devices changed.
|
// Callback fired once audio device is changed or list of available audio devices changed.
|
||||||
void onAudioDeviceChanged(
|
void onAudioDeviceChanged(
|
||||||
CallIntegration.AudioDevice selectedAudioDevice, Set<CallIntegration.AudioDevice> availableAudioDevices);
|
CallIntegration.AudioDevice selectedAudioDevice,
|
||||||
|
Set<CallIntegration.AudioDevice> availableAudioDevices);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receiver which handles changes in wired headset availability. */
|
/* Receiver which handles changes in wired headset availability. */
|
||||||
|
@ -520,11 +481,21 @@ public class AppRTCAudioManager {
|
||||||
int state = intent.getIntExtra("state", STATE_UNPLUGGED);
|
int state = intent.getIntExtra("state", STATE_UNPLUGGED);
|
||||||
int microphone = intent.getIntExtra("microphone", HAS_NO_MIC);
|
int microphone = intent.getIntExtra("microphone", HAS_NO_MIC);
|
||||||
String name = intent.getStringExtra("name");
|
String name = intent.getStringExtra("name");
|
||||||
Log.d(Config.LOGTAG, "WiredHeadsetReceiver.onReceive" + AppRTCUtils.getThreadInfo() + ": "
|
Log.d(
|
||||||
+ "a=" + intent.getAction() + ", s="
|
Config.LOGTAG,
|
||||||
+ (state == STATE_UNPLUGGED ? "unplugged" : "plugged") + ", m="
|
"WiredHeadsetReceiver.onReceive"
|
||||||
+ (microphone == HAS_MIC ? "mic" : "no mic") + ", n=" + name + ", sb="
|
+ AppRTCUtils.getThreadInfo()
|
||||||
+ isInitialStickyBroadcast());
|
+ ": "
|
||||||
|
+ "a="
|
||||||
|
+ intent.getAction()
|
||||||
|
+ ", s="
|
||||||
|
+ (state == STATE_UNPLUGGED ? "unplugged" : "plugged")
|
||||||
|
+ ", m="
|
||||||
|
+ (microphone == HAS_MIC ? "mic" : "no mic")
|
||||||
|
+ ", n="
|
||||||
|
+ name
|
||||||
|
+ ", sb="
|
||||||
|
+ isInitialStickyBroadcast());
|
||||||
hasWiredHeadset = (state == STATE_PLUGGED);
|
hasWiredHeadset = (state == STATE_PLUGGED);
|
||||||
updateAudioDeviceState();
|
updateAudioDeviceState();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue