Customize the speeds available.

This commit is contained in:
stonegate 2020-09-10 18:01:28 +08:00
parent 8aad960ed8
commit d1668a923f
14 changed files with 261 additions and 84 deletions

View File

@ -295,6 +295,8 @@ class MessageLookup extends MessageLookupByLibrary {
"settingsSTDefaultTime" : MessageLookupByLibrary.simpleMessage("Default time"),
"settingsSTDefautTimeDes" : MessageLookupByLibrary.simpleMessage("Default time for sleep timer"),
"settingsSTMode" : MessageLookupByLibrary.simpleMessage("Auto sleep timer mode"),
"settingsSpeeds" : MessageLookupByLibrary.simpleMessage("Speeds"),
"settingsSpeedsDes" : MessageLookupByLibrary.simpleMessage("Customize the speeds available"),
"settingsStorageDes" : MessageLookupByLibrary.simpleMessage("Manage cache and download storage"),
"settingsSyncing" : MessageLookupByLibrary.simpleMessage("Syncing"),
"settingsSyncingDes" : MessageLookupByLibrary.simpleMessage("Refresh podcasts in the background"),

View File

@ -295,6 +295,8 @@ class MessageLookup extends MessageLookupByLibrary {
"settingsSTDefaultTime" : MessageLookupByLibrary.simpleMessage("Tiempo predeterminado"),
"settingsSTDefautTimeDes" : MessageLookupByLibrary.simpleMessage("Tiempo predeterminado de temporizador de sueño"),
"settingsSTMode" : MessageLookupByLibrary.simpleMessage("Modo automático de tempo. de sueño"),
"settingsSpeeds" : MessageLookupByLibrary.simpleMessage("Speeds"),
"settingsSpeedsDes" : MessageLookupByLibrary.simpleMessage("Customize the speeds available"),
"settingsStorageDes" : MessageLookupByLibrary.simpleMessage("Administrar cache y almacenamiento de descargas"),
"settingsSyncing" : MessageLookupByLibrary.simpleMessage("Sincronización"),
"settingsSyncingDes" : MessageLookupByLibrary.simpleMessage("Actualizar podcasts en el fondo"),

View File

@ -295,6 +295,8 @@ class MessageLookup extends MessageLookupByLibrary {
"settingsSTDefaultTime" : MessageLookupByLibrary.simpleMessage("Durée par défaut"),
"settingsSTDefautTimeDes" : MessageLookupByLibrary.simpleMessage("Configuration de la minuterie"),
"settingsSTMode" : MessageLookupByLibrary.simpleMessage("Mode minuterie automatique"),
"settingsSpeeds" : MessageLookupByLibrary.simpleMessage("Speeds"),
"settingsSpeedsDes" : MessageLookupByLibrary.simpleMessage("Customize the speeds avaliable"),
"settingsStorageDes" : MessageLookupByLibrary.simpleMessage("Gestion du cache et de l\'espace de stockage"),
"settingsSyncing" : MessageLookupByLibrary.simpleMessage("Synchronisation"),
"settingsSyncingDes" : MessageLookupByLibrary.simpleMessage("Actualisation des podcasts en arrière-plan"),

View File

@ -295,6 +295,8 @@ class MessageLookup extends MessageLookupByLibrary {
"settingsSTDefaultTime" : MessageLookupByLibrary.simpleMessage("默认时长"),
"settingsSTDefautTimeDes" : MessageLookupByLibrary.simpleMessage("睡眠模式默认时长"),
"settingsSTMode" : MessageLookupByLibrary.simpleMessage("自动睡眠模式默认时长"),
"settingsSpeeds" : MessageLookupByLibrary.simpleMessage("播放速度"),
"settingsSpeedsDes" : MessageLookupByLibrary.simpleMessage("设置播放速度选项"),
"settingsStorageDes" : MessageLookupByLibrary.simpleMessage("管理缓存和下载空间"),
"settingsSyncing" : MessageLookupByLibrary.simpleMessage("同步"),
"settingsSyncingDes" : MessageLookupByLibrary.simpleMessage("在后台更新播客"),

View File

@ -2172,6 +2172,26 @@ class S {
);
}
/// `Speeds`
String get settingsSpeeds {
return Intl.message(
'Speeds',
name: 'settingsSpeeds',
desc: 'Playback speeds setting.',
args: [],
);
}
/// `Customize the speeds available`
String get settingsSpeedsDes {
return Intl.message(
'Customize the speeds available',
name: 'settingsSpeedsDes',
desc: 'Playback speed setting description',
args: [],
);
}
/// `Auto turn on sleep timer`
String get settingsSTAuto {
return Intl.message(

View File

@ -39,7 +39,6 @@ final List<BoxShadow> _customShadowNight = [
];
const List kMinsToSelect = [10, 15, 20, 25, 30, 45, 60, 70, 80, 90, 99];
const List kSpeedToSelect = [0.5, 0.6, 0.8, 1.0, 1.1, 1.2, 1.5, 2.0];
const List kMinPlayerHeight = <double>[70.0, 75.0, 80.0];
const List kMaxPlayerHeight = <double>[300.0, 325.0, 350.0];
@ -912,7 +911,6 @@ class ControlPanel extends StatefulWidget {
class _ControlPanelState extends State<ControlPanel>
with TickerProviderStateMixin {
double _speedSelected;
double _setSpeed;
AnimationController _controller;
Animation<double> _animation;
@ -939,9 +937,13 @@ class _ControlPanelState extends State<ControlPanel>
color: Colors.black)
];
Future<List<double>> _getSpeedList() async {
var storage = KeyValueStorage('speedListKey');
return await storage.getSpeedList();
}
@override
void initState() {
_speedSelected = 0;
_setSpeed = 0;
_tabController = TabController(vsync: this, length: 2)
..addListener(() {
@ -1294,10 +1296,14 @@ class _ControlPanelState extends State<ControlPanel>
children: [
if (height <= widget.maxHeight)
Selector<AudioPlayerNotifier,
Tuple3<EpisodeBrief, bool, bool>>(
selector: (_, audio) => Tuple3(audio.episode,
audio.stopOnComplete, audio.startSleepTimer),
Tuple4<EpisodeBrief, bool, bool, double>>(
selector: (_, audio) => Tuple4(
audio.episode,
audio.stopOnComplete,
audio.startSleepTimer,
audio.currentSpeed),
builder: (_, data, __) {
final currentSpeed = data.item4 ?? 1.0;
return Container(
padding:
const EdgeInsets.symmetric(horizontal: 20.0),
@ -1347,69 +1353,71 @@ class _ControlPanelState extends State<ControlPanel>
child: SingleChildScrollView(
padding: EdgeInsets.all(10.0),
scrollDirection: Axis.horizontal,
child: Row(
children: kSpeedToSelect
.map<Widget>((e) => InkWell(
onTap: () {
if (_setSpeed == 1) {
setState(() =>
_speedSelected = e);
audio.setSpeed(e);
}
},
child: Container(
height: 30,
width: 30,
margin:
EdgeInsets.symmetric(
horizontal: 5),
decoration: e ==
_speedSelected &&
_setSpeed > 0
? BoxDecoration(
color: context
.accentColor,
shape: BoxShape
.circle,
boxShadow: context
.brightness ==
Brightness
.light
? customShadow(
1.0)
: customShadowNight(
1.0),
)
: BoxDecoration(
color: context
.primaryColor,
shape: BoxShape
.circle,
boxShadow: context
.brightness ==
Brightness
.light
? customShadow(1 -
_setSpeed)
: customShadowNight(1 -
_setSpeed)),
alignment:
Alignment.center,
child: _setSpeed > 0
? Text(e.toString(),
style: TextStyle(
fontWeight:
FontWeight
.bold,
color: e ==
_speedSelected
? Colors
.white
: null))
: Center(),
),
))
.toList(),
child: FutureBuilder<List<double>>(
future: _getSpeedList(),
initialData: [],
builder: (context, snapshot) => Row(
children: snapshot.data
.map<Widget>((e) => InkWell(
onTap: () {
if (_setSpeed == 1) {
audio.setSpeed(e);
}
},
child: Container(
height: 30,
width: 30,
margin: EdgeInsets
.symmetric(
horizontal: 5),
decoration: e ==
currentSpeed &&
_setSpeed > 0
? BoxDecoration(
color: context
.accentColor,
shape: BoxShape
.circle,
boxShadow: context
.brightness ==
Brightness
.light
? customShadow(
1.0)
: customShadowNight(
1.0),
)
: BoxDecoration(
color: context
.primaryColor,
shape: BoxShape
.circle,
boxShadow: context
.brightness ==
Brightness
.light
? customShadow(1 -
_setSpeed)
: customShadowNight(1 -
_setSpeed)),
alignment:
Alignment.center,
child: _setSpeed > 0
? Text(e.toString(),
style: TextStyle(
fontWeight:
FontWeight
.bold,
color: e ==
currentSpeed
? Colors
.white
: null))
: Center(),
),
))
.toList(),
),
),
),
),
@ -1429,12 +1437,7 @@ class _ControlPanelState extends State<ControlPanel>
Transform.rotate(
angle: math.pi * _setSpeed,
child: Text('X')),
Selector<AudioPlayerNotifier, double>(
selector: (_, audio) =>
audio.currentSpeed ?? 1.0,
builder: (context, value, child) =>
Text(value.toString()),
),
Text(currentSpeed.toStringAsFixed(1)),
],
),
),

View File

@ -493,6 +493,14 @@
"@settingsRewindSec": {},
"settingsRewindSecDes": "Change the rewind seconds in player",
"@settingsRewindSecDes": {},
"settingsSpeeds": "Speeds",
"@settingsSpeeds": {
"description": "Playback speeds setting."
},
"settingsSpeedsDes": "Customize the speeds available",
"@settingsSpeedsDes": {
"description": "Playback speed setting description"
},
"settingsSTAuto": "Auto turn on sleep timer",
"@settingsSTAuto": {},
"settingsSTAutoDes": "Auto start sleep timer at scheduled time",

View File

@ -493,6 +493,14 @@
"@settingsRewindSec": {},
"settingsRewindSecDes": "Cambia los segundos de retroceso del reproductor",
"@settingsRewindSecDes": {},
"settingsSpeeds": "Speeds",
"@settingsSpeeds": {
"description": "Playback speeds setting."
},
"settingsSpeedsDes": "Customize the speeds available",
"@settingsSpeedsDes": {
"description": "Playback speed setting description"
},
"settingsSTAuto": "Encender temporizador de sueño automáticamente",
"@settingsSTAuto": {},
"settingsSTAutoDes": "Encender temporizador de sueño en un horario determinado",

View File

@ -493,6 +493,14 @@
"@settingsRewindSec": {},
"settingsRewindSecDes": "Saut arrière",
"@settingsRewindSecDes": {},
"settingsSpeeds": "Speeds",
"@settingsSpeeds": {
"description": "Playback speeds setting."
},
"settingsSpeedsDes": "Customize the speeds avaliable",
"@settingsSpeedsDes": {
"description": "Playback speed setting description"
},
"settingsSTAuto": "Activation automatique de la minuterie",
"@settingsSTAuto": {},
"settingsSTAutoDes": "Démarrer la minuterie à l'horaire programmé",

View File

@ -493,6 +493,14 @@
"@settingsRewindSec": {},
"settingsRewindSecDes": "修改播放器快退时间",
"@settingsRewindSecDes": {},
"settingsSpeeds": "播放速度",
"@settingsSpeeds": {
"description": "Playback speeds setting."
},
"settingsSpeedsDes": "设置播放速度选项",
"@settingsSpeedsDes": {
"description": "Playback speed setting description"
},
"settingsSTAuto": "自动睡眠模式",
"@settingsSTAuto": {},
"settingsSTAutoDes": "定期开启睡眠模式",

View File

@ -44,6 +44,7 @@ const String volumeGainKey = 'volumeGainKey';
const String hideListenedKey = 'hideListenedKey';
const String notificationLayoutKey = 'notificationLayoutKey';
const String showNotesFontKey = 'showNotesFontKey';
const String speedListKey = 'speedListKey';
class KeyValueStorage {
final String key;
@ -125,6 +126,24 @@ class KeyValueStorage {
return list.map(int.parse).toList();
}
/// For player speed settings.
Future<bool> saveSpeedList(List<double> list) async {
var prefs = await SharedPreferences.getInstance();
list.sort();
return await prefs.setStringList(
key, list.map((e) => e.toStringAsFixed(1)).toList());
}
Future<List<double>> getSpeedList() async {
var prefs = await SharedPreferences.getInstance();
if (prefs.getStringList(key) == null || prefs.getStringList(key).isEmpty) {
await prefs.setStringList(
key, ['0.5', '0.6', '0.8', '1.0', '1.1', '1.2', '1.5', '2.0']);
}
var list = prefs.getStringList(key);
return list.map(double.parse).toList();
}
/// Rreverse is used for compatite bool value save before which set true = 0, false = 1
Future<bool> getBool(
{@required bool defaultValue, bool reverse = false}) async {

View File

@ -14,7 +14,24 @@ import '../util/custom_dropdown.dart';
import '../util/custom_time_picker.dart';
import '../util/extension_helper.dart';
const List secondsToSelect = [5, 10, 15, 20, 25, 30, 45, 60];
const List kSecondsToSelect = [5, 10, 15, 20, 25, 30, 45, 60];
const List<double> 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 StatelessWidget {
String _volumeEffect(BuildContext context, int i) {
@ -281,7 +298,7 @@ class PlaySetting extends StatelessWidget {
value: data,
onChanged: (value) =>
settings.setFastForwardSeconds = value,
items: secondsToSelect.map<DropdownMenuItem<int>>((e) {
items: kSecondsToSelect.map<DropdownMenuItem<int>>((e) {
return DropdownMenuItem<int>(
value: e, child: Text(s.secCount(e)));
}).toList()),
@ -302,7 +319,7 @@ class PlaySetting extends StatelessWidget {
isDense: true,
value: data,
onChanged: (value) => settings.setRewindSeconds = value,
items: secondsToSelect.map<DropdownMenuItem<int>>((e) {
items: kSecondsToSelect.map<DropdownMenuItem<int>>((e) {
return DropdownMenuItem<int>(
value: e, child: Text(s.secCount(e)));
}).toList()),
@ -329,6 +346,7 @@ class PlaySetting extends StatelessWidget {
}).toList()),
),
),
_SpeedList(),
Divider(height: 1),
Padding(
padding: const EdgeInsets.all(10.0),
@ -529,3 +547,71 @@ class __NotificationLayoutState extends State<_NotificationLayout> {
);
}
}
class _SpeedList extends StatefulWidget {
_SpeedList({Key key}) : super(key: key);
@override
__SpeedListState createState() => __SpeedListState();
}
class __SpeedListState extends State<_SpeedList> {
Future<List<double>> _getSpeedList() async {
var storage = KeyValueStorage('speedListKey');
return await storage.getSpeedList();
}
Future<void> _saveSpeedList(List<double> 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<List<double>>(
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<String>(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());
}),
],
),
);
}
}

View File

@ -493,6 +493,7 @@ class SettingState extends ChangeNotifier {
var notificationLayout =
await KeyValueStorage(notificationLayoutKey).getInt(defaultValue: 0);
var showNotesFont = await showNotesFontStorage.getInt(defaultValue: 1);
var speedList = await KeyValueStorage(speedListKey).getStringList();
return SettingsBackup(
theme: theme,
@ -522,7 +523,8 @@ class SettingState extends ChangeNotifier {
locale: backupLocale,
hideListened: hideListened,
notificationLayout: notificationLayout,
showNotesFont: showNotesFont);
showNotesFont: showNotesFont,
speedList: speedList);
}
Future<void> restore(SettingsBackup backup) async {
@ -557,6 +559,8 @@ class SettingState extends ChangeNotifier {
await KeyValueStorage(notificationLayoutKey)
.saveInt(backup.notificationLayout);
await showNotesFontStorage.saveInt(backup.showNotesFont);
await KeyValueStorage(speedListKey).saveStringList(backup.speedList);
if (backup.locale == '') {
await localeStorage.saveStringList([]);
await S.load(Locale(Intl.systemLocale));

View File

@ -27,6 +27,7 @@ class SettingsBackup {
final bool hideListened;
final int notificationLayout;
final int showNotesFont;
final List<String> speedList;
SettingsBackup(
{this.theme,
this.accentColor,
@ -55,7 +56,8 @@ class SettingsBackup {
this.locale,
this.hideListened,
this.notificationLayout,
this.showNotesFont});
this.showNotesFont,
this.speedList});
Map<String, Object> toJson() {
return {
@ -85,12 +87,14 @@ class SettingsBackup {
'locale': locale,
'hideListened': hideListened,
'notificationLayout': notificationLayout,
'showNotesFont': showNotesFont
'showNotesFont': showNotesFont,
'speedList': speedList
};
}
static SettingsBackup fromJson(Map<String, Object> json) {
var list = List<String>.from(json['episodePopupMenu']);
final menuList = List<String>.from(json['episodePopupMenu']);
final speedList = List<String>.from(json['speedList']);
return SettingsBackup(
theme: json['theme'] as int,
accentColor: json['accentColor'] as String,
@ -105,7 +109,7 @@ class SettingsBackup {
favLayout: json['favLayout'] as int,
downloadLayout: json['downloadLayout'] as int,
autoDownloadNetwork: json['autoDownloadNetwork'] as bool,
episodePopupMenu: list,
episodePopupMenu: menuList,
autoDelete: json['autoDelete'] as int,
autoSleepTimer: json['autoSleepTimer'] as bool,
autoSleepTimerStart: json['autoSleepeTimerStart'] as int,
@ -118,6 +122,7 @@ class SettingsBackup {
locale: json['locale'] as String,
hideListened: json['hideListened'] as bool,
notificationLayout: json['notificationLayout'] as int,
showNotesFont: json['showNotesFont'] as int);
showNotesFont: json['showNotesFont'] as int,
speedList: speedList);
}
}