ExpandedControl: stub ad skip handling
This commit is contained in:
parent
6a1ec6c906
commit
0ddfa046b9
2 changed files with 144 additions and 12 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_cast_framework/src/cast/widgets/expanded_controls/ExpandedControlsAdSkipBox.dart';
|
||||||
|
|
||||||
import '../../../../cast.dart';
|
import '../../../../cast.dart';
|
||||||
import 'ExpandedControlsBasicButton.dart';
|
|
||||||
import 'ExpandedControlsConnectedDeviceLabel.dart';
|
import 'ExpandedControlsConnectedDeviceLabel.dart';
|
||||||
import 'ExpandedControlsHighlightedText.dart';
|
import 'ExpandedControlsHighlightedText.dart';
|
||||||
import 'ExpandedControlsInfoTextBox.dart';
|
import 'ExpandedControlsInfoTextBox.dart';
|
||||||
|
|
@ -34,16 +34,28 @@ const _bottomUpBlackGradient = BoxDecoration(
|
||||||
class ExpandedControls extends StatefulWidget {
|
class ExpandedControls extends StatefulWidget {
|
||||||
final FlutterCastFramework castFramework;
|
final FlutterCastFramework castFramework;
|
||||||
|
|
||||||
/// Label to introduce cast device. Default is "Casting to {{device_name}}", where {{device_name}} is replaced with the device name
|
/// Label to introduce cast device. Default is "Casting to {{cast_device_name}}", where {{cast_device_name}} is replaced with the device name
|
||||||
|
/// {{cast_device_name}} can be found in the constant CAST_DEVICE_NAME_PLACEHOLDER.
|
||||||
final String? castingToText;
|
final String? castingToText;
|
||||||
|
|
||||||
|
/// Label to indicate remaining time for ad. Default is "You can skip this ad in {{skip_remaining_time}}...",
|
||||||
|
/// where {{skip_remaining_time}} is replaced with the remaining time.
|
||||||
|
/// {{skip_remaining_time}} can be found in the constant SKIP_AD_TIMER_PLACEHOLDER.
|
||||||
|
final String? skipAdTimerText;
|
||||||
|
|
||||||
|
/// Label for the Skip Ad button. Default is "Skip Ad".
|
||||||
|
final String? skipAdButtonText;
|
||||||
|
|
||||||
/// This is called when the back button is tapped or when the session is closed
|
/// This is called when the back button is tapped or when the session is closed
|
||||||
final VoidCallback? onCloseRequested;
|
final VoidCallback? onCloseRequested;
|
||||||
final controller = ExpandedControlsProgressController();
|
final progressController = ExpandedControlsProgressController();
|
||||||
|
final adSkipBoxController = ExpandedControlsAdSkipBoxController();
|
||||||
|
|
||||||
ExpandedControls({
|
ExpandedControls({
|
||||||
required this.castFramework,
|
required this.castFramework,
|
||||||
this.castingToText,
|
this.castingToText,
|
||||||
|
this.skipAdTimerText,
|
||||||
|
this.skipAdButtonText,
|
||||||
this.onCloseRequested,
|
this.onCloseRequested,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -74,7 +86,7 @@ class _ExpandedControlsState extends State<ExpandedControls> {
|
||||||
sessionManager.remoteMediaClient.onProgressUpdated = null;
|
sessionManager.remoteMediaClient.onProgressUpdated = null;
|
||||||
sessionManager.remoteMediaClient.onAdBreakClipProgressUpdated = null;
|
sessionManager.remoteMediaClient.onAdBreakClipProgressUpdated = null;
|
||||||
|
|
||||||
widget.controller.dispose();
|
widget.progressController.dispose();
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +119,7 @@ class _ExpandedControlsState extends State<ExpandedControls> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onProgressUpdated(int progress, int duration) {
|
void _onProgressUpdated(int progress, int duration) {
|
||||||
widget.controller.updateProgress(progress, duration);
|
widget.progressController.updateProgress(progress, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAdBreakClipProgressUpdated(
|
void _onAdBreakClipProgressUpdated(
|
||||||
|
|
@ -117,9 +129,8 @@ class _ExpandedControlsState extends State<ExpandedControls> {
|
||||||
int durationMs,
|
int durationMs,
|
||||||
int whenSkippableMs,
|
int whenSkippableMs,
|
||||||
) {
|
) {
|
||||||
debugPrint(
|
widget.adSkipBoxController
|
||||||
"adBreakId: $adBreakId adBreakClipId: $adBreakClipId progress: $progressMs duration: $durationMs whenSkip: $whenSkippableMs");
|
.updateProgress(progressMs, durationMs, whenSkippableMs);
|
||||||
widget.controller.updateProgress(progressMs, durationMs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _getDecoratedToolbar(MediaInfo? mediaInfo) {
|
Widget _getDecoratedToolbar(MediaInfo? mediaInfo) {
|
||||||
|
|
@ -155,7 +166,7 @@ class _ExpandedControlsState extends State<ExpandedControls> {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: ExpandedControlsProgress(
|
child: ExpandedControlsProgress(
|
||||||
controller: widget.controller,
|
controller: widget.progressController,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
|
@ -202,9 +213,11 @@ class _ExpandedControlsState extends State<ExpandedControls> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(flex: 1),
|
const Spacer(flex: 1),
|
||||||
ExpandedControlsBasicButton(
|
ExpandedControlsAdSkipBox(
|
||||||
text: "Skip Ad", // TODO: localize label
|
controller: widget.adSkipBoxController,
|
||||||
onPressed: () {/* TODO: skip ad */},
|
skipAdButtonText: widget.skipAdButtonText,
|
||||||
|
skipAdTimerText: widget.skipAdTimerText,
|
||||||
|
onSkipPressed: () {/* TODO: skip ad */},
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'ExpandedControlsBasicButton.dart';
|
||||||
|
|
||||||
|
class ExpandedControlsAdSkipBoxController extends ChangeNotifier {
|
||||||
|
int progress = 0;
|
||||||
|
int duration = 0;
|
||||||
|
int whenSkippable = 0;
|
||||||
|
|
||||||
|
void updateProgress(int progress, int duration, int whenSkippable) {
|
||||||
|
this.progress = progress;
|
||||||
|
this.duration = duration;
|
||||||
|
this.whenSkippable = whenSkippable;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Placeholder to be used for the castingToText of ExpandedControlsConnectedDeviceLabel
|
||||||
|
const SKIP_AD_TIMER_PLACEHOLDER = "{{skip_remaining_time}}";
|
||||||
|
|
||||||
|
class ExpandedControlsAdSkipBox extends StatefulWidget {
|
||||||
|
final ExpandedControlsAdSkipBoxController controller;
|
||||||
|
|
||||||
|
final _defaultSkipAdTimerText =
|
||||||
|
"You can skip this ad in $SKIP_AD_TIMER_PLACEHOLDER...";
|
||||||
|
final _defaultSkipAdButtonText = "Skip Ad";
|
||||||
|
|
||||||
|
/// Label to indicate remaining time for ad. Default is "You can skip this ad in {{skip_remaining_time}}...",
|
||||||
|
/// where {{skip_remaining_time}} is replaced with the remaining time.
|
||||||
|
/// {{skip_remaining_time}} can be found in the constant SKIP_AD_TIMER_PLACEHOLDER.
|
||||||
|
final String? skipAdTimerText;
|
||||||
|
|
||||||
|
/// Label for the Skip Ad button. Default is "Skip Ad".
|
||||||
|
final String? skipAdButtonText;
|
||||||
|
final VoidCallback? onSkipPressed;
|
||||||
|
|
||||||
|
const ExpandedControlsAdSkipBox({
|
||||||
|
Key? key,
|
||||||
|
required this.controller,
|
||||||
|
this.skipAdButtonText,
|
||||||
|
this.skipAdTimerText,
|
||||||
|
this.onSkipPressed,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ExpandedControlsAdSkipBox> createState() =>
|
||||||
|
_ExpandedControlsAdSkipBoxState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ExpandedControlsAdSkipBoxState extends State<ExpandedControlsAdSkipBox> {
|
||||||
|
int progress = 0;
|
||||||
|
int duration = 0;
|
||||||
|
int whenSkippable = 5000;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
widget.controller.addListener(_onProgressUpdated);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
widget.controller.removeListener(_onProgressUpdated);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onProgressUpdated() {
|
||||||
|
setState(() {
|
||||||
|
if (!mounted) return;
|
||||||
|
this.progress = widget.controller.progress;
|
||||||
|
this.duration = widget.controller.duration;
|
||||||
|
this.whenSkippable = widget.controller.whenSkippable;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
String _replaceRemainingTime(
|
||||||
|
String textWithPlaceholder, String remainingTime) {
|
||||||
|
return textWithPlaceholder.replaceAll(
|
||||||
|
SKIP_AD_TIMER_PLACEHOLDER, remainingTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final canSkip = progress > whenSkippable;
|
||||||
|
if (canSkip) {
|
||||||
|
return ExpandedControlsBasicButton(
|
||||||
|
text: widget.skipAdButtonText ?? widget._defaultSkipAdButtonText,
|
||||||
|
onPressed: widget.onSkipPressed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final remainingTimeMs = this.whenSkippable - this.progress;
|
||||||
|
final remainingTimeD = Duration(milliseconds: remainingTimeMs);
|
||||||
|
final durationD = Duration(milliseconds: this.duration);
|
||||||
|
|
||||||
|
final remainingTime = _positionToString(remainingTimeD, durationD);
|
||||||
|
final baseLabel = widget.skipAdTimerText ?? widget._defaultSkipAdTimerText;
|
||||||
|
final label = _replaceRemainingTime(baseLabel, remainingTime);
|
||||||
|
return Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _positionToString(Duration d, Duration total) {
|
||||||
|
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
||||||
|
if (total.inSeconds <= 60) {
|
||||||
|
// This is less than a minute, I display only seconds
|
||||||
|
return "${d.inSeconds}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total.inMinutes <= 60) {
|
||||||
|
// This is less than a hour, I display only minutes and seconds
|
||||||
|
return "${twoDigits(d.inMinutes)}:${twoDigits(d.inSeconds.remainder(60))}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "${twoDigits(d.inHours)}:${twoDigits(d.inMinutes.remainder(60))}:${twoDigits(d.inSeconds.remainder(60))}";
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue