diff --git a/lib/local_storage/key_value_storage.dart b/lib/local_storage/key_value_storage.dart index 7dadcc2..c28948e 100644 --- a/lib/local_storage/key_value_storage.dart +++ b/lib/local_storage/key_value_storage.dart @@ -56,6 +56,7 @@ const String gpodderRemoteRemoveKey = 'gpodderRemoteRemoveKey'; const String hidePodcastDiscoveryKey = 'hidePodcastDiscoveryKey'; const String searchEngineKey = 'searchEngineKey'; const String markListenedAfterSkipKey = 'markListenedAfterSkipKey'; +const String downloadPositionKey = 'downloadPositionKey'; class KeyValueStorage { final String key; diff --git a/lib/settings/storage.dart b/lib/settings/storage.dart index 367f88b..349913e 100644 --- a/lib/settings/storage.dart +++ b/lib/settings/storage.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; +import 'package:tsacdop/util/general_dialog.dart'; import '../local_storage/key_value_storage.dart'; import '../settings/downloads_manage.dart'; @@ -20,7 +22,8 @@ class _StorageSettingState extends State final KeyValueStorage cacheStorage = KeyValueStorage(cacheMaxKey); AnimationController _controller; Animation _animation; - _getCacheMax() async { + List _dirs; + Future _getCacheMax() async { var cache = await cacheStorage.getInt(defaultValue: (200 * 1024 * 1024).toInt()); if (cache == 0) { @@ -56,17 +59,30 @@ class _StorageSettingState extends State return days; } - _setAutoDeleteDays(int days) async { + Future _getDownloadPasition() async { + final storage = KeyValueStorage(downloadPositionKey); + final index = await storage.getInt(); + final externalDirs = await getExternalStorageDirectories(); + _dirs = [for (var dir in externalDirs) dir.path]; + return index; + } + + Future _setAutoDeleteDays(int days) async { var storage = KeyValueStorage(autoDeleteKey); await storage.saveInt(days); setState(() {}); } - _setAudtDownloadNetwork(bool boo) async { + Future _setAudtDownloadNetwork(bool boo) async { var storage = KeyValueStorage(autoDownloadNetworkKey); await storage.saveBool(boo); } + Future _setDownloadPosition(int index) async { + final storage = KeyValueStorage(downloadPositionKey); + await storage.saveInt(index); + } + double _value; @override @@ -105,189 +121,181 @@ class _StorageSettingState extends State mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - Column( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: EdgeInsets.all(10.0), + Padding( + padding: EdgeInsets.all(10.0), + ), + Container( + height: 30.0, + padding: EdgeInsets.symmetric(horizontal: 70), + alignment: Alignment.centerLeft, + child: Text(s.network, + style: context.textTheme.bodyText1 + .copyWith(color: context.accentColor)), + ), + Selector( + selector: (_, settings) => settings.downloadUsingData, + builder: (_, data, __) { + return ListTile( + onTap: () => settings.downloadUsingData = !data, + contentPadding: EdgeInsets.only( + left: 70.0, right: 25, bottom: 10, top: 10), + title: Text(s.settingsNetworkCellular), + subtitle: Text(s.settingsNetworkCellularDes), + trailing: Transform.scale( + scale: 0.9, + child: Switch( + value: data, + onChanged: (value) => + settings.downloadUsingData = value, + ), ), - Container( - height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 70), - alignment: Alignment.centerLeft, - child: Text(s.network, - style: Theme.of(context) - .textTheme - .bodyText1 - .copyWith(color: Theme.of(context).accentColor)), - ), - ListView( - physics: const BouncingScrollPhysics(), - shrinkWrap: true, - scrollDirection: Axis.vertical, - children: [ - Selector( - selector: (_, settings) => settings.downloadUsingData, - builder: (_, data, __) { - return ListTile( - onTap: () => settings.downloadUsingData = !data, - contentPadding: EdgeInsets.only( - left: 70.0, right: 25, bottom: 10, top: 10), - title: Text(s.settingsNetworkCellular), - subtitle: Text(s.settingsNetworkCellularDes), - trailing: Transform.scale( - scale: 0.9, - child: Switch( - value: data, - onChanged: (value) => - settings.downloadUsingData = value, - ), - ), - ); + ); + }, + ), + FutureBuilder( + future: _getAutoDownloadNetwork(), + initialData: false, + builder: (context, snapshot) { + return ListTile( + onTap: () async { + _setAudtDownloadNetwork(!snapshot.data); + setState(() {}); + }, + contentPadding: EdgeInsets.only( + left: 70.0, right: 25, bottom: 10, top: 10), + title: Text(s.settingsNetworkCellularAuto), + subtitle: Text(s.settingsNetworkCellularAutoDes), + trailing: Transform.scale( + scale: 0.9, + child: Switch( + value: snapshot.data, + onChanged: (value) async { + await _setAudtDownloadNetwork(value); + setState(() {}); }, ), - Divider(height: 1), - FutureBuilder( - future: _getAutoDownloadNetwork(), - initialData: false, - builder: (context, snapshot) { - return ListTile( - onTap: () async { - _setAudtDownloadNetwork(!snapshot.data); - setState(() {}); - }, - contentPadding: EdgeInsets.only( - left: 70.0, right: 25, bottom: 10, top: 10), - title: Text(s.settingsNetworkCellularAuto), - subtitle: - Text(s.settingsNetworkCellularAutoDes), - trailing: Transform.scale( - scale: 0.9, - child: Switch( - value: snapshot.data, - onChanged: (value) async { - await _setAudtDownloadNetwork(value); - setState(() {}); - }, - ), - ), - ); - }), - Divider(height: 1), - ], - ), - ]), - Column( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: EdgeInsets.all(10.0), - ), - Container( - height: 30.0, - padding: EdgeInsets.symmetric(horizontal: 70), - alignment: Alignment.centerLeft, - child: Text(s.settingStorage, - style: Theme.of(context) - .textTheme - .bodyText1 - .copyWith(color: Theme.of(context).accentColor)), - ), - ListView( - physics: const BouncingScrollPhysics(), - shrinkWrap: true, - scrollDirection: Axis.vertical, - children: [ - ListTile( - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DownloadsManage())), - contentPadding: EdgeInsets.symmetric(horizontal: 70.0), - title: Text(s.download), - subtitle: Text(s.settingsManageDownloadDes), ), - Divider(height: 1), - FutureBuilder( - future: _getAutoDeleteDays(), - initialData: 30, - builder: (context, snapshot) { - return ListTile( - contentPadding: - EdgeInsets.only(left: 70.0, right: 20), - title: Text(s.settingsAutoDelete), - subtitle: Text(s.settingsAutoDeleteDes), - trailing: MyDropdownButton( - hint: snapshot.data == -1 - ? Text(s.daysCount(0)) - : Text(s.daysCount(snapshot.data)), - underline: Center(), - elevation: 1, - value: snapshot.data, - onChanged: (value) async { - await _setAutoDeleteDays(value); - }, - items: [-1, 5, 10, 15, 30] - .map>((e) { - return DropdownMenuItem( - value: e, - child: e == -1 - ? Text(s.daysCount(0)) - : Text(s.daysCount(e))); - }).toList()), - ); - }, - ), - Divider(height: 1), - ListTile( - contentPadding: EdgeInsets.only(left: 70.0, right: 25), - // leading: Icon(Icons.colorize), - title: Text(s.settingsAudioCache), - subtitle: Text(s.settingsAudioCacheDes), - trailing: Text.rich(TextSpan( - text: '${(_value ~/ 100) * 100}', - style: GoogleFonts.teko( - textStyle: context.textTheme.headline6 - .copyWith(color: context.accentColor)), - children: [ - TextSpan( - text: ' Mb', - style: context.textTheme.subtitle2), - ])), - ), - Padding( - padding: EdgeInsets.only( - left: 50.0, right: 20.0, bottom: 10.0), - child: SliderTheme( - data: Theme.of(context).sliderTheme.copyWith( - showValueIndicator: ShowValueIndicator.always, - trackHeight: 2, - thumbShape: - RoundSliderThumbShape(enabledThumbRadius: 6)), - child: Slider( - label: '${_value ~/ 100 * 100} Mb', - activeColor: context.accentColor, - inactiveColor: context.primaryColorDark, - value: _value, - min: 100, - max: 1000, - divisions: 9, - onChanged: (val) { - setState(() { - _value = val; - }); - cacheStorage - .saveInt((val * 1024 * 1024).toInt()); - }), - ), - ), - Divider(height: 1), - ], - ), - ], + ); + }), + Divider(height: 1), + Padding( + padding: EdgeInsets.all(10.0), ), + Container( + height: 30.0, + padding: EdgeInsets.symmetric(horizontal: 70), + alignment: Alignment.centerLeft, + child: Text(s.settingStorage, + style: context.textTheme.bodyText1 + .copyWith(color: context.accentColor)), + ), + ListTile( + onTap: () => Navigator.push(context, + MaterialPageRoute(builder: (context) => DownloadsManage())), + contentPadding: EdgeInsets.symmetric(horizontal: 70.0), + title: Text(s.download), + subtitle: Text(s.settingsManageDownloadDes), + ), + FutureBuilder( + future: _getDownloadPasition(), + initialData: 0, + builder: (context, snapshot) { + return ListTile( + contentPadding: EdgeInsets.only(left: 70.0, right: 20), + title: Text('Donwload position'), + subtitle: Text(_dirs == null ? '' : _dirs[snapshot.data], + maxLines: 2, overflow: TextOverflow.ellipsis), + onTap: () => generalSheet( + context, + title: 'Download position', + child: Column(children: [ + SizedBox( + height: 10, + ), + for (var dir in _dirs) + ListTile( + title: Text(dir), + onTap: () => + _setDownloadPosition(_dirs.indexOf(dir)), + trailing: Radio( + value: _dirs.indexOf(dir), + groupValue: snapshot.data, + onChanged: _setDownloadPosition), + ), + SizedBox( + height: 30, + ) + ]), + ), + ); + }), + FutureBuilder( + future: _getAutoDeleteDays(), + initialData: 30, + builder: (context, snapshot) { + return ListTile( + contentPadding: EdgeInsets.only(left: 70.0, right: 20), + title: Text(s.settingsAutoDelete), + subtitle: Text(s.settingsAutoDeleteDes), + trailing: MyDropdownButton( + hint: snapshot.data == -1 + ? Text(s.daysCount(0)) + : Text(s.daysCount(snapshot.data)), + underline: Center(), + elevation: 1, + value: snapshot.data, + onChanged: (value) async { + await _setAutoDeleteDays(value); + }, + items: [-1, 5, 10, 15, 30] + .map>((e) { + return DropdownMenuItem( + value: e, + child: e == -1 + ? Text(s.daysCount(0)) + : Text(s.daysCount(e))); + }).toList()), + ); + }, + ), + ListTile( + contentPadding: EdgeInsets.only(left: 70.0, right: 25), + // leading: Icon(Icons.colorize), + title: Text(s.settingsAudioCache), + subtitle: Text(s.settingsAudioCacheDes), + trailing: Text.rich(TextSpan( + text: '${(_value ~/ 100) * 100}', + style: GoogleFonts.teko( + textStyle: context.textTheme.headline6 + .copyWith(color: context.accentColor)), + children: [ + TextSpan(text: ' Mb', style: context.textTheme.subtitle2), + ])), + ), + Padding( + padding: EdgeInsets.only(left: 50.0, right: 20.0, bottom: 10.0), + child: SliderTheme( + data: Theme.of(context).sliderTheme.copyWith( + showValueIndicator: ShowValueIndicator.always, + trackHeight: 2, + thumbShape: RoundSliderThumbShape(enabledThumbRadius: 6)), + child: Slider( + label: '${_value ~/ 100 * 100} Mb', + activeColor: context.accentColor, + inactiveColor: context.primaryColorDark, + value: _value, + min: 100, + max: 1000, + divisions: 9, + onChanged: (val) { + setState(() { + _value = val; + }); + cacheStorage.saveInt((val * 1024 * 1024).toInt()); + }), + ), + ), + Divider(height: 1), ], ), ), @@ -295,3 +303,19 @@ class _StorageSettingState extends State ); } } + +class _DownloadPosition extends StatefulWidget { + _DownloadPosition({Key key}) : super(key: key); + + @override + __DownloadPositionState createState() => __DownloadPositionState(); +} + +class __DownloadPositionState extends State<_DownloadPosition> { + @override + Widget build(BuildContext context) { + return Column( + children: [], + ); + } +} diff --git a/lib/state/download_state.dart b/lib/state/download_state.dart index 8fd628e..37e27c0 100644 --- a/lib/state/download_state.dart +++ b/lib/state/download_state.dart @@ -73,6 +73,13 @@ class AutoDownloader { _completer?.complete(); } + Future _getDownloadDirectory() async { + final storage = KeyValueStorage(downloadPositionKey); + final index = await storage.getInt(); + final externalDirs = await getExternalStorageDirectories(); + return externalDirs[index]; + } + Future _saveMediaId(EpisodeTask episodeTask) async { final completeTask = await FlutterDownloader.loadTasksWithRawQuery( query: "SELECT * FROM task WHERE task_id = '${episodeTask.taskId}'"); @@ -91,7 +98,7 @@ class AutoDownloader { Future startTask(List episodes, {bool showNotification = false}) async { for (var episode in episodes) { - final dir = await getExternalStorageDirectory(); + final dir = await _getDownloadDirectory(); var localPath = path.join(dir.path, episode.feedTitle); final saveDir = Directory(localPath); var hasExisted = await saveDir.exists(); @@ -182,6 +189,13 @@ class DownloadState extends ChangeNotifier { notifyListeners(); } + Future _getDownloadDirectory() async { + final storage = KeyValueStorage(downloadPositionKey); + final index = await storage.getInt(); + final externalDirs = await getExternalStorageDirectories(); + return externalDirs[index]; + } + void _bindBackgroundIsolate() { var _port = ReceivePort(); var isSuccess = IsolateNameServer.registerPortWithName( @@ -256,7 +270,7 @@ class DownloadState extends ChangeNotifier { var dbHelper = DBHelper(); var isDownloaded = await dbHelper.isDownloaded(episode.enclosureUrl); if (!isDownloaded) { - final dir = await getExternalStorageDirectory(); + final dir = await _getDownloadDirectory(); var localPath = path.join(dir.path, episode.feedTitle?.replaceAll('/', '')); final saveDir = Directory(localPath);