zoomable_live_image.dart 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import 'dart:io' as io;
  2. import 'package:chewie/chewie.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:fluttertoast/fluttertoast.dart';
  5. import 'package:logging/logging.dart';
  6. import 'package:photos/core/constants.dart';
  7. import 'package:photos/models/file.dart';
  8. import 'package:photos/ui/viewer/file/zoomable_image.dart';
  9. import 'package:photos/utils/file_util.dart';
  10. import 'package:photos/utils/toast_util.dart';
  11. import 'package:shared_preferences/shared_preferences.dart';
  12. import 'package:video_player/video_player.dart';
  13. class ZoomableLiveImage extends StatefulWidget {
  14. final File file;
  15. final Function(bool) shouldDisableScroll;
  16. final String tagPrefix;
  17. final Decoration backgroundDecoration;
  18. const ZoomableLiveImage(
  19. this.file, {
  20. Key key,
  21. this.shouldDisableScroll,
  22. @required this.tagPrefix,
  23. this.backgroundDecoration,
  24. }) : super(key: key);
  25. @override
  26. State<ZoomableLiveImage> createState() => _ZoomableLiveImageState();
  27. }
  28. class _ZoomableLiveImageState extends State<ZoomableLiveImage>
  29. with SingleTickerProviderStateMixin {
  30. final Logger _logger = Logger("ZoomableLiveImage");
  31. File _file;
  32. bool _showVideo = false;
  33. bool _isLoadingVideoPlayer = false;
  34. VideoPlayerController _videoPlayerController;
  35. ChewieController _chewieController;
  36. @override
  37. void initState() {
  38. _file = widget.file;
  39. _showLivePhotoToast();
  40. super.initState();
  41. }
  42. void _onLongPressEvent(bool isPressed) {
  43. if (_videoPlayerController != null && isPressed == false) {
  44. // stop playing video
  45. _videoPlayerController.pause();
  46. }
  47. if (mounted) {
  48. setState(() {
  49. _showVideo = isPressed;
  50. });
  51. }
  52. }
  53. @override
  54. Widget build(BuildContext context) {
  55. Widget content;
  56. // check is long press is selected but videoPlayer is not configured yet
  57. if (_showVideo && _videoPlayerController == null) {
  58. _loadLiveVideo();
  59. }
  60. if (_showVideo && _videoPlayerController != null) {
  61. content = _getVideoPlayer();
  62. } else {
  63. content = ZoomableImage(
  64. _file,
  65. tagPrefix: widget.tagPrefix,
  66. shouldDisableScroll: widget.shouldDisableScroll,
  67. backgroundDecoration: widget.backgroundDecoration,
  68. );
  69. }
  70. return GestureDetector(
  71. onLongPressStart: (_) => {_onLongPressEvent(true)},
  72. onLongPressEnd: (_) => {_onLongPressEvent(false)},
  73. child: content,
  74. );
  75. }
  76. @override
  77. void dispose() {
  78. if (_videoPlayerController != null) {
  79. _videoPlayerController.pause();
  80. _videoPlayerController.dispose();
  81. }
  82. if (_chewieController != null) {
  83. _chewieController.dispose();
  84. }
  85. super.dispose();
  86. }
  87. Widget _getVideoPlayer() {
  88. _videoPlayerController.seekTo(Duration.zero);
  89. _chewieController = ChewieController(
  90. videoPlayerController: _videoPlayerController,
  91. aspectRatio: _videoPlayerController.value.aspectRatio,
  92. autoPlay: true,
  93. autoInitialize: true,
  94. looping: true,
  95. allowFullScreen: false,
  96. showControls: false,
  97. );
  98. return Container(
  99. color: Colors.black,
  100. child: Chewie(controller: _chewieController), // same for both theme
  101. );
  102. }
  103. Future<void> _loadLiveVideo() async {
  104. // do nothing is already loading or loaded
  105. if (_isLoadingVideoPlayer || _videoPlayerController != null) {
  106. return;
  107. }
  108. _isLoadingVideoPlayer = true;
  109. if (_file.isRemoteFile() && !(await isFileCached(_file, liveVideo: true))) {
  110. showToast(context, "Downloading...", toastLength: Toast.LENGTH_LONG);
  111. }
  112. var videoFile = await getFile(widget.file, liveVideo: true)
  113. .timeout(const Duration(seconds: 15))
  114. .onError((e, s) {
  115. _logger.info("getFile failed ${_file.tag()}", e);
  116. return null;
  117. });
  118. if ((videoFile == null || !videoFile.existsSync()) &&
  119. _file.isRemoteFile()) {
  120. videoFile = await getFileFromServer(widget.file, liveVideo: true)
  121. .timeout(const Duration(seconds: 15))
  122. .onError((e, s) {
  123. _logger.info("getRemoteFile failed ${_file.tag()}", e);
  124. return null;
  125. });
  126. }
  127. if (videoFile != null && videoFile.existsSync()) {
  128. _setVideoPlayerController(file: videoFile);
  129. } else {
  130. showShortToast(context, "Download failed");
  131. }
  132. _isLoadingVideoPlayer = false;
  133. }
  134. VideoPlayerController _setVideoPlayerController({io.File file}) {
  135. var videoPlayerController = VideoPlayerController.file(file);
  136. return _videoPlayerController = videoPlayerController
  137. ..initialize().whenComplete(() {
  138. if (mounted) {
  139. setState(() {
  140. _showVideo = true;
  141. });
  142. }
  143. });
  144. }
  145. void _showLivePhotoToast() async {
  146. var _preferences = await SharedPreferences.getInstance();
  147. int promptTillNow = _preferences.getInt(kLivePhotoToastCounterKey) ?? 0;
  148. if (promptTillNow < kMaxLivePhotoToastCount) {
  149. showToast(context, "Press and hold to play video");
  150. _preferences.setInt(kLivePhotoToastCounterKey, promptTillNow + 1);
  151. }
  152. }
  153. }