Skip to content

Commit

Permalink
Merge pull request #34 from yosemiteyss/feat/ios-audio-session-category
Browse files Browse the repository at this point in the history
support iOS audio session category control
  • Loading branch information
yosemiteyss authored Feb 10, 2023
2 parents 42d306f + 496b1f0 commit eef554c
Show file tree
Hide file tree
Showing 22 changed files with 540 additions and 245 deletions.
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,20 @@ await FlutterVolumeController.setMute(false);
await FlutterVolumeController.toggleMute();
```

#### Set the Audio Stream on Android
- Support two types of audio stream: `AudioStream.system`, `AudioStream.music`.
- This method should be called to ensure that volume controls adjust the correct stream.
#### Set Audio Stream on Android
- Adjusts to the audio stream whose volume should be changed by the hardware volume controls.
- Supported streams: `AudioStream.voiceCall`, `AudioStream.system`, `AudioStream.ring`, `AudioStream.music`, `AudioStream.alarm`.
- For more details, visit [AudioManager](https://developer.android.com/reference/android/media/AudioManager)
```dart
await FlutterVolumeController.setAndroidAudioStream(stream: AudioStream.system);
await FlutterVolumeController.setAndroidAudioStream(stream: AudioStream.music);
```

#### Set Audio Session Category on iOS
- Adjusts to a different set of audio behaviors.
- Supported categories: `AudioSessionCategory.ambient`, `AudioSessionCategory.multiRoute`, `AudioSessionCategory.playAndRecord`, `AudioSessionCategory.playback`, `AudioSessionCategory.record`, `AudioSessionCategory.soleAmbient`
- For more details, visit [AVAudioSession.Category](https://developer.apple.com/documentation/avfaudio/avaudiosession/category)
```dart
await FlutterVolumeController.setIOSAudioSessionCategory(category: AudioSessionCategory.playback);
```

#### Listen for Volume Changes
Expand All @@ -87,8 +95,6 @@ await FlutterVolumeController.setAndroidAudioStream(stream: AudioStream.music);
@override
void initState() {
super.initState();
// Ensure music stream in being controlled.
FlutterVolumeController.setAndroidAudioStream(stream: AudioStream.music);
FlutterVolumeController.addListener(
(volume) {
debugPrint('Volume changed: $volume');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ package com.yosemiteyss.flutter_volume_controller
import android.media.AudioManager

enum class AudioStream {
SYSTEM, MUSIC;
VOICE_CALL,
SYSTEM,
RING,
MUSIC,
ALARM;

val streamType: Int
get() {
return when (this) {
VOICE_CALL -> AudioManager.STREAM_VOICE_CALL
SYSTEM -> AudioManager.STREAM_SYSTEM
RING -> AudioManager.STREAM_RING
MUSIC -> AudioManager.STREAM_MUSIC
ALARM -> AudioManager.STREAM_ALARM
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package com.yosemiteyss.flutter_volume_controller

import android.app.Activity
import android.content.Context
import android.content.IntentFilter
import android.media.AudioManager
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.embedding.engine.plugins.lifecycle.FlutterLifecycleAdapter
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
Expand All @@ -18,14 +24,16 @@ internal const val EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM
internal const val VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION"

class FlutterVolumeControllerPlugin : FlutterPlugin, ActivityAware, MethodCallHandler,
EventChannel.StreamHandler {
EventChannel.StreamHandler, DefaultLifecycleObserver {
private lateinit var methodChannel: MethodChannel
private lateinit var eventChannel: EventChannel
private lateinit var volumeController: VolumeController
private lateinit var context: Context

private var activityPluginBinding: ActivityPluginBinding? = null
private var activity: Activity? = null
private var lifecycle: Lifecycle? = null
private var volumeBroadcastReceiver: VolumeBroadcastReceiver? = null
private var observedStream: AudioStream = AudioStream.MUSIC

override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext
Expand Down Expand Up @@ -136,8 +144,7 @@ class FlutterVolumeControllerPlugin : FlutterPlugin, ActivityAware, MethodCallHa
MethodName.SET_ANDROID_AUDIO_STREAM -> {
try {
val audioStream = call.argument<Int>(MethodArg.AUDIO_STREAM)!!
activityPluginBinding?.activity?.volumeControlStream =
AudioStream.values()[audioStream].streamType
setActivityAudioStream(AudioStream.values()[audioStream])
result.success(null)
} catch (e: Exception) {
result.error(
Expand All @@ -164,6 +171,8 @@ class FlutterVolumeControllerPlugin : FlutterPlugin, ActivityAware, MethodCallHa
val audioStream = AudioStream.values()[args[MethodArg.AUDIO_STREAM] as Int]
val emitOnStart = args[MethodArg.EMIT_ON_START] as Boolean

setActivityAudioStream(audioStream)

volumeBroadcastReceiver = VolumeBroadcastReceiver(events, audioStream).also {
context.registerReceiver(it, IntentFilter(VOLUME_CHANGED_ACTION))
}
Expand All @@ -182,21 +191,45 @@ class FlutterVolumeControllerPlugin : FlutterPlugin, ActivityAware, MethodCallHa
override fun onCancel(arguments: Any?) {
volumeBroadcastReceiver?.let(context::unregisterReceiver)
volumeBroadcastReceiver = null
resetActivityAudioStream()
}

override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activityPluginBinding = binding
activity = binding.activity
lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(binding).apply {
addObserver(this@FlutterVolumeControllerPlugin)
}
}

override fun onDetachedFromActivityForConfigChanges() {
activityPluginBinding = null
activity = null
}

override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activityPluginBinding = binding
activity = binding.activity
}

override fun onDetachedFromActivity() {
activityPluginBinding = null
activity = null
}

override fun onResume(owner: LifecycleOwner) {
if (volumeBroadcastReceiver != null) {
resumeActivityAudioStream()
}
}

private fun setActivityAudioStream(audioStream: AudioStream) {
activity?.volumeControlStream = audioStream.streamType
observedStream = audioStream
}

private fun resumeActivityAudioStream() {
activity?.volumeControlStream = observedStream.streamType
}

private fun resetActivityAudioStream() {
activity?.volumeControlStream = AudioManager.USE_DEFAULT_STREAM_TYPE
observedStream = AudioStream.MUSIC
}
}
4 changes: 3 additions & 1 deletion example/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ app.*.map.json
/android/app/release

# CMake
**/cmake-build-debug
**/cmake-build-debug

.fvm
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_volume_controller/ios"

SPEC CHECKSUMS:
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529

PODFILE CHECKSUM: ec7bdfce9f82e8314b94d9cd1cfee2974a0e1c97

COCOAPODS: 1.11.3
COCOAPODS: 1.11.2
10 changes: 6 additions & 4 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -199,6 +199,7 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
Expand Down Expand Up @@ -252,6 +253,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
Expand Down Expand Up @@ -339,7 +341,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down Expand Up @@ -418,7 +420,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -467,7 +469,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down
2 changes: 2 additions & 0 deletions example/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,7 @@
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
Loading

0 comments on commit eef554c

Please sign in to comment.