From d55ee725e059b261d29e5a06eb64190b23ac6fe6 Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Sun, 27 Aug 2023 03:31:03 +0200 Subject: [PATCH 1/3] feat: better descriptions, fix example --- .../example/lib/tabs/audio_context.dart | 48 +++++++++------ packages/audioplayers/example/lib/utils.dart | 2 +- .../lib/src/api/audio_context_config.dart | 58 ++++++++++--------- 3 files changed, 62 insertions(+), 46 deletions(-) diff --git a/packages/audioplayers/example/lib/tabs/audio_context.dart b/packages/audioplayers/example/lib/tabs/audio_context.dart index bb100c62e..722559dd7 100644 --- a/packages/audioplayers/example/lib/tabs/audio_context.dart +++ b/packages/audioplayers/example/lib/tabs/audio_context.dart @@ -3,6 +3,7 @@ import 'package:audioplayers_example/components/cbx.dart'; import 'package:audioplayers_example/components/drop_down.dart'; import 'package:audioplayers_example/components/tab_content.dart'; import 'package:audioplayers_example/components/tabs.dart'; +import 'package:audioplayers_example/utils.dart'; import 'package:flutter/material.dart'; class AudioContextTab extends StatefulWidget { @@ -82,10 +83,15 @@ class AudioContextTabState extends State } void updateConfig(AudioContextConfig newConfig) { - setState(() { - audioContextConfig = newConfig; - audioContext = audioContextConfig.build(); - }); + try { + final context = newConfig.build(); + setState(() { + audioContextConfig = newConfig; + audioContext = context; + }); + } on AssertionError catch (e) { + toast(e.message.toString()); + } } void updateAudioContextAndroid(AudioContextAndroid contextAndroid) { @@ -94,10 +100,15 @@ class AudioContextTabState extends State }); } - void updateAudioContextIOS(AudioContextIOS contextIOS) { - setState(() { - audioContext = audioContext.copy(iOS: contextIOS); - }); + void updateAudioContextIOS(AudioContextIOS Function() buildContextIOS) { + try { + final context = buildContextIOS(); + setState(() { + audioContext = audioContext.copy(iOS: context); + }); + } on AssertionError catch (e) { + toast(e.message.toString()); + } } Widget _genericTab() { @@ -194,19 +205,20 @@ class AudioContextTabState extends State Widget _iosTab() { final iosOptions = AVAudioSessionOptions.values.map( (option) { - final options = audioContext.iOS.options; + final options = {...audioContext.iOS.options}; return Cbx( option.name, value: options.contains(option), ({value}) { - if (value ?? false) { - options.add(option); - } else { - options.remove(option); - } - updateAudioContextIOS( - audioContext.iOS.copy(options: options), - ); + updateAudioContextIOS(() { + final iosContext = audioContext.iOS.copy(options: options); + if (value ?? false) { + options.add(option); + } else { + options.remove(option); + } + return iosContext; + }); }, ); }, @@ -219,7 +231,7 @@ class AudioContextTabState extends State options: {for (final e in AVAudioSessionCategory.values) e: e.name}, selected: audioContext.iOS.category, onChange: (v) => updateAudioContextIOS( - audioContext.iOS.copy(category: v), + () => audioContext.iOS.copy(category: v), ), ), ...iosOptions, diff --git a/packages/audioplayers/example/lib/utils.dart b/packages/audioplayers/example/lib/utils.dart index 50d3a7582..a8cfbcdb8 100644 --- a/packages/audioplayers/example/lib/utils.dart +++ b/packages/audioplayers/example/lib/utils.dart @@ -7,7 +7,7 @@ extension StateExt on State { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message, key: textKey), - duration: const Duration(milliseconds: 250), + duration: Duration(milliseconds: message.length * 25), ), ); } diff --git a/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart b/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart index 95611e3b0..38f4fde92 100644 --- a/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart +++ b/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart @@ -99,40 +99,44 @@ class AudioContextConfig { usageType: respectSilence ? AndroidUsageType.notificationRingtone : (route == AudioContextConfigRoute.earpiece - ? AndroidUsageType.voiceCommunication - : AndroidUsageType.media), + ? AndroidUsageType.voiceCommunication + : AndroidUsageType.media), audioFocus: duckAudio ? AndroidAudioFocus.gainTransientMayDuck : AndroidAudioFocus.gain, ); } - AudioContextIOS buildIOS() { - if (defaultTargetPlatform == TargetPlatform.iOS) { - validateIOS(); + AudioContextIOS? buildIOS() { + if (defaultTargetPlatform != TargetPlatform.iOS) { + return null; } - return AudioContextIOS( - category: respectSilence - ? AVAudioSessionCategory.ambient - : (route == AudioContextConfigRoute.speaker - ? AVAudioSessionCategory.playAndRecord - : (route == AudioContextConfigRoute.earpiece - ? AVAudioSessionCategory.playAndRecord - : AVAudioSessionCategory.playback)), - options: { - if (duckAudio) AVAudioSessionOptions.duckOthers, - if (route == AudioContextConfigRoute.speaker) - AVAudioSessionOptions.defaultToSpeaker, - }, - ); - } - - void validateIOS() { - // Please create a custom [AudioContextIOS] if the generic flags cannot - // represent your needs. - if (respectSilence && route == AudioContextConfigRoute.speaker) { - throw 'On iOS it is impossible to set both `respectSilence` and route ' - '`speaker`'; + try { + return AudioContextIOS( + category: respectSilence + ? AVAudioSessionCategory.ambient + : (route == AudioContextConfigRoute.speaker + ? AVAudioSessionCategory.playAndRecord + : (route == AudioContextConfigRoute.earpiece + ? AVAudioSessionCategory.playAndRecord + : AVAudioSessionCategory.playback)), + options: { + if (duckAudio) AVAudioSessionOptions.duckOthers, + if (route == AudioContextConfigRoute.speaker) + AVAudioSessionOptions.defaultToSpeaker, + }, + ); + } on AssertionError catch (e) { + // Please create a custom [AudioContextIOS] if the generic flags cannot + // represent your needs. + throw AssertionError( + 'Invalid AudioContextConfig on iOS platform: [' + 'route: $route, ' + 'duckAudio: $duckAudio, ' + 'respectSilence: $respectSilence, ' + 'stayAwake: $stayAwake]. \n' + 'Details: ${e.message}' + ); } } } From 299abde8a64b7a882f0acadb053b280e0c9eddd5 Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Mon, 28 Aug 2023 23:33:31 +0200 Subject: [PATCH 2/3] tests --- .../lib/src/api/audio_context_config.dart | 31 +++++---- .../test/audio_context_test.dart | 64 +++++++++++++++++++ 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart b/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart index 38f4fde92..9e006f363 100644 --- a/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart +++ b/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart @@ -99,8 +99,8 @@ class AudioContextConfig { usageType: respectSilence ? AndroidUsageType.notificationRingtone : (route == AudioContextConfigRoute.earpiece - ? AndroidUsageType.voiceCommunication - : AndroidUsageType.media), + ? AndroidUsageType.voiceCommunication + : AndroidUsageType.media), audioFocus: duckAudio ? AndroidAudioFocus.gainTransientMayDuck : AndroidAudioFocus.gain, @@ -116,10 +116,10 @@ class AudioContextConfig { category: respectSilence ? AVAudioSessionCategory.ambient : (route == AudioContextConfigRoute.speaker - ? AVAudioSessionCategory.playAndRecord - : (route == AudioContextConfigRoute.earpiece - ? AVAudioSessionCategory.playAndRecord - : AVAudioSessionCategory.playback)), + ? AVAudioSessionCategory.playAndRecord + : (route == AudioContextConfigRoute.earpiece + ? AVAudioSessionCategory.playAndRecord + : AVAudioSessionCategory.playback)), options: { if (duckAudio) AVAudioSessionOptions.duckOthers, if (route == AudioContextConfigRoute.speaker) @@ -130,15 +130,20 @@ class AudioContextConfig { // Please create a custom [AudioContextIOS] if the generic flags cannot // represent your needs. throw AssertionError( - 'Invalid AudioContextConfig on iOS platform: [' - 'route: $route, ' - 'duckAudio: $duckAudio, ' - 'respectSilence: $respectSilence, ' - 'stayAwake: $stayAwake]. \n' - 'Details: ${e.message}' - ); + 'Invalid AudioContextConfig on iOS platform: ${toString()}\n' + 'Details: ${e.message}'); } } + + @override + String toString() { + return 'AudioContextConfig(' + 'route: $route, ' + 'duckAudio: $duckAudio, ' + 'respectSilence: $respectSilence, ' + 'stayAwake: $stayAwake' + ')'; + } } enum AudioContextConfigRoute { diff --git a/packages/audioplayers_platform_interface/test/audio_context_test.dart b/packages/audioplayers_platform_interface/test/audio_context_test.dart index 7efcf657f..1313f542b 100644 --- a/packages/audioplayers_platform_interface/test/audio_context_test.dart +++ b/packages/audioplayers_platform_interface/test/audio_context_test.dart @@ -1,6 +1,8 @@ //ignore_for_file: avoid_redundant_argument_values import 'package:audioplayers_platform_interface/audioplayers_platform_interface.dart'; +import 'package:audioplayers_platform_interface/src/api/audio_context_config.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -27,6 +29,68 @@ void main() { ); }); + test('Compare AudioContextConfig with AudioContext', () async { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + const boolValues = {true, false}; + const routeValues = AudioContextConfigRoute.values; + + final throwsAssertion = []; + for (final isDuckAudio in boolValues) { + for (final isRespectSilence in boolValues) { + for (final isStayAwake in boolValues) { + for (final route in routeValues) { + final config = AudioContextConfig( + duckAudio: isDuckAudio, + respectSilence: isRespectSilence, + stayAwake: isStayAwake, + route: route, + ); + try { + config.build(); + throwsAssertion.add(false); + } on AssertionError catch (e) { + // if(e.startsWith('...')) {} + throwsAssertion.add(true); + print('#########'); + print(e); + } + } + } + } + } + + // Ensure assertions keep thrown on the correct cases. + expect( + throwsAssertion, + const [ + true, + true, + true, + true, + true, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + false, + false, + false + ], + ); + }); + test('Create invalid AudioContextIOS', () async { try { // Throws AssertionError: From 821ab3a63ab09465309b74ae5e1778143379f1dc Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Tue, 29 Aug 2023 00:09:50 +0200 Subject: [PATCH 3/3] better validation --- .../lib/src/api/audio_context_config.dart | 52 +++++++++++-------- .../test/audio_context_test.dart | 18 ++++--- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart b/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart index 9e006f363..3baec0a5c 100644 --- a/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart +++ b/packages/audioplayers_platform_interface/lib/src/api/audio_context_config.dart @@ -111,28 +111,36 @@ class AudioContextConfig { if (defaultTargetPlatform != TargetPlatform.iOS) { return null; } - try { - return AudioContextIOS( - category: respectSilence - ? AVAudioSessionCategory.ambient - : (route == AudioContextConfigRoute.speaker - ? AVAudioSessionCategory.playAndRecord - : (route == AudioContextConfigRoute.earpiece - ? AVAudioSessionCategory.playAndRecord - : AVAudioSessionCategory.playback)), - options: { - if (duckAudio) AVAudioSessionOptions.duckOthers, - if (route == AudioContextConfigRoute.speaker) - AVAudioSessionOptions.defaultToSpeaker, - }, - ); - } on AssertionError catch (e) { - // Please create a custom [AudioContextIOS] if the generic flags cannot - // represent your needs. - throw AssertionError( - 'Invalid AudioContextConfig on iOS platform: ${toString()}\n' - 'Details: ${e.message}'); - } + validateIOS(); + return AudioContextIOS( + category: respectSilence + ? AVAudioSessionCategory.ambient + : (route == AudioContextConfigRoute.speaker + ? AVAudioSessionCategory.playAndRecord + : (route == AudioContextConfigRoute.earpiece + ? AVAudioSessionCategory.playAndRecord + : AVAudioSessionCategory.playback)), + options: { + if (duckAudio) AVAudioSessionOptions.duckOthers, + if (route == AudioContextConfigRoute.speaker) + AVAudioSessionOptions.defaultToSpeaker, + }, + ); + } + + void validateIOS() { + const invalidMsg = + 'Invalid AudioContextConfig: On iOS it is not possible to set'; + const tip = 'Please create a custom [AudioContextIOS] if the generic flags ' + 'cannot represent your needs.'; + assert( + !(duckAudio && respectSilence), + '$invalidMsg `respectSilence` and `duckAudio`. $tip', + ); + assert( + !(respectSilence && route == AudioContextConfigRoute.speaker), + '$invalidMsg `respectSilence` and route `speaker`. $tip', + ); } @override diff --git a/packages/audioplayers_platform_interface/test/audio_context_test.dart b/packages/audioplayers_platform_interface/test/audio_context_test.dart index 1313f542b..b3aa72d09 100644 --- a/packages/audioplayers_platform_interface/test/audio_context_test.dart +++ b/packages/audioplayers_platform_interface/test/audio_context_test.dart @@ -29,7 +29,7 @@ void main() { ); }); - test('Compare AudioContextConfig with AudioContext', () async { + test('Check AudioContextConfig assertions', () async { debugDefaultTargetPlatformOverride = TargetPlatform.iOS; const boolValues = {true, false}; const routeValues = AudioContextConfigRoute.values; @@ -49,10 +49,16 @@ void main() { config.build(); throwsAssertion.add(false); } on AssertionError catch (e) { - // if(e.startsWith('...')) {} - throwsAssertion.add(true); - print('#########'); - print(e); + if (e.message + .toString() + .startsWith('Invalid AudioContextConfig')) { + throwsAssertion.add(true); + } else { + fail( + 'Assertion of $config does not match the expected ' + 'description. See: $e', + ); + } } } } @@ -86,7 +92,7 @@ void main() { false, false, false, - false + false, ], ); });