diff options
-rw-r--r-- | .gitreview | 5 | ||||
-rw-r--r-- | License.md | 201 | ||||
-rw-r--r-- | assets/music.png | bin | 0 -> 17810 bytes | |||
-rw-r--r-- | lib/class.dart | 30 | ||||
-rw-r--r-- | lib/loadingPage.dart | 108 | ||||
-rw-r--r-- | lib/main.dart | 33 | ||||
-rw-r--r-- | lib/musicPage.dart | 462 | ||||
-rw-r--r-- | lib/music_methods/controller.dart | 178 | ||||
-rw-r--r-- | lib/music_methods/modeClass.dart | 15 | ||||
-rw-r--r-- | lib/music_methods/mpd.dart | 20 | ||||
-rw-r--r-- | lib/music_methods/musicProvider.dart | 64 | ||||
-rw-r--r-- | lib/nomusic.dart | 27 | ||||
-rw-r--r-- | lib/playlistLoading.dart | 109 | ||||
-rw-r--r-- | lib/size.dart | 43 | ||||
-rw-r--r-- | lib/socketProblem.dart | 29 | ||||
-rw-r--r-- | pubspec.yaml | 90 |
16 files changed, 1414 insertions, 0 deletions
diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..d5d3bcb --- /dev/null +++ b/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=gerrit.automotivelinux.org +port=29418 +project=apps/flutter-mediaplayer +defaultbranch=master diff --git a/License.md b/License.md new file mode 100644 index 0000000..a0b7d15 --- /dev/null +++ b/License.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2022] [Hritik Chouhan] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/assets/music.png b/assets/music.png Binary files differnew file mode 100644 index 0000000..766de9d --- /dev/null +++ b/assets/music.png diff --git a/lib/class.dart b/lib/class.dart new file mode 100644 index 0000000..5aa74cf --- /dev/null +++ b/lib/class.dart @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:core'; + +class CurrentSong{ + CurrentSong({required this.title, required this.artist, required this.duration, required this.isPlaying, required this.time}); + + final String title; + final String artist; + final String duration; + final bool isPlaying; + final String time; + + CurrentSong copyWith({ + String ? title, + String ? artist, + String ? duration, + bool ? isPlaying, + String? time, + }){ + return CurrentSong(artist: artist ?? this.artist, + title: title ?? this.title, + duration: duration ?? this.duration, + isPlaying: isPlaying ?? this.isPlaying, + time: time ?? this.time, + ); + } + +} + diff --git a/lib/loadingPage.dart b/lib/loadingPage.dart new file mode 100644 index 0000000..7d5c1af --- /dev/null +++ b/lib/loadingPage.dart @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; + + +import 'package:musicplayer/music_methods/controller.dart'; +import 'package:musicplayer/nomusic.dart'; +import 'package:musicplayer/playlistLoading.dart'; +import 'package:musicplayer/socketProblem.dart'; + + +class loadingPage extends ConsumerStatefulWidget { + const loadingPage({Key? key}) : super(key: key); + + @override + _loadingPageState createState() => _loadingPageState(); +} + +class _loadingPageState extends ConsumerState<loadingPage> { + + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + MPDTalker mpdTalker = MPDTalker(); + List<Map<String, String>> list = []; + + String convertToMin(String str){ + String strforint = ''; + for(int i = 0; i<str.length;i++){ + if(str[i] == '.'){ + break; + } + strforint += str[i]; + } + int num = int.parse(strforint); + double min = num/60; + double sec = num%60; + String ans = min.toInt().toString()+'min ' + sec.toInt().toString()+'sec'; + return ans; + + } + + + Future mymethod() async{ + list = await mpdTalker.cmdListMap('listall'); + return list; + + } + + @override + Widget build(BuildContext context) { + + return Scaffold( + body: FutureBuilder( + future: mymethod(), + builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) { + + if (snapshot.connectionState == ConnectionState.done) { + // If we got an error + if (snapshot.hasError) { + return const SocketProblem(); + + // if we got our data + } else if (snapshot.hasData) { + // Extracting data from snapshot object + List list = snapshot.data as dynamic; + + if(list.isNotEmpty){ + mpdTalker.cmdStr('clear'); + for(int i =0; i<list.length;i++){ + + String addsong = list[i]['file']; + + mpdTalker.cmdStr('add "$addsong"'); + + } + return PlaylistLoading(); + + } + else{ + return NoMusicFound(); + } + + } else if(snapshot.data == null){ + return NoMusicFound(); + + } + + } + return const Center( + child: CircularProgressIndicator( + color: Colors.black, + ), + ); + + }, + ), + ); + + + + } +}
\ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..58a65e3 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:musicplayer/loadingPage.dart'; +import 'dart:io'; +import 'dart:convert'; +import 'dart:async'; + +import 'package:musicplayer/music_methods/controller.dart'; +import 'package:musicplayer/music_methods/musicProvider.dart'; + + +void main() { + runApp(ProviderScope(child: const MyApp())); +} + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'MediaPlayer', + theme: ThemeData( + + primarySwatch: Colors.blue, + ), + home: loadingPage(), + ); + } +} + diff --git a/lib/musicPage.dart b/lib/musicPage.dart new file mode 100644 index 0000000..ea01a4e --- /dev/null +++ b/lib/musicPage.dart @@ -0,0 +1,462 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:async'; + +import 'package:badges/badges.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter/material.dart'; + +import 'package:musicplayer/size.dart'; + +import 'music_methods/controller.dart'; +import 'music_methods/modeClass.dart'; +import 'music_methods/musicProvider.dart'; + +class MusicPageTest extends ConsumerStatefulWidget { + List<Map<String, String>> list; + + MusicPageTest({Key? key, required this.list}) : super(key: key); + + @override + _MusicPageTestState createState() => _MusicPageTestState(); +} + + + +class _MusicPageTestState extends ConsumerState<MusicPageTest> { + + + MPDTalker mpdTalker = MPDTalker(); + bool isPlaying = false; + late int currindex; + String currSongTime = '0'; + late Timer timer; + + late Mode mode; + + + + @override + void initState() { + // TODO: implement initState + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + + ref.read(CurrentSongProvider.notifier).update(artist:widget.list[0]['Artist'].toString(), title:widget.list[0]['file'].toString(), + duration: convertToMin(widget.list[0]['Time'].toString()), time: '0'); + + //timer for progress bar + + timer = Timer.periodic(Duration(seconds: 1), (timer) async{ + + Map info = await mpdTalker.cmdMap('status'); + ref.read(CurrentSongProvider.notifier).update(time: info['time']); + + + + + if(int.parse(currSongTime) == int.parse(convertTosimpleStr(widget.list[currindex]['Time'].toString()))-1 && mode.isSingle == false){ + + + + + ref.read(CurrentSongProvider.notifier).update(isPlaying: true); + + if(currindex == widget.list.length-1){ + currindex = 0; + ref.read(currIndexProvider.notifier).update(currindex); + ref.read(CurrentSongProvider.notifier).update(artist:widget.list[currindex]['Artist'].toString(), title: widget.list[currindex]['file'].toString(), + duration: convertToMin(widget.list[currindex]['Time'].toString(),)); + } + else{ + currindex++; + ref.read(currIndexProvider.notifier).update(currindex); + + + ref.read(CurrentSongProvider.notifier).update(artist:widget.list[currindex]['Artist'].toString(), title: widget.list[currindex]['file'].toString(), + duration: convertToMin(widget.list[currindex]['Time'].toString(),)); + } + + + + } + + + + + + + + + }); + + + }); + + + + + } + + @override + void dispose() { + timer.cancel(); + super.dispose(); + } + + + + + String convertToMin(String str){ + String strforint = ''; + for(int i = 0; i<str.length;i++){ + if(str[i] == '.'){ + break; + } + strforint += str[i]; + } + int num = int.parse(strforint); + double min = num/60; + double sec = num%60; + String ans = min.toInt().toString()+':' + sec.toInt().toString(); + return ans; + + } + String convertTosimpleStr(String str){ + String strforint = ''; + for(int i = 0; i<str.length;i++){ + if(str[i] == '.'){ + break; + } + strforint += str[i]; + } + return strforint; + + } + + + + + + + + @override + Widget build(BuildContext context) { + isPlaying = ref.watch(CurrentSongProvider).isPlaying; + currSongTime = ref.watch(CurrentSongProvider).time; + SizeConfig().init(context); + + mode = ref.watch(Modeprovider); + currindex = ref.watch(currIndexProvider); + + + + + + + + + + return Scaffold( + + body: Flex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + + children: [ + Flexible( + flex: 4, + child: Flex(direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Flexible( + flex: 1, + child: Container( + + height: SizeConfig.screenHeight*0.5, + width: SizeConfig.screenWidth*0.4, + // color: Colors.blueGrey, + decoration: BoxDecoration( + color: Colors.blueGrey, + borderRadius: BorderRadius.circular(10)), + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Flex( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + direction: Axis.vertical, + children: [ + Flexible( + flex: 3, + child: Image.asset('assets/music.png')), + Flexible( + flex: 1, + child: Flex( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + direction: Axis.horizontal, + children: [Flexible( + flex: 3, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Text(ref.watch(CurrentSongProvider).title, + style: TextStyle( + color: Colors.white, + fontSize: SizeConfig.fontsize/2, + + + ),), + ), + ), + Flexible( + flex: 1, + child: Text(ref.watch(CurrentSongProvider).duration, + style: TextStyle(color: Colors.white,fontSize: SizeConfig.fontsize*0.4),)), + ],)), + + Flexible( + flex: 1, + child:ref.watch(CurrentSongProvider).artist == 'null' ? const Text("Artist: unknown") : Text('Artist: ${ref.watch(CurrentSongProvider).artist}', + style: TextStyle(color: Colors.white,fontSize: SizeConfig.fontsize*0.4),), + ) + ], + ), + ), + + ),), + Flexible(flex : 1, + child: Container( + height: SizeConfig.screenHeight*0.5, + width: SizeConfig.screenWidth*0.4, + child: ListView.builder( + controller: ScrollController(), + scrollDirection: Axis.vertical, + itemCount: widget.list.length, + itemBuilder: ((context, index) => Card( + color: Colors.blueGrey, + shadowColor: Colors.blueAccent, + elevation: 5, + + child: ListTile( + minLeadingWidth: 4, + textColor: Color.fromARGB(199, 255, 255, 255), + + title: Text(widget.list[index]['file'].toString()), + subtitle: Text(widget.list[index]['Artist'] == null ? '' : widget.list[index]['Artist'].toString(), + style: TextStyle(color: Colors.black),), + trailing: Text( + convertToMin(widget.list[index]['Time'].toString(),), + style: TextStyle(color: Colors.black), + ), + onTap: (){ + + + ref.read(CurrentSongProvider.notifier).update(artist:widget.list[index]['Artist'] == null ? "Unknown": widget.list[index]['Artist'].toString(), + title: widget.list[index]['file'].toString(), + duration: convertToMin(widget.list[index]['Time'].toString(),) ,isPlaying: true,); + + mpdTalker.cmdStr('playid ' +widget.list[index]['Id'].toString()); + + currindex = index; + ref.read(currIndexProvider.notifier).update(currindex); + + + }, + ), + ))), + ),), + + + ], + )), + Flexible( + flex: 2, + child: Flex( + direction: Axis.vertical, + children: [ + Flexible( + flex: 1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + flex: 1, + + child: Text(convertToMin(currSongTime))), + Flexible( + flex: 6, + child: RotatedBox( + quarterTurns: 4, + child: Slider( + activeColor: Colors.blueGrey, + + thumbColor: Colors.transparent, + value: int.parse(currSongTime).toDouble()/((int.parse(convertTosimpleStr(widget.list[currindex]['Time'].toString())).toDouble())), + onChanged:(value){ + double seekTime = value*int.parse(convertTosimpleStr(widget.list[currindex]['Time'].toString())).toDouble(); + int seektime = seekTime.toInt(); + mpdTalker.cmd('seekcur $seektime'); + }, + max: 1, + min: 0, + ), + ), + ), + Flexible( + flex: 1, + child: Text(convertToMin(widget.list[currindex]['Time'].toString()))), + + + ], + ),), + Flex( + direction: Axis.horizontal, + + children: [ + Flexible( + flex: 1, + child: SizedBox( + width: SizeConfig.screenWidth/3, + )), + Flexible( + flex : 1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + + Badge( + badgeContent: mode.isSingle ? const Text('1') : null, + elevation: 0, + badgeColor: Colors.greenAccent, + child: IconButton( + icon: const Icon(Icons.loop), + color: Colors.greenAccent, + onPressed: (() { + if(mode.isSingle){ + ref.read(Modeprovider.notifier).update(isSingle: false); + mpdTalker.cmd('single 0'); + } + if(mode.isSingle == false ){ + ref.read(Modeprovider.notifier).update(isSingle: true); + mpdTalker.cmd('single 1'); + + + + + } + + + + }),), + ), + + + IconButton(onPressed: (){ + + mpdTalker.cmdStr('previous'); + ref.read(CurrentSongProvider.notifier).update(isPlaying: true); + + if(currindex == 0){ + + ref.read(currIndexProvider.notifier).update(widget.list.length-1); + currindex = widget.list.length-1; + + ref.read(CurrentSongProvider.notifier).update(artist:widget.list[currindex]['Artist'].toString(), title: widget.list[currindex]['file'].toString(), + duration: convertToMin(widget.list[currindex]['Time'].toString(),)); + } + else{ + currindex--; + ref.read(currIndexProvider.notifier).update(currindex); + ref.read(CurrentSongProvider.notifier).update(artist:widget.list[currindex]['Artist'].toString(), title: widget.list[currindex]['file'].toString(), + duration: convertToMin(widget.list[currindex]['Time'].toString(),)); + } + + + + }, icon: Icon(Icons.skip_previous)), + IconButton( + onPressed: (){ + + if(isPlaying){ + mpdTalker.cmdStr('pause 1'); + } + else{ + mpdTalker.cmdStr('pause 0'); + } + ref.read(CurrentSongProvider.notifier).update( isPlaying: !isPlaying); + + + + + + }, + icon: isPlaying ? Icon(Icons.pause): Icon(Icons.play_arrow) + ), + IconButton(onPressed: (){ + + mpdTalker.cmdStr('next'); + ref.read(CurrentSongProvider.notifier).update(isPlaying: true); + + if(currindex == widget.list.length-1){ + ref.read(currIndexProvider.notifier).update(0); + + + ref.read(CurrentSongProvider.notifier).update(artist:widget.list[0]['Artist'].toString(), title: widget.list[0]['file'].toString(), + duration: convertToMin(widget.list[0]['Time'].toString(),)); + } + else{ + currindex++; + ref.read(currIndexProvider.notifier).update(currindex); + + ref.read(CurrentSongProvider.notifier).update(artist:widget.list[currindex]['Artist'].toString(), title: widget.list[currindex]['file'].toString(), + duration: convertToMin(widget.list[currindex]['Time'].toString(),)); + } + + + + + }, icon: Icon(Icons.skip_next)), + + + ], + ),), + Flexible( + flex: 1, + child: Row( + children: [ + Icon(Icons.volume_up), + SizedBox( + width: SizeConfig.screenWidth/4, + child: RotatedBox( + quarterTurns: 4, + child: Slider(value: ref.watch(VolumeProvider).toDouble()/100, onChanged: (Value){ + double vol = Value*100; + int songVol = vol.toInt(); + mpdTalker.cmd('setvol $songVol'); + ref.read(VolumeProvider.notifier).update(songVol); + + }), + ), + ), + ], + ), + + ) + ], + ), + ], + ), + ), + + ], + + + + ), + ); + + + + } +}
\ No newline at end of file diff --git a/lib/music_methods/controller.dart b/lib/music_methods/controller.dart new file mode 100644 index 0000000..0ccd32b --- /dev/null +++ b/lib/music_methods/controller.dart @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: Apache-2.0 +import 'dart:async'; +import 'dart:io'; + +import 'package:musicplayer/music_methods/mpd.dart'; + +class MPDTalker { + String host; + int port; + // Socket socket; + + MPDTalker({ + this.host: "localhost", + this.port: 6600, + + }); + + /** + * Send a request to MPD and get the output as a String. + */ + Future<String> cmdStr(String cmd) { + Completer<String> com = new Completer<String>(); + String data = ""; + + // Connect to MPD. + Socket.connect(host, port) + .then((Socket socket) { + // Write the command to MPD. + socket.write("$cmd\n"); + + // Listen for MPD. + socket.listen((List<int> chars) { + // Get the response. + String line = new String.fromCharCodes(chars); + data += line; + + // Check if MPD is done sending data. + try { + if(_atEnd(line)) { + socket.close(); + } + } catch(error, stacktrace) { + com.completeError(error, stacktrace); + } + }, + onDone: () { + // Finish the future if there hasn't been an error. + if(!com.isCompleted) { + com.complete(data); + } + }); + }); + + return com.future; + } + + /** + * Check if MPD is done sending data. + */ + bool _atEnd(String str) { + + //str.contains(new RegExp("ACK \[[0-9]?([0-9])@[0-9]?([0-9])\]")); + if(str.contains("ACK [")) { + for(String line in str.split("\n")) { + if(line.startsWith("ACK [")) { + throw new MPDError(line); + break; + } + } + } + + return str.endsWith("OK\n"); + } + + Future cmd(String str) { + Completer com = new Completer(); + cmdStr(str).then((String data) { + com.complete(); + }); + + return com.future; + } + + /** + * Send a request to MPD and get the output as a List of Strings. + */ + Future cmdList(String cmd) { + Completer com = new Completer(); + + // Get the data as a String, then turn it into a List. + cmdStr(cmd).then((String dataStr) { + List<String> data = []; + + // For each line in the string: + for(String line in dataStr.split("\n")) { + // It will be separated into key/value pairs. + List<String> kv = line.split(":"); + + // We care about the value in this case, so add it to our list. + if(kv.length > 1) + data.add(kv[1].trim()); + } + + // Finished. + com.complete(data); + }); + + return com.future; + } + + /** + * Send a request to MPD and get the output as a Map of Strings keyed to Strings. + */ + Future<Map<String, String>> cmdMap(String cmd) async{ + Map<String, String> data = new Map<String, String>(); + + + // Get the data as a String, then turn it into a Map. + String dataStr = await cmdStr(cmd); + + // For every line in the String: + for(String line in dataStr.split("\n")) { + // Split it into key value pairs like a map. + List<String> kv = line.split(":"); + if(kv.length > 1) { + // Add our keys/values to the map. + data[kv[0].trim()] = kv[1].trim(); + } + } + + + + return data; + } + + /** + * Send a request to MPD and get the output as a List of Maps of Strings keyed to Strings. + * + * String newKey: The key that defines a new item in the list. + */ + Future<List<Map<String, String>>> cmdListMap(String cmd, {List<String>? newKeys}) async{ + Completer com = new Completer(); + + // Set newKey's default to ["file"]. + if(newKeys == null) + newKeys = ["file"]; + List<Map<String, String>> data = []; + + + // Get the data as a String, then turn it into a List of Maps. + + + String dataStr = await cmdStr(cmd); + + + // For every line: + for(String line in dataStr.split("\n")) { + // Split it into keys/values. + List<String> kv = line.split(":"); + if(kv.length > 1) { + // If the key is a new key, create a new Map in our list. + for(String key in newKeys) { + if(kv[0].trim() == key) { + data.add(Map<String, String>()); + break; + } + } + // If we have Maps in our list, add the new item to the last Map. + if(data.isNotEmpty) { + data.last[kv[0].trim()] = kv[1].trim(); + } + } + } + + return data; + + } +}
\ No newline at end of file diff --git a/lib/music_methods/modeClass.dart b/lib/music_methods/modeClass.dart new file mode 100644 index 0000000..1cb6122 --- /dev/null +++ b/lib/music_methods/modeClass.dart @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +class Mode{ + Mode({required this.isRepeat, required this.isSingle, required this.prev}); + + final bool isSingle; + final bool isRepeat; + final bool prev; + + + Mode copyWith({bool? isSingle , bool? isRepeat, bool? prev}){ + return Mode(isRepeat: isRepeat ?? this.isRepeat, isSingle: isSingle ?? this.isSingle, prev: prev ?? this.prev); + } + + +}
\ No newline at end of file diff --git a/lib/music_methods/mpd.dart b/lib/music_methods/mpd.dart new file mode 100644 index 0000000..124a5d4 --- /dev/null +++ b/lib/music_methods/mpd.dart @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 +library MPD; + +import "dart:async"; +import "dart:io"; + + + + +/** + * An error for MPD. + */ +class MPDError extends Error { + String msg; + MPDError(this.msg) : super(); + + String toString() { + return "MPD Error: $msg"; + } +}
\ No newline at end of file diff --git a/lib/music_methods/musicProvider.dart b/lib/music_methods/musicProvider.dart new file mode 100644 index 0000000..0e4d450 --- /dev/null +++ b/lib/music_methods/musicProvider.dart @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 +import 'package:flutter/widgets.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:musicplayer/class.dart'; +import 'package:musicplayer/music_methods/modeClass.dart'; + +final PositionProvider = StateNotifierProvider<Position,Duration>(((ref) => Position())); + +class Position extends StateNotifier<Duration>{ + Position() : super(Duration(seconds: 0)); + void update(value) async{ + state = value; + } + + +} + +final CurrentSongProvider = StateNotifierProvider<Current, CurrentSong>(((ref) => Current())); + +class Current extends StateNotifier<CurrentSong>{ + Current() : super(currentval); + static final CurrentSong currentval = CurrentSong(title: 'title', artist: 'unknown', duration: '0min 0sec',isPlaying: false, time: '0'); + + void update({ String? title, String? artist, String? duration, bool? isPlaying, String? time}){ + state = state.copyWith(title: title, artist: artist, duration: duration, isPlaying: isPlaying,time: time); + } + +} + +final VolumeProvider = StateNotifierProvider<Volume,int>(((ref) => Volume())); + +class Volume extends StateNotifier<int>{ + Volume() : super(70); + + void update(int val){ + state = val; + } + +} + +final Modeprovider = StateNotifierProvider<mode,Mode>(((ref) => mode())); + +class mode extends StateNotifier<Mode>{ + mode() : super(intial_value); + static final Mode intial_value = Mode(isRepeat: true, isSingle : false, prev: true); + + void update({bool? isSingle, bool? isRepeat, bool? prev}){ + state = state.copyWith(isRepeat : isRepeat, isSingle : isSingle,prev: prev); + } + +} + +final currIndexProvider = StateNotifierProvider<Index,int>(((ref) => Index())); + +class Index extends StateNotifier<int>{ + Index() : super(0); + + void update(int val){ + state = val; + } + +} + + diff --git a/lib/nomusic.dart b/lib/nomusic.dart new file mode 100644 index 0000000..8fd8b67 --- /dev/null +++ b/lib/nomusic.dart @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +import 'package:flutter/material.dart'; +import 'package:flutter/src/foundation/key.dart'; +import 'package:flutter/src/widgets/framework.dart'; + +class NoMusicFound extends StatelessWidget { + const NoMusicFound({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea(child: Center(child: Container( + height: MediaQuery.of(context).size.height*0.5, + width: MediaQuery.of(context).size.width*0.5, + color: Colors.white, + child: const Center( + child: Text("NO MUSIC FILE FOUND IN THE MUSIC DIRECTORY!!!!", style: TextStyle( + fontSize: 30, + color: Colors.black, + + + ),), + ), + ),)), + ); + } +}
\ No newline at end of file diff --git a/lib/playlistLoading.dart b/lib/playlistLoading.dart new file mode 100644 index 0000000..e643323 --- /dev/null +++ b/lib/playlistLoading.dart @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:flutter/src/foundation/key.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:musicplayer/musicPage.dart'; +import 'package:musicplayer/nomusic.dart'; + +import 'music_methods/controller.dart'; + +class PlaylistLoading extends StatelessWidget { + PlaylistLoading({Key? key}) : super(key: key); + + + + + @override + + MPDTalker mpdTalker = MPDTalker(); + List<Map<String, String>> playlist = []; + + + + String convertToMin(String str){ + String strforint = ''; + for(int i = 0; i<str.length;i++){ + if(str[i] == '.'){ + break; + } + strforint += str[i]; + } + int num = int.parse(strforint); + double min = num/60; + double sec = num%60; + String ans = min.toInt().toString()+'min ' + sec.toInt().toString()+'sec'; + return ans; + + } + + + Future mymethod() async{ + playlist = await mpdTalker.cmdListMap('playlistinfo'); + return playlist; + + } + Widget build(BuildContext context) { + return Scaffold( + body: FutureBuilder( + future: mymethod(), + builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) { + + if (snapshot.connectionState == ConnectionState.done) { + // If we got an error + if (snapshot.hasError) { + return Center( + child: Text( + '${snapshot.error} occurred', + style: TextStyle(fontSize: 18, color: Colors.black), + ), + ); + + // if we got our data + } else if (snapshot.hasData) { + // Extracting data from snapshot object + List<Map<String, String>> list = snapshot.data as dynamic; + + if(list.isNotEmpty){ + + mpdTalker.cmd('repeat 1'); + mpdTalker.cmd('single 0'); + mpdTalker.cmd('consume 0'); + mpdTalker.cmd('play'); + mpdTalker.cmd('pause 1'); + + + + + return MusicPageTest(list : list,); + + } + else{ + return const NoMusicFound(); + + } + + + + + + + + } else if(snapshot.data == null){ + return const NoMusicFound(); + + } + + } + return Center( + child: CircularProgressIndicator( + color: Colors.black, + ), + ); + + }, + ), + ); + + } +}
\ No newline at end of file diff --git a/lib/size.dart b/lib/size.dart new file mode 100644 index 0000000..7018a81 --- /dev/null +++ b/lib/size.dart @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +class SizeConfig { + static late MediaQueryData _mediaQueryData; + static late double screenWidth; + static late double screenHeight; + static late double blockSizeHorizontal; + static late double blockSizeVertical; + static late double _safeAreaHorizontal; + static late double _safeAreaVertical; + static late double safeBlockHorizontal; + static late double safeBlockVertical; + static late double fontsize; + static late TextStyle normalfont; + static late TextStyle smallnormalfont; + + void init(BuildContext context) { + _mediaQueryData = MediaQuery.of(context); + screenWidth = _mediaQueryData.size.width; + screenHeight = _mediaQueryData.size.height; + blockSizeHorizontal = screenWidth / 100; + blockSizeVertical = screenHeight / 100; + _safeAreaHorizontal = + _mediaQueryData.padding.left + _mediaQueryData.padding.right; + _safeAreaVertical = + _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; + safeBlockHorizontal = (screenWidth - _safeAreaHorizontal) / 100; + safeBlockVertical = (screenHeight - _safeAreaVertical) / 100; + fontsize = screenHeight * screenWidth * 0.01 * 0.01 * 0.4; + normalfont = TextStyle( + fontSize: fontsize * 0.8, + fontWeight: FontWeight.w700, + color: Colors.white, + ); + smallnormalfont = TextStyle( + fontSize: fontsize / 2, + fontWeight: FontWeight.w700, + color: Colors.white, + ); + } +}
\ No newline at end of file diff --git a/lib/socketProblem.dart b/lib/socketProblem.dart new file mode 100644 index 0000000..af4b2b5 --- /dev/null +++ b/lib/socketProblem.dart @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +import 'package:flutter/material.dart'; +import 'package:flutter/src/foundation/key.dart'; +import 'package:flutter/src/widgets/framework.dart'; + +class SocketProblem extends StatelessWidget { + const SocketProblem({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + + return Scaffold( + body: SafeArea(child: Center(child: Container( + height: MediaQuery.of(context).size.height*0.5, + width: MediaQuery.of(context).size.width*0.5, + color: Colors.white, + child: const Center( + child: Text("Unable to Connect with MPD server", style: TextStyle( + fontSize: 30, + color: Colors.black, + + + ),), + ), + ),)), + ); + + } +}
\ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..a7b2c3c --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,90 @@ +name: musicplayer +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.17.6 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + flutter_riverpod: ^1.0.4 + badges: ^2.0.3 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# 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 packages. +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: + - assets/ + + # 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 |