diff --git a/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java b/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java index 9a0fc6c..e47a854 100644 --- a/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java +++ b/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java @@ -1,14 +1,8 @@ package com.ryanheise.just_audio; -import android.Manifest; -import android.app.Activity; import android.content.Context; -import android.content.pm.PackageManager; -import android.media.audiofx.Visualizer; import android.net.Uri; import android.os.Handler; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.MediaItem; @@ -41,13 +35,13 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import io.flutter.Log; -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.EventChannel; +import io.flutter.plugin.common.EventChannel.EventSink; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; -import io.flutter.plugin.common.PluginRegistry; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -56,7 +50,7 @@ import java.util.List; import java.util.Map; import java.util.Random; -public class AudioPlayer implements MethodCallHandler, Player.EventListener, AudioListener, MetadataOutput, PluginRegistry.RequestPermissionsResultListener { +public class AudioPlayer implements MethodCallHandler, Player.EventListener, AudioListener, MetadataOutput { static final String TAG = "AudioPlayer"; @@ -64,8 +58,8 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud private final Context context; private final MethodChannel methodChannel; - private final BetterEventChannel eventChannel; - private ActivityPluginBinding activityPluginBinding; + private final EventChannel eventChannel; + private EventSink eventSink; private ProcessingState processingState; private long bufferedPosition; @@ -83,12 +77,6 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud private IcyHeaders icyHeaders; private int errorCount; private AudioAttributes pendingAudioAttributes; - private BetterVisualizer visualizer; - private Result startVisualizerResult; - private boolean enableWaveform; - private boolean enableFft; - private Integer visualizerCaptureRate; - private Integer visualizerCaptureSize; private SimpleExoPlayer player; private Integer audioSessionId; @@ -126,27 +114,19 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud this.context = applicationContext; methodChannel = new MethodChannel(messenger, "com.ryanheise.just_audio.methods." + id); methodChannel.setMethodCallHandler(this); - eventChannel = new BetterEventChannel(messenger, "com.ryanheise.just_audio.events." + id); - visualizer = new BetterVisualizer(messenger, id); - processingState = ProcessingState.none; - } - - private void requestPermissions() { - ActivityCompat.requestPermissions(activityPluginBinding.getActivity(), new String[] { Manifest.permission.RECORD_AUDIO }, 1); - } - - public void setActivityPluginBinding(ActivityPluginBinding activityPluginBinding) { - if (this.activityPluginBinding != null && this.activityPluginBinding != activityPluginBinding) { - this.activityPluginBinding.removeRequestPermissionsResultListener(this); - } - this.activityPluginBinding = activityPluginBinding; - if (activityPluginBinding != null) { - activityPluginBinding.addRequestPermissionsResultListener(this); - // If there is a pending startVisualizer request - if (startVisualizerResult != null) { - requestPermissions(); + eventChannel = new EventChannel(messenger, "com.ryanheise.just_audio.events." + id); + eventChannel.setStreamHandler(new EventChannel.StreamHandler() { + @Override + public void onListen(final Object arguments, final EventSink eventSink) { + AudioPlayer.this.eventSink = eventSink; } - } + + @Override + public void onCancel(final Object arguments) { + eventSink = null; + } + }); + processingState = ProcessingState.none; } private void startWatchingBuffer() { @@ -154,22 +134,6 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud handler.post(bufferWatcher); } - @Override - public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - for (int i = 0; i < permissions.length; i++) { - if (permissions[i].equals(Manifest.permission.RECORD_AUDIO)) { - if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { - visualizer.setHasPermission(true); - completeStartVisualizer(true); - return true; - } - completeStartVisualizer(false); - break; - } - } - return false; - } - @Override public void onAudioSessionId(int audioSessionId) { if (audioSessionId == C.AUDIO_SESSION_ID_UNSET) { @@ -177,7 +141,6 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud } else { this.audioSessionId = audioSessionId; } - visualizer.onAudioSessionId(this.audioSessionId); } @Override @@ -314,48 +277,12 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud seekResult = null; } - private void completeStartVisualizer(boolean success) { - if (startVisualizerResult == null) return; - if (success) { - visualizer.start(visualizerCaptureRate, visualizerCaptureSize, enableWaveform, enableFft); - Map resultMap = new HashMap(); - resultMap.put("samplingRate", visualizer.getSamplingRate()); - startVisualizerResult.success(resultMap); - } else { - startVisualizerResult.error("RECORD_AUDIO permission denied", null, null); - } - startVisualizerResult = null; - } - @Override public void onMethodCall(final MethodCall call, final Result result) { ensurePlayerInitialized(); try { switch (call.method) { - case "startVisualizer": - Boolean enableWaveform = call.argument("enableWaveform"); - Boolean enableFft = call.argument("enableFft"); - Integer captureRate = call.argument("captureRate"); - Integer captureSize = call.argument("captureSize"); - this.enableWaveform = enableWaveform; - this.enableFft = enableFft; - visualizerCaptureRate = captureRate; - visualizerCaptureSize = captureSize; - startVisualizerResult = result; - if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { - visualizer.setHasPermission(true); - completeStartVisualizer(true); - } else if (activityPluginBinding != null && activityPluginBinding.getActivity() != null) { - requestPermissions(); - } else { - // Will request permission in setActivityPluginBinding - } - break; - case "stopVisualizer": - visualizer.stop(); - result.success(new HashMap()); - break; case "load": Long initialPosition = getLong(call.argument("initialPosition")); Integer initialIndex = call.argument("initialIndex"); @@ -638,7 +565,9 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud event.put("currentIndex", currentIndex); event.put("androidAudioSessionId", audioSessionId); - eventChannel.success(event); + if (eventSink != null) { + eventSink.success(event); + } } private Map collectIcyMetadata() { @@ -686,7 +615,9 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud prepareResult = null; } - eventChannel.error(errorCode, errorMsg, null); + if (eventSink != null) { + eventSink.error(errorCode, errorMsg, null); + } } private void transition(final ProcessingState newState) { @@ -775,8 +706,9 @@ public class AudioPlayer implements MethodCallHandler, Player.EventListener, Aud player = null; transition(ProcessingState.none); } - eventChannel.endOfStream(); - visualizer.dispose(); + if (eventSink != null) { + eventSink.endOfStream(); + } } private void abortSeek() { diff --git a/just_audio/android/src/main/java/com/ryanheise/just_audio/BetterEventChannel.java b/just_audio/android/src/main/java/com/ryanheise/just_audio/BetterEventChannel.java deleted file mode 100644 index 2fdb443..0000000 --- a/just_audio/android/src/main/java/com/ryanheise/just_audio/BetterEventChannel.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.ryanheise.just_audio; - -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.EventChannel; -import io.flutter.plugin.common.EventChannel.EventSink; - -public class BetterEventChannel implements EventSink { - private EventSink eventSink; - - public BetterEventChannel(final BinaryMessenger messenger, final String id) { - EventChannel eventChannel = new EventChannel(messenger, id); - eventChannel.setStreamHandler(new EventChannel.StreamHandler() { - @Override - public void onListen(final Object arguments, final EventSink eventSink) { - BetterEventChannel.this.eventSink = eventSink; - } - - @Override - public void onCancel(final Object arguments) { - eventSink = null; - } - }); - } - - @Override - public void success(Object event) { - if (eventSink != null) eventSink.success(event); - } - - @Override - public void error(String errorCode, String errorMessage, Object errorDetails) { - if (eventSink != null) eventSink.error(errorCode, errorMessage, errorDetails); - } - - @Override - public void endOfStream() { - if (eventSink != null) eventSink.endOfStream(); - } -} diff --git a/just_audio/android/src/main/java/com/ryanheise/just_audio/BetterVisualizer.java b/just_audio/android/src/main/java/com/ryanheise/just_audio/BetterVisualizer.java deleted file mode 100644 index af274e6..0000000 --- a/just_audio/android/src/main/java/com/ryanheise/just_audio/BetterVisualizer.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.ryanheise.just_audio; - -import android.media.audiofx.Visualizer; -import io.flutter.plugin.common.BinaryMessenger; - -public class BetterVisualizer { - private Visualizer visualizer; - private final BetterEventChannel waveformEventChannel; - private final BetterEventChannel fftEventChannel; - private Integer audioSessionId; - private int captureRate; - private int captureSize; - private boolean enableWaveform; - private boolean enableFft; - private boolean pendingStartRequest; - private boolean hasPermission; - - public BetterVisualizer(final BinaryMessenger messenger, String id) { - waveformEventChannel = new BetterEventChannel(messenger, "com.ryanheise.just_audio.waveform_events." + id); - fftEventChannel = new BetterEventChannel(messenger, "com.ryanheise.just_audio.fft_events." + id); - } - - public int getSamplingRate() { - return visualizer.getSamplingRate(); - } - - public void setHasPermission(boolean hasPermission) { - this.hasPermission = hasPermission; - } - - public void onAudioSessionId(Integer audioSessionId) { - this.audioSessionId = audioSessionId; - if (audioSessionId != null && hasPermission && pendingStartRequest) { - start(captureRate, captureSize, enableWaveform, enableFft); - } - } - - public void start(Integer captureRate, Integer captureSize, final boolean enableWavefrom, final boolean enableFft) { - if (visualizer != null) return; - if (captureRate == null) { - captureRate = Visualizer.getMaxCaptureRate() / 2; - } else if (captureRate > Visualizer.getMaxCaptureRate()) { - captureRate = Visualizer.getMaxCaptureRate(); - } - if (captureSize == null) { - captureSize = Visualizer.getCaptureSizeRange()[1]; - } else if (captureSize > Visualizer.getCaptureSizeRange()[1]) { - captureSize = Visualizer.getCaptureSizeRange()[1]; - } else if (captureSize < Visualizer.getCaptureSizeRange()[0]) { - captureSize = Visualizer.getCaptureSizeRange()[0]; - } - this.enableWaveform = enableWaveform; - this.enableFft = enableFft; - this.captureRate = captureRate; - if (audioSessionId == null || !hasPermission) { - pendingStartRequest = true; - return; - } - pendingStartRequest = false; - visualizer = new Visualizer(audioSessionId); - visualizer.setCaptureSize(captureSize); - visualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() { - public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) { - waveformEventChannel.success(waveform); - } - public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) { - fftEventChannel.success(fft); - } - }, captureRate, enableWavefrom, enableFft); - visualizer.setEnabled(true); - } - - public void stop() { - if (visualizer == null) return; - visualizer.setDataCaptureListener(null, captureRate, enableWaveform, enableFft); - visualizer.setEnabled(false); - visualizer.release(); - visualizer = null; - } - - public void dispose() { - stop(); - waveformEventChannel.endOfStream(); - fftEventChannel.endOfStream(); - } -} diff --git a/just_audio/android/src/main/java/com/ryanheise/just_audio/JustAudioPlugin.java b/just_audio/android/src/main/java/com/ryanheise/just_audio/JustAudioPlugin.java index 90aae5c..52dbbb6 100644 --- a/just_audio/android/src/main/java/com/ryanheise/just_audio/JustAudioPlugin.java +++ b/just_audio/android/src/main/java/com/ryanheise/just_audio/JustAudioPlugin.java @@ -3,8 +3,6 @@ package com.ryanheise.just_audio; import android.content.Context; import androidx.annotation.NonNull; 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.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.PluginRegistry.Registrar; @@ -12,7 +10,7 @@ import io.flutter.plugin.common.PluginRegistry.Registrar; /** * JustAudioPlugin */ -public class JustAudioPlugin implements FlutterPlugin, ActivityAware { +public class JustAudioPlugin implements FlutterPlugin { private MethodChannel channel; private MainMethodCallHandler methodCallHandler; @@ -43,25 +41,6 @@ public class JustAudioPlugin implements FlutterPlugin, ActivityAware { stopListening(); } - @Override - public void onAttachedToActivity(ActivityPluginBinding binding) { - methodCallHandler.setActivityPluginBinding(binding); - } - - @Override - public void onDetachedFromActivityForConfigChanges() { - } - - @Override - public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) { - methodCallHandler.setActivityPluginBinding(binding); - } - - @Override - public void onDetachedFromActivity() { - methodCallHandler.setActivityPluginBinding(null); - } - private void startListening(Context applicationContext, BinaryMessenger messenger) { methodCallHandler = new MainMethodCallHandler(applicationContext, messenger); diff --git a/just_audio/android/src/main/java/com/ryanheise/just_audio/MainMethodCallHandler.java b/just_audio/android/src/main/java/com/ryanheise/just_audio/MainMethodCallHandler.java index b4146de..5e3064d 100644 --- a/just_audio/android/src/main/java/com/ryanheise/just_audio/MainMethodCallHandler.java +++ b/just_audio/android/src/main/java/com/ryanheise/just_audio/MainMethodCallHandler.java @@ -1,9 +1,7 @@ package com.ryanheise.just_audio; -import android.app.Activity; import android.content.Context; import androidx.annotation.NonNull; -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; @@ -17,7 +15,6 @@ public class MainMethodCallHandler implements MethodCallHandler { private final Context applicationContext; private final BinaryMessenger messenger; - private ActivityPluginBinding activityPluginBinding; private final Map players = new HashMap<>(); @@ -27,13 +24,6 @@ public class MainMethodCallHandler implements MethodCallHandler { this.messenger = messenger; } - void setActivityPluginBinding(ActivityPluginBinding activityPluginBinding) { - this.activityPluginBinding = activityPluginBinding; - for (AudioPlayer player : players.values()) { - player.setActivityPluginBinding(activityPluginBinding); - } - } - @Override public void onMethodCall(MethodCall call, @NonNull Result result) { final Map request = call.arguments(); @@ -44,11 +34,7 @@ public class MainMethodCallHandler implements MethodCallHandler { result.error("Platform player " + id + " already exists", null, null); break; } - final AudioPlayer player = new AudioPlayer(applicationContext, messenger, id); - players.put(id, player); - if (activityPluginBinding != null) { - player.setActivityPluginBinding(activityPluginBinding); - } + players.put(id, new AudioPlayer(applicationContext, messenger, id)); result.success(null); break; } diff --git a/just_audio/darwin/Classes/AudioPlayer.m b/just_audio/darwin/Classes/AudioPlayer.m index 76479f5..1cfbb15 100644 --- a/just_audio/darwin/Classes/AudioPlayer.m +++ b/just_audio/darwin/Classes/AudioPlayer.m @@ -134,7 +134,7 @@ result(FlutterMethodNotImplemented); } } @catch (id exception) { - //NSLog(@"Error in handleMethodCall"); + NSLog(@"Error in handleMethodCall"); FlutterError *flutterError = [FlutterError errorWithCode:@"error" message:@"Error in handleMethodCall" details:nil]; result(flutterError); } @@ -278,7 +278,7 @@ [self broadcastPlaybackEvent]; } else if (drift < -100) { [self enterBuffering:@"stalling"]; - //NSLog(@"Drift: %lld", drift); + NSLog(@"Drift: %lld", drift); [self updatePosition]; [self broadcastPlaybackEvent]; } @@ -632,16 +632,16 @@ - (void)onItemStalled:(NSNotification *)notification { //IndexedPlayerItem *playerItem = (IndexedPlayerItem *)notification.object; - //NSLog(@"onItemStalled"); + NSLog(@"onItemStalled"); } - (void)onFailToComplete:(NSNotification *)notification { //IndexedPlayerItem *playerItem = (IndexedPlayerItem *)notification.object; - //NSLog(@"onFailToComplete"); + NSLog(@"onFailToComplete"); } - (void)onComplete:(NSNotification *)notification { - //NSLog(@"onComplete"); + NSLog(@"onComplete"); IndexedPlayerItem *endedPlayerItem = (IndexedPlayerItem *)notification.object; IndexedAudioSource *endedSource = endedPlayerItem.audioSource; @@ -731,7 +731,7 @@ break; } case AVPlayerItemStatusFailed: { - //NSLog(@"AVPlayerItemStatusFailed"); + NSLog(@"AVPlayerItemStatusFailed"); [self sendErrorForItem:playerItem]; break; } @@ -753,7 +753,7 @@ _bufferUnconfirmed = NO; [self leaveBuffering:@"playing, _bufferUnconfirmed && playbackBufferFull"]; [self updatePosition]; - //NSLog(@"Buffering confirmed! leaving buffering"); + NSLog(@"Buffering confirmed! leaving buffering"); [self broadcastPlaybackEvent]; } } @@ -788,7 +788,7 @@ [self updatePosition]; [self broadcastPlaybackEvent]; } else { - //NSLog(@"Ignoring wait signal because we reached the end"); + NSLog(@"Ignoring wait signal because we reached the end"); } break; case AVPlayerTimeControlStatusPlaying: @@ -805,11 +805,11 @@ if ([_orderInv[_index] intValue] + 1 < [_order count]) { // account for automatic move to next item _index = [_order[[_orderInv[_index] intValue] + 1] intValue]; - //NSLog(@"advance to next on error: index = %d", _index); + NSLog(@"advance to next on error: index = %d", _index); [self updateEndAction]; [self broadcastPlaybackEvent]; } else { - //NSLog(@"error on last item"); + NSLog(@"error on last item"); } return; } else { @@ -817,7 +817,7 @@ if (_index != expectedIndex) { // AVQueuePlayer will sometimes skip over error items without // notifying this observer. - //NSLog(@"Queue change detected. Adjusting index from %d -> %d", _index, expectedIndex); + NSLog(@"Queue change detected. Adjusting index from %d -> %d", _index, expectedIndex); _index = expectedIndex; [self updateEndAction]; [self broadcastPlaybackEvent]; @@ -896,7 +896,7 @@ } - (void)sendError:(FlutterError *)flutterError playerItem:(IndexedPlayerItem *)playerItem { - //NSLog(@"sendError"); + NSLog(@"sendError"); if (_loadResult && playerItem == _player.currentItem) { _loadResult(flutterError); _loadResult = nil; @@ -936,7 +936,7 @@ } if (result) { if (_playResult) { - //NSLog(@"INTERRUPTING PLAY"); + NSLog(@"INTERRUPTING PLAY"); _playResult(@{}); } _playResult = result; @@ -960,7 +960,7 @@ [self updatePosition]; [self broadcastPlaybackEvent]; if (_playResult) { - //NSLog(@"PLAY FINISHED DUE TO PAUSE"); + NSLog(@"PLAY FINISHED DUE TO PAUSE"); _playResult(@{}); _playResult = nil; } @@ -971,7 +971,7 @@ _processingState = completed; [self broadcastPlaybackEvent]; if (_playResult) { - //NSLog(@"PLAY FINISHED DUE TO COMPLETE"); + NSLog(@"PLAY FINISHED DUE TO COMPLETE"); _playResult(@{}); _playResult = nil; } @@ -1029,7 +1029,7 @@ // - when the shuffle order changes. (TODO) // - when the shuffle mode changes. if (!_player) return; - if (_audioSource && (_loopMode != loopOff || ([_order count] > 0 && [_orderInv[_index] intValue] + 1 < [_order count]))) { + if (_audioSource && (_loopMode != loopOff || [_orderInv[_index] intValue] + 1 < [_order count])) { _player.actionAtItemEnd = AVPlayerActionAtItemEndAdvance; } else { _player.actionAtItemEnd = AVPlayerActionAtItemEndPause; // AVPlayerActionAtItemEndNone @@ -1037,7 +1037,7 @@ } - (void)setShuffleModeEnabled:(BOOL)shuffleModeEnabled { - //NSLog(@"setShuffleModeEnabled: %d", shuffleModeEnabled); + NSLog(@"setShuffleModeEnabled: %d", shuffleModeEnabled); _shuffleModeEnabled = shuffleModeEnabled; if (!_audioSource) return; diff --git a/just_audio/example/.gitignore b/just_audio/example/.gitignore new file mode 100644 index 0000000..7c36bfa --- /dev/null +++ b/just_audio/example/.gitignore @@ -0,0 +1,85 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* +**/ios/Flutter/Flutter.podspec + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/just_audio/example/.metadata b/just_audio/example/.metadata new file mode 100644 index 0000000..fea404f --- /dev/null +++ b/just_audio/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 68587a0916366e9512a78df22c44163d041dd5f3 + channel: stable + +project_type: app diff --git a/just_audio/example/README.md b/just_audio/example/README.md new file mode 100644 index 0000000..009ea35 --- /dev/null +++ b/just_audio/example/README.md @@ -0,0 +1,16 @@ +# just_audio_example + +Demonstrates how to use the just_audio plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/just_audio/example/android/.gitignore b/just_audio/example/android/.gitignore new file mode 100644 index 0000000..bc2100d --- /dev/null +++ b/just_audio/example/android/.gitignore @@ -0,0 +1,7 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java diff --git a/just_audio/example/android/app/build.gradle b/just_audio/example/android/app/build.gradle new file mode 100644 index 0000000..a6aa025 --- /dev/null +++ b/just_audio/example/android/app/build.gradle @@ -0,0 +1,61 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 28 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.ryanheise.just_audio_example" + minSdkVersion 19 + targetSdkVersion 28 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' +} diff --git a/just_audio/example/android/app/src/debug/AndroidManifest.xml b/just_audio/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..df636f4 --- /dev/null +++ b/just_audio/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/just_audio/example/android/app/src/main/AndroidManifest.xml b/just_audio/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e99a2c7 --- /dev/null +++ b/just_audio/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + diff --git a/just_audio/example/android/app/src/main/java/com/ryanheise/just_audio_example/MainActivity.java b/just_audio/example/android/app/src/main/java/com/ryanheise/just_audio_example/MainActivity.java new file mode 100644 index 0000000..ccea830 --- /dev/null +++ b/just_audio/example/android/app/src/main/java/com/ryanheise/just_audio_example/MainActivity.java @@ -0,0 +1,6 @@ +package com.ryanheise.just_audio_example; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/just_audio/example/android/app/src/main/res/drawable/launch_background.xml b/just_audio/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/just_audio/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/just_audio/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/just_audio/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/just_audio/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/just_audio/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/just_audio/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/just_audio/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/just_audio/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/just_audio/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/just_audio/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/just_audio/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/just_audio/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/just_audio/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/just_audio/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/just_audio/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/just_audio/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/just_audio/example/android/app/src/main/res/values/styles.xml b/just_audio/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..00fa441 --- /dev/null +++ b/just_audio/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + diff --git a/just_audio/example/android/app/src/profile/AndroidManifest.xml b/just_audio/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..df636f4 --- /dev/null +++ b/just_audio/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/just_audio/example/android/build.gradle b/just_audio/example/android/build.gradle new file mode 100644 index 0000000..e0d7ae2 --- /dev/null +++ b/just_audio/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/just_audio/example/android/gradle.properties b/just_audio/example/android/gradle.properties new file mode 100644 index 0000000..38c8d45 --- /dev/null +++ b/just_audio/example/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true diff --git a/just_audio/example/android/gradle/wrapper/gradle-wrapper.properties b/just_audio/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..7c4388a --- /dev/null +++ b/just_audio/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/just_audio/example/android/settings.gradle b/just_audio/example/android/settings.gradle new file mode 100644 index 0000000..5a2f14f --- /dev/null +++ b/just_audio/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/just_audio/example/android/settings_aar.gradle b/just_audio/example/android/settings_aar.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/just_audio/example/android/settings_aar.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/just_audio/example/ios/.gitignore b/just_audio/example/ios/.gitignore new file mode 100644 index 0000000..e96ef60 --- /dev/null +++ b/just_audio/example/ios/.gitignore @@ -0,0 +1,32 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/just_audio/example/ios/Flutter/AppFrameworkInfo.plist b/just_audio/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..6b4c0f7 --- /dev/null +++ b/just_audio/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 8.0 + + diff --git a/just_audio/example/ios/Flutter/Debug.xcconfig b/just_audio/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..e8efba1 --- /dev/null +++ b/just_audio/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/just_audio/example/ios/Flutter/Release.xcconfig b/just_audio/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..399e934 --- /dev/null +++ b/just_audio/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/just_audio/example/ios/Podfile b/just_audio/example/ios/Podfile new file mode 100644 index 0000000..f7d6a5e --- /dev/null +++ b/just_audio/example/ios/Podfile @@ -0,0 +1,38 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/just_audio/example/ios/Podfile.lock b/just_audio/example/ios/Podfile.lock new file mode 100644 index 0000000..af27c5e --- /dev/null +++ b/just_audio/example/ios/Podfile.lock @@ -0,0 +1,34 @@ +PODS: + - audio_session (0.0.1): + - Flutter + - Flutter (1.0.0) + - just_audio (0.0.1): + - Flutter + - path_provider (0.0.1): + - Flutter + +DEPENDENCIES: + - audio_session (from `.symlinks/plugins/audio_session/ios`) + - Flutter (from `Flutter`) + - just_audio (from `.symlinks/plugins/just_audio/ios`) + - path_provider (from `.symlinks/plugins/path_provider/ios`) + +EXTERNAL SOURCES: + audio_session: + :path: ".symlinks/plugins/audio_session/ios" + Flutter: + :path: Flutter + just_audio: + :path: ".symlinks/plugins/just_audio/ios" + path_provider: + :path: ".symlinks/plugins/path_provider/ios" + +SPEC CHECKSUMS: + audio_session: 4f3e461722055d21515cf3261b64c973c062f345 + Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa + path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c + +PODFILE CHECKSUM: 8e679eca47255a8ca8067c4c67aab20e64cb974d + +COCOAPODS: 1.9.3 diff --git a/just_audio/example/ios/Runner.xcodeproj/project.pbxproj b/just_audio/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..442406c --- /dev/null +++ b/just_audio/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,564 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + D06FA586B72D3A4E8145F7B3 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C5F18129E1310C9DA1B65F44 /* libPods-Runner.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 2920064AACAD73E894573C6E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 936C8FBACDB1725D477088CC /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C5F18129E1310C9DA1B65F44 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + EEEB488F061389F2C0725BDD /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D06FA586B72D3A4E8145F7B3 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1E7998A536E2BAD21DDFF12E /* Frameworks */ = { + isa = PBXGroup; + children = ( + C5F18129E1310C9DA1B65F44 /* libPods-Runner.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + A27F1C3EF07264C52FFA0B86 /* Pods */, + 1E7998A536E2BAD21DDFF12E /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + A27F1C3EF07264C52FFA0B86 /* Pods */ = { + isa = PBXGroup; + children = ( + EEEB488F061389F2C0725BDD /* Pods-Runner.debug.xcconfig */, + 2920064AACAD73E894573C6E /* Pods-Runner.release.xcconfig */, + 936C8FBACDB1725D477088CC /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 3D5B4DF09BB47DFA6E4B8495 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + A38593E728AFE0DAE382B1D1 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = "The Chromium Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 3D5B4DF09BB47DFA6E4B8495 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + A38593E728AFE0DAE382B1D1 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.ryanheise.audioPlayerExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.ryanheise.audioPlayerExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.ryanheise.audioPlayerExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/just_audio/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/just_audio/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/just_audio/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/just_audio/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/just_audio/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/just_audio/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/just_audio/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/just_audio/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/just_audio/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/just_audio/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/just_audio/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..a28140c --- /dev/null +++ b/just_audio/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/just_audio/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/just_audio/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/just_audio/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/just_audio/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/just_audio/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/just_audio/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/just_audio/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/just_audio/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/just_audio/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/just_audio/example/ios/Runner/AppDelegate.h b/just_audio/example/ios/Runner/AppDelegate.h new file mode 100644 index 0000000..36e21bb --- /dev/null +++ b/just_audio/example/ios/Runner/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/just_audio/example/ios/Runner/AppDelegate.m b/just_audio/example/ios/Runner/AppDelegate.m new file mode 100644 index 0000000..59a72e9 --- /dev/null +++ b/just_audio/example/ios/Runner/AppDelegate.m @@ -0,0 +1,13 @@ +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/just_audio/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/just_audio/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/just_audio/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/just_audio/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/just_audio/example/ios/Runner/Base.lproj/Main.storyboard b/just_audio/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/just_audio/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/just_audio/example/ios/Runner/Info.plist b/just_audio/example/ios/Runner/Info.plist new file mode 100644 index 0000000..9324af6 --- /dev/null +++ b/just_audio/example/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + just_audio_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/just_audio/example/ios/Runner/main.m b/just_audio/example/ios/Runner/main.m new file mode 100644 index 0000000..dff6597 --- /dev/null +++ b/just_audio/example/ios/Runner/main.m @@ -0,0 +1,9 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/just_audio/example/lib/main.dart b/just_audio/example/lib/main.dart new file mode 100644 index 0000000..e1f5a4e --- /dev/null +++ b/just_audio/example/lib/main.dart @@ -0,0 +1,430 @@ +import 'dart:math'; + +import 'package:audio_session/audio_session.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:just_audio/just_audio.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => _MyAppState(); +} + +class _MyAppState extends State { + AudioPlayer _player; + ConcatenatingAudioSource _playlist = ConcatenatingAudioSource(children: [ + LoopingAudioSource( + count: 2, + child: ClippingAudioSource( + start: Duration(seconds: 60), + end: Duration(seconds: 65), + child: AudioSource.uri(Uri.parse( + "https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3")), + tag: AudioMetadata( + album: "Science Friday", + title: "A Salute To Head-Scratching Science (5 seconds)", + artwork: + "https://media.wnyc.org/i/1400/1400/l/80/1/ScienceFriday_WNYCStudios_1400.jpg", + ), + ), + ), + AudioSource.uri( + Uri.parse( + "https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3"), + tag: AudioMetadata( + album: "Science Friday", + title: "A Salute To Head-Scratching Science", + artwork: + "https://media.wnyc.org/i/1400/1400/l/80/1/ScienceFriday_WNYCStudios_1400.jpg", + ), + ), + AudioSource.uri( + Uri.parse("https://s3.amazonaws.com/scifri-segments/scifri201711241.mp3"), + tag: AudioMetadata( + album: "Science Friday", + title: "From Cat Rheology To Operatic Incompetence", + artwork: + "https://media.wnyc.org/i/1400/1400/l/80/1/ScienceFriday_WNYCStudios_1400.jpg", + ), + ), + ]); + + @override + void initState() { + super.initState(); + _player = AudioPlayer(); + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + statusBarColor: Colors.black, + )); + _init(); + } + + _init() async { + final session = await AudioSession.instance; + await session.configure(AudioSessionConfiguration.speech()); + try { + await _player.setAudioSource(_playlist); + } catch (e) { + // catch load errors: 404, invalid url ... + print("An error occured $e"); + } + } + + @override + void dispose() { + _player.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + home: Scaffold( + body: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: StreamBuilder( + stream: _player.sequenceStateStream, + builder: (context, snapshot) { + final state = snapshot.data; + if (state?.sequence?.isEmpty ?? true) return SizedBox(); + final metadata = state.currentSource.tag as AudioMetadata; + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: + Center(child: Image.network(metadata.artwork)), + ), + ), + Text(metadata.album ?? '', + style: Theme.of(context).textTheme.headline6), + Text(metadata.title ?? ''), + ], + ); + }, + ), + ), + ControlButtons(_player), + StreamBuilder( + stream: _player.durationStream, + builder: (context, snapshot) { + final duration = snapshot.data ?? Duration.zero; + return StreamBuilder( + stream: _player.positionStream, + builder: (context, snapshot) { + var position = snapshot.data ?? Duration.zero; + if (position > duration) { + position = duration; + } + return SeekBar( + duration: duration, + position: position, + onChangeEnd: (newPosition) { + _player.seek(newPosition); + }, + ); + }, + ); + }, + ), + SizedBox(height: 8.0), + Row( + children: [ + StreamBuilder( + stream: _player.loopModeStream, + builder: (context, snapshot) { + final loopMode = snapshot.data ?? LoopMode.off; + const icons = [ + Icon(Icons.repeat, color: Colors.grey), + Icon(Icons.repeat, color: Colors.orange), + Icon(Icons.repeat_one, color: Colors.orange), + ]; + const cycleModes = [ + LoopMode.off, + LoopMode.all, + LoopMode.one, + ]; + final index = cycleModes.indexOf(loopMode); + return IconButton( + icon: icons[index], + onPressed: () { + _player.setLoopMode(cycleModes[ + (cycleModes.indexOf(loopMode) + 1) % + cycleModes.length]); + }, + ); + }, + ), + Expanded( + child: Text( + "Playlist", + style: Theme.of(context).textTheme.headline6, + textAlign: TextAlign.center, + ), + ), + StreamBuilder( + stream: _player.shuffleModeEnabledStream, + builder: (context, snapshot) { + final shuffleModeEnabled = snapshot.data ?? false; + return IconButton( + icon: shuffleModeEnabled + ? Icon(Icons.shuffle, color: Colors.orange) + : Icon(Icons.shuffle, color: Colors.grey), + onPressed: () async { + final enable = !shuffleModeEnabled; + if (enable) { + await _player.shuffle(); + } + await _player.setShuffleModeEnabled(enable); + }, + ); + }, + ), + ], + ), + Container( + height: 240.0, + child: StreamBuilder( + stream: _player.sequenceStateStream, + builder: (context, snapshot) { + final state = snapshot.data; + final sequence = state?.sequence ?? []; + return ListView.builder( + itemCount: sequence.length, + itemBuilder: (context, index) => Material( + color: index == state.currentIndex + ? Colors.grey.shade300 + : null, + child: ListTile( + title: Text(sequence[index].tag.title), + onTap: () { + _player.seek(Duration.zero, index: index); + }, + ), + ), + ); + }, + ), + ), + ], + ), + ), + ), + ); + } +} + +class ControlButtons extends StatelessWidget { + final AudioPlayer player; + + ControlButtons(this.player); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon(Icons.volume_up), + onPressed: () { + _showSliderDialog( + context: context, + title: "Adjust volume", + divisions: 10, + min: 0.0, + max: 1.0, + stream: player.volumeStream, + onChanged: player.setVolume, + ); + }, + ), + StreamBuilder( + stream: player.sequenceStateStream, + builder: (context, snapshot) => IconButton( + icon: Icon(Icons.skip_previous), + onPressed: player.hasPrevious ? player.seekToPrevious : null, + ), + ), + StreamBuilder( + stream: player.playerStateStream, + builder: (context, snapshot) { + final playerState = snapshot.data; + final processingState = playerState?.processingState; + final playing = playerState?.playing; + if (processingState == ProcessingState.loading || + processingState == ProcessingState.buffering) { + return Container( + margin: EdgeInsets.all(8.0), + width: 64.0, + height: 64.0, + child: CircularProgressIndicator(), + ); + } else if (playing != true) { + return IconButton( + icon: Icon(Icons.play_arrow), + iconSize: 64.0, + onPressed: player.play, + ); + } else if (processingState != ProcessingState.completed) { + return IconButton( + icon: Icon(Icons.pause), + iconSize: 64.0, + onPressed: player.pause, + ); + } else { + return IconButton( + icon: Icon(Icons.replay), + iconSize: 64.0, + onPressed: () => player.seek(Duration.zero, + index: player.effectiveIndices.first), + ); + } + }, + ), + StreamBuilder( + stream: player.sequenceStateStream, + builder: (context, snapshot) => IconButton( + icon: Icon(Icons.skip_next), + onPressed: player.hasNext ? player.seekToNext : null, + ), + ), + StreamBuilder( + stream: player.speedStream, + builder: (context, snapshot) => IconButton( + icon: Text("${snapshot.data?.toStringAsFixed(1)}x", + style: TextStyle(fontWeight: FontWeight.bold)), + onPressed: () { + _showSliderDialog( + context: context, + title: "Adjust speed", + divisions: 10, + min: 0.5, + max: 1.5, + stream: player.speedStream, + onChanged: player.setSpeed, + ); + }, + ), + ), + ], + ); + } +} + +class SeekBar extends StatefulWidget { + final Duration duration; + final Duration position; + final ValueChanged onChanged; + final ValueChanged onChangeEnd; + + SeekBar({ + @required this.duration, + @required this.position, + this.onChanged, + this.onChangeEnd, + }); + + @override + _SeekBarState createState() => _SeekBarState(); +} + +class _SeekBarState extends State { + double _dragValue; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Slider( + min: 0.0, + max: widget.duration.inMilliseconds.toDouble(), + value: min(_dragValue ?? widget.position.inMilliseconds.toDouble(), + widget.duration.inMilliseconds.toDouble()), + onChanged: (value) { + setState(() { + _dragValue = value; + }); + if (widget.onChanged != null) { + widget.onChanged(Duration(milliseconds: value.round())); + } + }, + onChangeEnd: (value) { + if (widget.onChangeEnd != null) { + widget.onChangeEnd(Duration(milliseconds: value.round())); + } + _dragValue = null; + }, + ), + Positioned( + right: 16.0, + bottom: 0.0, + child: Text( + RegExp(r'((^0*[1-9]\d*:)?\d{2}:\d{2})\.\d+$') + .firstMatch("$_remaining") + ?.group(1) ?? + '$_remaining', + style: Theme.of(context).textTheme.caption), + ), + ], + ); + } + + Duration get _remaining => widget.duration - widget.position; +} + +_showSliderDialog({ + BuildContext context, + String title, + int divisions, + double min, + double max, + String valueSuffix = '', + Stream stream, + ValueChanged onChanged, +}) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(title, textAlign: TextAlign.center), + content: StreamBuilder( + stream: stream, + builder: (context, snapshot) => Container( + height: 100.0, + child: Column( + children: [ + Text('${snapshot.data?.toStringAsFixed(1)}$valueSuffix', + style: TextStyle( + fontFamily: 'Fixed', + fontWeight: FontWeight.bold, + fontSize: 24.0)), + Slider( + divisions: divisions, + min: min, + max: max, + value: snapshot.data ?? 1.0, + onChanged: onChanged, + ), + ], + ), + ), + ), + ), + ); +} + +class AudioMetadata { + final String album; + final String title; + final String artwork; + + AudioMetadata({this.album, this.title, this.artwork}); +} diff --git a/just_audio/example/macos/.gitignore b/just_audio/example/macos/.gitignore new file mode 100644 index 0000000..d2fd377 --- /dev/null +++ b/just_audio/example/macos/.gitignore @@ -0,0 +1,6 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/xcuserdata/ diff --git a/just_audio/example/macos/Flutter/Flutter-Debug.xcconfig b/just_audio/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..785633d --- /dev/null +++ b/just_audio/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/just_audio/example/macos/Flutter/Flutter-Release.xcconfig b/just_audio/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..5fba960 --- /dev/null +++ b/just_audio/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/just_audio/example/macos/Flutter/GeneratedPluginRegistrant.swift b/just_audio/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..c891b67 --- /dev/null +++ b/just_audio/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,16 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import audio_session +import just_audio +import path_provider_macos + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) + JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) +} diff --git a/just_audio/example/macos/Podfile b/just_audio/example/macos/Podfile new file mode 100644 index 0000000..d60ec71 --- /dev/null +++ b/just_audio/example/macos/Podfile @@ -0,0 +1,82 @@ +platform :osx, '10.11' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def parse_KV_file(file, separator='=') + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return []; + end + pods_ary = [] + skip_line_start_symbols = ["#", "/"] + File.foreach(file_abs_path) { |line| + next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } + plugin = line.split(pattern=separator) + if plugin.length == 2 + podname = plugin[0].strip() + path = plugin[1].strip() + podpath = File.expand_path("#{path}", file_abs_path) + pods_ary.push({:name => podname, :path => podpath}); + else + puts "Invalid plugin specification: #{line}" + end + } + return pods_ary +end + +def pubspec_supports_macos(file) + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return false; + end + File.foreach(file_abs_path) { |line| + return true if line =~ /^\s*macos:/ + } + return false +end + +target 'Runner' do + use_frameworks! + use_modular_headers! + + # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock + # referring to absolute paths on developers' machines. + ephemeral_dir = File.join('Flutter', 'ephemeral') + symlink_dir = File.join(ephemeral_dir, '.symlinks') + symlink_plugins_dir = File.join(symlink_dir, 'plugins') + system("rm -rf #{symlink_dir}") + system("mkdir -p #{symlink_plugins_dir}") + + # Flutter Pods + generated_xcconfig = parse_KV_file(File.join(ephemeral_dir, 'Flutter-Generated.xcconfig')) + if generated_xcconfig.empty? + puts "Flutter-Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." + end + generated_xcconfig.map { |p| + if p[:name] == 'FLUTTER_FRAMEWORK_DIR' + symlink = File.join(symlink_dir, 'flutter') + File.symlink(File.dirname(p[:path]), symlink) + pod 'FlutterMacOS', :path => File.join(symlink, File.basename(p[:path])) + end + } + + # Plugin Pods + plugin_pods = parse_KV_file('../.flutter-plugins') + plugin_pods.map { |p| + symlink = File.join(symlink_plugins_dir, p[:name]) + File.symlink(p[:path], symlink) + if pubspec_supports_macos(File.join(symlink, 'pubspec.yaml')) + pod p[:name], :path => File.join(symlink, 'macos') + end + } +end + +# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. +install! 'cocoapods', :disable_input_output_paths => true diff --git a/just_audio/example/macos/Podfile.lock b/just_audio/example/macos/Podfile.lock new file mode 100644 index 0000000..f7d68bc --- /dev/null +++ b/just_audio/example/macos/Podfile.lock @@ -0,0 +1,39 @@ +PODS: + - audio_session (0.0.1): + - FlutterMacOS + - FlutterMacOS (1.0.0) + - just_audio (0.0.1): + - FlutterMacOS + - path_provider (0.0.1) + - path_provider_macos (0.0.1): + - FlutterMacOS + +DEPENDENCIES: + - audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`) + - FlutterMacOS (from `Flutter/ephemeral/.symlinks/flutter/darwin-x64`) + - just_audio (from `Flutter/ephemeral/.symlinks/plugins/just_audio/macos`) + - path_provider (from `Flutter/ephemeral/.symlinks/plugins/path_provider/macos`) + - path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`) + +EXTERNAL SOURCES: + audio_session: + :path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos + FlutterMacOS: + :path: Flutter/ephemeral/.symlinks/flutter/darwin-x64 + just_audio: + :path: Flutter/ephemeral/.symlinks/plugins/just_audio/macos + path_provider: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider/macos + path_provider_macos: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos + +SPEC CHECKSUMS: + audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72 + FlutterMacOS: 15bea8a44d2fa024068daa0140371c020b4b6ff9 + just_audio: 1a11463e7e522bd4e8caa925b4247f1360cbe5a2 + path_provider: e0848572d1d38b9a7dd099e79cf83f5b7e2cde9f + path_provider_macos: a0a3fd666cb7cd0448e936fb4abad4052961002b + +PODFILE CHECKSUM: d8ba9b3e9e93c62c74a660b46c6fcb09f03991a7 + +COCOAPODS: 1.9.3 diff --git a/just_audio/example/macos/Runner.xcodeproj/project.pbxproj b/just_audio/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..8523899 --- /dev/null +++ b/just_audio/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,654 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 1BEEF829E111499DFFB9AFA4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77D68B92366F60419FE029D8 /* Pods_Runner.framework */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; }; + 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D73912F022F37F9E000D13A0 /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; }; + D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */, + 33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */, + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FlutterMacOS.framework; path = Flutter/ephemeral/FlutterMacOS.framework; sourceTree = SOURCE_ROOT; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 409BAEF54B9B17B5585F3A06 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 77D68B92366F60419FE029D8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + 9C700360F2F4515FF4E74F2B /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + D73912EF22F37F9E000D13A0 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/ephemeral/App.framework; sourceTree = SOURCE_ROOT; }; + DA266879E6DA6E3956F07D12 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D73912F022F37F9E000D13A0 /* App.framework in Frameworks */, + 33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */, + 1BEEF829E111499DFFB9AFA4 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + B89C79F942A13E07457B4DD9 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + D73912EF22F37F9E000D13A0 /* App.framework */, + 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + B89C79F942A13E07457B4DD9 /* Pods */ = { + isa = PBXGroup; + children = ( + 409BAEF54B9B17B5585F3A06 /* Pods-Runner.debug.xcconfig */, + DA266879E6DA6E3956F07D12 /* Pods-Runner.release.xcconfig */, + 9C700360F2F4515FF4E74F2B /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 77D68B92366F60419FE029D8 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 2E7987DC1DC14DA8A4B13BF3 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 31D443903C4E40636BA59DB3 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 2E7987DC1DC14DA8A4B13BF3 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 31D443903C4E40636BA59DB3 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh\ntouch Flutter/ephemeral/tripwire\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter/ephemeral", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter/ephemeral", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter/ephemeral", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/just_audio/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/just_audio/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/just_audio/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/just_audio/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/just_audio/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..df12c33 --- /dev/null +++ b/just_audio/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/just_audio/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/just_audio/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/just_audio/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/just_audio/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/just_audio/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/just_audio/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/just_audio/example/macos/Runner/AppDelegate.swift b/just_audio/example/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..d53ef64 --- /dev/null +++ b/just_audio/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000..3c4935a Binary files /dev/null and b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000..ed4cc16 Binary files /dev/null and b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000..483be61 Binary files /dev/null and b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000..bcbf36d Binary files /dev/null and b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 0000000..9c0a652 Binary files /dev/null and b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000..e71a726 Binary files /dev/null and b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000..8a31fe2 Binary files /dev/null and b/just_audio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/just_audio/example/macos/Runner/Base.lproj/MainMenu.xib b/just_audio/example/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..537341a --- /dev/null +++ b/just_audio/example/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/just_audio/example/macos/Runner/Configs/AppInfo.xcconfig b/just_audio/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..ff69002 --- /dev/null +++ b/just_audio/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.ryanheise.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2020 com.ryanheise. All rights reserved. diff --git a/just_audio/example/macos/Runner/Configs/Debug.xcconfig b/just_audio/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/just_audio/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/just_audio/example/macos/Runner/Configs/Release.xcconfig b/just_audio/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/just_audio/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/just_audio/example/macos/Runner/Configs/Warnings.xcconfig b/just_audio/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/just_audio/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/just_audio/example/macos/Runner/DebugProfile.entitlements b/just_audio/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..08c3ab1 --- /dev/null +++ b/just_audio/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.network.client + + + diff --git a/just_audio/example/macos/Runner/Info.plist b/just_audio/example/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/just_audio/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/just_audio/example/macos/Runner/MainFlutterWindow.swift b/just_audio/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..2722837 --- /dev/null +++ b/just_audio/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/just_audio/example/macos/Runner/Release.entitlements b/just_audio/example/macos/Runner/Release.entitlements new file mode 100644 index 0000000..ee95ab7 --- /dev/null +++ b/just_audio/example/macos/Runner/Release.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + diff --git a/just_audio/example/pubspec.lock b/just_audio/example/pubspec.lock new file mode 100644 index 0000000..345b7c7 --- /dev/null +++ b/just_audio/example/pubspec.lock @@ -0,0 +1,306 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0-nullsafety.1" + audio_session: + dependency: "direct main" + description: + name: audio_session + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.10" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0-nullsafety.3" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.1" + just_audio: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.6.5" + just_audio_platform_interface: + dependency: transitive + description: + name: just_audio_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + just_audio_web: + dependency: transitive + description: + name: just_audio_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10-nullsafety.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.1" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.18" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+2" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+4" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+1" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.13" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.24.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0-nullsafety.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19-nullsafety.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.2" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.3" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.3" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" +sdks: + dart: ">=2.10.0-110 <2.11.0" + flutter: ">=1.12.13+hotfix.5 <2.0.0" diff --git a/just_audio/example/pubspec.yaml b/just_audio/example/pubspec.yaml new file mode 100644 index 0000000..090431f --- /dev/null +++ b/just_audio/example/pubspec.yaml @@ -0,0 +1,61 @@ +name: just_audio_example +description: Demonstrates how to use the just_audio plugin. +publish_to: 'none' + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + audio_session: ^0.0.10 + just_audio: + path: ../ + + cupertino_icons: ^0.1.2 + +dev_dependencies: + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/just_audio/example/test/widget_test.dart b/just_audio/example/test/widget_test.dart new file mode 100644 index 0000000..0cc2e4f --- /dev/null +++ b/just_audio/example/test/widget_test.dart @@ -0,0 +1,27 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:just_audio_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => widget is Text && + widget.data.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/just_audio/example/web/favicon.png b/just_audio/example/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/just_audio/example/web/favicon.png differ diff --git a/just_audio/example/web/icons/Icon-192.png b/just_audio/example/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/just_audio/example/web/icons/Icon-192.png differ diff --git a/just_audio/example/web/icons/Icon-512.png b/just_audio/example/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/just_audio/example/web/icons/Icon-512.png differ diff --git a/just_audio/example/web/index.html b/just_audio/example/web/index.html new file mode 100644 index 0000000..9b7a438 --- /dev/null +++ b/just_audio/example/web/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/just_audio/example/web/manifest.json b/just_audio/example/web/manifest.json new file mode 100644 index 0000000..8c01291 --- /dev/null +++ b/just_audio/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/just_audio/lib/just_audio.dart b/just_audio/lib/just_audio.dart index 76d6799..dcf8ba6 100644 --- a/just_audio/lib/just_audio.dart +++ b/just_audio/lib/just_audio.dart @@ -1,16 +1,12 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:io'; import 'dart:math'; -import 'dart:typed_data'; import 'package:audio_session/audio_session.dart'; -import 'package:crypto/crypto.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:just_audio_platform_interface/just_audio_platform_interface.dart'; -import 'package:meta/meta.dart' show experimental; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import 'package:rxdart/rxdart.dart'; @@ -60,24 +56,15 @@ class AudioPlayer { /// subscribe to the new platform's events. StreamSubscription _playbackEventSubscription; - /// The subscription to the event channel for waveform data. - StreamSubscription _visualizerWaveformSubscription; - - /// The subscription to the event channel for FFT data. - StreamSubscription _visualizerFftSubscription; - final String _id; _ProxyHttpServer _proxy; AudioSource _audioSource; Map _audioSources = {}; bool _disposed = false; _InitialSeekValues _initialSeekValues; - StartVisualizerRequest _startVisualizerRequest; PlaybackEvent _playbackEvent; final _playbackEventSubject = BehaviorSubject(sync: true); - final _visualizerWaveformSubject = BehaviorSubject(); - final _visualizerFftSubject = BehaviorSubject(); Future _durationFuture; final _durationSubject = BehaviorSubject(); final _processingStateSubject = BehaviorSubject(); @@ -211,24 +198,6 @@ class AudioPlayer { }); }); } - _removeOldAssetCacheDir(); - } - - /// Old versions of just_audio used an asset caching system that created a - /// separate cache file per asset per player instance, and was highly - /// dependent on the app calling [dispose] to clean up afterwards. If the app - /// is upgrading from an old version of just_audio, this will delete the old - /// cache directory. - Future _removeOldAssetCacheDir() async { - final oldAssetCacheDir = Directory( - p.join((await getTemporaryDirectory()).path, 'just_audio_asset_cache')); - if (oldAssetCacheDir.existsSync()) { - try { - oldAssetCacheDir.deleteSync(recursive: true); - } catch (e) { - print("Failed to delete old asset cache dir: $e"); - } - } } /// The latest [PlaybackEvent]. @@ -237,13 +206,6 @@ class AudioPlayer { /// A stream of [PlaybackEvent]s. Stream get playbackEventStream => _playbackEventSubject.stream; - /// A stream of visualizer waveform data in unsigned 8 bit PCM.. - Stream get visualizerWaveformStream => - _visualizerWaveformSubject.stream; - - /// A stream of visualizer FFT data. - Stream get visualizerFftStream => _visualizerFftSubject.stream; - /// The duration of the current audio or null if unknown. Duration get duration => _playbackEvent.duration; @@ -359,7 +321,7 @@ class AudioPlayer { shuffleModeEnabled == null) return null; if (loopMode == LoopMode.one) return currentIndex; final effectiveIndices = this.effectiveIndices; - if (effectiveIndices == null || effectiveIndices.isEmpty) return null; + if (effectiveIndices == null) return null; final effectiveIndicesInv = _effectiveIndicesInv; final invPos = effectiveIndicesInv[currentIndex]; var newInvPos = invPos + offset; @@ -642,7 +604,7 @@ class AudioPlayer { Future _load(AudioPlayerPlatform platform, AudioSource source, {_InitialSeekValues initialSeekValues}) async { try { - if (!kIsWeb && source._requiresProxy) { + if (!kIsWeb && source._requiresHeaders) { if (_proxy == null) { _proxy = _ProxyHttpServer(); await _proxy.start(); @@ -817,8 +779,12 @@ class AudioPlayer { await (await _platform).setSpeed(SetSpeedRequest(speed: speed)); } - /// Sets the [LoopMode]. Looping will be gapless on Android, iOS and macOS. On - /// web, there will be a slight gap at the loop point. + /// Sets the [LoopMode]. The gapless looping support is as follows: + /// + /// * Android: supported + /// * iOS/macOS: not supported, however, gapless looping can be achieved by + /// using [LoopingAudioSource]. + /// * Web: not supported Future setLoopMode(LoopMode mode) async { if (_disposed) return; _loopModeSubject.add(mode); @@ -911,42 +877,11 @@ class AudioPlayer { usage: audioAttributes.usage.value)); } - /// Start the visualizer by capturing [captureSize] samples of audio at - /// [captureRate] millihertz and return the sampling rate of the audio. If - /// [enableWaveform] is `true`, the captured samples will be broadcast via - /// [visualizerWaveformStream]. If [enableFft] is `true`, the FFT data for - /// each capture will be broadcast via [visualizerFftStream]. You should call - /// [stopVisualizer] to stop capturing audio data. - Future startVisualizer({ - bool enableWaveform = true, - bool enableFft = true, - int captureRate, - int captureSize, - }) async { - return (await (await _platform).startVisualizer(_startVisualizerRequest = - StartVisualizerRequest( - enableWaveform: enableWaveform, - enableFft: enableFft, - captureRate: captureRate, - captureSize: captureSize))) - ?.samplingRate; - } - - /// Stop capturing audio data for the visualizer. - Future stopVisualizer() async { - _startVisualizerRequest = null; - (await _platform).stopVisualizer(StopVisualizerRequest()); - } - /// Release all resources associated with this player. You must invoke this /// after you are done with the player. Future dispose() async { if (_disposed) return; _disposed = true; - await _visualizerWaveformSubscription?.cancel(); - await _visualizerFftSubscription?.cancel(); - await _visualizerWaveformSubject.close(); - await _visualizerFftSubject.close(); if (_nativePlatform != null) { await _disposePlatform(await _nativePlatform); _nativePlatform = null; @@ -976,7 +911,6 @@ class AudioPlayer { /// otherwise. Future _setPlatformActive(bool active, [Completer playCompleter]) { - if (_disposed) return null; if (active == _active) return _durationFuture; // This method updates _active and _platform before yielding to the next // task in the event loop. @@ -987,26 +921,19 @@ class AudioPlayer { final audioSource = _audioSource; final durationCompleter = Completer(); _platform = Future(() async { - _playbackEventSubscription?.cancel(); - _visualizerWaveformSubscription?.cancel(); - _visualizerFftSubscription?.cancel(); if (oldPlatformFuture != null) { final oldPlatform = await oldPlatformFuture; if (oldPlatform != _idlePlatform) { await _disposePlatform(oldPlatform); } } - if (_disposed) return null; // During initialisation, we must only use this platform reference in case // _platform is updated again during initialisation. final platform = active ? await (_nativePlatform = JustAudioPlatform.instance.init(InitRequest(id: _id))) : _idlePlatform; - _visualizerWaveformSubscription = platform.visualizerWaveformStream - .listen(_visualizerWaveformSubject.add); - _visualizerFftSubscription = - platform.visualizerFftStream.listen(_visualizerFftSubject.add); + _playbackEventSubscription?.cancel(); _playbackEventSubscription = platform.playbackEventMessageStream.listen((message) { var duration = message.duration; @@ -1059,9 +986,6 @@ class AudioPlayer { SetAutomaticallyWaitsToMinimizeStallingRequest( enabled: automaticallyWaitsToMinimizeStalling)); } - if (_startVisualizerRequest != null) { - await platform.startVisualizer(_startVisualizerRequest); - } await platform.setVolume(SetVolumeRequest(volume: volume)); await platform.setSpeed(SetSpeedRequest(speed: speed)); await platform.setLoopMode(SetLoopModeRequest( @@ -1366,42 +1290,30 @@ class SequenceState { } /// A local proxy HTTP server for making remote GET requests with headers. +/// +/// TODO: Recursively attach headers to items in playlists like m3u8. class _ProxyHttpServer { HttpServer _server; - /// Maps request keys to [_ProxyHandler]s. - final Map _handlerMap = {}; + /// Maps request keys to [_ProxyRequest]s. + final Map _uriMap = {}; /// The port this server is bound to on localhost. This is set only after /// [start] has completed. int get port => _server.port; - /// Register a [UriAudioSource] to be served through this proxy. This may be - /// called only after [start] has completed. - Uri addUriAudioSource(UriAudioSource source) { - final uri = source.uri; - final headers = source.headers?.cast(); - final path = _requestKey(uri); - _handlerMap[path] = _proxyHandlerForUri(uri, headers); - return uri.replace( + /// Associate headers with a URL. This may be called only after [start] has + /// completed. + Uri addUrl(Uri url, Map headers) { + final path = _requestKey(url); + _uriMap[path] = _ProxyRequest(url, headers); + return url.replace( scheme: 'http', host: InternetAddress.loopbackIPv4.address, port: port, ); } - /// Register a [StreamAudioSource] to be served through this proxy. This may - /// be called only after [start] has completed. - Uri addStreamAudioSource(StreamAudioSource source) { - final uri = _sourceUri(source); - final path = _requestKey(uri); - _handlerMap[path] = _proxyHandlerForSource(source); - return uri; - } - - Uri _sourceUri(StreamAudioSource source) => Uri.http( - '${InternetAddress.loopbackIPv4.address}:$port', '/id/${source._id}'); - /// A unique key for each request that can be processed by this proxy, /// made up of the URL path and query string. It is not possible to /// simultaneously track requests that have the same URL path and query @@ -1413,9 +1325,75 @@ class _ProxyHttpServer { _server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0); _server.listen((request) async { if (request.method == 'GET') { - final uriPath = _requestKey(request.uri); - final handler = _handlerMap[uriPath]; - handler(request); + final path = _requestKey(request.uri); + final proxyRequest = _uriMap[path]; + final originRequest = await HttpClient().getUrl(proxyRequest.uri); + + // Rewrite request headers + final host = originRequest.headers.value('host'); + originRequest.headers.clear(); + request.headers.forEach((name, value) { + originRequest.headers.set(name, value); + }); + for (var name in proxyRequest.headers.keys) { + originRequest.headers.set(name, proxyRequest.headers[name]); + } + originRequest.headers.set('host', host); + + // Try to make normal request + try { + final originResponse = await originRequest.close(); + + request.response.headers.clear(); + originResponse.headers.forEach((name, value) { + request.response.headers.set(name, value); + }); + request.response.statusCode = originResponse.statusCode; + + // Pipe response + await originResponse.pipe(request.response); + await request.response.close(); + } on HttpException { + // We likely are dealing with a streaming protocol + if (proxyRequest.uri.scheme == 'http') { + // Try parsing HTTP 0.9 response + //request.response.headers.clear(); + final socket = await Socket.connect( + proxyRequest.uri.host, proxyRequest.uri.port); + final clientSocket = + await request.response.detachSocket(writeHeaders: false); + Completer done = Completer(); + socket.listen( + clientSocket.add, + onDone: () async { + await clientSocket.flush(); + socket.close(); + clientSocket.close(); + done.complete(); + }, + ); + // Rewrite headers + final headers = {}; + request.headers.forEach((name, value) { + if (name.toLowerCase() != 'host') { + headers[name] = value.join(","); + } + }); + for (var name in proxyRequest.headers.keys) { + headers[name] = proxyRequest.headers[name]; + } + socket.write("GET ${proxyRequest.uri.path} HTTP/1.1\n"); + if (host != null) { + socket.write("Host: $host\n"); + } + for (var name in headers.keys) { + socket.write("$name: ${headers[name]}\n"); + } + socket.write("\n"); + await socket.flush(); + await done.future; + } + } } }); } @@ -1424,53 +1402,12 @@ class _ProxyHttpServer { Future stop() => _server.close(); } -/// Encapsulates the start and end of an HTTP range request. -class _HttpRangeRequest { - /// The starting byte position of the range request. - final int start; +/// A request for a URL and headers made by a [_ProxyHttpServer]. +class _ProxyRequest { + final Uri uri; + final Map headers; - /// The last byte position of the range request, or `null` if requesting - /// until the end of the media. - final int end; - - /// The end byte position (exclusive), defaulting to `null`. - int get endEx => end == null ? null : end + 1; - - _HttpRangeRequest(this.start, this.end); - - /// Creates an [_HttpRange] from [header]. - static _HttpRangeRequest parse(List header) { - if (header == null || header.isEmpty) return null; - final match = RegExp(r'^bytes=(\d+)(-(\d+)?)?').firstMatch(header.first); - if (match == null) return null; - int intGroup(int i) => match[i] != null ? int.parse(match[i]) : null; - return _HttpRangeRequest(intGroup(1), intGroup(3)); - } -} - -/// Encapsulates the range information in an HTTP range response. -class _HttpRange { - /// The starting byte position of the range. - final int start; - - /// The last byte position of the range, or `null` if until the end of the - /// media. - final int end; - - /// The total number of bytes in the entire media. - final int fullLength; - - _HttpRange(this.start, this.end, this.fullLength); - - /// The end byte position (exclusive), defaulting to [fullLength]. - int get endEx => end == null ? fullLength : end + 1; - - /// The number of bytes requested. - int get length => endEx == null ? null : endEx - start; - - /// The content-range header value to use in HTTP responses. - String get contentRangeHeader => - 'bytes $start-${end?.toString() ?? ""}/$fullLength'; + _ProxyRequest(this.uri, this.headers); } /// Specifies a source of audio to be played. Audio sources are composable @@ -1520,7 +1457,7 @@ abstract class AudioSource { AudioSourceMessage _toMessage(); - bool get _requiresProxy; + bool get _requiresHeaders; List get sequence; @@ -1572,7 +1509,7 @@ abstract class UriAudioSource extends IndexedAudioSource { _overrideUri = Uri.file( (await _loadAsset(uri.path.replaceFirst(RegExp(r'^/'), ''))).path); } else if (headers != null) { - _overrideUri = player._proxy.addUriAudioSource(this); + _overrideUri = player._proxy.addUrl(uri, headers.cast()); } } @@ -1587,10 +1524,8 @@ abstract class UriAudioSource extends IndexedAudioSource { Future _loadAsset(String assetPath) async { final file = await _getCacheFile(assetPath); this._cacheFile = file; - // Not technically inter-isolate-safe, although low risk. Could consider - // locking the file or creating a separate lock file. if (!file.existsSync()) { - file.createSync(recursive: true); + await file.create(recursive: true); await file.writeAsBytes( (await rootBundle.load(assetPath)).buffer.asUint8List()); } @@ -1598,14 +1533,13 @@ abstract class UriAudioSource extends IndexedAudioSource { } /// Get file for caching asset media with proper extension - Future _getCacheFile(final String assetPath) async => File(p.joinAll([ - (await _getCacheDir()).path, - 'assets', - ...Uri.parse(assetPath).pathSegments, - ])); + Future _getCacheFile(final String assetPath) async => File(p.join( + (await getTemporaryDirectory()).path, + 'just_audio_asset_cache', + '${_player._id}_$_id${p.extension(assetPath)}')); @override - bool get _requiresProxy => headers != null; + bool get _requiresHeaders => headers != null; } /// An [AudioSource] representing a regular media file such as an MP3 or M4A @@ -1874,7 +1808,8 @@ class ConcatenatingAudioSource extends AudioSource { } @override - bool get _requiresProxy => children.any((source) => source._requiresProxy); + bool get _requiresHeaders => + children.any((source) => source._requiresHeaders); @override AudioSourceMessage _toMessage() => ConcatenatingAudioSourceMessage( @@ -1909,7 +1844,7 @@ class ClippingAudioSource extends IndexedAudioSource { } @override - bool get _requiresProxy => child._requiresProxy; + bool get _requiresHeaders => child._requiresHeaders; @override AudioSourceMessage _toMessage() => ClippingAudioSourceMessage( @@ -1919,6 +1854,10 @@ class ClippingAudioSource extends IndexedAudioSource { // An [AudioSource] that loops a nested [AudioSource] a finite number of times. // NOTE: this can be inefficient when using a large loop count. If you wish to // loop an infinite number of times, use [AudioPlayer.setLoopMode]. +// +// On iOS and macOS, note that [LoopingAudioSource] will provide gapless +// playback while [AudioPlayer.setLoopMode] will not. (This will be supported +// in a future release.) class LoopingAudioSource extends AudioSource { AudioSource child; final int count; @@ -1945,302 +1884,13 @@ class LoopingAudioSource extends AudioSource { List get shuffleIndices => List.generate(count, (i) => i); @override - bool get _requiresProxy => child._requiresProxy; + bool get _requiresHeaders => child._requiresHeaders; @override AudioSourceMessage _toMessage() => LoopingAudioSourceMessage( id: _id, child: child._toMessage(), count: count); } -/// An [AudioSource] that provides audio dynamically. Subclasses must override -/// [request] to provide the encoded audio data. This API is experimental. -@experimental -abstract class StreamAudioSource extends IndexedAudioSource { - Uri _uri; - @required - StreamAudioSource(tag) : super(tag); - - @override - Future _setup(AudioPlayer player) async { - await super._setup(player); - _uri = player._proxy.addStreamAudioSource(this); - } - - /// Used by the player to request a byte range of encoded audio data in small - /// chunks, from byte position [start] inclusive (or from the beginning of the - /// audio data if not specified) to [end] exclusive (or the end of the audio - /// data if not specified). - Future request([int start, int end]); - - @override - bool get _requiresProxy => true; - - @override - AudioSourceMessage _toMessage() => ProgressiveAudioSourceMessage( - id: _id, uri: _uri.toString(), headers: null); -} - -/// The response for a [StreamAudioSource]. This API is experimental. -@experimental -class StreamAudioResponse { - /// The total number of bytes available. - final int sourceLength; - - /// The number of bytes returned in this response. - final int contentLength; - - /// The starting byte position of the response data. - final int offset; - - /// The audio content returned by this response. - final Stream> stream; - - StreamAudioResponse({ - @required this.sourceLength, - @required this.contentLength, - @required this.offset, - @required this.stream, - }); -} - -/// This is an experimental audio source that caches the audio while it is being -/// downloaded and played. -@experimental -class LockCachingAudioSource extends StreamAudioSource { - Future _response; - final Uri uri; - final Map headers; - final Future _cacheFile; - int _progress = 0; - final _requests = <_StreamingByteRangeRequest>[]; - - LockCachingAudioSource( - this.uri, { - this.headers, - File cacheFile, - dynamic tag, - }) : _cacheFile = - cacheFile != null ? Future.value(cacheFile) : _getCacheFile(uri), - super(tag); - - /// Get file for caching [uri] with proper extension - static Future _getCacheFile(final Uri uri) async => File(p.joinAll([ - (await _getCacheDir()).path, - 'remote', - sha256.convert(utf8.encode(uri.toString())).toString() + - p.extension(uri.path), - ])); - - Future get _partialCacheFile async => - File('${(await _cacheFile).path}.part'); - - Future _fetch() async { - HttpClient httpClient = HttpClient(); - final request = await httpClient.getUrl(uri); - if (headers != null) { - request.headers.clear(); - headers.forEach((name, value) => request.headers.set(name, value)); - } - final response = await request.close(); - if (response.statusCode != 200) { - httpClient.close(); - throw Exception('HTTP Status Error: ${response.statusCode}'); - } - (await _partialCacheFile).createSync(recursive: true); - // TODO: Should close sink after done, but it throws an error. - // ignore: close_sinks - final sink = (await _partialCacheFile).openWrite(); - var sourceLength = response.contentLength; - StreamSubscription subscription; - subscription = response.listen((data) { - _progress += data.length; - sink.add(data); - final readyRequests = _requests - .where((request) => request.end ?? sourceLength <= _progress) - .toList(); - if (readyRequests.isEmpty) return; - sink.flush().then((_) async { - for (var request in readyRequests) { - _requests.remove(request); - final start = request.start ?? 0; - final end = request.end ?? sourceLength; - request.complete(StreamAudioResponse( - sourceLength: sourceLength, - contentLength: end - start, - offset: start, - stream: (await _effectiveCacheFile).openRead(start, end), - )); - } - }); - }, onDone: () async { - (await _partialCacheFile).renameSync((await _cacheFile).path); - await subscription.cancel(); - httpClient.close(); - }, onError: (e, stackTrace) async { - print(stackTrace); - (await _partialCacheFile).deleteSync(); - httpClient.close(); - }); - return response; - } - - Future get _effectiveCacheFile async => - (await _partialCacheFile).existsSync() ? _partialCacheFile : _cacheFile; - - @override - Future request([int start, int end]) async { - final cacheFile = await _cacheFile; - start ??= 0; - if (cacheFile.existsSync()) { - final sourceLength = cacheFile.lengthSync(); - end ??= sourceLength; - return StreamAudioResponse( - sourceLength: sourceLength, - contentLength: end - start, - offset: start, - stream: cacheFile.openRead(start, end), - ); - } - final byteRangeRequest = _StreamingByteRangeRequest(start, end); - _requests.add(byteRangeRequest); - if (_response == null) { - _response = _fetch(); - } else {} - return byteRangeRequest.future; - } -} - -/// Request parameters for a [StreamingAudioSource]. -class _StreamingByteRangeRequest { - /// The start of the range request. - final int start; - - /// The end of the range request. - final int end; - - /// Completes when the response is available. - final _completer = Completer(); - - _StreamingByteRangeRequest(this.start, this.end); - - /// The response for this request. - Future get future => _completer.future; - - /// Completes this request with the given [response]. - void complete(StreamAudioResponse response) { - _completer.complete(response); - } -} - -/// The type of functions that can handle HTTP requests sent to the proxy. -typedef void _ProxyHandler(HttpRequest request); - -/// A proxy handler for serving audio from a [StreamAudioSource]. -_ProxyHandler _proxyHandlerForSource(StreamAudioSource source) { - Future handler(HttpRequest request) async { - final rangeRequest = - _HttpRangeRequest.parse(request.headers[HttpHeaders.rangeHeader]); - - request.response.headers.clear(); - request.response.headers.set(HttpHeaders.acceptRangesHeader, 'bytes'); - request.response.statusCode = rangeRequest == null ? 200 : 206; - final sourceResponse = - await source.request(rangeRequest?.start, rangeRequest?.endEx); - final range = _HttpRange(rangeRequest?.start ?? 0, rangeRequest?.end, - sourceResponse.sourceLength); - request.response.contentLength = range.length; - if (rangeRequest != null) { - request.response.headers - .set(HttpHeaders.contentRangeHeader, range.contentRangeHeader); - } - - // Pipe response - await sourceResponse.stream.pipe(request.response); - await request.response.close(); - } - - return handler; -} - -/// A proxy handler for serving audio from a URI with optional headers. -/// -/// TODO: Recursively attach headers to items in playlists like m3u8. -_ProxyHandler _proxyHandlerForUri(Uri uri, Map headers) { - Future handler(HttpRequest request) async { - final originRequest = await HttpClient().getUrl(uri); - - // Rewrite request headers - final host = originRequest.headers.value('host'); - originRequest.headers.clear(); - request.headers.forEach((name, value) { - originRequest.headers.set(name, value); - }); - for (var name in headers.keys) { - originRequest.headers.set(name, headers[name]); - } - originRequest.headers.set('host', host); - - // Try to make normal request - try { - final originResponse = await originRequest.close(); - - request.response.headers.clear(); - originResponse.headers.forEach((name, value) { - request.response.headers.set(name, value); - }); - request.response.statusCode = originResponse.statusCode; - - // Pipe response - await originResponse.pipe(request.response); - await request.response.close(); - } on HttpException { - // We likely are dealing with a streaming protocol - if (uri.scheme == 'http') { - // Try parsing HTTP 0.9 response - //request.response.headers.clear(); - final socket = await Socket.connect(uri.host, uri.port); - final clientSocket = - await request.response.detachSocket(writeHeaders: false); - Completer done = Completer(); - socket.listen( - clientSocket.add, - onDone: () async { - await clientSocket.flush(); - socket.close(); - clientSocket.close(); - done.complete(); - }, - ); - // Rewrite headers - final headers = {}; - request.headers.forEach((name, value) { - if (name.toLowerCase() != 'host') { - headers[name] = value.join(","); - } - }); - for (var name in headers.keys) { - headers[name] = headers[name]; - } - socket.write("GET ${uri.path} HTTP/1.1\n"); - if (host != null) { - socket.write("Host: $host\n"); - } - for (var name in headers.keys) { - socket.write("$name: ${headers[name]}\n"); - } - socket.write("\n"); - await socket.flush(); - await done.future; - } - } - } - - return handler; -} - -Future _getCacheDir() async => - Directory(p.join((await getTemporaryDirectory()).path, 'just_audio_cache')); - /// Defines the algorithm for shuffling the order of a /// [ConcatenatingAudioSource]. See [DefaultShuffleOrder] for a default /// implementation. @@ -2323,15 +1973,10 @@ class DefaultShuffleOrder extends ShuffleOrder { } } -/// An enumeration of modes that can be passed to [AudioPlayer.setLoopMode]. enum LoopMode { off, one, all } -/// The stand-in platform implementation to use when the player is in the idle -/// state and the native platform is deallocated. class _IdleAudioPlayer extends AudioPlayerPlatform { final _eventSubject = BehaviorSubject(); - final _visualizerWaveformSubject = BehaviorSubject(); - final _visualizerFftSubject = BehaviorSubject(); Duration _position; int _index; List _sequence; @@ -2369,13 +2014,6 @@ class _IdleAudioPlayer extends AudioPlayerPlatform { Stream get playbackEventMessageStream => _eventSubject.stream; - @override - Stream get visualizerWaveformStream => - _visualizerWaveformSubject.stream; - - @override - Stream get visualizerFftStream => _visualizerFftSubject.stream; - @override Future load(LoadRequest request) async { _index = request.initialIndex ?? 0; @@ -2445,9 +2083,6 @@ class _IdleAudioPlayer extends AudioPlayerPlatform { @override Future dispose(DisposeRequest request) async { - await _eventSubject.close(); - await _visualizerWaveformSubject.close(); - await _visualizerFftSubject.close(); return DisposeResponse(); } @@ -2468,18 +2103,6 @@ class _IdleAudioPlayer extends AudioPlayerPlatform { ConcatenatingMoveRequest request) async { return ConcatenatingMoveResponse(); } - - @override - Future startVisualizer( - StartVisualizerRequest request) async { - return StartVisualizerResponse(); - } - - @override - Future stopVisualizer( - StopVisualizerRequest request) async { - return StopVisualizerResponse(); - } } /// Holds the initial requested position and index for a newly loaded audio diff --git a/just_audio/pubspec.lock b/just_audio/pubspec.lock index 2ad6c29..7ab35a4 100644 --- a/just_audio/pubspec.lock +++ b/just_audio/pubspec.lock @@ -28,7 +28,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.5.0-nullsafety.1" audio_session: dependency: "direct main" description: @@ -42,7 +42,7 @@ packages: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.0-nullsafety.1" build: dependency: transitive description: @@ -70,14 +70,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.0-nullsafety.3" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.0-nullsafety.1" cli_util: dependency: transitive description: @@ -91,7 +91,7 @@ packages: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.0-nullsafety.1" code_builder: dependency: transitive description: @@ -105,7 +105,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" + version: "1.15.0-nullsafety.3" convert: dependency: transitive description: @@ -114,7 +114,7 @@ packages: source: hosted version: "2.1.1" crypto: - dependency: "direct main" + dependency: transitive description: name: crypto url: "https://pub.dartlang.org" @@ -133,7 +133,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.0-nullsafety.1" ffi: dependency: transitive description: @@ -190,20 +190,20 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.2" just_audio_platform_interface: dependency: "direct main" description: - path: "../just_audio_platform_interface" - relative: true - source: path + name: just_audio_platform_interface + url: "https://pub.dartlang.org" + source: hosted version: "2.0.0" just_audio_web: dependency: "direct main" description: - path: "../just_audio_web" - relative: true - source: path + name: just_audio_web + url: "https://pub.dartlang.org" + source: hosted version: "0.2.1" logging: dependency: transitive @@ -218,14 +218,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.10-nullsafety.1" meta: - dependency: "direct main" + dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.0-nullsafety.3" mockito: dependency: "direct dev" description: @@ -260,7 +260,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.0-nullsafety.1" path_provider: dependency: "direct main" description: @@ -363,49 +363,49 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.0-nullsafety.2" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0" + version: "1.10.0-nullsafety.1" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.0-nullsafety.1" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.0-nullsafety.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.0-nullsafety.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.2.19-nullsafety.2" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.0-nullsafety.3" uuid: dependency: "direct main" description: @@ -419,7 +419,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.0-nullsafety.3" watcher: dependency: transitive description: @@ -449,5 +449,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.12.0-0.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" + dart: ">=2.10.0 <2.11.0" + flutter: ">=1.12.13+hotfix.5 <2.0.0" diff --git a/just_audio/pubspec.yaml b/just_audio/pubspec.yaml index 6ea7a08..30c3c70 100644 --- a/just_audio/pubspec.yaml +++ b/just_audio/pubspec.yaml @@ -8,20 +8,14 @@ environment: flutter: ">=1.12.13+hotfix.5" dependencies: - # just_audio_platform_interface: ^2.0.0 - just_audio_platform_interface: - path: ../just_audio_platform_interface - # just_audio_web: ^0.2.1 - just_audio_web: - path: ../just_audio_web + just_audio_platform_interface: ^2.0.0 + just_audio_web: ^0.2.1 audio_session: ^0.0.10 rxdart: ">= 0.24.1 < 0.26.0" path: ^1.6.4 path_provider: ^1.6.10 async: ^2.4.0 uuid: ^2.2.0 - crypto: ^2.1.5 - meta: ^1.2.4 flutter: sdk: flutter diff --git a/just_audio/test/just_audio_test.dart b/just_audio/test/just_audio_test.dart new file mode 100644 index 0000000..871921b --- /dev/null +++ b/just_audio/test/just_audio_test.dart @@ -0,0 +1,1013 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:just_audio/just_audio.dart'; +import 'package:just_audio_platform_interface/just_audio_platform_interface.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + // We need an actual HttpClient to test the proxy server. + final overrides = MyHttpOverrides(); + HttpOverrides.global = overrides; + HttpOverrides.runWithHttpOverrides(runTests, overrides); +} + +void runTests() { + final mock = MockJustAudio(); + JustAudioPlatform.instance = mock; + final audioSessionChannel = MethodChannel('com.ryanheise.audio_session'); + + void expectDuration(Duration a, Duration b, {int epsilon = 200}) { + expect((a - b).inMilliseconds.abs(), lessThanOrEqualTo(epsilon)); + } + + void expectState({ + AudioPlayer player, + Duration position, + ProcessingState processingState, + bool playing, + }) { + if (position != null) { + expectDuration(player.position, position); + } + if (processingState != null) { + expect(player.processingState, equals(processingState)); + } + if (playing != null) { + expect(player.playing, equals(playing)); + } + } + + void checkIndices(List indices, int length) { + expect(indices.length, length); + final sorted = List.of(indices)..sort(); + expect(sorted, equals(List.generate(indices.length, (i) => i))); + } + + setUp(() { + audioSessionChannel.setMockMethodCallHandler((MethodCall methodCall) async { + return null; + }); + }); + + tearDown(() { + audioSessionChannel.setMockMethodCallHandler(null); + }); + + //test('init', () async { + // final player = AudioPlayer(); + // expect(player.processingState, equals(ProcessingState.idle)); + // expect(player.position, equals(Duration.zero)); + // //expect(player.bufferedPosition, equals(Duration.zero)); + // expect(player.duration, equals(null)); + // expect(player.icyMetadata, equals(null)); + // expect(player.currentIndex, equals(null)); + // expect(player.androidAudioSessionId, equals(null)); + // expect(player.playing, equals(false)); + // expect(player.volume, equals(1.0)); + // expect(player.speed, equals(1.0)); + // expect(player.sequence, equals(null)); + // expect(player.hasNext, equals(false)); + // expect(player.hasPrevious, equals(false)); + // //expect(player.loopMode, equals(LoopMode.off)); + // //expect(player.shuffleModeEnabled, equals(false)); + // expect(player.automaticallyWaitsToMinimizeStalling, equals(true)); + // player.dispose(); + //}); + + //test('load', () async { + // final player = AudioPlayer(); + // final duration = await player.setUrl('https://foo.foo/foo.mp3'); + // expect(duration, equals(audioSourceDuration)); + // expect(player.duration, equals(duration)); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.position, equals(Duration.zero)); + // expect(player.currentIndex, equals(0)); + // expect(player.hasNext, equals(false)); + // expect(player.hasPrevious, equals(false)); + // expect(player.sequence.length, equals(1)); + // expect(player.playing, equals(false)); + // player.dispose(); + //}); + + //test('load error', () async { + // final player = AudioPlayer(); + // var exception; + // try { + // await player.setUrl('https://foo.foo/404.mp3'); + // exception = null; + // } catch (e) { + // exception = e; + // } + // expect(exception != null, equals(true)); + // try { + // await player.setUrl('https://foo.foo/abort.mp3'); + // exception = null; + // } catch (e) { + // exception = e; + // } + // expect(exception != null, equals(true)); + // try { + // await player.setUrl('https://foo.foo/error.mp3'); + // exception = null; + // } catch (e) { + // exception = e; + // } + // expect(exception != null, equals(true)); + // player.dispose(); + //}); + + //test('control', () async { + // final player = AudioPlayer(); + // final duration = await player.setUrl('https://foo.foo/foo.mp3'); + // final point1 = duration * 0.3; + // final stopwatch = Stopwatch(); + // expectState( + // player: player, + // position: Duration.zero, + // processingState: ProcessingState.ready, + // playing: false, + // ); + // await player.seek(point1); + // expectState( + // player: player, + // position: point1, + // processingState: ProcessingState.ready, + // playing: false, + // ); + // player.play(); + // expectState( + // player: player, + // position: point1, + // processingState: ProcessingState.ready, + // ); + // await Future.delayed(Duration(milliseconds: 100)); + // expectState(player: player, playing: true); + // await Future.delayed(Duration(seconds: 1)); + // expectState( + // player: player, + // position: point1 + Duration(seconds: 1), + // processingState: ProcessingState.ready, + // playing: true, + // ); + // await player.seek(duration - Duration(seconds: 3)); + // expectState( + // player: player, + // position: duration - Duration(seconds: 3), + // processingState: ProcessingState.ready, + // playing: true, + // ); + // await player.pause(); + // expectState( + // player: player, + // position: duration - Duration(seconds: 3), + // processingState: ProcessingState.ready, + // playing: false, + // ); + // stopwatch.reset(); + // stopwatch.start(); + // final playFuture = player.play(); + // expectState( + // player: player, + // position: duration - Duration(seconds: 3), + // processingState: ProcessingState.ready, + // ); + // expectState(player: player, playing: true); + // await playFuture; + // expectDuration(stopwatch.elapsed, Duration(seconds: 3)); + // expectState( + // player: player, + // position: duration, + // processingState: ProcessingState.completed, + // playing: true, + // ); + // player.dispose(); + //}); + + //test('speed', () async { + // final player = AudioPlayer(); + // final duration = await player.setUrl('https://foo.foo/foo.mp3'); + // final period1 = Duration(seconds: 2); + // final period2 = Duration(seconds: 2); + // final speed1 = 0.75; + // final speed2 = 1.5; + // final position1 = period1 * speed1; + // final position2 = position1 + period2 * speed2; + // expectState(player: player, position: Duration.zero); + // await player.setSpeed(speed1); + // player.play(); + // await Future.delayed(period1); + // expectState(player: player, position: position1); + // await player.setSpeed(speed2); + // await Future.delayed(period2); + // expectState(player: player, position: position2); + // player.dispose(); + //}); + + //test('positionStream', () async { + // final player = AudioPlayer(); + // final duration = await player.setUrl('https://foo.foo/foo.mp3'); + // final period = Duration(seconds: 3); + // final position1 = period; + // final position2 = position1 + period; + // final speed1 = 0.75; + // final speed2 = 1.5; + // final stepDuration = period ~/ 5; + // var target = stepDuration; + // player.setSpeed(speed1); + // player.play(); + // final stopwatch = Stopwatch(); + // stopwatch.start(); + + // var completer = Completer(); + // StreamSubscription subscription; + // subscription = player.positionStream.listen((position) { + // if (position >= position1) { + // subscription.cancel(); + // completer.complete(); + // } else if (position >= target) { + // expectDuration(position, stopwatch.elapsed * speed1); + // target += stepDuration; + // } + // }); + // await completer.future; + // player.setSpeed(speed2); + // stopwatch.reset(); + + // target = position1 + target; + // completer = Completer(); + // subscription = player.positionStream.listen((position) { + // if (position >= position2) { + // subscription.cancel(); + // completer.complete(); + // } else if (position >= target) { + // expectDuration(position, position1 + stopwatch.elapsed * speed2); + // target += stepDuration; + // } + // }); + // await completer.future; + // player.dispose(); + //}); + + //test('icyMetadata', () async { + // final player = AudioPlayer(); + // expect(player.icyMetadata, equals(null)); + // final duration = await player.setUrl('https://foo.foo/foo.mp3'); + // player.play(); + // expect(player.icyMetadata.headers.genre, equals(icyMetadata.headers.genre)); + // expect((await player.icyMetadataStream.first).headers.genre, + // equals(icyMetadata.headers.genre)); + // player.dispose(); + //}); + + //test('proxy', () async { + // final server = MockWebServer(); + // await server.start(); + // final player = AudioPlayer(); + // // This simulates an actual URL + // final uri = Uri.parse( + // 'http://${InternetAddress.loopbackIPv4.address}:${server.port}/proxy/foo.mp3'); + // await player.setUrl('$uri', headers: {'custom-header': 'Hello'}); + // // Obtain the proxy URL that the platform side should use to load the data. + // final proxyUri = Uri.parse(player.icyMetadata.info.url); + // // Simulate the platform side requesting the data. + // final request = await HttpClient().getUrl(proxyUri); + // final response = await request.close(); + // final responseText = await response.transform(utf8.decoder).join(); + // expect(response.statusCode, equals(HttpStatus.ok)); + // expect(responseText, equals('Hello')); + // expect(response.headers.value(HttpHeaders.contentTypeHeader), + // equals('audio/mock')); + // await server.stop(); + //}); + + //test('proxy0.9', () async { + // final server = MockWebServer(); + // await server.start(); + // final player = AudioPlayer(); + // // This simulates an actual URL + // final uri = Uri.parse( + // 'http://${InternetAddress.loopbackIPv4.address}:${server.port}/proxy0.9/foo.mp3'); + // await player.setUrl('$uri', headers: {'custom-header': 'Hello'}); + // // Obtain the proxy URL that the platform side should use to load the data. + // final proxyUri = Uri.parse(player.icyMetadata.info.url); + // // Simulate the platform side requesting the data. + // final socket = await Socket.connect(proxyUri.host, proxyUri.port); + // //final socket = await Socket.connect(uri.host, uri.port); + // socket.write('GET ${uri.path} HTTP/1.1\n' 'test-header: value\n' '\n'); + // await socket.flush(); + // final responseText = await socket + // .transform(Converter.castFrom, String, Uint8List, dynamic>( + // utf8.decoder)) + // .join(); + // await socket.close(); + // expect(responseText, equals('Hello')); + // await server.stop(); + //}); + + //test('sequence', () async { + // final source1 = ConcatenatingAudioSource(children: [ + // LoopingAudioSource( + // count: 2, + // child: ClippingAudioSource( + // start: Duration(seconds: 60), + // end: Duration(seconds: 65), + // child: AudioSource.uri(Uri.parse("https://foo.foo/foo.mp3")), + // tag: 'a', + // ), + // ), + // AudioSource.uri( + // Uri.parse("https://bar.bar/bar.mp3"), + // tag: 'b', + // ), + // AudioSource.uri( + // Uri.parse("https://baz.baz/baz.mp3"), + // tag: 'c', + // ), + // ]); + // expect(source1.sequence.map((s) => s.tag as String).toList(), + // equals(['a', 'a', 'b', 'c'])); + // final source2 = ConcatenatingAudioSource(children: []); + // final player = AudioPlayer(); + // await player.setAudioSource(source2); + // expect(source2.sequence.length, equals(0)); + // await source2 + // .add(AudioSource.uri(Uri.parse('https://b.b/b.mp3'), tag: 'b')); + // await source2.insert( + // 0, AudioSource.uri(Uri.parse('https://a.a/a.mp3'), tag: 'a')); + // await source2.insert( + // 2, AudioSource.uri(Uri.parse('https://c.c/c.mp3'), tag: 'c')); + // await source2.addAll([ + // AudioSource.uri(Uri.parse('https://d.d/d.mp3'), tag: 'd'), + // AudioSource.uri(Uri.parse('https://e.e/e.mp3'), tag: 'e'), + // ]); + // await source2.insertAll(3, [ + // AudioSource.uri(Uri.parse('https://e.e/e.mp3'), tag: 'e'), + // AudioSource.uri(Uri.parse('https://f.f/f.mp3'), tag: 'f'), + // ]); + // expect(source2.sequence.map((s) => s.tag as String), + // equals(['a', 'b', 'c', 'e', 'f', 'd', 'e'])); + // await source2.removeAt(0); + // expect(source2.sequence.map((s) => s.tag as String), + // equals(['b', 'c', 'e', 'f', 'd', 'e'])); + // await source2.move(3, 2); + // expect(source2.sequence.map((s) => s.tag as String), + // equals(['b', 'c', 'f', 'e', 'd', 'e'])); + // await source2.move(2, 3); + // expect(source2.sequence.map((s) => s.tag as String), + // equals(['b', 'c', 'e', 'f', 'd', 'e'])); + // await source2.removeRange(0, 2); + // expect(source2.sequence.map((s) => s.tag as String), + // equals(['e', 'f', 'd', 'e'])); + // await source2.removeAt(3); + // expect( + // source2.sequence.map((s) => s.tag as String), equals(['e', 'f', 'd'])); + // await source2.removeRange(1, 3); + // expect(source2.sequence.map((s) => s.tag as String), equals(['e'])); + // await source2.clear(); + // expect(source2.sequence.map((s) => s.tag as String), equals([])); + //}); + + //test('detect', () async { + // expect(AudioSource.uri(Uri.parse('https://a.a/a.mpd')) is DashAudioSource, + // equals(true)); + // expect(AudioSource.uri(Uri.parse('https://a.a/a.m3u8')) is HlsAudioSource, + // equals(true)); + // expect( + // AudioSource.uri(Uri.parse('https://a.a/a.mp3')) + // is ProgressiveAudioSource, + // equals(true)); + // expect(AudioSource.uri(Uri.parse('https://a.a/a#.mpd')) is DashAudioSource, + // equals(true)); + //}); + + //test('shuffle order', () async { + // final shuffleOrder1 = DefaultShuffleOrder(random: Random(1001)); + // checkIndices(shuffleOrder1.indices, 0); + // //expect(shuffleOrder1.indices, equals([])); + // shuffleOrder1.insert(0, 5); + // //expect(shuffleOrder1.indices, equals([3, 0, 2, 4, 1])); + // checkIndices(shuffleOrder1.indices, 5); + // shuffleOrder1.insert(3, 2); + // checkIndices(shuffleOrder1.indices, 7); + // shuffleOrder1.insert(0, 2); + // checkIndices(shuffleOrder1.indices, 9); + // shuffleOrder1.insert(9, 2); + // checkIndices(shuffleOrder1.indices, 11); + + // final indices1 = List.of(shuffleOrder1.indices); + // shuffleOrder1.shuffle(); + // expect(shuffleOrder1.indices, isNot(indices1)); + // checkIndices(shuffleOrder1.indices, 11); + // final indices2 = List.of(shuffleOrder1.indices); + // shuffleOrder1.shuffle(initialIndex: 5); + // expect(shuffleOrder1.indices[0], equals(5)); + // expect(shuffleOrder1.indices, isNot(indices2)); + // checkIndices(shuffleOrder1.indices, 11); + + // shuffleOrder1.removeRange(4, 6); + // checkIndices(shuffleOrder1.indices, 9); + // shuffleOrder1.removeRange(0, 2); + // checkIndices(shuffleOrder1.indices, 7); + // shuffleOrder1.removeRange(5, 7); + // checkIndices(shuffleOrder1.indices, 5); + // shuffleOrder1.removeRange(0, 5); + // checkIndices(shuffleOrder1.indices, 0); + + // shuffleOrder1.insert(0, 5); + // checkIndices(shuffleOrder1.indices, 5); + // shuffleOrder1.clear(); + // checkIndices(shuffleOrder1.indices, 0); + //}); + + //test('shuffle', () async { + // AudioSource createSource() => ConcatenatingAudioSource( + // shuffleOrder: DefaultShuffleOrder(random: Random(1001)), + // children: [ + // LoopingAudioSource( + // count: 2, + // child: ClippingAudioSource( + // start: Duration(seconds: 60), + // end: Duration(seconds: 65), + // child: AudioSource.uri(Uri.parse("https://foo.foo/foo.mp3")), + // tag: 'a', + // ), + // ), + // AudioSource.uri( + // Uri.parse("https://bar.bar/bar.mp3"), + // tag: 'b', + // ), + // AudioSource.uri( + // Uri.parse("https://baz.baz/baz.mp3"), + // tag: 'c', + // ), + // ClippingAudioSource( + // child: AudioSource.uri( + // Uri.parse("https://baz.baz/baz.mp3"), + // tag: 'd', + // ), + // ), + // ], + // ); + // final source1 = createSource(); + // //expect(source1.shuffleIndices, [4, 0, 1, 3, 2]); + // checkIndices(source1.shuffleIndices, 5); + // expect(source1.shuffleIndices.skipWhile((i) => i != 0).skip(1).first, + // equals(1)); + // final player1 = AudioPlayer(); + // await player1.setAudioSource(source1); + // checkIndices(player1.shuffleIndices, 5); + // expect(player1.shuffleIndices.first, equals(0)); + // await player1.seek(Duration.zero, index: 3); + // await player1.shuffle(); + // checkIndices(player1.shuffleIndices, 5); + // expect(player1.shuffleIndices.first, equals(3)); + + // final source2 = createSource(); + // final player2 = AudioPlayer(); + // await player2.setAudioSource(source2, initialIndex: 3); + // checkIndices(player2.shuffleIndices, 5); + // expect(player2.shuffleIndices.first, equals(3)); + //}); + + //test('stop', () async { + // final source = ConcatenatingAudioSource( + // shuffleOrder: DefaultShuffleOrder(random: Random(1001)), + // children: [ + // AudioSource.uri( + // Uri.parse("https://bar.bar/foo.mp3"), + // tag: 'foo', + // ), + // AudioSource.uri( + // Uri.parse("https://baz.baz/bar.mp3"), + // tag: 'bar', + // ), + // ], + // ); + // final player = AudioPlayer(); + // expect(player.processingState, ProcessingState.idle); + // await player.setAudioSource(source, preload: false); + // expect(player.processingState, ProcessingState.idle); + // await player.load(); + // expect(player.processingState, ProcessingState.ready); + // await player.seek(Duration(seconds: 5), index: 1); + // await player.setVolume(0.5); + // await player.setSpeed(0.7); + // await player.setShuffleModeEnabled(true); + // await player.setLoopMode(LoopMode.one); + // await player.stop(); + // expect(player.processingState, ProcessingState.idle); + // expect(player.position, Duration(seconds: 5)); + // expect(player.volume, 0.5); + // expect(player.speed, 0.7); + // expect(player.shuffleModeEnabled, true); + // expect(player.loopMode, LoopMode.one); + // await player.load(); + // expect(player.processingState, ProcessingState.ready); + // expect(player.position, Duration(seconds: 5)); + // expect(player.volume, 0.5); + // expect(player.speed, 0.7); + // expect(player.shuffleModeEnabled, true); + // expect(player.loopMode, LoopMode.one); + //}); + + //test('play-load', () async { + // for (var delayMs in [0, 100]) { + // final player = AudioPlayer(); + // player.play(); + // if (delayMs != 0) { + // await Future.delayed(Duration(milliseconds: delayMs)); + // } + // expect(player.playing, equals(true)); + // expect(player.processingState, equals(ProcessingState.idle)); + // await player.setUrl('https://bar.bar/foo.mp3'); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.playing, equals(true)); + // expectDuration(player.position, Duration.zero); + // await Future.delayed(Duration(seconds: 1)); + // expectDuration(player.position, Duration(seconds: 1)); + // await player.dispose(); + // } + //}); + + //test('play-set', () async { + // for (var delayMs in [0, 100]) { + // final player = AudioPlayer(); + // player.play(); + // if (delayMs != 0) { + // await Future.delayed(Duration(milliseconds: delayMs)); + // } + // expect(player.playing, equals(true)); + // expect(player.processingState, equals(ProcessingState.idle)); + // await player.setUrl('https://bar.bar/foo.mp3', preload: false); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.playing, equals(true)); + // expectDuration(player.position, Duration.zero); + // await Future.delayed(Duration(seconds: 1)); + // expectDuration(player.position, Duration(seconds: 1)); + // await player.dispose(); + // } + //}); + + //test('set-play', () async { + // final player = AudioPlayer(); + // await player.setUrl('https://bar.bar/foo.mp3', preload: false); + // expect(player.processingState, equals(ProcessingState.idle)); + // expect(player.playing, equals(false)); + // player.play(); + // expect(player.playing, equals(true)); + // await player.processingStateStream + // .firstWhere((state) => state == ProcessingState.ready); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.playing, equals(true)); + // expectDuration(player.position, Duration.zero); + // await Future.delayed(Duration(seconds: 1)); + // expectDuration(player.position, Duration(seconds: 1)); + // await player.dispose(); + //}); + + //test('set-set', () async { + // final player = AudioPlayer(); + // await player.setAudioSource( + // ConcatenatingAudioSource( + // children: [ + // AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')), + // AudioSource.uri(Uri.parse('https://bar.bar/bar.mp3')), + // ], + // ), + // preload: false, + // ); + // expect(player.processingState, equals(ProcessingState.idle)); + // expect(player.sequence.length, equals(2)); + // expect(player.playing, equals(false)); + // await player.setAudioSource( + // ConcatenatingAudioSource( + // children: [ + // AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')), + // AudioSource.uri(Uri.parse('https://bar.bar/bar.mp3')), + // AudioSource.uri(Uri.parse('https://bar.bar/baz.mp3')), + // ], + // ), + // preload: false, + // ); + // expect(player.processingState, equals(ProcessingState.idle)); + // expect(player.sequence.length, equals(3)); + // expect(player.playing, equals(false)); + // await player.dispose(); + //}); + + //test('load-load', () async { + // final player = AudioPlayer(); + // await player.setAudioSource( + // ConcatenatingAudioSource( + // children: [ + // AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')), + // AudioSource.uri(Uri.parse('https://bar.bar/bar.mp3')), + // ], + // ), + // ); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.sequence.length, equals(2)); + // expect(player.playing, equals(false)); + // await player.setAudioSource( + // ConcatenatingAudioSource( + // children: [ + // AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')), + // AudioSource.uri(Uri.parse('https://bar.bar/bar.mp3')), + // AudioSource.uri(Uri.parse('https://bar.bar/baz.mp3')), + // ], + // ), + // ); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.sequence.length, equals(3)); + // expect(player.playing, equals(false)); + // await player.dispose(); + //}); + + //test('load-set-load', () async { + // final player = AudioPlayer(); + // await player.setAudioSource( + // ConcatenatingAudioSource( + // children: [ + // AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')), + // AudioSource.uri(Uri.parse('https://bar.bar/bar.mp3')), + // ], + // ), + // ); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.sequence.length, equals(2)); + // expect(player.playing, equals(false)); + // await player.setAudioSource( + // ConcatenatingAudioSource( + // children: [ + // AudioSource.uri(Uri.parse('https://bar.bar/foo.mp3')), + // AudioSource.uri(Uri.parse('https://bar.bar/bar.mp3')), + // AudioSource.uri(Uri.parse('https://bar.bar/baz.mp3')), + // ], + // ), + // preload: false, + // ); + // expect(player.processingState, equals(ProcessingState.idle)); + // expect(player.sequence.length, equals(3)); + // expect(player.playing, equals(false)); + // await player.load(); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.sequence.length, equals(3)); + // expect(player.playing, equals(false)); + // await player.dispose(); + //}); + + //test('play-load-load', () async { + // final player = AudioPlayer(); + // player.play(); + // await player.setUrl('https://bar.bar/foo.mp3'); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.playing, equals(true)); + // expectDuration(player.position, Duration(seconds: 0)); + // await Future.delayed(Duration(seconds: 1)); + // expectDuration(player.position, Duration(seconds: 1)); + // await player.setUrl('https://bar.bar/bar.mp3'); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.playing, equals(true)); + // expectDuration(player.position, Duration(seconds: 0)); + // await Future.delayed(Duration(seconds: 1)); + // expectDuration(player.position, Duration(seconds: 1)); + // await player.dispose(); + //}); + + //test('play-load-set-play-load', () async { + // final player = AudioPlayer(); + // player.play(); + // await player.setUrl('https://bar.bar/foo.mp3'); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.playing, equals(true)); + // expectDuration(player.position, Duration(seconds: 0)); + // await Future.delayed(Duration(seconds: 1)); + // expectDuration(player.position, Duration(seconds: 1)); + // player.pause(); + // expect(player.playing, equals(false)); + // await player.setUrl('https://bar.bar/bar.mp3', preload: false); + // expect(player.processingState, equals(ProcessingState.idle)); + // expect(player.playing, equals(false)); + // // TODO: Decide whether we want player.position to be null here. + // expectDuration(player.position ?? Duration.zero, Duration.zero); + // await player.load(); + // expect(player.processingState, equals(ProcessingState.ready)); + // expect(player.playing, equals(false)); + // expectDuration(player.position, Duration(seconds: 0)); + // player.play(); + // expect(player.playing, equals(true)); + // expectDuration(player.position, Duration(seconds: 0)); + // await Future.delayed(Duration(seconds: 1)); + // expectDuration(player.position, Duration(seconds: 1)); + // await player.dispose(); + //}); + + test('quick-reactivate', () async { + final player = AudioPlayer(); + await player.setUrl('https://bar.bar/foo.mp3'); + player.stop(); + await player.setUrl('https://bar.bar/foo.mp3'); + }); +} + +class MockJustAudio extends Mock + with MockPlatformInterfaceMixin + implements JustAudioPlatform { + final _players = {}; + + @override + Future init(InitRequest request) async { + if (_players.containsKey(request.id)) + throw PlatformException( + code: "error", + message: "Platform player ${request.id} already exists"); + final player = MockAudioPlayer(request.id); + _players[request.id] = player; + return player; + } + + @override + Future disposePlayer( + DisposePlayerRequest request) async { + _players[request.id].dispose(DisposeRequest()); + _players.remove(request.id); + return DisposePlayerResponse(); + } +} + +const audioSourceDuration = Duration(minutes: 2); + +final icyMetadata = IcyMetadata( + headers: IcyHeaders( + url: 'url', + genre: 'Genre', + metadataInterval: 3, + bitrate: 100, + isPublic: true, + name: 'name', + ), + info: IcyInfo( + title: 'title', + url: 'url', + ), +); + +final icyMetadataMessage = IcyMetadataMessage( + headers: IcyHeadersMessage( + url: 'url', + genre: 'Genre', + metadataInterval: 3, + bitrate: 100, + isPublic: true, + name: 'name', + ), + info: IcyInfoMessage( + title: 'title', + url: 'url', + ), +); + +class MockAudioPlayer implements AudioPlayerPlatform { + final String _id; + final eventController = StreamController(); + AudioSourceMessage _audioSource; + ProcessingStateMessage _processingState = ProcessingStateMessage.idle; + Duration _updatePosition = Duration.zero; + DateTime _updateTime = DateTime.now(); + Duration _duration = audioSourceDuration; + int _currentIndex; + int _index; + var _playing = false; + var _speed = 1.0; + var _volume = 1.0; + var _loopMode = LoopModeMessage.off; + var _shuffleModeEnabled = false; + Completer _playCompleter; + Timer _playTimer; + + MockAudioPlayer(String id) : this._id = id; + + @override + String get id => _id; + + @override + Stream get playbackEventMessageStream => + eventController.stream; + + @override + Future load(LoadRequest request) async { + final audioSource = request.audioSourceMessage; + if (audioSource is UriAudioSourceMessage) { + if (audioSource.uri.contains('abort')) { + throw PlatformException(code: 'abort', message: 'Failed to load URL'); + } else if (audioSource.uri.contains('404')) { + throw PlatformException(code: '404', message: 'Not found'); + } else if (audioSource.uri.contains('error')) { + throw PlatformException(code: 'error', message: 'Unknown error'); + } + } + _audioSource = audioSource; + _index = request.initialIndex ?? 0; + // Simulate loading time. + await Future.delayed(Duration(milliseconds: 100)); + _setPosition(request.initialPosition ?? Duration.zero); + _processingState = ProcessingStateMessage.ready; + _broadcastPlaybackEvent(); + return LoadResponse(duration: _duration); + } + + @override + Future play(PlayRequest request) async { + if (_playing) return PlayResponse(); + _playing = true; + _playTimer = Timer(_remaining, () { + _setPosition(_position); + _processingState = ProcessingStateMessage.completed; + _broadcastPlaybackEvent(); + _playCompleter?.complete(); + }); + _playCompleter = Completer(); + _broadcastPlaybackEvent(); + await _playCompleter.future; + return PlayResponse(); + } + + @override + Future pause(PauseRequest request) async { + if (!_playing) return PauseResponse(); + _playing = false; + _playTimer?.cancel(); + _playCompleter?.complete(); + _setPosition(_position); + _broadcastPlaybackEvent(); + return PauseResponse(); + } + + @override + Future seek(SeekRequest request) async { + _setPosition(request.position); + _index = request.index; + _broadcastPlaybackEvent(); + return SeekResponse(); + } + + @override + Future setAndroidAudioAttributes( + SetAndroidAudioAttributesRequest request) async { + return SetAndroidAudioAttributesResponse(); + } + + @override + Future + setAutomaticallyWaitsToMinimizeStalling( + SetAutomaticallyWaitsToMinimizeStallingRequest request) async { + return SetAutomaticallyWaitsToMinimizeStallingResponse(); + } + + @override + Future setLoopMode(SetLoopModeRequest request) async { + _loopMode = request.loopMode; + return SetLoopModeResponse(); + } + + @override + Future setShuffleMode( + SetShuffleModeRequest request) async { + _shuffleModeEnabled = request.shuffleMode == ShuffleModeMessage.all; + return SetShuffleModeResponse(); + } + + @override + Future setShuffleOrder( + SetShuffleOrderRequest request) async { + return SetShuffleOrderResponse(); + } + + @override + Future setSpeed(SetSpeedRequest request) async { + _speed = request.speed; + _setPosition(_position); + return SetSpeedResponse(); + } + + @override + Future setVolume(SetVolumeRequest request) async { + _volume = request.volume; + return SetVolumeResponse(); + } + + @override + Future dispose(DisposeRequest request) async { + _processingState = ProcessingStateMessage.idle; + _broadcastPlaybackEvent(); + return DisposeResponse(); + } + + @override + Future concatenatingInsertAll( + ConcatenatingInsertAllRequest request) async { + // TODO + return ConcatenatingInsertAllResponse(); + } + + @override + Future concatenatingMove( + ConcatenatingMoveRequest request) async { + // TODO + return ConcatenatingMoveResponse(); + } + + @override + Future concatenatingRemoveRange( + ConcatenatingRemoveRangeRequest request) async { + // TODO + return ConcatenatingRemoveRangeResponse(); + } + + _broadcastPlaybackEvent() { + String url; + if (_audioSource is UriAudioSourceMessage) { + // Not sure why this cast is necessary... + url = (_audioSource as UriAudioSourceMessage).uri.toString(); + } + eventController.add(PlaybackEventMessage( + processingState: _processingState, + updatePosition: _updatePosition, + updateTime: _updateTime, + bufferedPosition: _position ?? Duration.zero, + icyMetadata: IcyMetadataMessage( + headers: IcyHeadersMessage( + url: url, + genre: 'Genre', + metadataInterval: 3, + bitrate: 100, + isPublic: true, + name: 'name', + ), + info: IcyInfoMessage( + title: 'title', + url: url, + ), + ), + duration: _duration, + currentIndex: _index, + androidAudioSessionId: null, + )); + } + + Duration get _position { + if (_playing && _processingState == ProcessingStateMessage.ready) { + final result = + _updatePosition + (DateTime.now().difference(_updateTime)) * _speed; + return _duration == null || result <= _duration ? result : _duration; + } else { + return _updatePosition; + } + } + + Duration get _remaining => (_duration - _position) * (1 / _speed); + + void _setPosition(Duration position) { + _updatePosition = position; + _updateTime = DateTime.now(); + } +} + +class MockWebServer { + HttpServer _server; + int get port => _server.port; + + Future start() async { + _server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0); + _server.listen((request) async { + final response = request.response; + final body = utf8.encode('${request.headers.value("custom-header")}'); + if (request.uri.path == '/proxy0.9/foo.mp3') { + final clientSocket = + await request.response.detachSocket(writeHeaders: false); + clientSocket.add(body); + await clientSocket.flush(); + await clientSocket.close(); + } else { + response.contentLength = body.length; + response.statusCode = HttpStatus.ok; + response.headers.set(HttpHeaders.contentTypeHeader, 'audio/mock'); + response.add(body); + await response.flush(); + await response.close(); + } + }); + } + + Future stop() => _server.close(); +} + +class MyHttpOverrides extends HttpOverrides {} diff --git a/just_audio_platform_interface/lib/just_audio_platform_interface.dart b/just_audio_platform_interface/lib/just_audio_platform_interface.dart index 48b947d..36225ad 100644 --- a/just_audio_platform_interface/lib/just_audio_platform_interface.dart +++ b/just_audio_platform_interface/lib/just_audio_platform_interface.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:meta/meta.dart' show required; @@ -69,17 +68,6 @@ abstract class AudioPlayerPlatform { 'playbackEventMessageStream has not been implemented.'); } - /// A stream of visualizer waveform data. - Stream get visualizerWaveformStream { - throw UnimplementedError( - 'visualizerWaveformStream has not been implemented.'); - } - - /// A stream of visualizer fft data. - Stream get visualizerFftStream { - throw UnimplementedError('visualizerFftStream has not been implemented.'); - } - /// Loads an audio source. Future load(LoadRequest request) { throw UnimplementedError("load() has not been implemented."); @@ -169,17 +157,6 @@ abstract class AudioPlayerPlatform { ConcatenatingMoveRequest request) { throw UnimplementedError("concatenatingMove() has not been implemented."); } - - /// Starts the visualizer. - Future startVisualizer( - StartVisualizerRequest request) { - throw UnimplementedError("startVisualizer() has not been implemented."); - } - - /// Stops the visualizer. - Future stopVisualizer(StopVisualizerRequest request) { - throw UnimplementedError("stopVisualizer() has not been implemented."); - } } /// A playback event communicated from the platform implementation to the @@ -647,53 +624,6 @@ class ConcatenatingMoveResponse { ConcatenatingMoveResponse(); } -/// Information communicated to the platform implementation when starting the -/// visualizer. -class StartVisualizerRequest { - final bool enableWaveform; - final bool enableFft; - final int captureRate; - final int captureSize; - - StartVisualizerRequest({ - @required this.enableWaveform, - @required this.enableFft, - @required this.captureRate, - @required this.captureSize, - }); - - Map toMap() => { - 'enableWaveform': enableWaveform, - 'enableFft': enableFft, - 'captureRate': captureRate, - 'captureSize': captureSize, - }; -} - -/// Information returned by the platform implementation after starting the -/// visualizer. -class StartVisualizerResponse { - final int samplingRate; - - StartVisualizerResponse({@required this.samplingRate}); - - static StartVisualizerResponse fromMap(Map map) => - StartVisualizerResponse(samplingRate: map['samplingRate']); -} - -/// Information communicated to the platform implementation when stopping the -/// visualizer. -class StopVisualizerRequest { - Map toMap() => {}; -} - -/// Information returned by the platform implementation after stopping the -/// visualizer. -class StopVisualizerResponse { - static StopVisualizerResponse fromMap(Map map) => - StopVisualizerResponse(); -} - /// Information about an audio source to be communicated with the platform /// implementation. abstract class AudioSourceMessage { diff --git a/just_audio_platform_interface/lib/method_channel_just_audio.dart b/just_audio_platform_interface/lib/method_channel_just_audio.dart index 627dc1d..882670f 100644 --- a/just_audio_platform_interface/lib/method_channel_just_audio.dart +++ b/just_audio_platform_interface/lib/method_channel_just_audio.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:typed_data'; import 'package:flutter/services.dart'; @@ -37,18 +36,6 @@ class MethodChannelAudioPlayer extends AudioPlayerPlatform { .receiveBroadcastStream() .map((map) => PlaybackEventMessage.fromMap(map)); - @override - Stream get visualizerWaveformStream => - EventChannel('com.ryanheise.just_audio.waveform_events.$id') - .receiveBroadcastStream() - .cast(); - - @override - Stream get visualizerFftStream => - EventChannel('com.ryanheise.just_audio.fft_events.$id') - .receiveBroadcastStream() - .cast(); - @override Future load(LoadRequest request) async { return LoadResponse.fromMap( @@ -147,18 +134,4 @@ class MethodChannelAudioPlayer extends AudioPlayerPlatform { return ConcatenatingMoveResponse.fromMap( await _channel.invokeMethod('concatenatingMove', request?.toMap())); } - - @override - Future startVisualizer( - StartVisualizerRequest request) async { - return StartVisualizerResponse.fromMap( - await _channel.invokeMethod('startVisualizer', request?.toMap())); - } - - @override - Future stopVisualizer( - StopVisualizerRequest request) async { - return StopVisualizerResponse.fromMap( - await _channel.invokeMethod('stopVisualizer', request?.toMap())); - } } diff --git a/just_audio_web/pubspec.yaml b/just_audio_web/pubspec.yaml index b050aa1..a140906 100644 --- a/just_audio_web/pubspec.yaml +++ b/just_audio_web/pubspec.yaml @@ -11,9 +11,7 @@ flutter: fileName: just_audio_web.dart dependencies: - # just_audio_platform_interface: ^2.0.0 - just_audio_platform_interface: - path: ../just_audio_platform_interface + just_audio_platform_interface: ^2.0.0 flutter: sdk: flutter flutter_web_plugins: