| 
									
										
										
										
											2020-04-17 17:25:25 +02:00
										 |  |  | import 'dart:io'; | 
					
						
							|  |  |  | import 'dart:isolate'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-11 21:59:23 +02:00
										 |  |  | import 'package:flutter/services.dart'; | 
					
						
							| 
									
										
										
										
											2019-12-02 21:05:49 +01:00
										 |  |  | import 'package:flutter/widgets.dart'; | 
					
						
							| 
									
										
										
										
											2020-04-17 17:25:25 +02:00
										 |  |  | import 'package:moor/isolate.dart'; | 
					
						
							|  |  |  | import 'package:moor/moor.dart'; | 
					
						
							|  |  |  | import 'package:moor_ffi/moor_ffi.dart'; | 
					
						
							| 
									
										
										
										
											2020-04-25 15:02:11 +02:00
										 |  |  | import 'package:musicus_client/musicus_client.dart'; | 
					
						
							| 
									
										
										
										
											2020-04-24 22:41:52 +02:00
										 |  |  | import 'package:musicus_database/musicus_database.dart'; | 
					
						
							| 
									
										
										
										
											2020-04-17 17:25:25 +02:00
										 |  |  | import 'package:path/path.dart' as p; | 
					
						
							|  |  |  | import 'package:path_provider/path_provider.dart' as pp; | 
					
						
							| 
									
										
										
										
											2020-04-25 15:02:11 +02:00
										 |  |  | import 'package:rxdart/rxdart.dart'; | 
					
						
							| 
									
										
										
										
											2020-03-28 10:18:52 +01:00
										 |  |  | import 'package:shared_preferences/shared_preferences.dart'; | 
					
						
							| 
									
										
										
										
											2019-12-02 21:05:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 23:41:08 +02:00
										 |  |  | import 'music_library.dart'; | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  | import 'player.dart'; | 
					
						
							| 
									
										
										
										
											2019-12-03 12:44:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 17:25:25 +02:00
										 |  |  | // The following code was taken from
 | 
					
						
							|  |  |  | // https://moor.simonbinder.eu/docs/advanced-features/isolates/ and just
 | 
					
						
							|  |  |  | // slightly modified.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Future<MoorIsolate> _createMoorIsolate() async { | 
					
						
							|  |  |  |   // This method is called from the main isolate. Since we can't use
 | 
					
						
							|  |  |  |   // getApplicationDocumentsDirectory on a background isolate, we calculate
 | 
					
						
							|  |  |  |   // the database path in the foreground isolate and then inform the
 | 
					
						
							|  |  |  |   // background isolate about the path.
 | 
					
						
							|  |  |  |   final dir = await pp.getApplicationDocumentsDirectory(); | 
					
						
							|  |  |  |   final path = p.join(dir.path, 'db.sqlite'); | 
					
						
							|  |  |  |   final receivePort = ReceivePort(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await Isolate.spawn( | 
					
						
							|  |  |  |     _startBackground, | 
					
						
							|  |  |  |     _IsolateStartRequest(receivePort.sendPort, path), | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // _startBackground will send the MoorIsolate to this ReceivePort.
 | 
					
						
							|  |  |  |   return (await receivePort.first as MoorIsolate); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void _startBackground(_IsolateStartRequest request) { | 
					
						
							|  |  |  |   // This is the entrypoint from the background isolate! Let's create
 | 
					
						
							|  |  |  |   // the database from the path we received.
 | 
					
						
							|  |  |  |   final executor = VmDatabase(File(request.targetPath)); | 
					
						
							|  |  |  |   // We're using MoorIsolate.inCurrent here as this method already runs on a
 | 
					
						
							|  |  |  |   // background isolate. If we used MoorIsolate.spawn, a third isolate would be
 | 
					
						
							|  |  |  |   // started which is not what we want!
 | 
					
						
							|  |  |  |   final moorIsolate = MoorIsolate.inCurrent( | 
					
						
							|  |  |  |     () => DatabaseConnection.fromExecutor(executor), | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   // Inform the starting isolate about this, so that it can call .connect().
 | 
					
						
							|  |  |  |   request.sendMoorIsolate.send(moorIsolate); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Used to bundle the SendPort and the target path, since isolate entrypoint
 | 
					
						
							|  |  |  | // functions can only take one parameter.
 | 
					
						
							|  |  |  | class _IsolateStartRequest { | 
					
						
							|  |  |  |   final SendPort sendMoorIsolate; | 
					
						
							|  |  |  |   final String targetPath; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _IsolateStartRequest(this.sendMoorIsolate, this.targetPath); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-28 09:16:42 +01:00
										 |  |  | enum BackendStatus { | 
					
						
							|  |  |  |   loading, | 
					
						
							| 
									
										
										
										
											2020-03-28 10:18:52 +01:00
										 |  |  |   setup, | 
					
						
							| 
									
										
										
										
											2020-03-28 09:16:42 +01:00
										 |  |  |   ready, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-28 08:51:45 +01:00
										 |  |  | class Backend extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2019-12-02 21:05:49 +01:00
										 |  |  |   final Widget child; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-28 08:51:45 +01:00
										 |  |  |   Backend({ | 
					
						
							|  |  |  |     @required this.child, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   BackendState createState() => BackendState(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   static BackendState of(BuildContext context) => | 
					
						
							|  |  |  |       context.dependOnInheritedWidgetOfExactType<_InheritedBackend>().state; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BackendState extends State<Backend> { | 
					
						
							| 
									
										
										
										
											2020-04-25 21:05:17 +02:00
										 |  |  |   static const defaultUrl = 'https://musicus.johrpan.de/api'; | 
					
						
							| 
									
										
										
										
											2020-04-11 21:59:23 +02:00
										 |  |  |   static const _platform = MethodChannel('de.johrpan.musicus/platform'); | 
					
						
							| 
									
										
										
										
											2020-03-28 09:54:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |   final player = Player(); | 
					
						
							| 
									
										
										
										
											2019-12-02 21:05:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-28 09:16:42 +01:00
										 |  |  |   BackendStatus status = BackendStatus.loading; | 
					
						
							| 
									
										
										
										
											2020-04-11 21:59:23 +02:00
										 |  |  |   Database db; | 
					
						
							| 
									
										
										
										
											2020-04-25 15:02:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   final musicusServerUrl = BehaviorSubject<String>(); | 
					
						
							|  |  |  |   MusicusClient client; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-11 21:59:23 +02:00
										 |  |  |   String musicLibraryUri; | 
					
						
							| 
									
										
										
										
											2020-04-18 23:41:08 +02:00
										 |  |  |   MusicLibrary ml; | 
					
						
							| 
									
										
										
										
											2020-03-28 10:18:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 17:25:25 +02:00
										 |  |  |   MoorIsolate _moorIsolate; | 
					
						
							| 
									
										
										
										
											2020-03-28 10:18:52 +01:00
										 |  |  |   SharedPreferences _shPref; | 
					
						
							| 
									
										
										
										
											2020-03-28 09:16:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							|  |  |  |     _load(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-28 08:51:45 +01:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return _InheritedBackend( | 
					
						
							|  |  |  |       child: widget.child, | 
					
						
							|  |  |  |       state: this, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-12-02 21:05:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-28 09:16:42 +01:00
										 |  |  |   Future<void> _load() async { | 
					
						
							| 
									
										
										
										
											2020-04-17 17:25:25 +02:00
										 |  |  |     _moorIsolate = await _createMoorIsolate(); | 
					
						
							|  |  |  |     final dbConnection = await _moorIsolate.connect(); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  |     player.setup(); | 
					
						
							| 
									
										
										
										
											2020-04-17 17:25:25 +02:00
										 |  |  |     db = Database.connect(dbConnection); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-11 21:59:23 +02:00
										 |  |  |     _shPref = await SharedPreferences.getInstance(); | 
					
						
							| 
									
										
										
										
											2020-04-25 21:05:17 +02:00
										 |  |  |     var url = _shPref.getString('musicusServerUrl'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (url == null) { | 
					
						
							|  |  |  |       url = defaultUrl; | 
					
						
							|  |  |  |       await _shPref.setString('musicusServerUrl', url); | 
					
						
							| 
									
										
										
										
											2020-04-25 15:02:11 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-25 21:05:17 +02:00
										 |  |  |     musicusServerUrl.add(url); | 
					
						
							|  |  |  |     client = MusicusClient(url); | 
					
						
							| 
									
										
										
										
											2020-04-25 15:02:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-11 21:59:23 +02:00
										 |  |  |     musicLibraryUri = _shPref.getString('musicLibraryUri'); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:50:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-11 21:59:23 +02:00
										 |  |  |     _loadMusicLibrary(); | 
					
						
							| 
									
										
										
										
											2020-03-28 10:18:52 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Future<void> _loadMusicLibrary() async { | 
					
						
							| 
									
										
										
										
											2020-04-11 21:59:23 +02:00
										 |  |  |     if (musicLibraryUri == null) { | 
					
						
							| 
									
										
										
										
											2020-03-28 10:18:52 +01:00
										 |  |  |       setState(() { | 
					
						
							|  |  |  |         status = BackendStatus.setup; | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2020-03-28 09:54:04 +01:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2020-04-18 23:41:08 +02:00
										 |  |  |       ml = MusicLibrary(musicLibraryUri); | 
					
						
							|  |  |  |       await ml.load(); | 
					
						
							| 
									
										
										
										
											2020-03-28 09:54:04 +01:00
										 |  |  |       setState(() { | 
					
						
							|  |  |  |         status = BackendStatus.ready; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-11 21:59:23 +02:00
										 |  |  |   Future<void> chooseMusicLibraryUri() async { | 
					
						
							|  |  |  |     final uri = await _platform.invokeMethod<String>('openTree'); | 
					
						
							| 
									
										
										
										
											2020-03-28 10:18:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-11 21:59:23 +02:00
										 |  |  |     if (uri != null) { | 
					
						
							|  |  |  |       musicLibraryUri = uri; | 
					
						
							|  |  |  |       await _shPref.setString('musicLibraryUri', uri); | 
					
						
							|  |  |  |       setState(() { | 
					
						
							|  |  |  |         status = BackendStatus.loading; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       await _loadMusicLibrary(); | 
					
						
							| 
									
										
										
										
											2020-03-28 09:54:04 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-28 09:16:42 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-25 15:02:11 +02:00
										 |  |  |   Future<void> setMusicusServer(String serverUrl) async { | 
					
						
							| 
									
										
										
										
											2020-04-25 21:05:17 +02:00
										 |  |  |     final url = serverUrl.isNotEmpty ? serverUrl : defaultUrl; | 
					
						
							| 
									
										
										
										
											2020-04-25 15:02:11 +02:00
										 |  |  |     await _shPref.setString('musicusServerUrl', url); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (client != null) { | 
					
						
							|  |  |  |       client.dispose(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (url != null) { | 
					
						
							|  |  |  |       client = MusicusClient(url); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     musicusServerUrl.add(url); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 17:25:25 +02:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							| 
									
										
										
										
											2020-04-25 15:02:11 +02:00
										 |  |  |     client.dispose(); | 
					
						
							| 
									
										
										
										
											2020-04-17 17:25:25 +02:00
										 |  |  |     _moorIsolate.shutdownAll(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-28 08:51:45 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _InheritedBackend extends InheritedWidget { | 
					
						
							|  |  |  |   final Widget child; | 
					
						
							|  |  |  |   final BackendState state; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _InheritedBackend({ | 
					
						
							|  |  |  |     @required this.child, | 
					
						
							|  |  |  |     @required this.state, | 
					
						
							|  |  |  |   }) : super(child: child); | 
					
						
							| 
									
										
										
										
											2019-12-02 21:05:49 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2020-03-28 09:52:42 +01:00
										 |  |  |   bool updateShouldNotify(_InheritedBackend old) => true; | 
					
						
							| 
									
										
										
										
											2019-12-02 21:05:49 +01:00
										 |  |  | } |