import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:provider/provider.dart'; import 'package:tuple/tuple.dart'; import '../home/audioplayer.dart'; import '../local_storage/key_value_storage.dart'; import '../state/audio_state.dart'; import '../state/setting_state.dart'; import '../util/extension_helper.dart'; import '../widgets/custom_dropdown.dart'; import '../widgets/custom_time_picker.dart'; import '../widgets/custom_widget.dart'; const List kSecondsToSelect = [5, 10, 15, 20, 25, 30, 45, 60]; const List kSpeedToSelect = [ 0.5, 0.6, 0.8, 0.9, 1.0, 1.1, 1.2, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0 ]; class PlaySetting extends StatefulWidget { @override _PlaySettingState createState() => _PlaySettingState(); } class _PlaySettingState extends State { String _volumeEffect(BuildContext context, int i) { final s = context.s; if (i == 2000) { return s.playerHeightShort; } else if (i == 3000) { return s.playerHeightMed; } return s.playerHeightTall; } Future _getMarkListenedSkip() async { final storage = KeyValueStorage(markListenedAfterSkipKey); return storage.getBool(defaultValue: false); } Future _saveMarkListenedSkip(bool boo) async { final storage = KeyValueStorage(markListenedAfterSkipKey); await storage.saveBool(boo); if (mounted) setState(() {}); } Widget _modeWidget(BuildContext context) { var settings = Provider.of(context, listen: false); return Selector>( selector: (_, settings) => Tuple2(settings.autoSleepTimerMode, settings.defaultSleepTimer), builder: (_, data, __) => Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ InkWell( onTap: () => settings.setAutoSleepTimerMode = 0, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(5), topLeft: Radius.circular(5)), child: Material( color: Colors.transparent, child: AnimatedContainer( duration: Duration(milliseconds: 400), decoration: BoxDecoration( color: data.item1 == 0 ? context.accentColor : context.primaryColorDark, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(5), topLeft: Radius.circular(5)), ), padding: const EdgeInsets.all(8.0), child: Text(context.s.endOfEpisode, style: TextStyle( color: data.item1 == 0 ? Colors.white : null)), ), ), ), InkWell( onTap: () => settings.setAutoSleepTimerMode = 1, borderRadius: BorderRadius.only( bottomRight: Radius.circular(5), topRight: Radius.circular(5)), child: Material( color: Colors.transparent, child: AnimatedContainer( duration: Duration(milliseconds: 400), decoration: BoxDecoration( color: data.item1 == 1 ? context.accentColor : context.primaryColorDark, borderRadius: BorderRadius.only( bottomRight: Radius.circular(5), topRight: Radius.circular(5)), ), padding: const EdgeInsets.all(8.0), child: Text(context.s.minsCount(data.item2), style: TextStyle( color: data.item1 == 1 ? Colors.white : null)), ), ), ), ], ), ), ); } Widget _scheduleWidget(BuildContext context) { var settings = Provider.of(context, listen: false); final s = context.s; return Selector>( selector: (_, settings) => Tuple2(settings.autoSleepTimerStart, settings.autoSleepTimerEnd), builder: (_, data, __) => Padding( padding: const EdgeInsets.symmetric(vertical: 10.0), child: Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ InkWell( onTap: () async { var startTime = data.item1; final timeOfDay = await showCustomTimePicker( context: context, cancelText: s.cancel, confirmText: s.confirm, helpText: '', initialTime: TimeOfDay( hour: startTime ~/ 60, minute: startTime % 60)); if (timeOfDay != null) { startTime = timeOfDay.hour * 60 + timeOfDay.minute; if (startTime != data.item2) { settings.setAutoSleepTimerStart = startTime; } else { Fluttertoast.showToast( msg: s.toastTimeEqualEnd, gravity: ToastGravity.BOTTOM, ); } } }, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(5), topLeft: Radius.circular(5)), child: Material( color: Colors.transparent, child: Container( decoration: BoxDecoration( color: context.primaryColorDark, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(5), topLeft: Radius.circular(5)), ), padding: const EdgeInsets.all(8.0), child: Text(s.from(data.item1.toTime)), ), ), ), InkWell( onTap: () async { var endTime = data.item2; final timeOfDay = await showCustomTimePicker( context: context, cancelText: s.cancel, confirmText: s.confirm, helpText: '', initialTime: TimeOfDay(hour: endTime ~/ 60, minute: endTime % 60)); if (timeOfDay != null) { endTime = timeOfDay.hour * 60 + timeOfDay.minute; if (endTime != data.item1) { settings.setAutoSleepTimerEnd = endTime; } else { Fluttertoast.showToast( msg: s.toastTimeEqualStart, gravity: ToastGravity.BOTTOM, ); } } }, borderRadius: BorderRadius.only( bottomRight: Radius.circular(5), topRight: Radius.circular(5)), child: Material( color: Colors.transparent, child: Container( padding: const EdgeInsets.all(8.0), decoration: BoxDecoration( color: Colors.black54, borderRadius: BorderRadius.only( bottomRight: Radius.circular(5), topRight: Radius.circular(5))), child: Text(s.to(data.item2.toTime), style: TextStyle(color: Colors.white)), ), ), ), ], ), ), ); } @override Widget build(BuildContext context) { var settings = context.watch(); var audio = context.watch(); final s = context.s; return AnnotatedRegion( value: SystemUiOverlayStyle( statusBarIconBrightness: Theme.of(context).accentColorBrightness, systemNavigationBarColor: Theme.of(context).primaryColor, systemNavigationBarIconBrightness: Theme.of(context).accentColorBrightness, ), child: Scaffold( appBar: AppBar( title: Text(s.play), leading: CustomBackButton(), elevation: 0, backgroundColor: context.primaryColor, ), body: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( height: 60.0, padding: EdgeInsets.symmetric(horizontal: 40), alignment: Alignment.center, child: Text(s.notificationSetting, style: context.textTheme.bodyText1 .copyWith(color: context.accentColor)), ), _NotificationLayout(), Divider( height: 1, ), Padding( padding: const EdgeInsets.all(10.0), ), Container( height: 30.0, padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text(s.homeMenuPlaylist, style: Theme.of(context) .textTheme .bodyText1 .copyWith(color: Theme.of(context).accentColor)), ), Selector( selector: (_, settings) => settings.autoPlay, builder: (_, data, __) => ListTile( onTap: () => settings.setAutoPlay = !data, contentPadding: EdgeInsets.only(left: 70.0, right: 20, bottom: 10), title: Text(s.settingsMenuAutoPlay), subtitle: Text(s.settingsAutoPlayDes), trailing: Transform.scale( scale: 0.9, child: Switch( value: data, onChanged: (boo) => settings.setAutoPlay = boo), ), ), ), FutureBuilder( initialData: false, future: _getMarkListenedSkip(), builder: (context, snapshot) => ListTile( onTap: () => _saveMarkListenedSkip(!snapshot.data), contentPadding: EdgeInsets.only(left: 70.0, right: 20, bottom: 10), title: Text(s.settingsMarkListenedSkip), subtitle: Text(s.settingsMarkListenedSkipDes), trailing: Transform.scale( scale: 0.9, child: Switch( value: snapshot.data, onChanged: _saveMarkListenedSkip), ), ), ), Divider(height: 1), Padding( padding: const EdgeInsets.all(10.0), ), Container( height: 30.0, padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text(s.playback, style: context.textTheme.bodyText1 .copyWith(color: context.accentColor)), ), ListTile( contentPadding: EdgeInsets.only(left: 70.0, right: 20, bottom: 10, top: 10), title: Text(s.settingsFastForwardSec), subtitle: Text(s.settingsFastForwardSecDes), trailing: Selector( selector: (_, settings) => settings.fastForwardSeconds, builder: (_, data, __) => MyDropdownButton( hint: Text(s.secCount(data)), underline: Center(), elevation: 1, displayItemCount: 5, isDense: true, value: data, onChanged: (value) => settings.setFastForwardSeconds = value, items: kSecondsToSelect.map>((e) { return DropdownMenuItem( value: e, child: Text(s.secCount(e))); }).toList()), ), ), ListTile( contentPadding: EdgeInsets.only(left: 70.0, right: 20, bottom: 10, top: 10), title: Text(s.settingsRewindSec), subtitle: Text(s.settingsRewindSecDes), trailing: Selector( selector: (_, settings) => settings.rewindSeconds, builder: (_, data, __) => MyDropdownButton( hint: Text(s.secCount(data)), underline: Center(), elevation: 1, displayItemCount: 5, isDense: true, value: data, onChanged: (value) => settings.setRewindSeconds = value, items: kSecondsToSelect.map>((e) { return DropdownMenuItem( value: e, child: Text(s.secCount(e))); }).toList()), ), ), ListTile( contentPadding: EdgeInsets.only(left: 70.0, right: 20, bottom: 10, top: 10), title: Text(s.settingsBoostVolume), subtitle: Text(s.settingsBoostVolumeDes), trailing: Selector( selector: (_, audio) => audio.volumeGain, builder: (_, volumeGain, __) => MyDropdownButton( hint: Text(_volumeEffect(context, volumeGain)), underline: Center(), elevation: 1, displayItemCount: 5, isDense: true, value: volumeGain, onChanged: (value) => audio.setVolumeGain = value, items: [2000, 3000, 4000].map>((e) { return DropdownMenuItem( value: e, child: Text(_volumeEffect(context, e))); }).toList()), ), ), _SpeedList(), Divider(height: 1), Padding( padding: const EdgeInsets.all(10.0), ), Container( height: 30.0, padding: EdgeInsets.symmetric(horizontal: 70), alignment: Alignment.centerLeft, child: Text(s.sleepTimer, style: context.textTheme.bodyText1 .copyWith(color: Theme.of(context).accentColor)), ), ListView( physics: const BouncingScrollPhysics(), shrinkWrap: true, scrollDirection: Axis.vertical, children: [ ListTile( contentPadding: EdgeInsets.only(left: 70.0, right: 20), title: Text(s.settingsSTDefaultTime), subtitle: Text(s.settingsSTDefautTimeDes), trailing: Selector( selector: (_, settings) => settings.defaultSleepTimer, builder: (_, data, __) => MyDropdownButton( hint: Text(s.minsCount(data)), underline: Center(), elevation: 1, displayItemCount: 5, isDense: true, value: data, onChanged: (value) => settings.setDefaultSleepTimer = value, items: kMinsToSelect.map>((e) { return DropdownMenuItem( value: e, child: Text(s.minsCount(e))); }).toList()), ), ), Selector( selector: (_, settings) => settings.autoSleepTimer, builder: (_, data, __) => ListTile( onTap: () => settings.setAutoSleepTimer = !data, contentPadding: const EdgeInsets.only( left: 70.0, right: 20.0, bottom: 10.0, top: 10.0), title: Text(s.settingsSTAuto), subtitle: Text(s.settingsSTAutoDes), trailing: Transform.scale( scale: 0.9, child: Switch( value: data, onChanged: (boo) => settings.setAutoSleepTimer = boo), ), ), ), ListTile( contentPadding: const EdgeInsets.only( left: 70.0, right: 20.0, bottom: 10.0, top: 10.0), title: Text(s.settingsSTMode), subtitle: context.width > 360 ? null : _modeWidget(context), trailing: context.width > 360 ? _modeWidget(context) : null), ListTile( contentPadding: EdgeInsets.only(left: 70.0, right: 20), title: Text(s.schedule), subtitle: context.width > 360 ? null : _scheduleWidget(context), trailing: context.width > 360 ? _scheduleWidget(context) : null), Divider(height: 1) ], ), SizedBox(height: 20) ], ), ), ), ); } } class _NotificationLayout extends StatefulWidget { _NotificationLayout({Key key}) : super(key: key); @override __NotificationLayoutState createState() => __NotificationLayoutState(); } class __NotificationLayoutState extends State<_NotificationLayout> { Future _getNotificationLayout() async { final storage = KeyValueStorage(notificationLayoutKey); var index = await storage.getInt(defaultValue: 0); return index; } Future _setNotificationLayout(int index) async { final storage = KeyValueStorage(notificationLayoutKey); await storage.saveInt(index); if (mounted) setState(() {}); } Widget _notificationIcon(Widget icon, String des) { return LimitedBox( maxWidth: 60, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ icon, SizedBox(height: 8), Text(des, style: TextStyle( fontSize: 12, color: context.textColor.withOpacity(0.5)), textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.clip), ], ), ); } Widget _notificationOptions(int index, {int selected}) { final s = context.s; return InkWell( borderRadius: BorderRadius.circular(10.0), onTap: () => _setNotificationLayout(index), child: Container( padding: EdgeInsets.symmetric(vertical: 10), decoration: BoxDecoration( border: Border.all( color: index == selected ? context.accentColor.withAlpha(70) : context.primaryColorDark, ), borderRadius: BorderRadius.circular(10), color: index == selected ? context.accentColor.withAlpha(70) : Colors.transparent, ), child: index == 0 ? Row( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.start, children: [ _notificationIcon( Icon(Icons.pause_circle_filled), '${s.play}| ${s.pause}'), _notificationIcon(Icon(Icons.fast_forward), s.fastForward), _notificationIcon(Icon(Icons.skip_next), s.skipToNext), _notificationIcon(Icon(Icons.close), s.stop), ], ) : index == 1 ? Row( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.start, children: [ _notificationIcon(Icon(Icons.pause_circle_filled), '${s.play}| ${s.pause}'), _notificationIcon( Icon(Icons.fast_rewind), s.fastRewind), _notificationIcon(Icon(Icons.skip_next), s.skipToNext), _notificationIcon(Icon(Icons.close), s.stop), ]) : Row( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.start, children: [ _notificationIcon(Icon(Icons.fast_rewind), s.fastRewind), _notificationIcon(Icon(Icons.pause_circle_filled), '${s.play}| ${s.pause}'), _notificationIcon( Icon(Icons.fast_forward), s.fastForward), _notificationIcon(Icon(Icons.close), s.stop), ], ), ), ); } @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.fromLTRB(40, 0, 40, 30), child: FutureBuilder( future: _getNotificationLayout(), initialData: 0, builder: (context, snapshot) => Column( children: [ _notificationOptions(0, selected: snapshot.data), SizedBox(height: 20), _notificationOptions(1, selected: snapshot.data), SizedBox(height: 20), _notificationOptions(2, selected: snapshot.data), ], ), ), ); } } class _SpeedList extends StatefulWidget { _SpeedList({Key key}) : super(key: key); @override __SpeedListState createState() => __SpeedListState(); } class __SpeedListState extends State<_SpeedList> { Future> _getSpeedList() async { var storage = KeyValueStorage('speedListKey'); return await storage.getSpeedList(); } Future _saveSpeedList(List list) async { var storage = KeyValueStorage('speedListKey'); await storage.saveSpeedList(list); } @override Widget build(BuildContext context) { final s = context.s; return ListTile( contentPadding: EdgeInsets.only(left: 70.0, right: 20, bottom: 10, top: 10), title: Text(s.settingsSpeeds), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(s.settingsSpeedsDes), FutureBuilder>( future: _getSpeedList(), initialData: [], builder: (context, snapshot) { var speedSelected = snapshot.data; return Wrap( children: kSpeedToSelect .map((e) => Padding( padding: const EdgeInsets.only(right: 8.0), child: FilterChip( key: ValueKey(e.toString()), label: Text('X ${e.toStringAsFixed(1)}'), selectedColor: context.accentColor, labelStyle: TextStyle( color: snapshot.data.contains(e) ? Colors.white : context.textColor), elevation: 0, showCheckmark: false, selected: snapshot.data.contains(e), onSelected: (value) async { if (!value) { speedSelected.remove(e); } else { speedSelected.add(e); } await _saveSpeedList(speedSelected); setState(() {}); }, ), )) .toList()); }), ], ), ); } }