ente/lib/ui/viewer/file/video_widget.dart

255 lines
7.2 KiB
Dart
Raw Normal View History

import 'dart:async';
import 'dart:io' as io;
2020-06-19 23:03:26 +00:00
import 'package:chewie/chewie.dart';
import 'package:flutter/cupertino.dart';
2020-06-20 23:51:10 +00:00
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:photos/core/configuration.dart';
2022-03-14 11:44:18 +00:00
import 'package:photos/core/constants.dart';
2023-04-07 05:57:56 +00:00
import "package:photos/generated/l10n.dart";
2020-06-19 23:03:26 +00:00
import 'package:photos/models/file.dart';
import 'package:photos/services/files_service.dart';
import 'package:photos/ui/viewer/file/thumbnail_widget.dart';
import 'package:photos/ui/viewer/file/video_controls.dart';
2020-08-13 20:03:29 +00:00
import 'package:photos/utils/file_util.dart';
import 'package:photos/utils/toast_util.dart';
2020-06-19 23:03:26 +00:00
import 'package:video_player/video_player.dart';
2020-07-29 19:17:03 +00:00
import 'package:visibility_detector/visibility_detector.dart';
import 'package:wakelock/wakelock.dart';
2020-06-19 23:03:26 +00:00
class VideoWidget extends StatefulWidget {
final File file;
final bool? autoPlay;
final String? tagPrefix;
final Function(bool)? playbackCallback;
2021-07-06 23:24:19 +00:00
const VideoWidget(
this.file, {
this.autoPlay = false,
this.tagPrefix,
2021-07-06 23:24:19 +00:00
this.playbackCallback,
Key? key,
}) : super(key: key);
2020-06-19 23:03:26 +00:00
@override
2022-07-03 09:45:00 +00:00
State<VideoWidget> createState() => _VideoWidgetState();
2020-06-19 23:03:26 +00:00
}
class _VideoWidgetState extends State<VideoWidget> {
2021-10-06 10:48:38 +00:00
final _logger = Logger("VideoWidget");
VideoPlayerController? _videoPlayerController;
ChewieController? _chewieController;
double? _progress;
bool _isPlaying = false;
bool _wakeLockEnabledHere = false;
2020-06-19 23:03:26 +00:00
@override
void initState() {
super.initState();
2022-09-23 01:48:25 +00:00
if (widget.file.isRemoteFile) {
_loadNetworkVideo();
_setFileSizeIfNull();
2022-09-23 01:48:25 +00:00
} else if (widget.file.isSharedMediaToAppSandbox) {
final localFile = io.File(getSharedMediaFilePath(widget.file));
if (localFile.existsSync()) {
2021-09-20 12:27:12 +00:00
_logger.fine("loading from app cache");
_setVideoPlayerController(file: localFile);
} else if (widget.file.uploadedFileID != null) {
_loadNetworkVideo();
}
} else {
2022-09-23 01:48:25 +00:00
widget.file.getAsset.then((asset) async {
if (asset == null || !(await asset.exists)) {
if (widget.file.uploadedFileID != null) {
_loadNetworkVideo();
2020-08-13 20:03:29 +00:00
}
} else {
asset.getMediaUrl().then((url) {
_setVideoPlayerController(url: url);
});
}
});
}
}
void _setFileSizeIfNull() {
if (widget.file.fileSize == null &&
widget.file.ownerID == Configuration.instance.getUserID()) {
FilesService.instance
.getFileSize(widget.file.uploadedFileID!)
.then((value) {
widget.file.fileSize = value;
2022-10-21 08:05:16 +00:00
if (mounted) {
setState(() {});
}
});
}
}
void _loadNetworkVideo() {
getFileFromServer(
widget.file,
progressCallback: (count, total) {
if (mounted) {
setState(() {
2022-09-24 10:58:08 +00:00
_progress = count / (widget.file.fileSize ?? total);
if (_progress == 1) {
2023-04-07 05:57:56 +00:00
showShortToast(context, S.of(context).decryptingVideo);
}
});
}
},
).then((file) {
if (file != null) {
_setVideoPlayerController(file: file);
}
});
2020-06-19 23:03:26 +00:00
}
@override
void dispose() {
if (_videoPlayerController != null) {
_videoPlayerController!.dispose();
}
if (_chewieController != null) {
_chewieController!.dispose();
}
if (_wakeLockEnabledHere) {
unawaited(
Wakelock.enabled.then((isEnabled) {
isEnabled ? Wakelock.disable() : null;
}),
);
}
2020-06-19 23:03:26 +00:00
super.dispose();
}
2022-12-30 15:42:03 +00:00
VideoPlayerController _setVideoPlayerController({
String? url,
io.File? file,
}) {
2021-10-06 10:48:38 +00:00
VideoPlayerController videoPlayerController;
if (url != null) {
videoPlayerController = VideoPlayerController.network(url);
} else {
videoPlayerController = VideoPlayerController.file(file!);
}
return _videoPlayerController = videoPlayerController
..initialize().whenComplete(() {
if (mounted) {
setState(() {});
}
});
}
2020-06-19 23:03:26 +00:00
@override
Widget build(BuildContext context) {
2022-07-03 09:49:33 +00:00
final content = _videoPlayerController != null &&
_videoPlayerController!.value.isInitialized
? _getVideoPlayer()
: _getLoadingWidget();
2022-03-14 11:44:18 +00:00
final contentWithDetector = GestureDetector(
child: content,
onVerticalDragUpdate: (d) => {
if (d.delta.dy > dragSensitivity) {Navigator.of(context).pop()}
2022-03-14 11:44:18 +00:00
},
);
2020-07-29 19:17:03 +00:00
return VisibilityDetector(
2022-09-23 01:48:25 +00:00
key: Key(widget.file.tag),
2020-07-29 19:17:03 +00:00
onVisibilityChanged: (info) {
if (info.visibleFraction < 1) {
if (mounted && _chewieController != null) {
_chewieController!.pause();
}
2020-07-29 19:17:03 +00:00
}
},
child: Hero(
tag: widget.tagPrefix! + widget.file.tag,
2022-03-14 11:44:18 +00:00
child: contentWithDetector,
2020-07-29 19:17:03 +00:00
),
2020-06-23 15:05:48 +00:00
);
}
Widget _getLoadingWidget() {
2022-06-11 08:23:52 +00:00
return Stack(
children: [
_getThumbnail(),
Container(
color: Colors.black12,
2022-07-04 06:02:17 +00:00
constraints: const BoxConstraints.expand(),
2022-06-11 08:23:52 +00:00
),
Center(
child: SizedBox.fromSize(
2022-07-04 06:02:17 +00:00
size: const Size.square(30),
2022-06-11 08:23:52 +00:00
child: _progress == null || _progress == 1
2022-07-04 06:02:17 +00:00
? const CupertinoActivityIndicator(
2022-06-11 08:23:52 +00:00
color: Colors.white,
)
: CircularProgressIndicator(
backgroundColor: Colors.black,
value: _progress,
2022-07-04 06:02:17 +00:00
valueColor: const AlwaysStoppedAnimation<Color>(
2022-06-11 08:23:52 +00:00
Color.fromRGBO(45, 194, 98, 1.0),
),
),
2022-06-11 08:23:52 +00:00
),
),
2022-06-11 08:23:52 +00:00
],
);
}
Widget _getThumbnail() {
return Container(
2022-06-09 15:03:08 +00:00
color: Colors.black,
2022-07-04 06:02:17 +00:00
constraints: const BoxConstraints.expand(),
2020-08-13 20:03:29 +00:00
child: ThumbnailWidget(
widget.file,
fit: BoxFit.contain,
),
);
2020-06-19 23:03:26 +00:00
}
Future<void> _keepScreenAliveOnPlaying(bool isPlaying) async {
if (isPlaying) {
return Wakelock.enabled.then((value) {
if (value == false) {
Wakelock.enable();
//wakeLockEnabledHere will not be set to true if wakeLock is already enabled from settings on iOS.
//We shouldn't disable when video is not playing if it was enabled manually by the user from ente settings by user.
_wakeLockEnabledHere = true;
}
});
}
if (_wakeLockEnabledHere && !isPlaying) {
return Wakelock.disable();
}
}
Widget _getVideoPlayer() {
_videoPlayerController!.addListener(() {
if (_isPlaying != _videoPlayerController!.value.isPlaying) {
_isPlaying = _videoPlayerController!.value.isPlaying;
2021-07-06 23:24:19 +00:00
if (widget.playbackCallback != null) {
widget.playbackCallback!(_isPlaying);
2021-07-06 23:24:19 +00:00
}
2022-12-30 15:42:03 +00:00
unawaited(_keepScreenAliveOnPlaying(_isPlaying));
2021-07-06 23:24:19 +00:00
}
});
2020-06-20 22:47:09 +00:00
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController!,
aspectRatio: _videoPlayerController!.value.aspectRatio,
autoPlay: widget.autoPlay!,
2020-06-20 22:47:09 +00:00
autoInitialize: true,
looping: true,
2022-06-09 15:03:08 +00:00
allowMuting: true,
2020-06-20 22:47:09 +00:00
allowFullScreen: false,
2022-07-04 06:02:17 +00:00
customControls: const VideoControls(),
2020-06-20 22:47:09 +00:00
);
2022-06-09 15:03:08 +00:00
return Container(
color: Colors.black,
child: Chewie(controller: _chewieController!),
2022-06-09 15:03:08 +00:00
);
2020-06-19 23:03:26 +00:00
}
}