Implement sleep timer functionality (#84)

This commit is contained in:
Tommy Chow 2022-03-22 17:07:49 -04:00
parent 17a200b8f4
commit 3d79cd41ff
3 changed files with 164 additions and 1 deletions

View file

@ -1,4 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:frosty/api/twitch_api.dart';
@ -19,6 +21,17 @@ abstract class _VideoStoreBase with Store {
late Timer _overlayTimer;
Timer? sleepTimer;
@observable
var sleepHours = 0;
@observable
var sleepMinutes = 0;
@observable
var timeRemaining = const Duration();
@readonly
var _paused = true;
@ -149,4 +162,42 @@ abstract class _VideoStoreBase with Store {
debugPrint(e.toString());
}
}
/// Updates the sleep timer with [sleepHours] and [sleepMinutes].
void updateSleepTimer() {
// If hours and minutes are 0, do nothing.
if (sleepHours == 0 && sleepMinutes == 0) return;
// If there is an ongoing timer, cancel it since it'll be replaced.
if (sleepTimer != null) cancelSleepTimer();
// Update the new time remaining
timeRemaining = Duration(hours: sleepHours, minutes: sleepMinutes);
// Reset the hours and minutes in the dropdown buttons.
sleepHours = 0;
sleepMinutes = 0;
// Set a periodic timer that will update the time remaining every second.
sleepTimer = Timer.periodic(
const Duration(seconds: 1),
(timer) {
// If the timer is up, cancel the timer and exit the app.
if (timeRemaining.inSeconds == 0) {
timer.cancel();
Platform.isIOS ? exit(0) : SystemNavigator.pop();
return;
}
// Decrement the time remaining.
timeRemaining = Duration(seconds: timeRemaining.inSeconds - 1);
},
);
}
/// Cancels the sleep timer and resets the time remaining.
void cancelSleepTimer() {
sleepTimer?.cancel();
timeRemaining = const Duration();
}
}

View file

@ -17,6 +17,51 @@ mixin _$VideoStore on _VideoStoreBase, Store {
name: '_VideoStoreBase.videoUrl'))
.value;
final _$sleepHoursAtom = Atom(name: '_VideoStoreBase.sleepHours');
@override
int get sleepHours {
_$sleepHoursAtom.reportRead();
return super.sleepHours;
}
@override
set sleepHours(int value) {
_$sleepHoursAtom.reportWrite(value, super.sleepHours, () {
super.sleepHours = value;
});
}
final _$sleepMinutesAtom = Atom(name: '_VideoStoreBase.sleepMinutes');
@override
int get sleepMinutes {
_$sleepMinutesAtom.reportRead();
return super.sleepMinutes;
}
@override
set sleepMinutes(int value) {
_$sleepMinutesAtom.reportWrite(value, super.sleepMinutes, () {
super.sleepMinutes = value;
});
}
final _$timeRemainingAtom = Atom(name: '_VideoStoreBase.timeRemaining');
@override
Duration get timeRemaining {
_$timeRemainingAtom.reportRead();
return super.timeRemaining;
}
@override
set timeRemaining(Duration value) {
_$timeRemainingAtom.reportWrite(value, super.timeRemaining, () {
super.timeRemaining = value;
});
}
final _$_pausedAtom = Atom(name: '_VideoStoreBase._paused');
bool get paused {
@ -124,6 +169,9 @@ mixin _$VideoStore on _VideoStoreBase, Store {
@override
String toString() {
return '''
sleepHours: ${sleepHours},
sleepMinutes: ${sleepMinutes},
timeRemaining: ${timeRemaining},
videoUrl: ${videoUrl}
''';
}

View file

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
@ -12,6 +13,69 @@ class VideoOverlay extends StatelessWidget {
const VideoOverlay({Key? key, required this.videoStore}) : super(key: key);
Future<void> _showSleepTimerDialog(BuildContext context) {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Sleep Timer'),
content: Observer(
builder: (context) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton(
value: videoStore.sleepHours,
items: List.generate(24, (index) => index).map((e) => DropdownMenuItem(value: e, child: Text(e.toString()))).toList(),
onChanged: (int? hours) => videoStore.sleepHours = hours!,
menuMaxHeight: 200,
),
const SizedBox(width: 10.0),
const Text('Hours'),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton(
value: videoStore.sleepMinutes,
items: List.generate(60, (index) => index).map((e) => DropdownMenuItem(value: e, child: Text(e.toString()))).toList(),
onChanged: (int? minutes) => videoStore.sleepMinutes = minutes!,
menuMaxHeight: 200,
),
const SizedBox(width: 10.0),
const Text('Minutes'),
],
),
Row(
children: [
Text('Timer: ${videoStore.timeRemaining.toString().split('.')[0]}'),
const Spacer(),
IconButton(
onPressed: videoStore.cancelSleepTimer,
icon: const Icon(Icons.cancel),
),
],
),
],
),
),
actions: [
TextButton(
onPressed: Navigator.of(context).pop,
child: const Text('Close'),
),
TextButton(
onPressed: videoStore.updateSleepTimer,
child: const Text('Set Timer'),
style: TextButton.styleFrom(primary: Colors.green),
),
],
),
);
}
@override
Widget build(BuildContext context) {
final orientation = MediaQuery.of(context).orientation;
@ -70,7 +134,7 @@ class VideoOverlay extends StatelessWidget {
final sleepTimerButton = IconButton(
tooltip: 'Sleep Timer',
icon: const Icon(Icons.timer),
onPressed: () {},
onPressed: () => _showSleepTimerDialog(context),
);
final streamInfo = videoStore.streamInfo;