run some AppRTCAudioManager actions on main thread
This commit is contained in:
parent
6ba9208eea
commit
d79fc1bb79
|
@ -23,6 +23,7 @@ import android.os.Build;
|
|||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.webrtc.ThreadUtils;
|
||||
|
||||
|
@ -44,8 +45,6 @@ public class AppRTCAudioManager {
|
|||
|
||||
private final Context apprtcContext;
|
||||
// Contains speakerphone setting: auto, true or false
|
||||
@Nullable
|
||||
private SpeakerPhonePreference speakerPhonePreference;
|
||||
// Handles all tasks related to Bluetooth headset devices.
|
||||
private final AppRTCBluetoothManager bluetoothManager;
|
||||
@Nullable
|
||||
|
@ -70,12 +69,7 @@ public class AppRTCAudioManager {
|
|||
// TODO(henrika): always set to AudioDevice.NONE today. Add support for
|
||||
// explicit selection based on choice by userSelectedAudioDevice.
|
||||
private CallIntegration.AudioDevice userSelectedAudioDevice;
|
||||
// Proximity sensor object. It measures the proximity of an object in cm
|
||||
// relative to the view screen of a device and can therefore be used to
|
||||
// assist device switching (close to ear <=> use headset earpiece if
|
||||
// available, far from ear <=> use speaker phone).
|
||||
@Nullable
|
||||
private AppRTCProximitySensor proximitySensor;
|
||||
|
||||
// Contains a list of available audio devices. A Set collection is used to
|
||||
// avoid duplicate elements.
|
||||
private Set<CallIntegration.AudioDevice> audioDevices = new HashSet<>();
|
||||
|
@ -86,7 +80,6 @@ public class AppRTCAudioManager {
|
|||
private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener;
|
||||
|
||||
public AppRTCAudioManager(final Context context) {
|
||||
ThreadUtils.checkIsOnMainThread();
|
||||
apprtcContext = context;
|
||||
audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
|
||||
bluetoothManager = AppRTCBluetoothManager.create(context, this);
|
||||
|
@ -98,28 +91,10 @@ public class AppRTCAudioManager {
|
|||
} else {
|
||||
defaultAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE;
|
||||
}
|
||||
// Create and initialize the proximity sensor.
|
||||
// Tablet devices (e.g. Nexus 7) does not support proximity sensors.
|
||||
// Note that, the sensor will not be active until start() has been called.
|
||||
proximitySensor = AppRTCProximitySensor.create(context,
|
||||
// This method will be called each time a state change is detected.
|
||||
// Example: user holds his hand over the device (closer than ~5 cm),
|
||||
// or removes his hand from the device.
|
||||
this::onProximitySensorChangedState);
|
||||
Log.d(Config.LOGTAG, "defaultAudioDevice: " + defaultAudioDevice);
|
||||
AppRTCUtils.logDeviceInfo(Config.LOGTAG);
|
||||
}
|
||||
|
||||
public void switchSpeakerPhonePreference(final SpeakerPhonePreference speakerPhonePreference) {
|
||||
this.speakerPhonePreference = speakerPhonePreference;
|
||||
if (speakerPhonePreference == SpeakerPhonePreference.EARPIECE && hasEarpiece()) {
|
||||
defaultAudioDevice = CallIntegration.AudioDevice.EARPIECE;
|
||||
} else {
|
||||
defaultAudioDevice = CallIntegration.AudioDevice.SPEAKER_PHONE;
|
||||
}
|
||||
updateAudioDeviceState();
|
||||
}
|
||||
|
||||
public static boolean isMicrophoneAvailable() {
|
||||
microphoneLatch = new CountDownLatch(1);
|
||||
AudioRecord audioRecord = null;
|
||||
|
@ -156,30 +131,6 @@ public class AppRTCAudioManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when the proximity sensor reports a state change,
|
||||
* e.g. from "NEAR to FAR" or from "FAR to NEAR".
|
||||
*/
|
||||
private void onProximitySensorChangedState() {
|
||||
if (speakerPhonePreference != SpeakerPhonePreference.AUTO) {
|
||||
return;
|
||||
}
|
||||
// The proximity sensor should only be activated when there are exactly two
|
||||
// available audio devices.
|
||||
if (audioDevices.size() == 2 && audioDevices.contains(CallIntegration.AudioDevice.EARPIECE)
|
||||
&& audioDevices.contains(CallIntegration.AudioDevice.SPEAKER_PHONE)) {
|
||||
if (proximitySensor.sensorReportsNearState()) {
|
||||
// Sensor reports that a "handset is being held up to a person's ear",
|
||||
// or "something is covering the light sensor".
|
||||
setAudioDeviceInternal(CallIntegration.AudioDevice.EARPIECE);
|
||||
} else {
|
||||
// Sensor reports that a "handset is removed from a person's ear", or
|
||||
// "the light sensor is no longer covered".
|
||||
setAudioDeviceInternal(CallIntegration.AudioDevice.SPEAKER_PHONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void start(AudioManagerEvents audioManagerEvents) {
|
||||
Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".start()");
|
||||
|
@ -280,6 +231,7 @@ public class AppRTCAudioManager {
|
|||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void stop() {
|
||||
Log.d(Config.LOGTAG,"appRtpAudioManager.stop()");
|
||||
Log.d(Config.LOGTAG, AppRTCAudioManager.class.getName() + ".stop()");
|
||||
ThreadUtils.checkIsOnMainThread();
|
||||
if (amState != AudioManagerState.RUNNING) {
|
||||
|
@ -296,12 +248,8 @@ public class AppRTCAudioManager {
|
|||
// Abandon audio focus. Gives the previous focus owner, if any, focus.
|
||||
audioManager.abandonAudioFocus(audioFocusChangeListener);
|
||||
audioFocusChangeListener = null;
|
||||
Log.d(Config.LOGTAG, "Abandoned audio focus for VOICE_CALL streams");
|
||||
if (proximitySensor != null) {
|
||||
proximitySensor.stop();
|
||||
proximitySensor = null;
|
||||
}
|
||||
audioManagerEvents = null;
|
||||
Log.d(Config.LOGTAG,"appRtpAudioManager.stopped()");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -375,7 +323,6 @@ public class AppRTCAudioManager {
|
|||
* Returns the currently selected audio device.
|
||||
*/
|
||||
public CallIntegration.AudioDevice getSelectedAudioDevice() {
|
||||
ThreadUtils.checkIsOnMainThread();
|
||||
return selectedAudioDevice;
|
||||
}
|
||||
|
||||
|
@ -574,6 +521,10 @@ public class AppRTCAudioManager {
|
|||
Log.d(Config.LOGTAG, "--- updateAudioDeviceState done");
|
||||
}
|
||||
|
||||
public void executeOnMain(final Runnable runnable) {
|
||||
ContextCompat.getMainExecutor(apprtcContext).execute(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioManager state.
|
||||
*/
|
||||
|
@ -583,18 +534,6 @@ public class AppRTCAudioManager {
|
|||
RUNNING,
|
||||
}
|
||||
|
||||
public enum SpeakerPhonePreference {
|
||||
AUTO, EARPIECE, SPEAKER;
|
||||
|
||||
public static SpeakerPhonePreference of(final Set<Media> media) {
|
||||
if (media.contains(Media.VIDEO)) {
|
||||
return SPEAKER;
|
||||
} else {
|
||||
return EARPIECE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selected audio device change event.
|
||||
*/
|
||||
|
|
|
@ -68,8 +68,6 @@ public class AppRTCBluetoothManager {
|
|||
};
|
||||
|
||||
protected AppRTCBluetoothManager(Context context, AppRTCAudioManager audioManager) {
|
||||
Log.d(Config.LOGTAG, "ctor");
|
||||
ThreadUtils.checkIsOnMainThread();
|
||||
apprtcContext = context;
|
||||
apprtcAudioManager = audioManager;
|
||||
this.audioManager = getAudioManager(context);
|
||||
|
|
|
@ -1,171 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
package eu.siacs.conversations.services;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.webrtc.ThreadUtils;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.utils.AppRTCUtils;
|
||||
|
||||
/**
|
||||
* AppRTCProximitySensor manages functions related to the proximity sensor in
|
||||
* the AppRTC demo.
|
||||
* On most device, the proximity sensor is implemented as a boolean-sensor.
|
||||
* It returns just two values "NEAR" or "FAR". Thresholding is done on the LUX
|
||||
* value i.e. the LUX value of the light sensor is compared with a threshold.
|
||||
* A LUX-value more than the threshold means the proximity sensor returns "FAR".
|
||||
* Anything less than the threshold value and the sensor returns "NEAR".
|
||||
*/
|
||||
public class AppRTCProximitySensor implements SensorEventListener {
|
||||
// This class should be created, started and stopped on one thread
|
||||
// (e.g. the main thread). We use |nonThreadSafe| to ensure that this is
|
||||
// the case. Only active when |DEBUG| is set to true.
|
||||
private final ThreadUtils.ThreadChecker threadChecker = new ThreadUtils.ThreadChecker();
|
||||
private final Runnable onSensorStateListener;
|
||||
private final SensorManager sensorManager;
|
||||
@Nullable
|
||||
private Sensor proximitySensor;
|
||||
private boolean lastStateReportIsNear;
|
||||
|
||||
private AppRTCProximitySensor(Context context, Runnable sensorStateListener) {
|
||||
Log.d(Config.LOGTAG, "AppRTCProximitySensor" + AppRTCUtils.getThreadInfo());
|
||||
onSensorStateListener = sensorStateListener;
|
||||
sensorManager = ((SensorManager) context.getSystemService(Context.SENSOR_SERVICE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construction
|
||||
*/
|
||||
static AppRTCProximitySensor create(Context context, Runnable sensorStateListener) {
|
||||
return new AppRTCProximitySensor(context, sensorStateListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the proximity sensor. Also do initialization if called for the
|
||||
* first time.
|
||||
*/
|
||||
public boolean start() {
|
||||
threadChecker.checkIsOnValidThread();
|
||||
Log.d(Config.LOGTAG, "start" + AppRTCUtils.getThreadInfo());
|
||||
if (!initDefaultSensor()) {
|
||||
// Proximity sensor is not supported on this device.
|
||||
return false;
|
||||
}
|
||||
sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the proximity sensor.
|
||||
*/
|
||||
public void stop() {
|
||||
threadChecker.checkIsOnValidThread();
|
||||
Log.d(Config.LOGTAG, "stop" + AppRTCUtils.getThreadInfo());
|
||||
if (proximitySensor == null) {
|
||||
return;
|
||||
}
|
||||
sensorManager.unregisterListener(this, proximitySensor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for last reported state. Set to true if "near" is reported.
|
||||
*/
|
||||
public boolean sensorReportsNearState() {
|
||||
threadChecker.checkIsOnValidThread();
|
||||
return lastStateReportIsNear;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
threadChecker.checkIsOnValidThread();
|
||||
AppRTCUtils.assertIsTrue(sensor.getType() == Sensor.TYPE_PROXIMITY);
|
||||
if (accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) {
|
||||
Log.e(Config.LOGTAG, "The values returned by this sensor cannot be trusted");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onSensorChanged(SensorEvent event) {
|
||||
threadChecker.checkIsOnValidThread();
|
||||
AppRTCUtils.assertIsTrue(event.sensor.getType() == Sensor.TYPE_PROXIMITY);
|
||||
// As a best practice; do as little as possible within this method and
|
||||
// avoid blocking.
|
||||
float distanceInCentimeters = event.values[0];
|
||||
if (distanceInCentimeters < proximitySensor.getMaximumRange()) {
|
||||
Log.d(Config.LOGTAG, "Proximity sensor => NEAR state");
|
||||
lastStateReportIsNear = true;
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, "Proximity sensor => FAR state");
|
||||
lastStateReportIsNear = false;
|
||||
}
|
||||
// Report about new state to listening client. Client can then call
|
||||
// sensorReportsNearState() to query the current state (NEAR or FAR).
|
||||
if (onSensorStateListener != null) {
|
||||
onSensorStateListener.run();
|
||||
}
|
||||
Log.d(Config.LOGTAG, "onSensorChanged" + AppRTCUtils.getThreadInfo() + ": "
|
||||
+ "accuracy=" + event.accuracy + ", timestamp=" + event.timestamp + ", distance="
|
||||
+ event.values[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default proximity sensor if it exists. Tablet devices (e.g. Nexus 7)
|
||||
* does not support this type of sensor and false will be returned in such
|
||||
* cases.
|
||||
*/
|
||||
private boolean initDefaultSensor() {
|
||||
if (proximitySensor != null) {
|
||||
return true;
|
||||
}
|
||||
proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
|
||||
if (proximitySensor == null) {
|
||||
return false;
|
||||
}
|
||||
logProximitySensorInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for logging information about the proximity sensor.
|
||||
*/
|
||||
private void logProximitySensorInfo() {
|
||||
if (proximitySensor == null) {
|
||||
return;
|
||||
}
|
||||
StringBuilder info = new StringBuilder("Proximity sensor: ");
|
||||
info.append("name=").append(proximitySensor.getName());
|
||||
info.append(", vendor: ").append(proximitySensor.getVendor());
|
||||
info.append(", power: ").append(proximitySensor.getPower());
|
||||
info.append(", resolution: ").append(proximitySensor.getResolution());
|
||||
info.append(", max range: ").append(proximitySensor.getMaximumRange());
|
||||
info.append(", min delay: ").append(proximitySensor.getMinDelay());
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
|
||||
// Added in API level 20.
|
||||
info.append(", type: ").append(proximitySensor.getStringType());
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// Added in API level 21.
|
||||
info.append(", max delay: ").append(proximitySensor.getMaxDelay());
|
||||
info.append(", reporting mode: ").append(proximitySensor.getReportingMode());
|
||||
info.append(", isWakeUpSensor: ").append(proximitySensor.isWakeUpSensor());
|
||||
}
|
||||
Log.d(Config.LOGTAG, info.toString());
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import android.util.Log;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
@ -42,8 +43,8 @@ public class CallIntegration extends Connection {
|
|||
this.appRTCAudioManager = null;
|
||||
} else {
|
||||
this.appRTCAudioManager = new AppRTCAudioManager(context);
|
||||
this.appRTCAudioManager.start(this::onAudioDeviceChanged);
|
||||
// TODO WebRTCWrapper would issue one call to eventCallback.onAudioDeviceChanged
|
||||
ContextCompat.getMainExecutor(context)
|
||||
.execute(() -> this.appRTCAudioManager.start(this::onAudioDeviceChanged));
|
||||
}
|
||||
setRingbackRequested(true);
|
||||
}
|
||||
|
@ -149,6 +150,12 @@ public class CallIntegration extends Connection {
|
|||
final var available = getAudioDevices();
|
||||
if (available.contains(audioDevice)) {
|
||||
this.setAudioDevice(audioDevice);
|
||||
} else {
|
||||
Log.d(
|
||||
Config.LOGTAG,
|
||||
"application requested to switch to "
|
||||
+ audioDevice
|
||||
+ " but device was not available");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,7 +276,8 @@ public class CallIntegration extends Connection {
|
|||
}
|
||||
|
||||
private void setAudioDeviceFallback(final AudioDevice audioDevice) {
|
||||
requireAppRtcAudioManager().setDefaultAudioDevice(audioDevice);
|
||||
final var audioManager = requireAppRtcAudioManager();
|
||||
audioManager.executeOnMain(() -> audioManager.setDefaultAudioDevice(audioDevice));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -287,7 +295,7 @@ public class CallIntegration extends Connection {
|
|||
if (state == STATE_DISCONNECTED) {
|
||||
final var audioManager = this.appRTCAudioManager;
|
||||
if (audioManager != null) {
|
||||
audioManager.stop();
|
||||
audioManager.executeOnMain(audioManager::stop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -382,8 +390,11 @@ public class CallIntegration extends Connection {
|
|||
return;
|
||||
}
|
||||
final var audioManager = requireAppRtcAudioManager();
|
||||
this.onAudioDeviceChanged(
|
||||
audioManager.getSelectedAudioDevice(), audioManager.getAudioDevices());
|
||||
audioManager.executeOnMain(
|
||||
() ->
|
||||
this.onAudioDeviceChanged(
|
||||
audioManager.getSelectedAudioDevice(),
|
||||
audioManager.getAudioDevices()));
|
||||
}
|
||||
|
||||
/** AudioDevice is the names of possible audio devices that we currently support. */
|
||||
|
|
|
@ -122,6 +122,7 @@ public class CallIntegrationConnectionService extends ConnectionService {
|
|||
|
||||
public Connection onCreateIncomingConnection(
|
||||
final PhoneAccountHandle phoneAccountHandle, final ConnectionRequest request) {
|
||||
Log.d(Config.LOGTAG, "onCreateIncomingConnection()");
|
||||
final var service = ServiceConnectionService.get(this.serviceFuture);
|
||||
final Bundle extras = request.getExtras();
|
||||
final Bundle extraExtras = extras.getBundle(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
|
||||
|
@ -182,7 +183,8 @@ public class CallIntegrationConnectionService extends ConnectionService {
|
|||
}
|
||||
|
||||
public static void unregisterPhoneAccount(final Context context, final Account account) {
|
||||
context.getSystemService(TelecomManager.class).unregisterPhoneAccount(getHandle(context, account));
|
||||
context.getSystemService(TelecomManager.class)
|
||||
.unregisterPhoneAccount(getHandle(context, account));
|
||||
}
|
||||
|
||||
public static PhoneAccountHandle getHandle(final Context context, final Account account) {
|
||||
|
|
|
@ -2300,8 +2300,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
|
|||
final boolean trickle)
|
||||
throws WebRTCWrapper.InitializationException {
|
||||
this.jingleConnectionManager.ensureConnectionIsRegistered(this);
|
||||
this.webRTCWrapper.setup(
|
||||
this.xmppConnectionService, AppRTCAudioManager.SpeakerPhonePreference.of(media));
|
||||
this.webRTCWrapper.setup(this.xmppConnectionService);
|
||||
this.webRTCWrapper.initializePeerConnection(media, iceServers, trickle);
|
||||
}
|
||||
|
||||
|
|
|
@ -222,9 +222,7 @@ public class WebRTCWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
public void setup(
|
||||
final XmppConnectionService service,
|
||||
@Nonnull final AppRTCAudioManager.SpeakerPhonePreference speakerPhonePreference)
|
||||
public void setup(final XmppConnectionService service)
|
||||
throws InitializationException {
|
||||
try {
|
||||
PeerConnectionFactory.initialize(
|
||||
|
|
Loading…
Reference in a new issue