Skip to content

Commit

Permalink
[All] Implements track picker and info display (#8 & #26)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucas Elisei committed Mar 19, 2018
1 parent 011b643 commit 697daa3
Show file tree
Hide file tree
Showing 15 changed files with 153 additions and 28 deletions.
Binary file added example/assets/images/artwork_default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion example/ios/Flutter/flutter_assets/AssetManifest.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"assets/songs/dubstep.mp3":["assets/songs/dubstep.mp3"],"assets/songs/pi.mp3":["assets/songs/pi.mp3"],"packages/font_awesome_flutter/fonts/fontawesome.woff":["packages/font_awesome_flutter/fonts/fontawesome.woff"]}
{"assets/images/artwork_default.png":["assets/images/artwork_default.png"],"assets/songs/dubstep.mp3":["assets/songs/dubstep.mp3"],"assets/songs/pi.mp3":["assets/songs/pi.mp3"],"packages/font_awesome_flutter/fonts/fontawesome.woff":["packages/font_awesome_flutter/fonts/fontawesome.woff"]}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified example/ios/Flutter/flutter_assets/kernel_blob.bin
Binary file not shown.
4 changes: 2 additions & 2 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

/* 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 */; };
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
Expand Down Expand Up @@ -42,8 +42,8 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
623AC0BD734F52C3AA512567 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
Expand Down
2 changes: 2 additions & 0 deletions example/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Stereo Example</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
Expand Down
14 changes: 8 additions & 6 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class _HomeScreenState extends State<HomeScreen> {
appBar: new AppBar(title: new Text('Stereo Plugin Example')),
body: new Column(children: <Widget>[
new Center(
heightFactor: 2.0,
heightFactor: 1.5,
child: new Text('Choose an action:',
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 20.0))),
Expand All @@ -55,12 +55,14 @@ class _HomeScreenState extends State<HomeScreen> {
child: new Text('Invalid URL'),
onPressed: () => _playFile("invalid_file.mp3")),
new RaisedButton(
child: new Text('Pick file'),
onPressed: () => _pickFile())
child: new Text('Pick file'), onPressed: () => _pickFile())
]),
new Container(height: 5.0),
new MediaInfoWidget(),
new Padding(
padding: new EdgeInsets.all(16.0), child: new MediaPlayerWidget())
padding:
new EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
child: new MediaPlayerWidget())
]));
}

Expand All @@ -82,8 +84,8 @@ class _HomeScreenState extends State<HomeScreen> {
if (fromAppDir) {
await _copyFiles();

await getApplicationDocumentsDirectory()
.then((Directory directory) => dir = directory.path + '/');
await getApplicationDocumentsDirectory().then(
(Directory directory) => dir = 'file://' + directory.path + '/');
}

try {
Expand Down
20 changes: 8 additions & 12 deletions example/lib/media_info_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,22 @@ class _MediaInfoState extends State<MediaInfoWidget> {
}

Widget _getArtwork() {
Widget child;
Image image;

if (_stereo.currentTrack?.artwork != null) {
child = new Image.memory(_stereo.currentTrack.artwork,
height: 250.0, width: 250.0);
image =
new Image.memory(_stereo.currentTrack.artwork, fit: BoxFit.fitHeight);
} else {
child = new Container(
width: 250.0,
height: 250.0,
decoration: new BoxDecoration(color: const Color(0xFF000000)));
image = new Image.asset('assets/images/artwork_default.png',
fit: BoxFit.fitHeight);
}

return child;

// return new Padding(padding: const EdgeInsets.only(top: 44.0), child: child);
return new Expanded(child: image);
}

Widget _getSubtitleText() {
return new Padding(
padding: const EdgeInsets.symmetric(horizontal: 80.0),
padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: new Text(
'${_stereo.currentTrack?.artist ??
AudioTrack.defaults['artist']} - ${_stereo.currentTrack
Expand All @@ -60,7 +56,7 @@ class _MediaInfoState extends State<MediaInfoWidget> {

Widget _getTitleText() {
return new Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 50.0),
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
child: new Text(
'${_stereo.currentTrack?.title ?? AudioTrack.defaults['title']}',
textAlign: TextAlign.center,
Expand Down
1 change: 1 addition & 0 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ dev_dependencies:

flutter:
assets:
- assets/images/artwork_default.png
- assets/songs/dubstep.mp3
- assets/songs/pi.mp3
fonts:
Expand Down
2 changes: 1 addition & 1 deletion example/stereo_example.iml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart Packages" level="project" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
14 changes: 14 additions & 0 deletions src/ios/Classes/MCMediaPickerController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#import <Flutter/Flutter.h>
#import <MediaPlayer/MediaPlayer.h>

@interface MCAudioTrack : NSObject

+ (NSDictionary * _Nonnull)toJson:(NSURL * _Nonnull)url;

@end

@interface MCMediaPickerController : MPMediaPickerController <MPMediaPickerControllerDelegate>

- (MCMediaPickerController * _Nonnull)initWithResult:(FlutterResult _Nonnull)result;

@end
78 changes: 78 additions & 0 deletions src/ios/Classes/MCMediaPickerController.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#import <AVFoundation/AVFoundation.h>
#import <Flutter/Flutter.h>
#import <MediaPlayer/MediaPlayer.h>

#import "MCMediaPickerController.h"

@implementation MCAudioTrack

+ (NSDictionary *)toJson:(NSURL *)url {
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];

NSString *queryString = [url query];
NSArray *components = [queryString componentsSeparatedByString:@"="];
// File isn't in the Music Library.
if ([components count] < 2) {
return data;
}

id trackID = [components objectAtIndex:1];

MPMediaQuery *query = [[MPMediaQuery alloc] init];
[query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:trackID forProperty:MPMediaItemPropertyPersistentID]];

NSArray *items = [query items];
MPMediaItem *item = [items objectAtIndex:0];

UIImage *artworkImage = [item.artwork imageWithSize:CGSizeMake(100, 100)];
NSData *artworkData = [FlutterStandardTypedData typedDataWithBytes:UIImagePNGRepresentation(artworkImage)];

[data setObject:item.albumTitle forKey:@"album"];
[data setObject:item.artist forKey:@"artist"];
[data setObject:artworkData forKey:@"artwork"];
[data setObject:[url absoluteString] forKey:@"path"];
[data setObject:item.title forKey:@"title"];

return data;
}

@end

@implementation MCMediaPickerController {
FlutterResult _result;
}

- (MCMediaPickerController *)initWithResult:(FlutterResult _Nonnull)result {
self = [super initWithMediaTypes:MPMediaTypeAnyAudio];

if (self) {
_result = result;

[self setDelegate:self];
[self setAllowsPickingMultipleItems:NO];
}

return self;
}

#pragma mark - MPMediaPickerControllerDelegate methods

- (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection * _Nonnull)mediaItemCollection {
[mediaPicker dismissViewControllerAnimated:YES completion:nil];

MPMediaItem *item = mediaItemCollection.items[0];

NSDictionary *data = [MCAudioTrack toJson:item.assetURL];

_result(data);
_result = nil;
}

- (void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker {
[mediaPicker dismissViewControllerAnimated:YES completion:nil];

_result([FlutterError errorWithCode:@"NO_TRACK_SELECTED" message:@"No track has been selected." details:nil]);
_result = nil;
}

@end
40 changes: 37 additions & 3 deletions src/ios/Classes/StereoPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
#import <Flutter/Flutter.h>
#import <MediaPlayer/MediaPlayer.h>

#import "MCMediaPickerController.h"
#import "StereoPlugin.h"

@implementation StereoPlugin {
FlutterMethodChannel *_channel;
FlutterViewController *_flutterController;
BOOL _isPlaying;
AVPlayer *_player;
FlutterResult _result;
id _timeObserver;
}

Expand Down Expand Up @@ -56,7 +59,12 @@ - (void)handleMethodCall:(FlutterMethodCall * _Nonnull)call result:(FlutterResul
result([FlutterError errorWithCode:@"WRONG_FORMAT" message:@"The specified URL must be a string." details:nil]);
}

NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"file://%@", (NSString *)call.arguments]];
NSString *arg = (NSString *)call.arguments;
if (![arg hasPrefix:@"ipod-library"]) {
[NSString stringWithFormat:@"file://%@", arg];
}

NSURL *url = [NSURL URLWithString: arg];

result(@([self _loadItemWithURL:url]));
}
Expand All @@ -70,6 +78,17 @@ - (void)handleMethodCall:(FlutterMethodCall * _Nonnull)call result:(FlutterResul

result(nil);
}
// picker() method.
else if ([@"app.picker" isEqualToString:call.method]) {
if (_result != nil) {
_result([FlutterError errorWithCode:@"MULTIPLE_REQUESTS" message:@"Cannot make multiple requests." details:nil]);

_result = nil;
}
_result = result;

[self _picker];
}
// play() method.
else if ([@"app.play" isEqualToString:call.method]) {
[self _play];
Expand Down Expand Up @@ -168,6 +187,10 @@ - (int)_loadItemWithURL:(NSURL * _Nonnull)url {

[_player replaceCurrentItemWithPlayerItem:item];

// Send new track to the application.
NSDictionary *metadata = [MCAudioTrack toJson:url];
[_channel invokeMethod:@"platform.currentTrack" arguments:metadata];

// Send new duration to the application.
int seconds = (int)CMTimeGetSeconds(asset.duration);
[_channel invokeMethod:@"platform.duration" arguments:@(seconds)];
Expand Down Expand Up @@ -204,6 +227,18 @@ - (void)_pause {
_timeObserver = nil;
}

- (void)_picker {
MCMediaPickerController *picker = [[MCMediaPickerController alloc] initWithResult:_result];

// If Flutter controller isn't initialized, do it.
if (_flutterController == nil) {
_flutterController = (FlutterViewController *)[[UIApplication sharedApplication] keyWindow].rootViewController;
}

// Show controller.
[_flutterController presentViewController:picker animated:YES completion:nil];
}

- (void)_play {
if ([_player currentItem] != nil) {
_isPlaying = true;
Expand Down Expand Up @@ -241,12 +276,11 @@ - (void)_stop {
}

- (void)_showMediaPlayerAlert {
FlutterViewController *controller = (FlutterViewController *)[[UIApplication sharedApplication] keyWindow].rootViewController;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message:@"There was an error with the music player. Please restart the app." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okButton = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];

[alert addAction:okButton];
[controller presentViewController:alert animated:YES completion:nil];
[_flutterController presentViewController:alert animated:YES completion:nil];
}

- (void)_updatePosition:(CMTime)time {
Expand Down
2 changes: 0 additions & 2 deletions src/lib/src/stereo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,6 @@ class Stereo {
/// Throws a [StereoFileNotPlayableException] if the specified [uri] points to
/// a file which is not playable.
Future load(String uri) async {
print('[stereo] Loading path: $uri');

int rc = await _channel.invokeMethod('app.load', uri);

_isPlayingNotifier.value = await _isPlaying();
Expand Down
2 changes: 1 addition & 1 deletion src/stereo.iml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
</module>

0 comments on commit 697daa3

Please sign in to comment.