import 'package:connectivity/connectivity.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import '../local_storage/key_value_storage.dart'; import '../local_storage/sqflite_localpodcast.dart'; import '../state/audio_state.dart'; import '../state/download_state.dart'; import '../type/episodebrief.dart'; import '../type/play_histroy.dart'; import '../type/playlist.dart'; import '../util/extension_helper.dart'; import 'custom_widget.dart'; import 'general_dialog.dart'; class MultiSelectMenuBar extends StatefulWidget { MultiSelectMenuBar( {this.selectedList, this.selectAll, this.onSelectAll, required this.onClose, this.onSelectAfter, this.onSelectBefore, this.hideFavorite = false, Key? key}) : assert(onClose != null), super(key: key); final List? selectedList; final bool? selectAll; final ValueChanged? onSelectAll; final ValueChanged onClose; final ValueChanged? onSelectBefore; final ValueChanged? onSelectAfter; final bool hideFavorite; @override _MultiSelectMenuBarState createState() => _MultiSelectMenuBarState(); } ///Multi select menu bar. class _MultiSelectMenuBarState extends State { late bool _liked; late bool _marked; late bool _inPlaylist; late bool _downloaded; late bool _showPlaylists; final _dbHelper = DBHelper(); @override void initState() { super.initState(); _liked = false; _marked = false; _downloaded = false; _inPlaylist = false; _showPlaylists = false; } @override void didUpdateWidget(MultiSelectMenuBar oldWidget) { if (oldWidget.selectedList != widget.selectedList) { setState(() { _liked = false; _marked = false; _downloaded = false; _inPlaylist = false; _showPlaylists = false; }); super.didUpdateWidget(oldWidget); } } Future _saveLiked() async { for (var episode in widget.selectedList!) { await _dbHelper.setLiked(episode.enclosureUrl); } if (mounted) { setState(() => _liked = true); widget.onClose(false); } } Future _setUnliked() async { for (var episode in widget.selectedList!) { await _dbHelper.setUniked(episode.enclosureUrl); } if (mounted) { setState(() => _liked = false); widget.onClose(false); } } Future _markListened() async { for (var episode in widget.selectedList!) { final history = PlayHistory(episode.title, episode.enclosureUrl, 0, 1); await _dbHelper.saveHistory(history); } if (mounted) { setState(() => _marked = true); widget.onClose(false); } } Future _markNotListened() async { for (var episode in widget.selectedList!) { await _dbHelper.markNotListened(episode.enclosureUrl); } if (mounted) { setState(() => _marked = false); widget.onClose(false); } } Future _requestDownload() async { final permissionReady = await _checkPermmison(); final downloadUsingData = await KeyValueStorage(downloadUsingDataKey) .getBool(defaultValue: true, reverse: true); var dataConfirm = true; final result = await Connectivity().checkConnectivity(); final usingData = result == ConnectivityResult.mobile; if (permissionReady) { if (downloadUsingData && usingData) { dataConfirm = await _useDataConfirm(); } if (dataConfirm) { for (var episode in widget.selectedList!) { Provider.of(context, listen: false).startTask(episode); } if (mounted) { setState(() { _downloaded = true; }); } } } } Future _useDataConfirm() async { var ifUseData = false; final s = context.s; await generalDialog( context, title: Text(s.cellularConfirm), content: Text(s.cellularConfirmDes), actions: [ FlatButton( onPressed: () { Navigator.of(context).pop(); }, child: Text( s.cancel, style: TextStyle(color: Colors.grey[600]), ), ), FlatButton( onPressed: () { ifUseData = true; Navigator.of(context).pop(); }, child: Text( s.confirm, style: TextStyle(color: Colors.red), ), ) ], ); return ifUseData; } Future _checkPermmison() async { var permission = await Permission.storage.status; if (permission != PermissionStatus.granted) { var permissions = await [Permission.storage].request(); if (permissions[Permission.storage] == PermissionStatus.granted) { return true; } else { return false; } } else { return true; } } Future _getEpisode(String url) async { var dbHelper = DBHelper(); return await dbHelper.getRssItemWithUrl(url); } Widget _buttonOnMenu({Widget? child, VoidCallback? onTap}) => Material( color: Colors.transparent, child: InkWell( onTap: onTap, child: SizedBox( height: 40, child: Padding( padding: EdgeInsets.symmetric(horizontal: 12.0), child: child), ), ), ); Widget _playlistList() => SizedBox( height: 50, child: Selector>( selector: (_, audio) => audio.playlists, builder: (_, data, child) { return Align( alignment: Alignment.centerLeft, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ for (var p in data) if (p.name == 'Queue') Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), child: InkWell( onTap: () { setState(() => _showPlaylists = false); showGeneralDialog( context: context, barrierDismissible: true, barrierLabel: MaterialLocalizations.of(context) .modalBarrierDismissLabel, barrierColor: Colors.black54, transitionDuration: const Duration(milliseconds: 200), pageBuilder: (context, animaiton, secondaryAnimation) => _NewPlaylist(widget.selectedList)); }, child: Container( height: 30, child: Row( children: [ Container( height: 30, width: 30, color: context.primaryColorDark, child: Center(child: Icon(Icons.add)), ), SizedBox(width: 10), Text('New') ], ), ), ), ) else Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), child: InkWell( onTap: () { context .read() .addEpisodesToPlaylist(p, episodes: widget.selectedList!); setState(() { _showPlaylists = false; }); }, child: Container( height: 30, child: Row( children: [ Container( height: 30, width: 30, color: context.primaryColorDark, child: p.episodeList.isEmpty ? Center() : FutureBuilder( future: _getEpisode(p.episodeList.first), builder: (_, snapshot) { if (snapshot.data != null) { return SizedBox( height: 30, width: 30, child: Image( image: snapshot .data!.avatarImage)); } return Center(); }), ), SizedBox(width: 10), Text(p.name!), ], ), ), ), ), ], ), ), ); }, )); @override Widget build(BuildContext context) { final s = context.s; var audio = context.watch(); return TweenAnimationBuilder( tween: Tween(begin: 0, end: 1), duration: Duration(milliseconds: 500), builder: (context, dynamic value, child) => Container( height: widget.selectAll == null ? _showPlaylists ? 90 : 40 : _showPlaylists ? 140 : 90.0 * value, decoration: BoxDecoration(color: context.primaryColor), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ if (widget.selectAll != null) Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( height: 40, child: Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 20.0), child: Text( '${widget.selectedList!.length} selected', style: context.textTheme.headline6! .copyWith(color: context.accentColor))), ), ), Spacer(), if (widget.selectedList!.length == 1) SizedBox( height: 25, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: OutlinedButton( style: OutlinedButton.styleFrom( side: BorderSide(color: context.accentColor), primary: context.textColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(100)))), onPressed: () { widget.onSelectBefore!(true); }, child: Text('Before')), ), ), if (widget.selectedList!.length == 1) SizedBox( height: 25, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: OutlinedButton( style: OutlinedButton.styleFrom( side: BorderSide(color: context.accentColor), primary: context.textColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(100)))), onPressed: () { widget.onSelectAfter!(true); }, child: Text('After')), ), ), SizedBox( height: 25, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: OutlinedButton( style: OutlinedButton.styleFrom( side: BorderSide(color: context.accentColor), backgroundColor: widget.selectAll! ? context.accentColor : null, primary: widget.selectAll! ? Colors.white : context.textColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(100)))), onPressed: () { widget.onSelectAll!(!widget.selectAll!); }, child: Text('All')), ), ) ], ), if (_showPlaylists) _playlistList(), Row( children: [ if (!widget.hideFavorite) _buttonOnMenu( child: _liked ? Icon(Icons.favorite, color: Colors.red) : Icon( Icons.favorite_border, color: Colors.grey[700], ), onTap: () async { if (widget.selectedList!.isNotEmpty) { if (!_liked) { await _saveLiked(); Fluttertoast.showToast( msg: s!.liked, gravity: ToastGravity.BOTTOM, ); } else { await _setUnliked(); Fluttertoast.showToast( msg: s!.unliked, gravity: ToastGravity.BOTTOM, ); } audio.setEpisodeState = true; } // OverlayEntry _overlayEntry; // _overlayEntry = _createOverlayEntry(); // Overlay.of(context).insert(_overlayEntry); // await Future.delayed(Duration(seconds: 2)); // _overlayEntry?.remove(); }), _buttonOnMenu( child: _downloaded ? Center( child: SizedBox( height: 20, width: 20, child: CustomPaint( painter: DownloadPainter( color: context.accentColor, fraction: 1, progressColor: context.accentColor, progress: 1), ), ), ) : Center( child: SizedBox( height: 20, width: 20, child: CustomPaint( painter: DownloadPainter( color: Colors.grey[700], fraction: 0, progressColor: context.accentColor, ), ), ), ), onTap: () { if (widget.selectedList!.isNotEmpty) { if (!_downloaded) _requestDownload(); } }, ), _buttonOnMenu( child: _inPlaylist ? Icon(Icons.playlist_add_check, color: context.accentColor) : Icon( Icons.playlist_add, color: Colors.grey[700], ), onTap: () async { if (widget.selectedList!.isNotEmpty) { if (!_inPlaylist) { for (var episode in widget.selectedList!) { audio.addToPlaylist(episode); Fluttertoast.showToast( msg: s!.toastAddPlaylist, gravity: ToastGravity.BOTTOM, ); } setState(() => _inPlaylist = true); } else { for (var episode in widget.selectedList!) { audio.delFromPlaylist(episode); Fluttertoast.showToast( msg: s!.toastRemovePlaylist, gravity: ToastGravity.BOTTOM, ); } setState(() => _inPlaylist = false); } } }), _buttonOnMenu( child: Padding( padding: EdgeInsets.symmetric(vertical: 8), child: CustomPaint( size: Size(25, 25), painter: ListenedAllPainter( _marked ? context.accentColor : Colors.grey[700], stroke: 2.0), ), ), onTap: () async { if (widget.selectedList!.isNotEmpty) { if (!_marked) { await _markListened(); Fluttertoast.showToast( msg: s!.markListened, gravity: ToastGravity.BOTTOM, ); } else { await _markNotListened(); Fluttertoast.showToast( msg: s!.markNotListened, gravity: ToastGravity.BOTTOM, ); } } }), _buttonOnMenu( child: Icon( Icons.add_box_outlined, color: Colors.grey[700], ), onTap: () { if (widget.selectedList!.isNotEmpty) { setState(() { _showPlaylists = !_showPlaylists; }); } }), Spacer(), if (widget.selectAll == null) SizedBox( height: 40, child: Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 10.0), child: Text( '${widget.selectedList!.length} selected', style: context.textTheme.headline6! .copyWith(color: context.accentColor))), ), ), _buttonOnMenu( child: Icon(Icons.close), onTap: () => widget.onClose(true)) ], ), ], ), ), ), ); } } class _NewPlaylist extends StatefulWidget { final List? episodes; _NewPlaylist(this.episodes, {Key? key}) : super(key: key); @override __NewPlaylistState createState() => __NewPlaylistState(); } class __NewPlaylistState extends State<_NewPlaylist> { String? _playlistName; int? _error; @override Widget build(BuildContext context) { final s = context.s; return AnnotatedRegion( value: SystemUiOverlayStyle( statusBarIconBrightness: Brightness.light, systemNavigationBarColor: Theme.of(context).brightness == Brightness.light ? Color.fromRGBO(113, 113, 113, 1) : Color.fromRGBO(5, 5, 5, 1), ), child: AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), elevation: 1, contentPadding: EdgeInsets.symmetric(horizontal: 20), titlePadding: EdgeInsets.all(20), actionsPadding: EdgeInsets.zero, actions: [ FlatButton( splashColor: context.accentColor.withAlpha(70), onPressed: () => Navigator.of(context).pop(), child: Text( s.cancel, style: TextStyle(color: Colors.grey[600]), ), ), FlatButton( splashColor: context.accentColor.withAlpha(70), onPressed: () async { if (context .read() .playlistExisted(_playlistName)) { setState(() => _error = 1); } else { final episodesList = widget.episodes!.map((e) => e.enclosureUrl).toList(); final playlist = Playlist(_playlistName, episodeList: episodesList, episodes: widget.episodes); context.read().addPlaylist(playlist); Navigator.of(context).pop(); } }, child: Text(s.confirm, style: TextStyle(color: context.accentColor)), ) ], title: SizedBox(width: context.width - 160, child: Text('New playlist')), content: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( decoration: InputDecoration( contentPadding: EdgeInsets.symmetric(horizontal: 10), hintText: 'New playlist', hintStyle: TextStyle(fontSize: 18), filled: true, focusedBorder: UnderlineInputBorder( borderSide: BorderSide(color: context.accentColor, width: 2.0), ), enabledBorder: UnderlineInputBorder( borderSide: BorderSide(color: context.accentColor, width: 2.0), ), ), cursorRadius: Radius.circular(2), autofocus: true, maxLines: 1, onChanged: (value) { _playlistName = value; }, ), Container( alignment: Alignment.centerLeft, child: (_error == 1) ? Text( 'Playlist existed', style: TextStyle(color: Colors.red[400]), ) : Center(), ), ], ), ), ); } }