Skip to content

Commit

Permalink
AHOYAPPS-821: Change AudioDevice auto select order (#73)
Browse files Browse the repository at this point in the history
* Change AudioDevice auto select first attempt

* Update target SDK to 30

* Use AudioDevice instance for the order

* Add device selection order parameter check

* Temporarily disable spotless

* Upgrade spotless

* Fix e2e test error

* Revert "Temporarily disable spotless"

This reverts commit 052d636.

* Temporarily disable spotless

* Fix runnable bug

* Fix new bluetooth headset activation when user previously selected bluetooth

* Clean up getHeadset logic a bit

* Add unit test for bluetooth headset activation

* Change preferredDeviceList element type to Class

* Change preferredList validation to check for duplicates in list

* Add audio device combination unit tests

* Add helper method to clean up test params

* Fix device removal bug in preferred device algorithm

* Revert "Temporarily disable spotless"

This reverts commit 052d636.

* Fix formatting

* No longer activate wired headset over preferred device list

* Replace public array preferred device param with vararg

* Add UnitTest logger

* Require check format for tests

* Disable java unit test logger

* Add JavaDoc for the public preferredDevices public constructor argument

* Swap top with front

* Change preferredDevices param javadoc wording

* Implement remaining AutomaticDeviceSelection instrumentation tests

* Revert back to list as type for preferredDeviceList

* Update changelog and gradle.properties

* Allow empty preferredDeviceList

* Refactor AudioSwitchTestParams test cases

* Add IllegalArgumentException @throws in the AudioSwitch constructor JavaDoc

* Remove optional buildToolsVersion from build.gradle

Co-authored-by: Aaron Alaniz <aalaniz@twilio.com>

* Add back ticks for null JavaDoc formatting

Co-authored-by: Aaron Alaniz <aalaniz@twilio.com>

* Update changelog with API version change

Co-authored-by: Aaron Alaniz <aalaniz@twilio.com>
  • Loading branch information
John Qualls and Aaron Alaniz authored Oct 8, 2020
1 parent 342e741 commit d799496
Show file tree
Hide file tree
Showing 21 changed files with 700 additions and 159 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

### 1.1.0

Enhancements

- Added a constructor parameter named `preferredDeviceList` to configure the order in which audio devices are automatically selected and activated when `selectedAudioDevice` is null.
```kotlin
val audioSwitch = AudioSwitch(application, preferredDeviceList = listOf(Speakerphone::class.java, BluetoothHeadset::class.java))
```
- Updated `compileSdkVersion` and `targetSdkVersion` to Android API version `30`.


### 1.0.1

Enhancements
Expand Down
5 changes: 2 additions & 3 deletions audioswitch/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ apply plugin: "com.jfrog.artifactory"
apply plugin: 'kotlin-android'

android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
compileSdkVersion 30

defaultConfig {
minSdkVersion 16
targetSdkVersion 29
targetSdkVersion 30
versionName audioSwitchVersion

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.twilio.audioswitch.AudioDevice.Earpiece
import com.twilio.audioswitch.AudioDevice.Speakerphone
import com.twilio.audioswitch.AudioDevice.WiredHeadset
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith

Expand All @@ -17,20 +18,53 @@ class AutomaticDeviceSelectionTest {
@UiThreadTest
@Test
fun `it_should_select_the_bluetooth_audio_device_by_default`() {
val (audioSwitch, bluetoothHeadsetReceiver) = setupFakeAudioSwitch(getInstrumentationContext())
val context = getInstrumentationContext()
val (audioSwitch, bluetoothHeadsetReceiver, wiredHeadsetReceiver) = setupFakeAudioSwitch(context)

audioSwitch.start { _, _ -> }
simulateBluetoothSystemIntent(getInstrumentationContext(), bluetoothHeadsetReceiver)
simulateBluetoothSystemIntent(context, bluetoothHeadsetReceiver)
simulateWiredHeadsetSystemIntent(context, wiredHeadsetReceiver)

assertEquals("Fake Headset", audioSwitch.selectedAudioDevice!!.name)
assertThat(audioSwitch.selectedAudioDevice!! is AudioDevice.BluetoothHeadset, equalTo(true))

audioSwitch.activate()
}

@UiThreadTest
@Test
fun `it_should_select_the_wired_headset_by_default`() {
val context = getInstrumentationContext()
val (audioSwitch, bluetoothHeadsetReceiver, wiredHeadsetReceiver) =
setupFakeAudioSwitch(context, listOf(WiredHeadset::class.java))

audioSwitch.start { _, _ -> }
simulateBluetoothSystemIntent(context, bluetoothHeadsetReceiver)
simulateWiredHeadsetSystemIntent(context, wiredHeadsetReceiver)

assertThat(audioSwitch.selectedAudioDevice!! is WiredHeadset, equalTo(true))
}

@UiThreadTest
@Test
fun `it_should_select_the_earpiece_audio_device_by_default`() {
val audioSwitch = AudioSwitch(getInstrumentationContext())
val context = getInstrumentationContext()
val (audioSwitch, bluetoothHeadsetReceiver) =
setupFakeAudioSwitch(context, listOf(Earpiece::class.java))
audioSwitch.start { _, _ -> }
simulateBluetoothSystemIntent(context, bluetoothHeadsetReceiver)

assertThat(audioSwitch.selectedAudioDevice!! is Earpiece, equalTo(true))
}

@UiThreadTest
@Test
fun `it_should_select_the_speakerphone_audio_device_by_default`() {
val context = getInstrumentationContext()
val (audioSwitch, bluetoothHeadsetReceiver) =
setupFakeAudioSwitch(context, listOf(Speakerphone::class.java))
audioSwitch.start { _, _ -> }
simulateBluetoothSystemIntent(context, bluetoothHeadsetReceiver)

assertThat(audioSwitch.selectedAudioDevice is Earpiece, equalTo(true))
assertThat(audioSwitch.selectedAudioDevice!! is Speakerphone, equalTo(true))
}
}
44 changes: 33 additions & 11 deletions audioswitch/src/androidTest/java/com.twilio.audioswitch/TestUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,58 +5,80 @@ import android.bluetooth.BluetoothHeadset
import android.content.Context
import android.content.Intent
import android.media.AudioManager
import android.media.AudioManager.OnAudioFocusChangeListener
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.twilio.audioswitch.AudioDevice.Earpiece
import com.twilio.audioswitch.AudioDevice.Speakerphone
import com.twilio.audioswitch.AudioDevice.WiredHeadset
import com.twilio.audioswitch.android.BuildWrapper
import com.twilio.audioswitch.android.DEVICE_NAME
import com.twilio.audioswitch.android.FakeBluetoothIntentProcessor
import com.twilio.audioswitch.android.HEADSET_NAME
import com.twilio.audioswitch.android.Logger
import com.twilio.audioswitch.android.ProductionLogger
import com.twilio.audioswitch.bluetooth.BluetoothHeadsetManager
import com.twilio.audioswitch.wired.INTENT_STATE
import com.twilio.audioswitch.wired.STATE_PLUGGED
import com.twilio.audioswitch.wired.WiredHeadsetReceiver
import java.util.concurrent.TimeoutException

internal fun setupFakeAudioSwitch(context: Context):
Pair<AudioSwitch, BluetoothHeadsetManager> {
internal fun setupFakeAudioSwitch(
context: Context,
preferredDevicesList: List<Class<out AudioDevice>> =
listOf(AudioDevice.BluetoothHeadset::class.java, WiredHeadset::class.java,
Earpiece::class.java, Speakerphone::class.java)
):
Triple<AudioSwitch, BluetoothHeadsetManager, WiredHeadsetReceiver> {

val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val logger = Logger()
val logger = ProductionLogger(true)
val audioDeviceManager =
AudioDeviceManager(context,
logger,
audioManager,
BuildWrapper(),
AudioFocusRequestWrapper(),
OnAudioFocusChangeListener {})
{})
val wiredHeadsetReceiver = WiredHeadsetReceiver(context, logger)
val headsetManager = BluetoothAdapter.getDefaultAdapter()?.let { bluetoothAdapter ->
BluetoothHeadsetManager(context, logger, bluetoothAdapter, audioDeviceManager,
bluetoothIntentProcessor = FakeBluetoothIntentProcessor())
} ?: run {
null
}
return Pair(AudioSwitch(context,
return Triple(AudioSwitch(context,
logger,
OnAudioFocusChangeListener {},
{},
preferredDevicesList,
audioDeviceManager,
wiredHeadsetReceiver,
headsetManager),
headsetManager!!)
headsetManager!!,
wiredHeadsetReceiver)
}

internal fun simulateBluetoothSystemIntent(
context: Context,
headsetManager: BluetoothHeadsetManager,
deviceName: String = HEADSET_NAME,
action: String = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED
action: String = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
connectionState: Int = BluetoothHeadset.STATE_CONNECTED
) {
val intent = Intent(action).apply {
putExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_CONNECTED)
putExtra(BluetoothHeadset.EXTRA_STATE, connectionState)
putExtra(DEVICE_NAME, deviceName)
}
headsetManager.onReceive(context, intent)
}

internal fun simulateWiredHeadsetSystemIntent(
context: Context,
wiredHeadsetReceiver: WiredHeadsetReceiver
) {
val intent = Intent().apply {
putExtra(INTENT_STATE, STATE_PLUGGED)
}
wiredHeadsetReceiver.onReceive(context, intent)
}

fun getTargetContext(): Context = getInstrumentation().targetContext

fun getInstrumentationContext(): Context = getInstrumentation().context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sealed class AudioDevice {
abstract val name: String

/** An [AudioDevice] representing a Bluetooth Headset.*/
data class BluetoothHeadset internal constructor(override val name: String) : AudioDevice()
data class BluetoothHeadset internal constructor(override val name: String = "Bluetooth") : AudioDevice()

/** An [AudioDevice] representing a Wired Headset.*/
data class WiredHeadset internal constructor(override val name: String = "Wired Headset") : AudioDevice()
Expand Down
Loading

0 comments on commit d799496

Please sign in to comment.