mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2025-12-06 02:59:28 +00:00
ui: better sleep timer ui in player and fix auto turn on settings (#43)
* refactor: enhance sleep timer functionality and improve duration formatting * refactor: update sleep timer settings handling * refactor: update cancel icon for sleep timer button * refactor: implement isBetween method for TimeOfDay and simplify sleep timer logic * refactor: update alwaysAutoTurnOnTimer default value and improve icon usage in settings * refactor: remove unused IconButton and update sleep timer preset durations
This commit is contained in:
parent
933bfc5750
commit
12100ffbcd
18 changed files with 755 additions and 383 deletions
|
|
@ -1,17 +1,11 @@
|
|||
import 'package:duration_picker/duration_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:miniplayer/miniplayer.dart';
|
||||
import 'package:vaani/constants/sizes.dart';
|
||||
import 'package:vaani/features/player/providers/currently_playing_provider.dart';
|
||||
import 'package:vaani/features/player/providers/player_form.dart';
|
||||
import 'package:vaani/features/player/view/audiobook_player.dart';
|
||||
import 'package:vaani/features/sleep_timer/core/sleep_timer.dart';
|
||||
import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart'
|
||||
show sleepTimerProvider;
|
||||
import 'package:vaani/settings/app_settings_provider.dart';
|
||||
import 'package:vaani/shared/extensions/duration_format.dart';
|
||||
import 'package:vaani/features/sleep_timer/view/sleep_timer_button.dart';
|
||||
import 'package:vaani/shared/extensions/inverse_lerp.dart';
|
||||
import 'package:vaani/shared/widgets/not_implemented.dart';
|
||||
|
||||
|
|
@ -246,143 +240,18 @@ class PlayerWhenExpanded extends HookConsumerWidget {
|
|||
// chapter list
|
||||
const ChapterSelectionButton(),
|
||||
// settings
|
||||
IconButton(
|
||||
icon: const Icon(Icons.more_horiz),
|
||||
onPressed: () {
|
||||
// show toast
|
||||
showNotImplementedToast(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SleepTimerButton extends HookConsumerWidget {
|
||||
const SleepTimerButton({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sleepTimer = ref.watch(sleepTimerProvider);
|
||||
// if sleep timer is not active, show the button with the sleep timer icon
|
||||
// if the sleep timer is active, show the remaining time in a pill shaped container
|
||||
return Tooltip(
|
||||
message: 'Sleep Timer',
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
pendingPlayerModals++;
|
||||
// show the sleep timer dialog
|
||||
final resultingDuration = await showDurationPicker(
|
||||
context: context,
|
||||
initialTime: ref
|
||||
.watch(appSettingsProvider)
|
||||
.playerSettings
|
||||
.sleepTimerSettings
|
||||
.defaultDuration,
|
||||
);
|
||||
pendingPlayerModals--;
|
||||
if (resultingDuration != null) {
|
||||
// if 0 is selected, cancel the timer
|
||||
if (resultingDuration.inSeconds == 0) {
|
||||
ref.read(sleepTimerProvider.notifier).cancelTimer();
|
||||
} else {
|
||||
ref.read(sleepTimerProvider.notifier).setTimer(resultingDuration);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: sleepTimer == null
|
||||
? Icon(
|
||||
Icons.timer_rounded,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
)
|
||||
: RemainingSleepTimeDisplay(
|
||||
timer: sleepTimer,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SleepTimerDialog extends HookConsumerWidget {
|
||||
const SleepTimerDialog({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sleepTimer = ref.watch(sleepTimerProvider);
|
||||
final sleepTimerSettings =
|
||||
ref.watch(appSettingsProvider).playerSettings.sleepTimerSettings;
|
||||
final timerDurationController = useTextEditingController(
|
||||
text: sleepTimerSettings.defaultDuration.inMinutes.toString(),
|
||||
);
|
||||
|
||||
return AlertDialog(
|
||||
title: const Text('Sleep Timer'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Set the duration for the sleep timer'),
|
||||
TextField(
|
||||
controller: timerDurationController,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Duration in minutes',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// sleepTimer.setTimer(
|
||||
// Duration(
|
||||
// minutes: int.tryParse(timerDurationController.text) ?? 0,
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.more_horiz),
|
||||
// onPressed: () {
|
||||
// // show toast
|
||||
// showNotImplementedToast(context);
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text('Set Timer'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RemainingSleepTimeDisplay extends HookConsumerWidget {
|
||||
const RemainingSleepTimeDisplay({
|
||||
super.key,
|
||||
required this.timer,
|
||||
});
|
||||
|
||||
final SleepTimer timer;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final remainingTime = useStream(timer.remainingTimeStream).data;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
child: Text(
|
||||
timer.timer == null
|
||||
? timer.duration.smartBinaryFormat
|
||||
: remainingTime?.smartBinaryFormat ?? '',
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:list_wheel_scroll_view_nls/list_wheel_scroll_view_nls.dart';
|
||||
import 'package:vaani/features/player/providers/audiobook_player.dart';
|
||||
import 'package:vaani/settings/app_settings_provider.dart';
|
||||
import 'package:vaani/shared/hooks.dart';
|
||||
|
||||
const double itemExtent = 25;
|
||||
|
||||
class SpeedSelector extends HookConsumerWidget {
|
||||
const SpeedSelector({
|
||||
|
|
@ -34,11 +37,11 @@ class SpeedSelector extends HookConsumerWidget {
|
|||
|
||||
// the speed options
|
||||
final minSpeed = min(
|
||||
speeds.reduce((minSpeedSoFar, element) => min(minSpeedSoFar, element)),
|
||||
speeds.reduce(min),
|
||||
playerSettings.minSpeed,
|
||||
);
|
||||
final maxSpeed = max(
|
||||
speeds.reduce((maxSpeedSoFar, element) => max(maxSpeedSoFar, element)),
|
||||
speeds.reduce(max),
|
||||
playerSettings.maxSpeed,
|
||||
);
|
||||
final speedIncrement = playerSettings.speedIncrement;
|
||||
|
|
@ -53,10 +56,9 @@ class SpeedSelector extends HookConsumerWidget {
|
|||
},
|
||||
);
|
||||
|
||||
final scrollController = FixedExtentScrollController(
|
||||
final scrollController = useFixedExtentScrollController(
|
||||
initialItem: availableSpeedsList.indexOf(currentSpeed),
|
||||
);
|
||||
const double itemExtent = 25;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
|
@ -89,7 +91,6 @@ class SpeedSelector extends HookConsumerWidget {
|
|||
availableSpeedsList: availableSpeedsList,
|
||||
speedState: speedState,
|
||||
scrollController: scrollController,
|
||||
itemExtent: itemExtent,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -159,14 +160,12 @@ class SpeedWheel extends StatelessWidget {
|
|||
required this.availableSpeedsList,
|
||||
required this.speedState,
|
||||
required this.scrollController,
|
||||
required this.itemExtent,
|
||||
this.showIncrementButtons = true,
|
||||
});
|
||||
|
||||
final List<double> availableSpeedsList;
|
||||
final ValueNotifier<double> speedState;
|
||||
final FixedExtentScrollController scrollController;
|
||||
final double itemExtent;
|
||||
final bool showIncrementButtons;
|
||||
|
||||
@override
|
||||
|
|
@ -203,7 +202,7 @@ class SpeedWheel extends StatelessWidget {
|
|||
physics: const FixedExtentScrollPhysics(),
|
||||
children: availableSpeedsList
|
||||
.map(
|
||||
(speed) => SpeedLine(itemExtent: itemExtent, speed: speed),
|
||||
(speed) => SpeedLine(speed: speed),
|
||||
)
|
||||
.toList(),
|
||||
onSelectedItemChanged: (index) {
|
||||
|
|
@ -236,11 +235,9 @@ class SpeedWheel extends StatelessWidget {
|
|||
class SpeedLine extends StatelessWidget {
|
||||
const SpeedLine({
|
||||
super.key,
|
||||
required this.itemExtent,
|
||||
required this.speed,
|
||||
});
|
||||
|
||||
final double itemExtent;
|
||||
final double speed;
|
||||
|
||||
@override
|
||||
|
|
@ -250,7 +247,6 @@ class SpeedLine extends StatelessWidget {
|
|||
// a vertical line
|
||||
Expanded(
|
||||
child: Container(
|
||||
// height: itemExtent,
|
||||
// thick if multiple of 1, thin if multiple of 0.5 and transparent if multiple of 0.05
|
||||
width: speed % 0.5 == 0
|
||||
? 3
|
||||
|
|
|
|||
|
|
@ -19,8 +19,18 @@ class SleepTimer {
|
|||
|
||||
set duration(Duration value) {
|
||||
_duration = value;
|
||||
_logger.fine('duration set to $value');
|
||||
|
||||
/// if the timer is active, restart it with the new duration
|
||||
/// if the timer is not active, do nothing
|
||||
if (isActive && player.playing) {
|
||||
_logger.fine('timer is active counting down with new duration');
|
||||
startCountDown(value);
|
||||
} else {
|
||||
_logger.fine('timer is not active');
|
||||
clearCountDownTimer();
|
||||
}
|
||||
}
|
||||
|
||||
/// The player to be paused
|
||||
final AudiobookPlayer player;
|
||||
|
|
|
|||
|
|
@ -11,20 +11,16 @@ part 'sleep_timer_provider.g.dart';
|
|||
class SleepTimer extends _$SleepTimer {
|
||||
@override
|
||||
core.SleepTimer? build() {
|
||||
final appSettings = ref.watch(appSettingsProvider);
|
||||
final sleepTimerSettings = appSettings.playerSettings.sleepTimerSettings;
|
||||
bool isEnabled = sleepTimerSettings.autoTurnOnTimer;
|
||||
if (!isEnabled) {
|
||||
final sleepTimerSettings = ref.watch(sleepTimerSettingsProvider);
|
||||
if (!sleepTimerSettings.autoTurnOnTimer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((!sleepTimerSettings.alwaysAutoTurnOnTimer) &&
|
||||
(sleepTimerSettings.autoTurnOnTime
|
||||
.toTimeOfDay()
|
||||
.isAfter(TimeOfDay.now()) &&
|
||||
sleepTimerSettings.autoTurnOffTime
|
||||
.toTimeOfDay()
|
||||
.isBefore(TimeOfDay.now()))) {
|
||||
!shouldBuildRightNow(
|
||||
sleepTimerSettings.autoTurnOnTime,
|
||||
sleepTimerSettings.autoTurnOffTime,
|
||||
)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -36,10 +32,16 @@ class SleepTimer extends _$SleepTimer {
|
|||
return sleepTimer;
|
||||
}
|
||||
|
||||
void setTimer(Duration resultingDuration) {
|
||||
void setTimer(Duration? resultingDuration, {bool notifyListeners = true}) {
|
||||
if (resultingDuration == null || resultingDuration.inSeconds == 0) {
|
||||
cancelTimer();
|
||||
return;
|
||||
}
|
||||
if (state != null) {
|
||||
state!.duration = resultingDuration;
|
||||
if (notifyListeners) {
|
||||
ref.notifyListeners();
|
||||
}
|
||||
} else {
|
||||
final timer = core.SleepTimer(
|
||||
duration: resultingDuration,
|
||||
|
|
@ -62,3 +64,11 @@ class SleepTimer extends _$SleepTimer {
|
|||
state = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldBuildRightNow(Duration autoTurnOnTime, Duration autoTurnOffTime) {
|
||||
final now = TimeOfDay.now();
|
||||
return now.isBetween(
|
||||
autoTurnOnTime.toTimeOfDay(),
|
||||
autoTurnOffTime.toTimeOfDay(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ part of 'sleep_timer_provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$sleepTimerHash() => r'4f80bcc342e918c70c547b8b24790ccd88aba8c3';
|
||||
String _$sleepTimerHash() => r'2679454a217d0630a833d730557ab4e4feac2e56';
|
||||
|
||||
/// See also [SleepTimer].
|
||||
@ProviderFor(SleepTimer)
|
||||
|
|
|
|||
366
lib/features/sleep_timer/view/sleep_timer_button.dart
Normal file
366
lib/features/sleep_timer/view/sleep_timer_button.dart
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:list_wheel_scroll_view_nls/list_wheel_scroll_view_nls.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:vaani/features/player/view/player_when_expanded.dart';
|
||||
import 'package:vaani/features/player/view/widgets/speed_selector.dart';
|
||||
import 'package:vaani/features/sleep_timer/core/sleep_timer.dart';
|
||||
import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart'
|
||||
show sleepTimerProvider;
|
||||
import 'package:vaani/main.dart';
|
||||
import 'package:vaani/settings/app_settings_provider.dart';
|
||||
import 'package:vaani/shared/extensions/duration_format.dart';
|
||||
import 'package:vaani/shared/hooks.dart';
|
||||
|
||||
class SleepTimerButton extends HookConsumerWidget {
|
||||
const SleepTimerButton({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sleepTimer = ref.watch(sleepTimerProvider);
|
||||
final durationState = useState(sleepTimer?.duration);
|
||||
|
||||
// if sleep timer is not active, show the button with the sleep timer icon
|
||||
// if the sleep timer is active, show the remaining time in a pill shaped container
|
||||
return Tooltip(
|
||||
message: 'Sleep Timer',
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
appLogger.fine('Sleep Timer button pressed');
|
||||
pendingPlayerModals++;
|
||||
// show the sleep timer dialog
|
||||
await showModalBottomSheet<Duration?>(
|
||||
context: context,
|
||||
barrierLabel: 'Sleep Timer',
|
||||
builder: (context) {
|
||||
return SleepTimerBottomSheet(
|
||||
onDurationSelected: (duration) {
|
||||
durationState.value = duration;
|
||||
// ref
|
||||
// .read(sleepTimerProvider.notifier)
|
||||
// .setTimer(duration, notifyListeners: false);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
pendingPlayerModals--;
|
||||
ref.read(sleepTimerProvider.notifier).setTimer(durationState.value);
|
||||
appLogger
|
||||
.fine('Sleep Timer dialog closed with ${durationState.value}');
|
||||
},
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: sleepTimer == null
|
||||
? Icon(
|
||||
Symbols.bedtime,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
)
|
||||
: RemainingSleepTimeDisplay(
|
||||
timer: sleepTimer,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SleepTimerBottomSheet extends HookConsumerWidget {
|
||||
const SleepTimerBottomSheet({
|
||||
super.key,
|
||||
this.onDurationSelected,
|
||||
});
|
||||
|
||||
final void Function(Duration?)? onDurationSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sleepTimer = ref.watch(sleepTimerProvider);
|
||||
final sleepTimerSettings = ref.watch(sleepTimerSettingsProvider);
|
||||
|
||||
final durationOptions = sleepTimerSettings.presetDurations;
|
||||
final minDuration = Duration.zero;
|
||||
final maxDuration = <Duration>[
|
||||
...durationOptions,
|
||||
sleepTimerSettings.maxDuration,
|
||||
].reduce((a, b) => a > b ? a : b);
|
||||
final incrementStep = Duration(minutes: 1);
|
||||
final allPossibleDurations = [
|
||||
for (var i = minDuration; i <= maxDuration; i += incrementStep) i,
|
||||
];
|
||||
|
||||
final scrollController = useFixedExtentScrollController(
|
||||
initialItem:
|
||||
allPossibleDurations.indexOf(sleepTimer?.duration ?? minDuration),
|
||||
);
|
||||
|
||||
final durationState = useState<Duration>(
|
||||
sleepTimer?.duration ?? minDuration,
|
||||
);
|
||||
|
||||
// useEffect to rebuild the sleep timer when the duration changes
|
||||
useEffect(
|
||||
() {
|
||||
onDurationSelected?.call(durationState.value);
|
||||
return null;
|
||||
},
|
||||
[durationState.value],
|
||||
);
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// the title
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0, bottom: 8.0),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Sleep Timer',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// a inverted triangle to indicate the speed selector
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Icon(
|
||||
Icons.arrow_drop_down,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
|
||||
// the speed selector
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0, right: 8.0, left: 8.0),
|
||||
child: SizedBox(
|
||||
height: 80,
|
||||
child: SleepTimerWheel(
|
||||
durationState: durationState,
|
||||
availableDurations: allPossibleDurations,
|
||||
scrollController: scrollController,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// a cancel button to cancel the sleep timer
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: TextButton.icon(
|
||||
onPressed: () {
|
||||
ref.read(sleepTimerProvider.notifier).cancelTimer();
|
||||
onDurationSelected?.call(null);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
icon: const Icon(Symbols.bedtime_off),
|
||||
label: const Text('Cancel Sleep Timer'),
|
||||
),
|
||||
),
|
||||
|
||||
// the speed buttons
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Wrap(
|
||||
spacing: 8.0,
|
||||
alignment: WrapAlignment.center,
|
||||
runAlignment: WrapAlignment.center,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: durationOptions
|
||||
.map(
|
||||
(timerDuration) => TextButton(
|
||||
style: timerDuration == durationState.value
|
||||
? TextButton.styleFrom(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.primaryContainer,
|
||||
foregroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.onPrimaryContainer,
|
||||
)
|
||||
// border if not selected
|
||||
: TextButton.styleFrom(
|
||||
side: BorderSide(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primaryContainer,
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
// animate the wheel to the selected speed
|
||||
var index = allPossibleDurations.indexOf(timerDuration);
|
||||
// if the speed is not in the list
|
||||
if (index == -1) {
|
||||
// find the nearest speed
|
||||
final nearestDuration = allPossibleDurations.firstWhere(
|
||||
(element) => element > timerDuration,
|
||||
orElse: () => allPossibleDurations.last,
|
||||
);
|
||||
index = allPossibleDurations.indexOf(nearestDuration);
|
||||
}
|
||||
await scrollController.animateToItem(
|
||||
index,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
},
|
||||
child: Text(timerDuration.smartBinaryFormat),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RemainingSleepTimeDisplay extends HookConsumerWidget {
|
||||
const RemainingSleepTimeDisplay({
|
||||
super.key,
|
||||
required this.timer,
|
||||
});
|
||||
|
||||
final SleepTimer timer;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final remainingTime = useStream(timer.remainingTimeStream).data;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
child: Text(
|
||||
timer.timer == null
|
||||
? timer.duration.smartBinaryFormat
|
||||
: remainingTime?.smartBinaryFormat ?? '',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SleepTimerWheel extends StatelessWidget {
|
||||
const SleepTimerWheel({
|
||||
super.key,
|
||||
required this.availableDurations,
|
||||
required this.scrollController,
|
||||
required this.durationState,
|
||||
this.showIncrementButtons = true,
|
||||
});
|
||||
|
||||
final List<Duration> availableDurations;
|
||||
final ValueNotifier<Duration?> durationState;
|
||||
final FixedExtentScrollController scrollController;
|
||||
final bool showIncrementButtons;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// a minus button to decrease the speed
|
||||
if (showIncrementButtons)
|
||||
IconButton.filledTonal(
|
||||
icon: const Icon(Icons.remove),
|
||||
onPressed: () {
|
||||
// animate to index - 1
|
||||
final index = availableDurations
|
||||
.indexOf(durationState.value ?? Duration.zero);
|
||||
if (index > 0) {
|
||||
scrollController.animateToItem(
|
||||
index - 1,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
// the speed selector wheel
|
||||
Flexible(
|
||||
child: ListWheelScrollViewX(
|
||||
controller: scrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemExtent: itemExtent,
|
||||
diameterRatio: 1.5, squeeze: 1.2,
|
||||
// useMagnifier: true,
|
||||
// magnification: 1.5,
|
||||
physics: const FixedExtentScrollPhysics(),
|
||||
children: availableDurations
|
||||
.map(
|
||||
(duration) => DurationLine(duration: duration),
|
||||
)
|
||||
.toList(),
|
||||
onSelectedItemChanged: (index) {
|
||||
durationState.value = availableDurations[index];
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
if (showIncrementButtons)
|
||||
// a plus button to increase the speed
|
||||
IconButton.filledTonal(
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () {
|
||||
// animate to index + 1
|
||||
final index = availableDurations
|
||||
.indexOf(durationState.value ?? Duration.zero);
|
||||
if (index < availableDurations.length - 1) {
|
||||
scrollController.animateToItem(
|
||||
index + 1,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DurationLine extends StatelessWidget {
|
||||
const DurationLine({
|
||||
super.key,
|
||||
required this.duration,
|
||||
});
|
||||
|
||||
final Duration duration;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
// a vertical line
|
||||
Expanded(
|
||||
child: Container(
|
||||
// thick if multiple of 1, thin if multiple of 0.5 and transparent if multiple of 0.05
|
||||
width: duration.inMinutes % 5 == 0
|
||||
? 3
|
||||
: duration.inMinutes % 2.5 == 0
|
||||
? 2
|
||||
: 0.5,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
Opacity(
|
||||
opacity: duration.inMinutes % 2.5 == 0 ? 1 : 0,
|
||||
child: Text(
|
||||
'${duration.inMinutes}m',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelSmall?.fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -60,3 +60,19 @@ class AppSettings extends _$AppSettings {
|
|||
state = const model.AppSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// SleepTimerSettings provider but only rebuilds when the sleep timer settings change
|
||||
@Riverpod(keepAlive: true)
|
||||
class SleepTimerSettings extends _$SleepTimerSettings {
|
||||
@override
|
||||
model.SleepTimerSettings build() {
|
||||
final settings = ref.read(appSettingsProvider).sleepTimerSettings;
|
||||
state = settings;
|
||||
ref.listen(appSettingsProvider, (a, b) {
|
||||
if (a?.sleepTimerSettings != b.sleepTimerSettings) {
|
||||
state = b.sleepTimerSettings;
|
||||
}
|
||||
});
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,5 +21,22 @@ final appSettingsProvider =
|
|||
);
|
||||
|
||||
typedef _$AppSettings = Notifier<model.AppSettings>;
|
||||
String _$sleepTimerSettingsHash() =>
|
||||
r'85bb3d3fb292b9a3a5b771d86e5fc57718519c69';
|
||||
|
||||
/// See also [SleepTimerSettings].
|
||||
@ProviderFor(SleepTimerSettings)
|
||||
final sleepTimerSettingsProvider =
|
||||
NotifierProvider<SleepTimerSettings, model.SleepTimerSettings>.internal(
|
||||
SleepTimerSettings.new,
|
||||
name: r'sleepTimerSettingsProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$sleepTimerSettingsHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$SleepTimerSettings = Notifier<model.SleepTimerSettings>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ class AppSettings with _$AppSettings {
|
|||
const factory AppSettings({
|
||||
@Default(ThemeSettings()) ThemeSettings themeSettings,
|
||||
@Default(PlayerSettings()) PlayerSettings playerSettings,
|
||||
@Default(SleepTimerSettings()) SleepTimerSettings sleepTimerSettings,
|
||||
@Default(DownloadSettings()) DownloadSettings downloadSettings,
|
||||
@Default(NotificationSettings()) NotificationSettings notificationSettings,
|
||||
@Default(ShakeDetectionSettings())
|
||||
|
|
@ -49,7 +50,6 @@ class PlayerSettings with _$PlayerSettings {
|
|||
@Default(0.05) double speedIncrement,
|
||||
@Default(0.1) double minSpeed,
|
||||
@Default(4) double maxSpeed,
|
||||
@Default(SleepTimerSettings()) SleepTimerSettings sleepTimerSettings,
|
||||
@Default(Duration(seconds: 10)) Duration minimumPositionForReporting,
|
||||
@Default(Duration(seconds: 10)) Duration playbackReportInterval,
|
||||
@Default(Duration(seconds: 15)) Duration markCompleteWhenTimeLeft,
|
||||
|
|
@ -81,22 +81,23 @@ class MinimizedPlayerSettings with _$MinimizedPlayerSettings {
|
|||
_$MinimizedPlayerSettingsFromJson(json);
|
||||
}
|
||||
|
||||
enum SleepTimerShakeSenseMode { never, always, nearEnds }
|
||||
|
||||
@freezed
|
||||
class SleepTimerSettings with _$SleepTimerSettings {
|
||||
const factory SleepTimerSettings({
|
||||
@Default(Duration(minutes: 15)) Duration defaultDuration,
|
||||
@Default(SleepTimerShakeSenseMode.always)
|
||||
SleepTimerShakeSenseMode shakeSenseMode,
|
||||
|
||||
/// the duration in which the shake is detected before the end of the timer and after the timer ends
|
||||
/// only used if [shakeSenseMode] is [SleepTimerShakeSenseMode.nearEnds]
|
||||
@Default(Duration(seconds: 30)) Duration shakeSenseDuration,
|
||||
@Default(true) bool vibrateWhenReset,
|
||||
@Default(false) bool beepWhenReset,
|
||||
@Default(
|
||||
[
|
||||
Duration(minutes: 5),
|
||||
Duration(minutes: 10),
|
||||
Duration(minutes: 15),
|
||||
Duration(minutes: 20),
|
||||
Duration(minutes: 30),
|
||||
],
|
||||
)
|
||||
List<Duration> presetDurations,
|
||||
@Default(Duration(minutes: 100)) Duration maxDuration,
|
||||
@Default(false) bool fadeOutAudio,
|
||||
@Default(0.5) double shakeDetectThreshold,
|
||||
@Default(Duration(seconds: 20)) Duration fadeOutDuration,
|
||||
|
||||
/// if true, the player will automatically rewind the audio when the sleep timer is stopped
|
||||
@Default(false) bool autoRewindWhenStopped,
|
||||
|
|
@ -115,7 +116,7 @@ class SleepTimerSettings with _$SleepTimerSettings {
|
|||
@Default(false) bool autoTurnOnTimer,
|
||||
|
||||
/// always auto turn on timer settings or during specific times
|
||||
@Default(true) bool alwaysAutoTurnOnTimer,
|
||||
@Default(false) bool alwaysAutoTurnOnTimer,
|
||||
|
||||
/// auto timer settings, only used if [alwaysAutoTurnOnTimer] is false
|
||||
///
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) {
|
|||
mixin _$AppSettings {
|
||||
ThemeSettings get themeSettings => throw _privateConstructorUsedError;
|
||||
PlayerSettings get playerSettings => throw _privateConstructorUsedError;
|
||||
SleepTimerSettings get sleepTimerSettings =>
|
||||
throw _privateConstructorUsedError;
|
||||
DownloadSettings get downloadSettings => throw _privateConstructorUsedError;
|
||||
NotificationSettings get notificationSettings =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
|
@ -47,12 +49,14 @@ abstract class $AppSettingsCopyWith<$Res> {
|
|||
$Res call(
|
||||
{ThemeSettings themeSettings,
|
||||
PlayerSettings playerSettings,
|
||||
SleepTimerSettings sleepTimerSettings,
|
||||
DownloadSettings downloadSettings,
|
||||
NotificationSettings notificationSettings,
|
||||
ShakeDetectionSettings shakeDetectionSettings});
|
||||
|
||||
$ThemeSettingsCopyWith<$Res> get themeSettings;
|
||||
$PlayerSettingsCopyWith<$Res> get playerSettings;
|
||||
$SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings;
|
||||
$DownloadSettingsCopyWith<$Res> get downloadSettings;
|
||||
$NotificationSettingsCopyWith<$Res> get notificationSettings;
|
||||
$ShakeDetectionSettingsCopyWith<$Res> get shakeDetectionSettings;
|
||||
|
|
@ -75,6 +79,7 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings>
|
|||
$Res call({
|
||||
Object? themeSettings = null,
|
||||
Object? playerSettings = null,
|
||||
Object? sleepTimerSettings = null,
|
||||
Object? downloadSettings = null,
|
||||
Object? notificationSettings = null,
|
||||
Object? shakeDetectionSettings = null,
|
||||
|
|
@ -88,6 +93,10 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings>
|
|||
? _value.playerSettings
|
||||
: playerSettings // ignore: cast_nullable_to_non_nullable
|
||||
as PlayerSettings,
|
||||
sleepTimerSettings: null == sleepTimerSettings
|
||||
? _value.sleepTimerSettings
|
||||
: sleepTimerSettings // ignore: cast_nullable_to_non_nullable
|
||||
as SleepTimerSettings,
|
||||
downloadSettings: null == downloadSettings
|
||||
? _value.downloadSettings
|
||||
: downloadSettings // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -123,6 +132,17 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings>
|
|||
});
|
||||
}
|
||||
|
||||
/// Create a copy of AppSettings
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings {
|
||||
return $SleepTimerSettingsCopyWith<$Res>(_value.sleepTimerSettings,
|
||||
(value) {
|
||||
return _then(_value.copyWith(sleepTimerSettings: value) as $Val);
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a copy of AppSettings
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
|
|
@ -167,6 +187,7 @@ abstract class _$$AppSettingsImplCopyWith<$Res>
|
|||
$Res call(
|
||||
{ThemeSettings themeSettings,
|
||||
PlayerSettings playerSettings,
|
||||
SleepTimerSettings sleepTimerSettings,
|
||||
DownloadSettings downloadSettings,
|
||||
NotificationSettings notificationSettings,
|
||||
ShakeDetectionSettings shakeDetectionSettings});
|
||||
|
|
@ -176,6 +197,8 @@ abstract class _$$AppSettingsImplCopyWith<$Res>
|
|||
@override
|
||||
$PlayerSettingsCopyWith<$Res> get playerSettings;
|
||||
@override
|
||||
$SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings;
|
||||
@override
|
||||
$DownloadSettingsCopyWith<$Res> get downloadSettings;
|
||||
@override
|
||||
$NotificationSettingsCopyWith<$Res> get notificationSettings;
|
||||
|
|
@ -198,6 +221,7 @@ class __$$AppSettingsImplCopyWithImpl<$Res>
|
|||
$Res call({
|
||||
Object? themeSettings = null,
|
||||
Object? playerSettings = null,
|
||||
Object? sleepTimerSettings = null,
|
||||
Object? downloadSettings = null,
|
||||
Object? notificationSettings = null,
|
||||
Object? shakeDetectionSettings = null,
|
||||
|
|
@ -211,6 +235,10 @@ class __$$AppSettingsImplCopyWithImpl<$Res>
|
|||
? _value.playerSettings
|
||||
: playerSettings // ignore: cast_nullable_to_non_nullable
|
||||
as PlayerSettings,
|
||||
sleepTimerSettings: null == sleepTimerSettings
|
||||
? _value.sleepTimerSettings
|
||||
: sleepTimerSettings // ignore: cast_nullable_to_non_nullable
|
||||
as SleepTimerSettings,
|
||||
downloadSettings: null == downloadSettings
|
||||
? _value.downloadSettings
|
||||
: downloadSettings // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -233,6 +261,7 @@ class _$AppSettingsImpl implements _AppSettings {
|
|||
const _$AppSettingsImpl(
|
||||
{this.themeSettings = const ThemeSettings(),
|
||||
this.playerSettings = const PlayerSettings(),
|
||||
this.sleepTimerSettings = const SleepTimerSettings(),
|
||||
this.downloadSettings = const DownloadSettings(),
|
||||
this.notificationSettings = const NotificationSettings(),
|
||||
this.shakeDetectionSettings = const ShakeDetectionSettings()});
|
||||
|
|
@ -248,6 +277,9 @@ class _$AppSettingsImpl implements _AppSettings {
|
|||
final PlayerSettings playerSettings;
|
||||
@override
|
||||
@JsonKey()
|
||||
final SleepTimerSettings sleepTimerSettings;
|
||||
@override
|
||||
@JsonKey()
|
||||
final DownloadSettings downloadSettings;
|
||||
@override
|
||||
@JsonKey()
|
||||
|
|
@ -258,7 +290,7 @@ class _$AppSettingsImpl implements _AppSettings {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppSettings(themeSettings: $themeSettings, playerSettings: $playerSettings, downloadSettings: $downloadSettings, notificationSettings: $notificationSettings, shakeDetectionSettings: $shakeDetectionSettings)';
|
||||
return 'AppSettings(themeSettings: $themeSettings, playerSettings: $playerSettings, sleepTimerSettings: $sleepTimerSettings, downloadSettings: $downloadSettings, notificationSettings: $notificationSettings, shakeDetectionSettings: $shakeDetectionSettings)';
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -270,6 +302,8 @@ class _$AppSettingsImpl implements _AppSettings {
|
|||
other.themeSettings == themeSettings) &&
|
||||
(identical(other.playerSettings, playerSettings) ||
|
||||
other.playerSettings == playerSettings) &&
|
||||
(identical(other.sleepTimerSettings, sleepTimerSettings) ||
|
||||
other.sleepTimerSettings == sleepTimerSettings) &&
|
||||
(identical(other.downloadSettings, downloadSettings) ||
|
||||
other.downloadSettings == downloadSettings) &&
|
||||
(identical(other.notificationSettings, notificationSettings) ||
|
||||
|
|
@ -280,8 +314,14 @@ class _$AppSettingsImpl implements _AppSettings {
|
|||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, themeSettings, playerSettings,
|
||||
downloadSettings, notificationSettings, shakeDetectionSettings);
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
themeSettings,
|
||||
playerSettings,
|
||||
sleepTimerSettings,
|
||||
downloadSettings,
|
||||
notificationSettings,
|
||||
shakeDetectionSettings);
|
||||
|
||||
/// Create a copy of AppSettings
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
|
@ -303,6 +343,7 @@ abstract class _AppSettings implements AppSettings {
|
|||
const factory _AppSettings(
|
||||
{final ThemeSettings themeSettings,
|
||||
final PlayerSettings playerSettings,
|
||||
final SleepTimerSettings sleepTimerSettings,
|
||||
final DownloadSettings downloadSettings,
|
||||
final NotificationSettings notificationSettings,
|
||||
final ShakeDetectionSettings shakeDetectionSettings}) = _$AppSettingsImpl;
|
||||
|
|
@ -315,6 +356,8 @@ abstract class _AppSettings implements AppSettings {
|
|||
@override
|
||||
PlayerSettings get playerSettings;
|
||||
@override
|
||||
SleepTimerSettings get sleepTimerSettings;
|
||||
@override
|
||||
DownloadSettings get downloadSettings;
|
||||
@override
|
||||
NotificationSettings get notificationSettings;
|
||||
|
|
@ -552,8 +595,6 @@ mixin _$PlayerSettings {
|
|||
double get speedIncrement => throw _privateConstructorUsedError;
|
||||
double get minSpeed => throw _privateConstructorUsedError;
|
||||
double get maxSpeed => throw _privateConstructorUsedError;
|
||||
SleepTimerSettings get sleepTimerSettings =>
|
||||
throw _privateConstructorUsedError;
|
||||
Duration get minimumPositionForReporting =>
|
||||
throw _privateConstructorUsedError;
|
||||
Duration get playbackReportInterval => throw _privateConstructorUsedError;
|
||||
|
|
@ -585,7 +626,6 @@ abstract class $PlayerSettingsCopyWith<$Res> {
|
|||
double speedIncrement,
|
||||
double minSpeed,
|
||||
double maxSpeed,
|
||||
SleepTimerSettings sleepTimerSettings,
|
||||
Duration minimumPositionForReporting,
|
||||
Duration playbackReportInterval,
|
||||
Duration markCompleteWhenTimeLeft,
|
||||
|
|
@ -593,7 +633,6 @@ abstract class $PlayerSettingsCopyWith<$Res> {
|
|||
|
||||
$MinimizedPlayerSettingsCopyWith<$Res> get miniPlayerSettings;
|
||||
$ExpandedPlayerSettingsCopyWith<$Res> get expandedPlayerSettings;
|
||||
$SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
|
@ -619,7 +658,6 @@ class _$PlayerSettingsCopyWithImpl<$Res, $Val extends PlayerSettings>
|
|||
Object? speedIncrement = null,
|
||||
Object? minSpeed = null,
|
||||
Object? maxSpeed = null,
|
||||
Object? sleepTimerSettings = null,
|
||||
Object? minimumPositionForReporting = null,
|
||||
Object? playbackReportInterval = null,
|
||||
Object? markCompleteWhenTimeLeft = null,
|
||||
|
|
@ -658,10 +696,6 @@ class _$PlayerSettingsCopyWithImpl<$Res, $Val extends PlayerSettings>
|
|||
? _value.maxSpeed
|
||||
: maxSpeed // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
sleepTimerSettings: null == sleepTimerSettings
|
||||
? _value.sleepTimerSettings
|
||||
: sleepTimerSettings // ignore: cast_nullable_to_non_nullable
|
||||
as SleepTimerSettings,
|
||||
minimumPositionForReporting: null == minimumPositionForReporting
|
||||
? _value.minimumPositionForReporting
|
||||
: minimumPositionForReporting // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -702,17 +736,6 @@ class _$PlayerSettingsCopyWithImpl<$Res, $Val extends PlayerSettings>
|
|||
return _then(_value.copyWith(expandedPlayerSettings: value) as $Val);
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a copy of PlayerSettings
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings {
|
||||
return $SleepTimerSettingsCopyWith<$Res>(_value.sleepTimerSettings,
|
||||
(value) {
|
||||
return _then(_value.copyWith(sleepTimerSettings: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
|
@ -732,7 +755,6 @@ abstract class _$$PlayerSettingsImplCopyWith<$Res>
|
|||
double speedIncrement,
|
||||
double minSpeed,
|
||||
double maxSpeed,
|
||||
SleepTimerSettings sleepTimerSettings,
|
||||
Duration minimumPositionForReporting,
|
||||
Duration playbackReportInterval,
|
||||
Duration markCompleteWhenTimeLeft,
|
||||
|
|
@ -742,8 +764,6 @@ abstract class _$$PlayerSettingsImplCopyWith<$Res>
|
|||
$MinimizedPlayerSettingsCopyWith<$Res> get miniPlayerSettings;
|
||||
@override
|
||||
$ExpandedPlayerSettingsCopyWith<$Res> get expandedPlayerSettings;
|
||||
@override
|
||||
$SleepTimerSettingsCopyWith<$Res> get sleepTimerSettings;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
|
@ -767,7 +787,6 @@ class __$$PlayerSettingsImplCopyWithImpl<$Res>
|
|||
Object? speedIncrement = null,
|
||||
Object? minSpeed = null,
|
||||
Object? maxSpeed = null,
|
||||
Object? sleepTimerSettings = null,
|
||||
Object? minimumPositionForReporting = null,
|
||||
Object? playbackReportInterval = null,
|
||||
Object? markCompleteWhenTimeLeft = null,
|
||||
|
|
@ -806,10 +825,6 @@ class __$$PlayerSettingsImplCopyWithImpl<$Res>
|
|||
? _value.maxSpeed
|
||||
: maxSpeed // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
sleepTimerSettings: null == sleepTimerSettings
|
||||
? _value.sleepTimerSettings
|
||||
: sleepTimerSettings // ignore: cast_nullable_to_non_nullable
|
||||
as SleepTimerSettings,
|
||||
minimumPositionForReporting: null == minimumPositionForReporting
|
||||
? _value.minimumPositionForReporting
|
||||
: minimumPositionForReporting // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -842,7 +857,6 @@ class _$PlayerSettingsImpl implements _PlayerSettings {
|
|||
this.speedIncrement = 0.05,
|
||||
this.minSpeed = 0.1,
|
||||
this.maxSpeed = 4,
|
||||
this.sleepTimerSettings = const SleepTimerSettings(),
|
||||
this.minimumPositionForReporting = const Duration(seconds: 10),
|
||||
this.playbackReportInterval = const Duration(seconds: 10),
|
||||
this.markCompleteWhenTimeLeft = const Duration(seconds: 15),
|
||||
|
|
@ -884,9 +898,6 @@ class _$PlayerSettingsImpl implements _PlayerSettings {
|
|||
final double maxSpeed;
|
||||
@override
|
||||
@JsonKey()
|
||||
final SleepTimerSettings sleepTimerSettings;
|
||||
@override
|
||||
@JsonKey()
|
||||
final Duration minimumPositionForReporting;
|
||||
@override
|
||||
@JsonKey()
|
||||
|
|
@ -900,7 +911,7 @@ class _$PlayerSettingsImpl implements _PlayerSettings {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PlayerSettings(miniPlayerSettings: $miniPlayerSettings, expandedPlayerSettings: $expandedPlayerSettings, preferredDefaultVolume: $preferredDefaultVolume, preferredDefaultSpeed: $preferredDefaultSpeed, speedOptions: $speedOptions, speedIncrement: $speedIncrement, minSpeed: $minSpeed, maxSpeed: $maxSpeed, sleepTimerSettings: $sleepTimerSettings, minimumPositionForReporting: $minimumPositionForReporting, playbackReportInterval: $playbackReportInterval, markCompleteWhenTimeLeft: $markCompleteWhenTimeLeft, configurePlayerForEveryBook: $configurePlayerForEveryBook)';
|
||||
return 'PlayerSettings(miniPlayerSettings: $miniPlayerSettings, expandedPlayerSettings: $expandedPlayerSettings, preferredDefaultVolume: $preferredDefaultVolume, preferredDefaultSpeed: $preferredDefaultSpeed, speedOptions: $speedOptions, speedIncrement: $speedIncrement, minSpeed: $minSpeed, maxSpeed: $maxSpeed, minimumPositionForReporting: $minimumPositionForReporting, playbackReportInterval: $playbackReportInterval, markCompleteWhenTimeLeft: $markCompleteWhenTimeLeft, configurePlayerForEveryBook: $configurePlayerForEveryBook)';
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -924,8 +935,6 @@ class _$PlayerSettingsImpl implements _PlayerSettings {
|
|||
other.minSpeed == minSpeed) &&
|
||||
(identical(other.maxSpeed, maxSpeed) ||
|
||||
other.maxSpeed == maxSpeed) &&
|
||||
(identical(other.sleepTimerSettings, sleepTimerSettings) ||
|
||||
other.sleepTimerSettings == sleepTimerSettings) &&
|
||||
(identical(other.minimumPositionForReporting,
|
||||
minimumPositionForReporting) ||
|
||||
other.minimumPositionForReporting ==
|
||||
|
|
@ -953,7 +962,6 @@ class _$PlayerSettingsImpl implements _PlayerSettings {
|
|||
speedIncrement,
|
||||
minSpeed,
|
||||
maxSpeed,
|
||||
sleepTimerSettings,
|
||||
minimumPositionForReporting,
|
||||
playbackReportInterval,
|
||||
markCompleteWhenTimeLeft,
|
||||
|
|
@ -986,7 +994,6 @@ abstract class _PlayerSettings implements PlayerSettings {
|
|||
final double speedIncrement,
|
||||
final double minSpeed,
|
||||
final double maxSpeed,
|
||||
final SleepTimerSettings sleepTimerSettings,
|
||||
final Duration minimumPositionForReporting,
|
||||
final Duration playbackReportInterval,
|
||||
final Duration markCompleteWhenTimeLeft,
|
||||
|
|
@ -1012,8 +1019,6 @@ abstract class _PlayerSettings implements PlayerSettings {
|
|||
@override
|
||||
double get maxSpeed;
|
||||
@override
|
||||
SleepTimerSettings get sleepTimerSettings;
|
||||
@override
|
||||
Duration get minimumPositionForReporting;
|
||||
@override
|
||||
Duration get playbackReportInterval;
|
||||
|
|
@ -1374,16 +1379,10 @@ SleepTimerSettings _$SleepTimerSettingsFromJson(Map<String, dynamic> json) {
|
|||
/// @nodoc
|
||||
mixin _$SleepTimerSettings {
|
||||
Duration get defaultDuration => throw _privateConstructorUsedError;
|
||||
SleepTimerShakeSenseMode get shakeSenseMode =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
/// the duration in which the shake is detected before the end of the timer and after the timer ends
|
||||
/// only used if [shakeSenseMode] is [SleepTimerShakeSenseMode.nearEnds]
|
||||
Duration get shakeSenseDuration => throw _privateConstructorUsedError;
|
||||
bool get vibrateWhenReset => throw _privateConstructorUsedError;
|
||||
bool get beepWhenReset => throw _privateConstructorUsedError;
|
||||
List<Duration> get presetDurations => throw _privateConstructorUsedError;
|
||||
Duration get maxDuration => throw _privateConstructorUsedError;
|
||||
bool get fadeOutAudio => throw _privateConstructorUsedError;
|
||||
double get shakeDetectThreshold => throw _privateConstructorUsedError;
|
||||
Duration get fadeOutDuration => throw _privateConstructorUsedError;
|
||||
|
||||
/// if true, the player will automatically rewind the audio when the sleep timer is stopped
|
||||
bool get autoRewindWhenStopped => throw _privateConstructorUsedError;
|
||||
|
|
@ -1422,12 +1421,10 @@ abstract class $SleepTimerSettingsCopyWith<$Res> {
|
|||
@useResult
|
||||
$Res call(
|
||||
{Duration defaultDuration,
|
||||
SleepTimerShakeSenseMode shakeSenseMode,
|
||||
Duration shakeSenseDuration,
|
||||
bool vibrateWhenReset,
|
||||
bool beepWhenReset,
|
||||
List<Duration> presetDurations,
|
||||
Duration maxDuration,
|
||||
bool fadeOutAudio,
|
||||
double shakeDetectThreshold,
|
||||
Duration fadeOutDuration,
|
||||
bool autoRewindWhenStopped,
|
||||
Map<int, Duration> autoRewindDurations,
|
||||
bool autoTurnOnTimer,
|
||||
|
|
@ -1452,12 +1449,10 @@ class _$SleepTimerSettingsCopyWithImpl<$Res, $Val extends SleepTimerSettings>
|
|||
@override
|
||||
$Res call({
|
||||
Object? defaultDuration = null,
|
||||
Object? shakeSenseMode = null,
|
||||
Object? shakeSenseDuration = null,
|
||||
Object? vibrateWhenReset = null,
|
||||
Object? beepWhenReset = null,
|
||||
Object? presetDurations = null,
|
||||
Object? maxDuration = null,
|
||||
Object? fadeOutAudio = null,
|
||||
Object? shakeDetectThreshold = null,
|
||||
Object? fadeOutDuration = null,
|
||||
Object? autoRewindWhenStopped = null,
|
||||
Object? autoRewindDurations = null,
|
||||
Object? autoTurnOnTimer = null,
|
||||
|
|
@ -1470,30 +1465,22 @@ class _$SleepTimerSettingsCopyWithImpl<$Res, $Val extends SleepTimerSettings>
|
|||
? _value.defaultDuration
|
||||
: defaultDuration // ignore: cast_nullable_to_non_nullable
|
||||
as Duration,
|
||||
shakeSenseMode: null == shakeSenseMode
|
||||
? _value.shakeSenseMode
|
||||
: shakeSenseMode // ignore: cast_nullable_to_non_nullable
|
||||
as SleepTimerShakeSenseMode,
|
||||
shakeSenseDuration: null == shakeSenseDuration
|
||||
? _value.shakeSenseDuration
|
||||
: shakeSenseDuration // ignore: cast_nullable_to_non_nullable
|
||||
presetDurations: null == presetDurations
|
||||
? _value.presetDurations
|
||||
: presetDurations // ignore: cast_nullable_to_non_nullable
|
||||
as List<Duration>,
|
||||
maxDuration: null == maxDuration
|
||||
? _value.maxDuration
|
||||
: maxDuration // ignore: cast_nullable_to_non_nullable
|
||||
as Duration,
|
||||
vibrateWhenReset: null == vibrateWhenReset
|
||||
? _value.vibrateWhenReset
|
||||
: vibrateWhenReset // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
beepWhenReset: null == beepWhenReset
|
||||
? _value.beepWhenReset
|
||||
: beepWhenReset // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
fadeOutAudio: null == fadeOutAudio
|
||||
? _value.fadeOutAudio
|
||||
: fadeOutAudio // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
shakeDetectThreshold: null == shakeDetectThreshold
|
||||
? _value.shakeDetectThreshold
|
||||
: shakeDetectThreshold // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
fadeOutDuration: null == fadeOutDuration
|
||||
? _value.fadeOutDuration
|
||||
: fadeOutDuration // ignore: cast_nullable_to_non_nullable
|
||||
as Duration,
|
||||
autoRewindWhenStopped: null == autoRewindWhenStopped
|
||||
? _value.autoRewindWhenStopped
|
||||
: autoRewindWhenStopped // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -1532,12 +1519,10 @@ abstract class _$$SleepTimerSettingsImplCopyWith<$Res>
|
|||
@useResult
|
||||
$Res call(
|
||||
{Duration defaultDuration,
|
||||
SleepTimerShakeSenseMode shakeSenseMode,
|
||||
Duration shakeSenseDuration,
|
||||
bool vibrateWhenReset,
|
||||
bool beepWhenReset,
|
||||
List<Duration> presetDurations,
|
||||
Duration maxDuration,
|
||||
bool fadeOutAudio,
|
||||
double shakeDetectThreshold,
|
||||
Duration fadeOutDuration,
|
||||
bool autoRewindWhenStopped,
|
||||
Map<int, Duration> autoRewindDurations,
|
||||
bool autoTurnOnTimer,
|
||||
|
|
@ -1560,12 +1545,10 @@ class __$$SleepTimerSettingsImplCopyWithImpl<$Res>
|
|||
@override
|
||||
$Res call({
|
||||
Object? defaultDuration = null,
|
||||
Object? shakeSenseMode = null,
|
||||
Object? shakeSenseDuration = null,
|
||||
Object? vibrateWhenReset = null,
|
||||
Object? beepWhenReset = null,
|
||||
Object? presetDurations = null,
|
||||
Object? maxDuration = null,
|
||||
Object? fadeOutAudio = null,
|
||||
Object? shakeDetectThreshold = null,
|
||||
Object? fadeOutDuration = null,
|
||||
Object? autoRewindWhenStopped = null,
|
||||
Object? autoRewindDurations = null,
|
||||
Object? autoTurnOnTimer = null,
|
||||
|
|
@ -1578,30 +1561,22 @@ class __$$SleepTimerSettingsImplCopyWithImpl<$Res>
|
|||
? _value.defaultDuration
|
||||
: defaultDuration // ignore: cast_nullable_to_non_nullable
|
||||
as Duration,
|
||||
shakeSenseMode: null == shakeSenseMode
|
||||
? _value.shakeSenseMode
|
||||
: shakeSenseMode // ignore: cast_nullable_to_non_nullable
|
||||
as SleepTimerShakeSenseMode,
|
||||
shakeSenseDuration: null == shakeSenseDuration
|
||||
? _value.shakeSenseDuration
|
||||
: shakeSenseDuration // ignore: cast_nullable_to_non_nullable
|
||||
presetDurations: null == presetDurations
|
||||
? _value._presetDurations
|
||||
: presetDurations // ignore: cast_nullable_to_non_nullable
|
||||
as List<Duration>,
|
||||
maxDuration: null == maxDuration
|
||||
? _value.maxDuration
|
||||
: maxDuration // ignore: cast_nullable_to_non_nullable
|
||||
as Duration,
|
||||
vibrateWhenReset: null == vibrateWhenReset
|
||||
? _value.vibrateWhenReset
|
||||
: vibrateWhenReset // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
beepWhenReset: null == beepWhenReset
|
||||
? _value.beepWhenReset
|
||||
: beepWhenReset // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
fadeOutAudio: null == fadeOutAudio
|
||||
? _value.fadeOutAudio
|
||||
: fadeOutAudio // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
shakeDetectThreshold: null == shakeDetectThreshold
|
||||
? _value.shakeDetectThreshold
|
||||
: shakeDetectThreshold // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
fadeOutDuration: null == fadeOutDuration
|
||||
? _value.fadeOutDuration
|
||||
: fadeOutDuration // ignore: cast_nullable_to_non_nullable
|
||||
as Duration,
|
||||
autoRewindWhenStopped: null == autoRewindWhenStopped
|
||||
? _value.autoRewindWhenStopped
|
||||
: autoRewindWhenStopped // ignore: cast_nullable_to_non_nullable
|
||||
|
|
@ -1635,12 +1610,16 @@ class __$$SleepTimerSettingsImplCopyWithImpl<$Res>
|
|||
class _$SleepTimerSettingsImpl implements _SleepTimerSettings {
|
||||
const _$SleepTimerSettingsImpl(
|
||||
{this.defaultDuration = const Duration(minutes: 15),
|
||||
this.shakeSenseMode = SleepTimerShakeSenseMode.always,
|
||||
this.shakeSenseDuration = const Duration(seconds: 30),
|
||||
this.vibrateWhenReset = true,
|
||||
this.beepWhenReset = false,
|
||||
final List<Duration> presetDurations = const [
|
||||
Duration(minutes: 5),
|
||||
Duration(minutes: 10),
|
||||
Duration(minutes: 15),
|
||||
Duration(minutes: 20),
|
||||
Duration(minutes: 30)
|
||||
],
|
||||
this.maxDuration = const Duration(minutes: 100),
|
||||
this.fadeOutAudio = false,
|
||||
this.shakeDetectThreshold = 0.5,
|
||||
this.fadeOutDuration = const Duration(seconds: 20),
|
||||
this.autoRewindWhenStopped = false,
|
||||
final Map<int, Duration> autoRewindDurations = const {
|
||||
5: Duration(seconds: 10),
|
||||
|
|
@ -1650,10 +1629,11 @@ class _$SleepTimerSettingsImpl implements _SleepTimerSettings {
|
|||
120: Duration(minutes: 2)
|
||||
},
|
||||
this.autoTurnOnTimer = false,
|
||||
this.alwaysAutoTurnOnTimer = true,
|
||||
this.alwaysAutoTurnOnTimer = false,
|
||||
this.autoTurnOnTime = const Duration(hours: 22, minutes: 0),
|
||||
this.autoTurnOffTime = const Duration(hours: 6, minutes: 0)})
|
||||
: _autoRewindDurations = autoRewindDurations;
|
||||
: _presetDurations = presetDurations,
|
||||
_autoRewindDurations = autoRewindDurations;
|
||||
|
||||
factory _$SleepTimerSettingsImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$SleepTimerSettingsImplFromJson(json);
|
||||
|
|
@ -1661,27 +1641,24 @@ class _$SleepTimerSettingsImpl implements _SleepTimerSettings {
|
|||
@override
|
||||
@JsonKey()
|
||||
final Duration defaultDuration;
|
||||
final List<Duration> _presetDurations;
|
||||
@override
|
||||
@JsonKey()
|
||||
final SleepTimerShakeSenseMode shakeSenseMode;
|
||||
List<Duration> get presetDurations {
|
||||
if (_presetDurations is EqualUnmodifiableListView) return _presetDurations;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_presetDurations);
|
||||
}
|
||||
|
||||
/// the duration in which the shake is detected before the end of the timer and after the timer ends
|
||||
/// only used if [shakeSenseMode] is [SleepTimerShakeSenseMode.nearEnds]
|
||||
@override
|
||||
@JsonKey()
|
||||
final Duration shakeSenseDuration;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool vibrateWhenReset;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool beepWhenReset;
|
||||
final Duration maxDuration;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool fadeOutAudio;
|
||||
@override
|
||||
@JsonKey()
|
||||
final double shakeDetectThreshold;
|
||||
final Duration fadeOutDuration;
|
||||
|
||||
/// if true, the player will automatically rewind the audio when the sleep timer is stopped
|
||||
@override
|
||||
|
|
@ -1723,7 +1700,7 @@ class _$SleepTimerSettingsImpl implements _SleepTimerSettings {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SleepTimerSettings(defaultDuration: $defaultDuration, shakeSenseMode: $shakeSenseMode, shakeSenseDuration: $shakeSenseDuration, vibrateWhenReset: $vibrateWhenReset, beepWhenReset: $beepWhenReset, fadeOutAudio: $fadeOutAudio, shakeDetectThreshold: $shakeDetectThreshold, autoRewindWhenStopped: $autoRewindWhenStopped, autoRewindDurations: $autoRewindDurations, autoTurnOnTimer: $autoTurnOnTimer, alwaysAutoTurnOnTimer: $alwaysAutoTurnOnTimer, autoTurnOnTime: $autoTurnOnTime, autoTurnOffTime: $autoTurnOffTime)';
|
||||
return 'SleepTimerSettings(defaultDuration: $defaultDuration, presetDurations: $presetDurations, maxDuration: $maxDuration, fadeOutAudio: $fadeOutAudio, fadeOutDuration: $fadeOutDuration, autoRewindWhenStopped: $autoRewindWhenStopped, autoRewindDurations: $autoRewindDurations, autoTurnOnTimer: $autoTurnOnTimer, alwaysAutoTurnOnTimer: $alwaysAutoTurnOnTimer, autoTurnOnTime: $autoTurnOnTime, autoTurnOffTime: $autoTurnOffTime)';
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -1733,18 +1710,14 @@ class _$SleepTimerSettingsImpl implements _SleepTimerSettings {
|
|||
other is _$SleepTimerSettingsImpl &&
|
||||
(identical(other.defaultDuration, defaultDuration) ||
|
||||
other.defaultDuration == defaultDuration) &&
|
||||
(identical(other.shakeSenseMode, shakeSenseMode) ||
|
||||
other.shakeSenseMode == shakeSenseMode) &&
|
||||
(identical(other.shakeSenseDuration, shakeSenseDuration) ||
|
||||
other.shakeSenseDuration == shakeSenseDuration) &&
|
||||
(identical(other.vibrateWhenReset, vibrateWhenReset) ||
|
||||
other.vibrateWhenReset == vibrateWhenReset) &&
|
||||
(identical(other.beepWhenReset, beepWhenReset) ||
|
||||
other.beepWhenReset == beepWhenReset) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other._presetDurations, _presetDurations) &&
|
||||
(identical(other.maxDuration, maxDuration) ||
|
||||
other.maxDuration == maxDuration) &&
|
||||
(identical(other.fadeOutAudio, fadeOutAudio) ||
|
||||
other.fadeOutAudio == fadeOutAudio) &&
|
||||
(identical(other.shakeDetectThreshold, shakeDetectThreshold) ||
|
||||
other.shakeDetectThreshold == shakeDetectThreshold) &&
|
||||
(identical(other.fadeOutDuration, fadeOutDuration) ||
|
||||
other.fadeOutDuration == fadeOutDuration) &&
|
||||
(identical(other.autoRewindWhenStopped, autoRewindWhenStopped) ||
|
||||
other.autoRewindWhenStopped == autoRewindWhenStopped) &&
|
||||
const DeepCollectionEquality()
|
||||
|
|
@ -1764,12 +1737,10 @@ class _$SleepTimerSettingsImpl implements _SleepTimerSettings {
|
|||
int get hashCode => Object.hash(
|
||||
runtimeType,
|
||||
defaultDuration,
|
||||
shakeSenseMode,
|
||||
shakeSenseDuration,
|
||||
vibrateWhenReset,
|
||||
beepWhenReset,
|
||||
const DeepCollectionEquality().hash(_presetDurations),
|
||||
maxDuration,
|
||||
fadeOutAudio,
|
||||
shakeDetectThreshold,
|
||||
fadeOutDuration,
|
||||
autoRewindWhenStopped,
|
||||
const DeepCollectionEquality().hash(_autoRewindDurations),
|
||||
autoTurnOnTimer,
|
||||
|
|
@ -1797,12 +1768,10 @@ class _$SleepTimerSettingsImpl implements _SleepTimerSettings {
|
|||
abstract class _SleepTimerSettings implements SleepTimerSettings {
|
||||
const factory _SleepTimerSettings(
|
||||
{final Duration defaultDuration,
|
||||
final SleepTimerShakeSenseMode shakeSenseMode,
|
||||
final Duration shakeSenseDuration,
|
||||
final bool vibrateWhenReset,
|
||||
final bool beepWhenReset,
|
||||
final List<Duration> presetDurations,
|
||||
final Duration maxDuration,
|
||||
final bool fadeOutAudio,
|
||||
final double shakeDetectThreshold,
|
||||
final Duration fadeOutDuration,
|
||||
final bool autoRewindWhenStopped,
|
||||
final Map<int, Duration> autoRewindDurations,
|
||||
final bool autoTurnOnTimer,
|
||||
|
|
@ -1816,20 +1785,13 @@ abstract class _SleepTimerSettings implements SleepTimerSettings {
|
|||
@override
|
||||
Duration get defaultDuration;
|
||||
@override
|
||||
SleepTimerShakeSenseMode get shakeSenseMode;
|
||||
|
||||
/// the duration in which the shake is detected before the end of the timer and after the timer ends
|
||||
/// only used if [shakeSenseMode] is [SleepTimerShakeSenseMode.nearEnds]
|
||||
List<Duration> get presetDurations;
|
||||
@override
|
||||
Duration get shakeSenseDuration;
|
||||
@override
|
||||
bool get vibrateWhenReset;
|
||||
@override
|
||||
bool get beepWhenReset;
|
||||
Duration get maxDuration;
|
||||
@override
|
||||
bool get fadeOutAudio;
|
||||
@override
|
||||
double get shakeDetectThreshold;
|
||||
Duration get fadeOutDuration;
|
||||
|
||||
/// if true, the player will automatically rewind the audio when the sleep timer is stopped
|
||||
@override
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ _$AppSettingsImpl _$$AppSettingsImplFromJson(Map<String, dynamic> json) =>
|
|||
? const PlayerSettings()
|
||||
: PlayerSettings.fromJson(
|
||||
json['playerSettings'] as Map<String, dynamic>),
|
||||
sleepTimerSettings: json['sleepTimerSettings'] == null
|
||||
? const SleepTimerSettings()
|
||||
: SleepTimerSettings.fromJson(
|
||||
json['sleepTimerSettings'] as Map<String, dynamic>),
|
||||
downloadSettings: json['downloadSettings'] == null
|
||||
? const DownloadSettings()
|
||||
: DownloadSettings.fromJson(
|
||||
|
|
@ -34,6 +38,7 @@ Map<String, dynamic> _$$AppSettingsImplToJson(_$AppSettingsImpl instance) =>
|
|||
<String, dynamic>{
|
||||
'themeSettings': instance.themeSettings,
|
||||
'playerSettings': instance.playerSettings,
|
||||
'sleepTimerSettings': instance.sleepTimerSettings,
|
||||
'downloadSettings': instance.downloadSettings,
|
||||
'notificationSettings': instance.notificationSettings,
|
||||
'shakeDetectionSettings': instance.shakeDetectionSettings,
|
||||
|
|
@ -77,10 +82,6 @@ _$PlayerSettingsImpl _$$PlayerSettingsImplFromJson(Map<String, dynamic> json) =>
|
|||
speedIncrement: (json['speedIncrement'] as num?)?.toDouble() ?? 0.05,
|
||||
minSpeed: (json['minSpeed'] as num?)?.toDouble() ?? 0.1,
|
||||
maxSpeed: (json['maxSpeed'] as num?)?.toDouble() ?? 4,
|
||||
sleepTimerSettings: json['sleepTimerSettings'] == null
|
||||
? const SleepTimerSettings()
|
||||
: SleepTimerSettings.fromJson(
|
||||
json['sleepTimerSettings'] as Map<String, dynamic>),
|
||||
minimumPositionForReporting: json['minimumPositionForReporting'] == null
|
||||
? const Duration(seconds: 10)
|
||||
: Duration(
|
||||
|
|
@ -109,7 +110,6 @@ Map<String, dynamic> _$$PlayerSettingsImplToJson(
|
|||
'speedIncrement': instance.speedIncrement,
|
||||
'minSpeed': instance.minSpeed,
|
||||
'maxSpeed': instance.maxSpeed,
|
||||
'sleepTimerSettings': instance.sleepTimerSettings,
|
||||
'minimumPositionForReporting':
|
||||
instance.minimumPositionForReporting.inMicroseconds,
|
||||
'playbackReportInterval': instance.playbackReportInterval.inMicroseconds,
|
||||
|
|
@ -150,17 +150,23 @@ _$SleepTimerSettingsImpl _$$SleepTimerSettingsImplFromJson(
|
|||
defaultDuration: json['defaultDuration'] == null
|
||||
? const Duration(minutes: 15)
|
||||
: Duration(microseconds: (json['defaultDuration'] as num).toInt()),
|
||||
shakeSenseMode: $enumDecodeNullable(
|
||||
_$SleepTimerShakeSenseModeEnumMap, json['shakeSenseMode']) ??
|
||||
SleepTimerShakeSenseMode.always,
|
||||
shakeSenseDuration: json['shakeSenseDuration'] == null
|
||||
? const Duration(seconds: 30)
|
||||
: Duration(microseconds: (json['shakeSenseDuration'] as num).toInt()),
|
||||
vibrateWhenReset: json['vibrateWhenReset'] as bool? ?? true,
|
||||
beepWhenReset: json['beepWhenReset'] as bool? ?? false,
|
||||
presetDurations: (json['presetDurations'] as List<dynamic>?)
|
||||
?.map((e) => Duration(microseconds: (e as num).toInt()))
|
||||
.toList() ??
|
||||
const [
|
||||
Duration(minutes: 5),
|
||||
Duration(minutes: 10),
|
||||
Duration(minutes: 15),
|
||||
Duration(minutes: 20),
|
||||
Duration(minutes: 30)
|
||||
],
|
||||
maxDuration: json['maxDuration'] == null
|
||||
? const Duration(minutes: 100)
|
||||
: Duration(microseconds: (json['maxDuration'] as num).toInt()),
|
||||
fadeOutAudio: json['fadeOutAudio'] as bool? ?? false,
|
||||
shakeDetectThreshold:
|
||||
(json['shakeDetectThreshold'] as num?)?.toDouble() ?? 0.5,
|
||||
fadeOutDuration: json['fadeOutDuration'] == null
|
||||
? const Duration(seconds: 20)
|
||||
: Duration(microseconds: (json['fadeOutDuration'] as num).toInt()),
|
||||
autoRewindWhenStopped: json['autoRewindWhenStopped'] as bool? ?? false,
|
||||
autoRewindDurations:
|
||||
(json['autoRewindDurations'] as Map<String, dynamic>?)?.map(
|
||||
|
|
@ -175,7 +181,7 @@ _$SleepTimerSettingsImpl _$$SleepTimerSettingsImplFromJson(
|
|||
120: Duration(minutes: 2)
|
||||
},
|
||||
autoTurnOnTimer: json['autoTurnOnTimer'] as bool? ?? false,
|
||||
alwaysAutoTurnOnTimer: json['alwaysAutoTurnOnTimer'] as bool? ?? true,
|
||||
alwaysAutoTurnOnTimer: json['alwaysAutoTurnOnTimer'] as bool? ?? false,
|
||||
autoTurnOnTime: json['autoTurnOnTime'] == null
|
||||
? const Duration(hours: 22, minutes: 0)
|
||||
: Duration(microseconds: (json['autoTurnOnTime'] as num).toInt()),
|
||||
|
|
@ -188,13 +194,11 @@ Map<String, dynamic> _$$SleepTimerSettingsImplToJson(
|
|||
_$SleepTimerSettingsImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'defaultDuration': instance.defaultDuration.inMicroseconds,
|
||||
'shakeSenseMode':
|
||||
_$SleepTimerShakeSenseModeEnumMap[instance.shakeSenseMode]!,
|
||||
'shakeSenseDuration': instance.shakeSenseDuration.inMicroseconds,
|
||||
'vibrateWhenReset': instance.vibrateWhenReset,
|
||||
'beepWhenReset': instance.beepWhenReset,
|
||||
'presetDurations':
|
||||
instance.presetDurations.map((e) => e.inMicroseconds).toList(),
|
||||
'maxDuration': instance.maxDuration.inMicroseconds,
|
||||
'fadeOutAudio': instance.fadeOutAudio,
|
||||
'shakeDetectThreshold': instance.shakeDetectThreshold,
|
||||
'fadeOutDuration': instance.fadeOutDuration.inMicroseconds,
|
||||
'autoRewindWhenStopped': instance.autoRewindWhenStopped,
|
||||
'autoRewindDurations': instance.autoRewindDurations
|
||||
.map((k, e) => MapEntry(k.toString(), e.inMicroseconds)),
|
||||
|
|
@ -204,12 +208,6 @@ Map<String, dynamic> _$$SleepTimerSettingsImplToJson(
|
|||
'autoTurnOffTime': instance.autoTurnOffTime.inMicroseconds,
|
||||
};
|
||||
|
||||
const _$SleepTimerShakeSenseModeEnumMap = {
|
||||
SleepTimerShakeSenseMode.never: 'never',
|
||||
SleepTimerShakeSenseMode.always: 'always',
|
||||
SleepTimerShakeSenseMode.nearEnds: 'nearEnds',
|
||||
};
|
||||
|
||||
_$DownloadSettingsImpl _$$DownloadSettingsImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$DownloadSettingsImpl(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||
import 'package:flutter_settings_ui/flutter_settings_ui.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:vaani/api/authenticated_user_provider.dart';
|
||||
import 'package:vaani/api/server_provider.dart';
|
||||
import 'package:vaani/router/router.dart';
|
||||
|
|
@ -26,7 +27,7 @@ class AppSettingsPage extends HookConsumerWidget {
|
|||
final registeredServersAsList = registeredServers.toList();
|
||||
final availableUsers = ref.watch(authenticatedUserProvider);
|
||||
final serverURIController = useTextEditingController();
|
||||
final sleepTimerSettings = appSettings.playerSettings.sleepTimerSettings;
|
||||
final sleepTimerSettings = appSettings.sleepTimerSettings;
|
||||
|
||||
return SimpleSettingsPage(
|
||||
title: const Text('App Settings'),
|
||||
|
|
@ -91,15 +92,15 @@ class AppSettingsPage extends HookConsumerWidget {
|
|||
'Automatically turn on the sleep timer based on the time of day',
|
||||
),
|
||||
leading: sleepTimerSettings.autoTurnOnTimer
|
||||
? const Icon(Icons.timer)
|
||||
: const Icon(Icons.timer_off),
|
||||
? const Icon(Symbols.time_auto, fill: 1)
|
||||
: const Icon(Symbols.timer_off, fill: 1),
|
||||
onPressed: (context) {
|
||||
context.pushNamed(Routes.autoSleepTimerSettings.name);
|
||||
},
|
||||
value: sleepTimerSettings.autoTurnOnTimer,
|
||||
onToggle: (value) {
|
||||
ref.read(appSettingsProvider.notifier).update(
|
||||
appSettings.copyWith.playerSettings.sleepTimerSettings(
|
||||
appSettings.copyWith.sleepTimerSettings(
|
||||
autoTurnOnTimer: value,
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_settings_ui/flutter_settings_ui.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:vaani/settings/app_settings_provider.dart';
|
||||
import 'package:vaani/settings/view/simple_settings_page.dart';
|
||||
import 'package:vaani/shared/extensions/time_of_day.dart';
|
||||
|
|
@ -13,8 +14,13 @@ class AutoSleepTimerSettingsPage extends HookConsumerWidget {
|
|||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final appSettings = ref.watch(appSettingsProvider);
|
||||
final sleepTimerSettings = appSettings.playerSettings.sleepTimerSettings;
|
||||
final sleepTimerSettings = appSettings.sleepTimerSettings;
|
||||
|
||||
var enabled = sleepTimerSettings.autoTurnOnTimer &&
|
||||
!sleepTimerSettings.alwaysAutoTurnOnTimer;
|
||||
final selectedValueColor = enabled
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).disabledColor;
|
||||
return SimpleSettingsPage(
|
||||
title: const Text('Auto Sleep Timer Settings'),
|
||||
sections: [
|
||||
|
|
@ -31,11 +37,11 @@ class AutoSleepTimerSettingsPage extends HookConsumerWidget {
|
|||
'Automatically turn on the sleep timer based on the time of day',
|
||||
),
|
||||
leading: sleepTimerSettings.autoTurnOnTimer
|
||||
? const Icon(Icons.timer)
|
||||
: const Icon(Icons.timer_off),
|
||||
? const Icon(Symbols.time_auto)
|
||||
: const Icon(Symbols.timer_off),
|
||||
onToggle: (value) {
|
||||
ref.read(appSettingsProvider.notifier).update(
|
||||
appSettings.copyWith.playerSettings.sleepTimerSettings(
|
||||
appSettings.copyWith.sleepTimerSettings(
|
||||
autoTurnOnTimer: value,
|
||||
),
|
||||
);
|
||||
|
|
@ -44,8 +50,9 @@ class AutoSleepTimerSettingsPage extends HookConsumerWidget {
|
|||
),
|
||||
// auto turn on time settings, enabled only when autoTurnOnTimer is enabled
|
||||
SettingsTile.navigation(
|
||||
enabled: sleepTimerSettings.autoTurnOnTimer,
|
||||
title: const Text('Auto Turn On Time'),
|
||||
enabled: enabled,
|
||||
leading: const Icon(Symbols.timer_play),
|
||||
title: const Text('From'),
|
||||
description: const Text(
|
||||
'Turn on the sleep timer at the specified time',
|
||||
),
|
||||
|
|
@ -57,22 +64,24 @@ class AutoSleepTimerSettingsPage extends HookConsumerWidget {
|
|||
);
|
||||
if (selected != null) {
|
||||
ref.read(appSettingsProvider.notifier).update(
|
||||
appSettings.copyWith.playerSettings.sleepTimerSettings(
|
||||
appSettings.copyWith.sleepTimerSettings(
|
||||
autoTurnOnTime: selected.toDuration(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
value: Text(
|
||||
trailing: Text(
|
||||
sleepTimerSettings.autoTurnOnTime.toTimeOfDay().format(context),
|
||||
style: TextStyle(color: selectedValueColor),
|
||||
),
|
||||
),
|
||||
SettingsTile.navigation(
|
||||
title: const Text('Auto Turn Off Time'),
|
||||
enabled: enabled,
|
||||
leading: const Icon(Symbols.timer_pause),
|
||||
title: const Text('Until'),
|
||||
description: const Text(
|
||||
'Turn off the sleep timer at the specified time',
|
||||
),
|
||||
enabled: sleepTimerSettings.autoTurnOnTimer,
|
||||
onPressed: (context) async {
|
||||
// navigate to the time picker
|
||||
final selected = await showTimePicker(
|
||||
|
|
@ -81,18 +90,37 @@ class AutoSleepTimerSettingsPage extends HookConsumerWidget {
|
|||
);
|
||||
if (selected != null) {
|
||||
ref.read(appSettingsProvider.notifier).update(
|
||||
appSettings.copyWith.playerSettings.sleepTimerSettings(
|
||||
appSettings.copyWith.sleepTimerSettings(
|
||||
autoTurnOffTime: selected.toDuration(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
value: Text(
|
||||
trailing: Text(
|
||||
sleepTimerSettings.autoTurnOffTime
|
||||
.toTimeOfDay()
|
||||
.format(context),
|
||||
style: TextStyle(color: selectedValueColor),
|
||||
),
|
||||
),
|
||||
|
||||
// switch tile for always auto turn on timer no matter what
|
||||
SettingsTile.switchTile(
|
||||
leading: const Icon(Symbols.all_inclusive),
|
||||
title: const Text('Always Auto Turn On Timer'),
|
||||
description: const Text(
|
||||
'Always turn on the sleep timer, no matter what',
|
||||
),
|
||||
onToggle: (value) {
|
||||
ref.read(appSettingsProvider.notifier).update(
|
||||
appSettings.copyWith.sleepTimerSettings(
|
||||
alwaysAutoTurnOnTimer: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
enabled: sleepTimerSettings.autoTurnOnTimer,
|
||||
initialValue: sleepTimerSettings.alwaysAutoTurnOnTimer,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -9,14 +9,27 @@ extension DurationFormat on Duration {
|
|||
final minutes = inMinutes.remainder(60);
|
||||
final seconds = inSeconds.remainder(60);
|
||||
if (hours > 0) {
|
||||
return '${hours}h ${minutes}m';
|
||||
// skip minutes if it's 0
|
||||
if (minutes == 0) {
|
||||
return smartSingleFormat;
|
||||
}
|
||||
return '${Duration(hours: hours).smartBinaryFormat} ${Duration(minutes: minutes).smartSingleFormat}';
|
||||
} else if (minutes > 0) {
|
||||
return '${minutes}m ${seconds}s';
|
||||
if (seconds == 0) {
|
||||
return smartSingleFormat;
|
||||
}
|
||||
return '${Duration(minutes: minutes).smartSingleFormat} ${Duration(seconds: seconds).smartSingleFormat}';
|
||||
} else {
|
||||
return '${seconds}s';
|
||||
return smartSingleFormat;
|
||||
}
|
||||
}
|
||||
|
||||
/// formats the duration using only 1 unit
|
||||
/// if the duration is more than 1 hour, it will return `10h`
|
||||
/// if the duration is less than 1 hour, it will return `30m`
|
||||
/// if the duration is less than 1 minute, it will return `20s`
|
||||
///
|
||||
/// rest of the duration will be ignored
|
||||
String get smartSingleFormat {
|
||||
if (inHours > 0) {
|
||||
return '${inHours}h';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ import 'package:flutter/material.dart';
|
|||
|
||||
extension ToTimeOfDay on Duration {
|
||||
TimeOfDay toTimeOfDay() {
|
||||
return TimeOfDay(hour: inHours, minute: inMinutes % 60);
|
||||
return TimeOfDay(
|
||||
hour: inHours % 24,
|
||||
minute: inMinutes % 60,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -28,4 +31,16 @@ extension TimeOfDayExtension on TimeOfDay {
|
|||
|
||||
bool isBefore(TimeOfDay other) => this < other;
|
||||
bool isAfter(TimeOfDay other) => this > other;
|
||||
|
||||
bool isBetween(TimeOfDay start, TimeOfDay end) {
|
||||
// needs more logic to handle the case where start is after end
|
||||
//but on the other day
|
||||
if (start == end) {
|
||||
return this == start;
|
||||
}
|
||||
if (start < end) {
|
||||
return this >= start && this <= end;
|
||||
}
|
||||
return this >= start || this <= end;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,3 +28,64 @@ void useTimer(VoidCallback callback, Duration delay) {
|
|||
[delay],
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates [FixedExtentScrollController] that will be disposed automatically.
|
||||
///
|
||||
/// See also:
|
||||
/// - [FixedExtentScrollController]
|
||||
FixedExtentScrollController useFixedExtentScrollController({
|
||||
String? debugLabel,
|
||||
List<Object?>? keys,
|
||||
int initialItem = 0,
|
||||
void Function(ScrollPosition)? onAttach,
|
||||
void Function(ScrollPosition)? onDetach,
|
||||
}) {
|
||||
return use(
|
||||
_FixedExtentScrollControllerHook(
|
||||
debugLabel: debugLabel,
|
||||
keys: keys,
|
||||
initialItem: initialItem,
|
||||
onAttach: onAttach,
|
||||
onDetach: onDetach,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _FixedExtentScrollControllerHook
|
||||
extends Hook<FixedExtentScrollController> {
|
||||
const _FixedExtentScrollControllerHook({
|
||||
this.debugLabel,
|
||||
super.keys,
|
||||
required this.initialItem,
|
||||
this.onAttach,
|
||||
this.onDetach,
|
||||
});
|
||||
|
||||
final int initialItem;
|
||||
final void Function(ScrollPosition)? onAttach;
|
||||
final void Function(ScrollPosition)? onDetach;
|
||||
|
||||
final String? debugLabel;
|
||||
|
||||
@override
|
||||
HookState<FixedExtentScrollController, Hook<FixedExtentScrollController>>
|
||||
createState() => _FixedExtentScrollControllerHookState();
|
||||
}
|
||||
|
||||
class _FixedExtentScrollControllerHookState extends HookState<
|
||||
FixedExtentScrollController, _FixedExtentScrollControllerHook> {
|
||||
late final controller = FixedExtentScrollController(
|
||||
initialItem: hook.initialItem,
|
||||
onAttach: hook.onAttach,
|
||||
onDetach: hook.onDetach,
|
||||
);
|
||||
|
||||
@override
|
||||
FixedExtentScrollController build(BuildContext context) => controller;
|
||||
|
||||
@override
|
||||
void dispose() => controller.dispose();
|
||||
|
||||
@override
|
||||
String get debugLabel => 'useFixedExtentScrollController';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -814,6 +814,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
material_symbols_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: material_symbols_icons
|
||||
sha256: "66416c4e30bd363508e12669634fc4f3250b83b69e862de67f4f9c480cf42414"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2785.1"
|
||||
media_kit:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ dependencies:
|
|||
list_wheel_scroll_view_nls: ^0.0.3
|
||||
logging: ^1.2.0
|
||||
lottie: ^3.1.0
|
||||
material_symbols_icons: ^4.2785.1
|
||||
media_kit_libs_linux: any
|
||||
media_kit_libs_windows_audio: any
|
||||
miniplayer:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue