zoomable_live_image.dart 5.0 KB

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