Handle bluetooth permission crash during calls.

This commit is contained in:
Cody Henthorne
2022-05-13 12:39:23 -04:00
committed by GitHub
parent 97d41fdd1e
commit a3bbf944e5
10 changed files with 65 additions and 2 deletions

View File

@@ -44,6 +44,8 @@ import androidx.window.DisplayFeature;
import androidx.window.FoldingFeature;
import androidx.window.WindowLayoutInfo;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -112,6 +114,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
private TooltipPopup videoTooltip;
private WebRtcCallViewModel viewModel;
private boolean enableVideoIfAvailable;
private boolean hasWarnedAboutBluetooth;
private androidx.window.WindowManager windowManager;
private WindowLayoutInfoConsumer windowLayoutInfoConsumer;
private ThrottledDebouncer requestNewSizesThrottle;
@@ -686,6 +689,17 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
enableVideoIfAvailable = false;
handleSetMuteVideo(false);
}
if (event.getBluetoothPermissionDenied() && !hasWarnedAboutBluetooth && !isFinishing()) {
new MaterialAlertDialogBuilder(this)
.setTitle(R.string.WebRtcCallActivity__bluetooth_permission_denied)
.setMessage(R.string.WebRtcCallActivity__please_enable_the_nearby_devices_permission_to_use_bluetooth_during_a_call)
.setPositiveButton(R.string.WebRtcCallActivity__open_settings, (d, w) -> startActivity(Permissions.getApplicationSettingsIntent(this)))
.setNegativeButton(R.string.WebRtcCallActivity__not_now, null)
.show();
hasWarnedAboutBluetooth = true;
}
}
private void handleCallPreJoin(@NonNull WebRtcViewModel event) {

View File

@@ -96,6 +96,7 @@ class WebRtcViewModel(state: WebRtcServiceState) {
val ringerRecipient: Recipient = state.getCallSetupState(state.callInfoState.activePeer?.callId).ringerRecipient
val activeDevice: SignalAudioManager.AudioDevice = state.localDeviceState.activeDevice
val availableDevices: Set<SignalAudioManager.AudioDevice> = state.localDeviceState.availableDevices
val bluetoothPermissionDenied: Boolean = state.localDeviceState.bluetoothPermissionDenied
val localParticipant: CallParticipant = createLocal(
state.localDeviceState.cameraState,
@@ -124,6 +125,7 @@ class WebRtcViewModel(state: WebRtcServiceState) {
participantLimit=$participantLimit,
activeDevice=$activeDevice,
availableDevices=$availableDevices,
bluetoothPermissionDenied=$bluetoothPermissionDenied,
ringGroup=$ringGroup
}
""".trimIndent()

View File

@@ -309,6 +309,10 @@ private void processStateless(@NonNull Function1<WebRtcEphemeralState, WebRtcEph
process((s, p) -> p.handleAudioDeviceChanged(s, activeDevice, availableDevices));
}
public void onBluetoothPermissionDenied() {
process((s, p) -> p.handleBluetoothPermissionDenied(s));
}
public void selectAudioDevice(@NonNull SignalAudioManager.AudioDevice desiredDevice) {
process((s, p) -> p.handleSetUserAudioDevice(s, desiredDevice));
}

View File

@@ -457,6 +457,13 @@ public abstract class WebRtcActionProcessor {
return currentState;
}
public @NonNull WebRtcServiceState handleBluetoothPermissionDenied(@NonNull WebRtcServiceState currentState) {
return currentState.builder()
.changeLocalDeviceState()
.setBluetoothPermissionDenied(true)
.build();
}
protected @NonNull WebRtcServiceState handleSetUserAudioDevice(@NonNull WebRtcServiceState currentState, @NonNull SignalAudioManager.AudioDevice userDevice) {
Log.i(tag, "handleSetUserAudioDevice not processed");
return currentState;

View File

@@ -267,6 +267,11 @@ public final class WebRtcCallService extends Service implements SignalAudioManag
callManager.onAudioDeviceChanged(activeDevice, availableDevices);
}
@Override
public void onBluetoothPermissionDenied() {
callManager.onBluetoothPermissionDenied();
}
private class HangUpRtcOnPstnCallAnsweredListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, @NonNull String phoneNumber) {

View File

@@ -14,7 +14,8 @@ data class LocalDeviceState constructor(
var isLandscapeEnabled: Boolean = false,
var deviceOrientation: Orientation = Orientation.PORTRAIT_BOTTOM_EDGE,
var activeDevice: SignalAudioManager.AudioDevice = SignalAudioManager.AudioDevice.NONE,
var availableDevices: Set<SignalAudioManager.AudioDevice> = emptySet()
var availableDevices: Set<SignalAudioManager.AudioDevice> = emptySet(),
var bluetoothPermissionDenied: Boolean = false
) {
fun duplicate(): LocalDeviceState {

View File

@@ -125,6 +125,11 @@ public class WebRtcServiceStateBuilder {
toBuild.setAvailableDevices(availableDevices);
return this;
}
public @NonNull LocalDeviceStateBuilder setBluetoothPermissionDenied(boolean bluetoothPermissionDenied) {
toBuild.setBluetoothPermissionDenied(bluetoothPermissionDenied);
return this;
}
}
public class CallSetupStateBuilder {

View File

@@ -98,6 +98,7 @@ sealed class SignalAudioManager(protected val context: Context, protected val ev
interface EventListener {
@JvmSuppressWildcards
fun onAudioDeviceChanged(activeDevice: AudioDevice, devices: Set<AudioDevice>)
fun onBluetoothPermissionDenied()
}
}
@@ -121,6 +122,7 @@ class FullSignalAudioManager(context: Context, eventListener: EventListener?) :
private var audioDevices: MutableSet<AudioDevice> = mutableSetOf()
private var defaultAudioDevice: AudioDevice = AudioDevice.EARPIECE
private var userSelectedAudioDevice: AudioDevice = AudioDevice.NONE
private var previousBluetoothState: SignalBluetoothManager.State? = null
private var savedAudioMode = AudioManager.MODE_INVALID
private var savedIsSpeakerPhoneOn = false
@@ -294,6 +296,11 @@ class FullSignalAudioManager(context: Context, eventListener: EventListener?) :
autoSwitchToBluetooth = false
}
if (previousBluetoothState != null && previousBluetoothState != SignalBluetoothManager.State.PERMISSION_DENIED && signalBluetoothManager.state == SignalBluetoothManager.State.PERMISSION_DENIED) {
eventListener?.onBluetoothPermissionDenied()
}
previousBluetoothState = signalBluetoothManager.state
val newAudioDevice: AudioDevice = when {
audioDevices.contains(userSelectedAudioDevice) -> userSelectedAudioDevice
audioDevices.contains(defaultAudioDevice) -> defaultAudioDevice

View File

@@ -162,7 +162,16 @@ class SignalBluetoothManager(
return
}
val devices: List<BluetoothDevice>? = bluetoothHeadset?.connectedDevices
val devices: List<BluetoothDevice>?
try {
devices = bluetoothHeadset?.connectedDevices
} catch (e: SecurityException) {
Log.w(TAG, "Unable to get bluetooth devices", e)
stop()
state = State.PERMISSION_DENIED
return
}
if (devices == null || devices.isEmpty()) {
bluetoothDevice = null
state = State.UNAVAILABLE
@@ -320,6 +329,7 @@ class SignalBluetoothManager(
DISCONNECTING,
CONNECTING,
CONNECTED,
PERMISSION_DENIED,
ERROR;
fun shouldUpdate(): Boolean {