Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cuddly-moose-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"client-sdk-android": patch
---

Update audio handling to use AudioManager communication device APIs on S and above
5 changes: 5 additions & 0 deletions .changeset/great-zoos-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"client-sdk-android": patch
---

Implement changing preferred audio device list on AudioSwitchHandler mid-call
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ androidx-camera = "1.4.2"
androidx-core = "1.13.1"
androidx-fragment = "1.5.1"
androidx-lifecycle = "2.8.0"
audioswitch = "89582c47c9a04c62f90aa5e57251af4800a62c9a"
audioswitch = "70efa204dda4f468f989b4b3e1b5ecb6ffe56ceb"
autoService = '1.0.1'
coroutines = "1.6.0"
dagger = "2.46"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2025 LiveKit, Inc.
* Copyright 2023-2026 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,6 +27,7 @@ import com.twilio.audioswitch.AbstractAudioSwitch
import com.twilio.audioswitch.AudioDevice
import com.twilio.audioswitch.AudioDeviceChangeListener
import com.twilio.audioswitch.AudioSwitch
import com.twilio.audioswitch.CommDeviceAudioSwitch
import com.twilio.audioswitch.LegacyAudioSwitch
import io.livekit.android.room.Room
import io.livekit.android.util.LKLog
Expand All @@ -48,7 +49,7 @@ constructor(private val context: Context) : AudioHandler {
/**
* Toggle whether logging is enabled for [AudioSwitch]. By default, this is set to false.
*/
var loggingEnabled = false
var loggingEnabled = true

/**
* Listen to changes in the available and active audio devices.
Expand Down Expand Up @@ -96,6 +97,9 @@ constructor(private val context: Context) : AudioHandler {
}
}

@Volatile
private var preferredDeviceListBacking: List<Class<out AudioDevice>>? = null

/**
* The preferred priority of audio devices to use. The first available audio device will be used.
*
Expand All @@ -104,8 +108,27 @@ constructor(private val context: Context) : AudioHandler {
* 2. WiredHeadset
* 3. Speakerphone
* 4. Earpiece
*
* Changes to this value after [start] has been called will still be applied
* to the underlying [AbstractAudioSwitch] instance.
*/
var preferredDeviceList: List<Class<out AudioDevice>>? = null
var preferredDeviceList: List<Class<out AudioDevice>>?
get() = preferredDeviceListBacking
set(value) {
preferredDeviceListBacking = value
val list = value ?: defaultPreferredDeviceList
val h = handler
val sw = audioSwitch
if (h != null && sw != null) {
if (Looper.myLooper() == h.looper) {
sw.setPreferredDeviceList(list)
} else {
h.post {
audioSwitch?.setPreferredDeviceList(list)
}
}
}
}

/**
* When true, AudioSwitchHandler will request audio focus on start and abandon on stop.
Expand Down Expand Up @@ -199,7 +222,14 @@ constructor(private val context: Context) : AudioHandler {
handler?.removeCallbacksAndMessages(null)
handler?.postAtFrontOfQueue {
val switch =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
CommDeviceAudioSwitch(
context = context,
loggingEnabled = loggingEnabled,
audioFocusChangeListener = onAudioFocusChangeDispatcher,
preferredDeviceList = preferredDeviceList ?: defaultPreferredDeviceList,
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
AudioSwitch(
context = context,
loggingEnabled = loggingEnabled,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2025 LiveKit, Inc.
* Copyright 2023-2026 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,6 +22,7 @@ import android.os.Parcelable
import android.view.WindowManager
import android.widget.Toast
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.background
Expand All @@ -35,6 +36,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyRow
Expand Down Expand Up @@ -107,6 +109,7 @@ class CallActivity : AppCompatActivity() {

@OptIn(ExperimentalMaterialApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

Expand Down Expand Up @@ -198,6 +201,7 @@ class CallActivity : AppCompatActivity() {
ConstraintLayout(
modifier = Modifier
.fillMaxSize()
.safeDrawingPadding()
.background(MaterialTheme.colors.background),
) {
val (speakerView, audienceRow, buttonBar) = createRefs()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2025 LiveKit, Inc.
* Copyright 2023-2026 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,9 +22,12 @@ import android.os.Parcelable
import android.view.WindowManager
import android.widget.EditText
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
Expand Down Expand Up @@ -70,11 +73,19 @@ class CallActivity : AppCompatActivity() {

@androidx.camera.camera2.interop.ExperimentalCamera2Interop
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
binding = CallActivityBinding.inflate(layoutInflater)

setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, windowInsets ->
val bars = windowInsets.getInsets(
WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout(),
)
v.setPadding(bars.left, bars.top, bars.right, bars.bottom)
windowInsets
}

// Audience row setup
val audienceAdapter = GroupieAdapter()
Expand Down
Loading