@siteed/expo-audio-studio 2.11.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -114,8 +114,17 @@ class AudioRecorderManager(
114
114
  private var telephonyManager: TelephonyManager? = null
115
115
  get() {
116
116
  if (field == null) {
117
- field = context.getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager
118
- LogUtils.d(CLASS_NAME, "TelephonyManager initialization: ${if (field != null) "successful" else "failed"}")
117
+ try {
118
+ field = context.getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager
119
+ if (field == null) {
120
+ LogUtils.w(CLASS_NAME, "TelephonyManager is null - device may not have telephony service (tablet/emulator)")
121
+ } else {
122
+ LogUtils.d(CLASS_NAME, "TelephonyManager initialization: successful")
123
+ }
124
+ } catch (e: Exception) {
125
+ LogUtils.w(CLASS_NAME, "Failed to initialize TelephonyManager: ${e.message}")
126
+ field = null
127
+ }
119
128
  }
120
129
  return field
121
130
  }
@@ -409,8 +418,9 @@ class AudioRecorderManager(
409
418
  }
410
419
  TelephonyManager.CALL_STATE_IDLE -> {
411
420
  if (_isRecording.get() && isPaused.get()) {
412
- LogUtils.d(CLASS_NAME, "Call ended, handling auto-resume (enabled: ${recordingConfig.autoResumeAfterInterruption})")
413
- if (recordingConfig.autoResumeAfterInterruption) {
421
+ val autoResume = if (::recordingConfig.isInitialized) recordingConfig.autoResumeAfterInterruption else false
422
+ LogUtils.d(CLASS_NAME, "Call ended, handling auto-resume (enabled: $autoResume)")
423
+ if (autoResume) {
414
424
  mainHandler.post {
415
425
  resumeRecording(object : Promise {
416
426
  override fun resolve(value: Any?) {
@@ -438,15 +448,18 @@ class AudioRecorderManager(
438
448
  }
439
449
  }
440
450
 
441
- if (telephonyManager != null) {
451
+ val localTelephonyManager = telephonyManager
452
+ if (localTelephonyManager != null) {
442
453
  try {
443
- telephonyManager?.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE)
454
+ localTelephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE)
444
455
  LogUtils.d(CLASS_NAME, "Successfully registered phone state listener")
456
+ } catch (e: SecurityException) {
457
+ LogUtils.w(CLASS_NAME, "Missing permission for phone state listener: ${e.message}")
445
458
  } catch (e: Exception) {
446
459
  LogUtils.e(CLASS_NAME, "Failed to register phone state listener", e)
447
460
  }
448
461
  } else {
449
- LogUtils.e(CLASS_NAME, "TelephonyManager is null, cannot register phone state listener")
462
+ LogUtils.w(CLASS_NAME, "TelephonyManager is null, phone call interruption handling disabled (device may not have telephony service)")
450
463
  }
451
464
  } else {
452
465
  LogUtils.w(CLASS_NAME, "READ_PHONE_STATE permission not granted, phone call interruption handling disabled")
@@ -479,7 +492,8 @@ class AudioRecorderManager(
479
492
  }
480
493
  }
481
494
  AudioManager.AUDIOFOCUS_GAIN -> {
482
- if (_isRecording.get() && isPaused.get() && recordingConfig.autoResumeAfterInterruption) {
495
+ val autoResume = if (::recordingConfig.isInitialized) recordingConfig.autoResumeAfterInterruption else false
496
+ if (_isRecording.get() && isPaused.get() && autoResume) {
483
497
  mainHandler.post {
484
498
  resumeRecording(object : Promise {
485
499
  override fun resolve(value: Any?) {
@@ -608,7 +622,11 @@ class AudioRecorderManager(
608
622
 
609
623
  } catch (e: Exception) {
610
624
  releaseAudioFocus()
611
- telephonyManager?.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
625
+ try {
626
+ telephonyManager?.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
627
+ } catch (e: Exception) {
628
+ LogUtils.w(CLASS_NAME, "Failed to unregister phone state listener: ${e.message}")
629
+ }
612
630
  promise.reject("UNEXPECTED_ERROR", "Unexpected error: ${e.message}", e)
613
631
  }
614
632
  }
@@ -985,15 +1003,30 @@ class AudioRecorderManager(
985
1003
  val fileSize = audioFile?.length() ?: 0
986
1004
  LogUtils.d(CLASS_NAME, "WAV File validation - Size: $fileSize bytes, Path: ${audioFile?.absolutePath}")
987
1005
 
988
- val dataFileSize = fileSize - 44 // Subtract header size
989
- val byteRate =
990
- recordingConfig.sampleRate * recordingConfig.channels * when (recordingConfig.encoding) {
991
- "pcm_8bit" -> 1
992
- "pcm_16bit" -> 2
993
- "pcm_32bit" -> 4
994
- else -> 2 // Default to 2 bytes per sample if the encoding is not recognized
1006
+ // Calculate duration based on context - use actual recording time for streaming-only mode
1007
+ val duration = if (!recordingConfig.output.primary.enabled) {
1008
+ // For streaming-only mode, calculate duration from actual recording time
1009
+ val actualRecordingTime = if (recordingStartTime > 0) {
1010
+ System.currentTimeMillis() - recordingStartTime - pausedDuration
1011
+ } else {
1012
+ 0L
995
1013
  }
996
- val duration = if (byteRate > 0) (dataFileSize * 1000 / byteRate) else 0
1014
+ LogUtils.d(CLASS_NAME, "Streaming-only mode: Using actual recording time: ${actualRecordingTime}ms")
1015
+ actualRecordingTime
1016
+ } else {
1017
+ // For file-based recording, calculate duration from file size
1018
+ val dataFileSize = fileSize - 44 // Subtract header size
1019
+ val byteRate =
1020
+ recordingConfig.sampleRate * recordingConfig.channels * when (recordingConfig.encoding) {
1021
+ "pcm_8bit" -> 1
1022
+ "pcm_16bit" -> 2
1023
+ "pcm_32bit" -> 4
1024
+ else -> 2 // Default to 2 bytes per sample if the encoding is not recognized
1025
+ }
1026
+ val fileDuration = if (byteRate > 0) (dataFileSize * 1000 / byteRate) else 0
1027
+ LogUtils.d(CLASS_NAME, "File-based mode: Using file size duration: ${fileDuration}ms")
1028
+ fileDuration
1029
+ }
997
1030
 
998
1031
  compressedRecorder?.apply {
999
1032
  stop()
@@ -1687,6 +1720,55 @@ class AudioRecorderManager(
1687
1720
 
1688
1721
  @SuppressLint("NewApi")
1689
1722
  private fun requestAudioFocus(): Boolean {
1723
+ val strategy = getAudioFocusStrategy()
1724
+
1725
+ when (strategy) {
1726
+ "none" -> {
1727
+ LogUtils.d(CLASS_NAME, "Skipping audio focus request (strategy: none)")
1728
+ return true
1729
+ }
1730
+
1731
+ "background" -> {
1732
+ LogUtils.d(CLASS_NAME, "Background recording - minimal audio focus")
1733
+ // For true background recording, we don't request audio focus
1734
+ // This allows recording to continue uninterrupted when users switch apps
1735
+ return true
1736
+ }
1737
+
1738
+ "communication" -> {
1739
+ return requestCommunicationAudioFocus()
1740
+ }
1741
+
1742
+ "interactive" -> {
1743
+ return requestInteractiveAudioFocus()
1744
+ }
1745
+
1746
+ else -> {
1747
+ LogUtils.w(CLASS_NAME, "Unknown audio focus strategy: $strategy, using interactive")
1748
+ return requestInteractiveAudioFocus()
1749
+ }
1750
+ }
1751
+ }
1752
+
1753
+ private fun getAudioFocusStrategy(): String {
1754
+ // Use explicit strategy if provided
1755
+ if (::recordingConfig.isInitialized) {
1756
+ recordingConfig.audioFocusStrategy?.let { return it }
1757
+
1758
+ // Smart defaults based on other config
1759
+ return if (recordingConfig.keepAwake && enableBackgroundAudio) {
1760
+ "background"
1761
+ } else {
1762
+ "interactive"
1763
+ }
1764
+ }
1765
+
1766
+ // Default strategy if recordingConfig is not initialized
1767
+ return "interactive"
1768
+ }
1769
+
1770
+ @SuppressLint("NewApi")
1771
+ private fun requestInteractiveAudioFocus(): Boolean {
1690
1772
  audioFocusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
1691
1773
  when (focusChange) {
1692
1774
  AudioManager.AUDIOFOCUS_LOSS,
@@ -1709,7 +1791,8 @@ class AudioRecorderManager(
1709
1791
  }
1710
1792
  }
1711
1793
  AudioManager.AUDIOFOCUS_GAIN -> {
1712
- if (_isRecording.get() && isPaused.get() && recordingConfig.autoResumeAfterInterruption) {
1794
+ val autoResume = if (::recordingConfig.isInitialized) recordingConfig.autoResumeAfterInterruption else false
1795
+ if (_isRecording.get() && isPaused.get() && autoResume) {
1713
1796
  mainHandler.post {
1714
1797
  resumeRecording(object : Promise {
1715
1798
  override fun resolve(value: Any?) {
@@ -1750,6 +1833,78 @@ class AudioRecorderManager(
1750
1833
  return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
1751
1834
  }
1752
1835
 
1836
+ @SuppressLint("NewApi")
1837
+ private fun requestCommunicationAudioFocus(): Boolean {
1838
+ audioFocusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
1839
+ when (focusChange) {
1840
+ AudioManager.AUDIOFOCUS_LOSS -> {
1841
+ // Only pause for permanent focus loss (like phone calls)
1842
+ if (_isRecording.get() && !isPaused.get()) {
1843
+ mainHandler.post {
1844
+ pauseRecording(object : Promise {
1845
+ override fun resolve(value: Any?) {
1846
+ isPaused.set(true)
1847
+ eventSender.sendExpoEvent(Constants.RECORDING_INTERRUPTED_EVENT_NAME, bundleOf(
1848
+ "reason" to "audioFocusLoss",
1849
+ "isPaused" to true
1850
+ ))
1851
+ }
1852
+ override fun reject(code: String, message: String?, cause: Throwable?) {
1853
+ LogUtils.e(CLASS_NAME, "Failed to pause recording on audio focus loss")
1854
+ }
1855
+ })
1856
+ }
1857
+ }
1858
+ }
1859
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
1860
+ // Don't pause for temporary loss in communication mode
1861
+ LogUtils.d(CLASS_NAME, "Ignoring transient audio focus loss in communication mode")
1862
+ }
1863
+ AudioManager.AUDIOFOCUS_GAIN -> {
1864
+ val autoResume = if (::recordingConfig.isInitialized) recordingConfig.autoResumeAfterInterruption else false
1865
+ if (_isRecording.get() && isPaused.get() && autoResume) {
1866
+ mainHandler.post {
1867
+ resumeRecording(object : Promise {
1868
+ override fun resolve(value: Any?) {
1869
+ eventSender.sendExpoEvent(Constants.RECORDING_INTERRUPTED_EVENT_NAME, bundleOf(
1870
+ "reason" to "audioFocusGain",
1871
+ "isPaused" to false
1872
+ ))
1873
+ }
1874
+ override fun reject(code: String, message: String?, cause: Throwable?) {
1875
+ LogUtils.e(CLASS_NAME, "Failed to resume recording on audio focus gain")
1876
+ }
1877
+ })
1878
+ }
1879
+ }
1880
+ }
1881
+ }
1882
+ }
1883
+
1884
+ val result = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1885
+ val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
1886
+ .setAudioAttributes(AudioAttributes.Builder()
1887
+ .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
1888
+ .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
1889
+ .build())
1890
+ .setAcceptsDelayedFocusGain(false)
1891
+ .setWillPauseWhenDucked(false)
1892
+ .setOnAudioFocusChangeListener(audioFocusChangeListener!!)
1893
+ .build()
1894
+ audioFocusRequest = focusRequest
1895
+ audioManager.requestAudioFocus(focusRequest)
1896
+ } else {
1897
+ @Suppress("DEPRECATION")
1898
+ audioManager.requestAudioFocus(
1899
+ audioFocusChangeListener,
1900
+ AudioManager.STREAM_VOICE_CALL,
1901
+ AudioManager.AUDIOFOCUS_GAIN
1902
+ )
1903
+ }
1904
+
1905
+ return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
1906
+ }
1907
+
1753
1908
  private fun releaseAudioFocus() {
1754
1909
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
1755
1910
  (audioFocusRequest as? AudioFocusRequest)?.let { request ->
@@ -64,6 +64,7 @@ data class RecordingConfig(
64
64
  val filename: String? = null,
65
65
  val deviceId: String? = null,
66
66
  val deviceDisconnectionBehavior: String? = null,
67
+ val audioFocusStrategy: String? = null,
67
68
  val bufferDurationSeconds: Double? = null,
68
69
  ) {
69
70
  companion object {
@@ -125,6 +126,10 @@ data class RecordingConfig(
125
126
  // Get device-related settings
126
127
  val deviceId = options["deviceId"] as? String
127
128
  val deviceDisconnectionBehavior = options["deviceDisconnectionBehavior"] as? String
129
+
130
+ // Get Android-specific settings
131
+ val androidConfig = options["android"] as? Map<String, Any>
132
+ val audioFocusStrategy = androidConfig?.get("audioFocusStrategy") as? String
128
133
 
129
134
  // Initialize the recording configuration with cleaned directory path
130
135
  val tempRecordingConfig = RecordingConfig(
@@ -151,6 +156,7 @@ data class RecordingConfig(
151
156
  filename = options["filename"] as? String,
152
157
  deviceId = deviceId,
153
158
  deviceDisconnectionBehavior = deviceDisconnectionBehavior,
159
+ audioFocusStrategy = audioFocusStrategy,
154
160
  bufferDurationSeconds = (options["bufferDurationSeconds"] as? Number)?.toDouble(),
155
161
  )
156
162
 
@@ -0,0 +1,249 @@
1
+ package net.siteed.audiostream
2
+
3
+ import org.junit.Test
4
+ import org.junit.Assert.*
5
+
6
+ /**
7
+ * Unit tests for audio focus strategy configuration and logic.
8
+ * These tests verify that the RecordingConfig correctly handles audioFocusStrategy
9
+ * parameter and that the smart defaults work as expected.
10
+ */
11
+ class AudioFocusStrategyTest {
12
+
13
+ @Test
14
+ fun testRecordingConfigWithExplicitBackgroundStrategy() {
15
+ val options = mapOf(
16
+ "sampleRate" to 44100,
17
+ "channels" to 1,
18
+ "encoding" to "pcm_16bit",
19
+ "android" to mapOf(
20
+ "audioFocusStrategy" to "background"
21
+ )
22
+ )
23
+
24
+ val result = RecordingConfig.fromMap(options)
25
+ assertTrue("Config creation should succeed", result.isSuccess)
26
+
27
+ val (config, _) = result.getOrThrow()
28
+ assertEquals("Audio focus strategy should be background", "background", config.audioFocusStrategy)
29
+ }
30
+
31
+ @Test
32
+ fun testRecordingConfigWithExplicitInteractiveStrategy() {
33
+ val options = mapOf(
34
+ "sampleRate" to 44100,
35
+ "channels" to 1,
36
+ "encoding" to "pcm_16bit",
37
+ "android" to mapOf(
38
+ "audioFocusStrategy" to "interactive"
39
+ )
40
+ )
41
+
42
+ val result = RecordingConfig.fromMap(options)
43
+ assertTrue("Config creation should succeed", result.isSuccess)
44
+
45
+ val (config, _) = result.getOrThrow()
46
+ assertEquals("Audio focus strategy should be interactive", "interactive", config.audioFocusStrategy)
47
+ }
48
+
49
+ @Test
50
+ fun testRecordingConfigWithExplicitCommunicationStrategy() {
51
+ val options = mapOf(
52
+ "sampleRate" to 44100,
53
+ "channels" to 1,
54
+ "encoding" to "pcm_16bit",
55
+ "android" to mapOf(
56
+ "audioFocusStrategy" to "communication"
57
+ )
58
+ )
59
+
60
+ val result = RecordingConfig.fromMap(options)
61
+ assertTrue("Config creation should succeed", result.isSuccess)
62
+
63
+ val (config, _) = result.getOrThrow()
64
+ assertEquals("Audio focus strategy should be communication", "communication", config.audioFocusStrategy)
65
+ }
66
+
67
+ @Test
68
+ fun testRecordingConfigWithExplicitNoneStrategy() {
69
+ val options = mapOf(
70
+ "sampleRate" to 44100,
71
+ "channels" to 1,
72
+ "encoding" to "pcm_16bit",
73
+ "android" to mapOf(
74
+ "audioFocusStrategy" to "none"
75
+ )
76
+ )
77
+
78
+ val result = RecordingConfig.fromMap(options)
79
+ assertTrue("Config creation should succeed", result.isSuccess)
80
+
81
+ val (config, _) = result.getOrThrow()
82
+ assertEquals("Audio focus strategy should be none", "none", config.audioFocusStrategy)
83
+ }
84
+
85
+ @Test
86
+ fun testRecordingConfigWithoutAudioFocusStrategy() {
87
+ val options = mapOf(
88
+ "sampleRate" to 44100,
89
+ "channels" to 1,
90
+ "encoding" to "pcm_16bit"
91
+ )
92
+
93
+ val result = RecordingConfig.fromMap(options)
94
+ assertTrue("Config creation should succeed", result.isSuccess)
95
+
96
+ val (config, _) = result.getOrThrow()
97
+ assertNull("Audio focus strategy should be null when not specified", config.audioFocusStrategy)
98
+ }
99
+
100
+ @Test
101
+ fun testRecordingConfigWithInvalidAudioFocusStrategy() {
102
+ val options = mapOf(
103
+ "sampleRate" to 44100,
104
+ "channels" to 1,
105
+ "encoding" to "pcm_16bit",
106
+ "android" to mapOf(
107
+ "audioFocusStrategy" to "invalid_strategy"
108
+ )
109
+ )
110
+
111
+ val result = RecordingConfig.fromMap(options)
112
+ assertTrue("Config creation should succeed even with invalid strategy", result.isSuccess)
113
+
114
+ val (config, _) = result.getOrThrow()
115
+ assertEquals("Invalid audio focus strategy should be preserved", "invalid_strategy", config.audioFocusStrategy)
116
+ }
117
+
118
+ @Test
119
+ fun testRecordingConfigWithNullAudioFocusStrategy() {
120
+ val options = mapOf(
121
+ "sampleRate" to 44100,
122
+ "channels" to 1,
123
+ "encoding" to "pcm_16bit",
124
+ "android" to mapOf(
125
+ "audioFocusStrategy" to null
126
+ )
127
+ )
128
+
129
+ val result = RecordingConfig.fromMap(options)
130
+ assertTrue("Config creation should succeed", result.isSuccess)
131
+
132
+ val (config, _) = result.getOrThrow()
133
+ assertNull("Audio focus strategy should be null", config.audioFocusStrategy)
134
+ }
135
+
136
+ @Test
137
+ fun testRecordingConfigKeepAwakeAndBackgroundStrategy() {
138
+ val options = mapOf(
139
+ "sampleRate" to 44100,
140
+ "channels" to 1,
141
+ "encoding" to "pcm_16bit",
142
+ "keepAwake" to true,
143
+ "android" to mapOf(
144
+ "audioFocusStrategy" to "background"
145
+ )
146
+ )
147
+
148
+ val result = RecordingConfig.fromMap(options)
149
+ assertTrue("Config creation should succeed", result.isSuccess)
150
+
151
+ val (config, _) = result.getOrThrow()
152
+ assertTrue("keepAwake should be true", config.keepAwake)
153
+ assertEquals("Audio focus strategy should be background", "background", config.audioFocusStrategy)
154
+ }
155
+
156
+ @Test
157
+ fun testRecordingConfigKeepAwakeFalseAndInteractiveStrategy() {
158
+ val options = mapOf(
159
+ "sampleRate" to 44100,
160
+ "channels" to 1,
161
+ "encoding" to "pcm_16bit",
162
+ "keepAwake" to false,
163
+ "android" to mapOf(
164
+ "audioFocusStrategy" to "interactive"
165
+ )
166
+ )
167
+
168
+ val result = RecordingConfig.fromMap(options)
169
+ assertTrue("Config creation should succeed", result.isSuccess)
170
+
171
+ val (config, _) = result.getOrThrow()
172
+ assertFalse("keepAwake should be false", config.keepAwake)
173
+ assertEquals("Audio focus strategy should be interactive", "interactive", config.audioFocusStrategy)
174
+ }
175
+
176
+ @Test
177
+ fun testRecordingConfigWithAutoResumeAndBackgroundStrategy() {
178
+ val options = mapOf(
179
+ "sampleRate" to 44100,
180
+ "channels" to 1,
181
+ "encoding" to "pcm_16bit",
182
+ "autoResumeAfterInterruption" to true,
183
+ "android" to mapOf(
184
+ "audioFocusStrategy" to "background"
185
+ )
186
+ )
187
+
188
+ val result = RecordingConfig.fromMap(options)
189
+ assertTrue("Config creation should succeed", result.isSuccess)
190
+
191
+ val (config, _) = result.getOrThrow()
192
+ assertEquals("Audio focus strategy should be background", "background", config.audioFocusStrategy)
193
+ assertTrue("autoResumeAfterInterruption should be true", config.autoResumeAfterInterruption)
194
+ }
195
+
196
+ @Test
197
+ fun testRecordingConfigWithCommunicationStrategyAndSpeechSampleRate() {
198
+ val options = mapOf(
199
+ "sampleRate" to 16000, // Common speech sample rate
200
+ "channels" to 1,
201
+ "encoding" to "pcm_16bit",
202
+ "android" to mapOf(
203
+ "audioFocusStrategy" to "communication"
204
+ )
205
+ )
206
+
207
+ val result = RecordingConfig.fromMap(options)
208
+ assertTrue("Config creation should succeed", result.isSuccess)
209
+
210
+ val (config, _) = result.getOrThrow()
211
+ assertEquals("Sample rate should be 16000", 16000, config.sampleRate)
212
+ assertEquals("Audio focus strategy should be communication", "communication", config.audioFocusStrategy)
213
+ }
214
+
215
+ @Test
216
+ fun testDefaultRecordingConfigValues() {
217
+ val result = RecordingConfig.fromMap(null)
218
+ assertTrue("Config creation should succeed with null input", result.isSuccess)
219
+
220
+ val (config, _) = result.getOrThrow()
221
+ assertNull("Default audio focus strategy should be null", config.audioFocusStrategy)
222
+ assertTrue("Default keepAwake should be true", config.keepAwake)
223
+ assertFalse("Default autoResumeAfterInterruption should be false", config.autoResumeAfterInterruption)
224
+ }
225
+
226
+ @Test
227
+ fun testRecordingConfigCompleteAudioFocusConfiguration() {
228
+ val options = mapOf(
229
+ "sampleRate" to 44100,
230
+ "channels" to 1,
231
+ "encoding" to "pcm_16bit",
232
+ "keepAwake" to true,
233
+ "autoResumeAfterInterruption" to true,
234
+ "showNotification" to true,
235
+ "android" to mapOf(
236
+ "audioFocusStrategy" to "background"
237
+ )
238
+ )
239
+
240
+ val result = RecordingConfig.fromMap(options)
241
+ assertTrue("Config creation should succeed", result.isSuccess)
242
+
243
+ val (config, _) = result.getOrThrow()
244
+ assertEquals("Audio focus strategy should be background", "background", config.audioFocusStrategy)
245
+ assertTrue("keepAwake should be true", config.keepAwake)
246
+ assertTrue("autoResumeAfterInterruption should be true", config.autoResumeAfterInterruption)
247
+ assertTrue("showNotification should be true", config.showNotification)
248
+ }
249
+ }