Detailed changes
@@ -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(