Add widget for live images

This commit is contained in:
Neeraj Gupta 2021-08-04 11:34:36 +05:30
parent 3530c36d50
commit acffc4f269
2 changed files with 296 additions and 1 deletions

View file

@ -11,6 +11,7 @@ import 'package:photos/ui/fading_bottom_bar.dart';
import 'package:photos/ui/gallery.dart';
import 'package:photos/ui/image_editor_page.dart';
import 'package:photos/ui/video_widget.dart';
import 'package:photos/ui/zoomable_live_image.dart';
import 'package:photos/ui/zoomable_image.dart';
import 'package:photos/utils/dialog_util.dart';
import 'package:photos/utils/file_util.dart';
@ -135,7 +136,18 @@ class _DetailPageState extends State<DetailPage> {
},
tagPrefix: widget.config.tagPrefix,
);
} else if (file.fileType == FileType.video) {
} else if(file.fileType == FileType.livePhoto) {
content = ZoomableLiveImage(
file,
shouldDisableScroll: (value) {
setState(() {
_shouldDisableScroll = value;
});
},
tagPrefix: widget.config.tagPrefix,
);
}
else if (file.fileType == FileType.video) {
content = VideoWidget(
file,
autoPlay: !_hasPageChanged, // Autoplay if it was opened directly

View file

@ -0,0 +1,283 @@
import 'dart:io' as io;
import 'package:chewie/chewie.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:logging/logging.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photos/core/cache/thumbnail_cache.dart';
import 'package:photos/core/constants.dart';
import 'package:photos/core/event_bus.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/events/local_photos_updated_event.dart';
import 'package:photos/models/file.dart';
import 'package:photos/ui/loading_widget.dart';
import 'package:photos/utils/file_util.dart';
import 'package:photos/utils/thumbnail_util.dart';
import 'package:video_player/video_player.dart';
class ZoomableLiveImage extends StatefulWidget {
final File photo;
final Function(bool) shouldDisableScroll;
final String tagPrefix;
final Decoration backgroundDecoration;
ZoomableLiveImage(
this.photo, {
Key key,
this.shouldDisableScroll,
@required this.tagPrefix,
this.backgroundDecoration,
}) : super(key: key);
@override
_ZoomableLiveImageState createState() => _ZoomableLiveImageState();
}
class _ZoomableLiveImageState extends State<ZoomableLiveImage>
with SingleTickerProviderStateMixin {
final Logger _logger = Logger("ZoomableLiveImage");
File _photo;
ImageProvider _imageProvider;
bool _loadedSmallThumbnail = false;
bool _loadingLargeThumbnail = false;
bool _loadedLargeThumbnail = false;
bool _loadingFinalImage = false;
bool _loadedFinalImage = false;
bool _loadLivePhotoVideo = false;
ValueChanged<PhotoViewScaleState> _scaleStateChangedCallback;
VideoPlayerController _videoPlayerController;
ChewieController _chewieController;
@override
void initState() {
_photo = widget.photo;
_scaleStateChangedCallback = (value) {
if (widget.shouldDisableScroll != null) {
widget.shouldDisableScroll(value != PhotoViewScaleState.initial);
}
};
super.initState();
}
void _onLongPressEvent(bool isPressed) {
if (isPressed != _loadLivePhotoVideo) {
_loadLivePhotoVideo = isPressed;
if (mounted) {
setState(() {});
}
}
}
@override
Widget build(BuildContext context) {
if (_photo.isRemoteFile()) {
_loadNetworkImage();
} else {
_loadLocalImage(context);
}
_loadLiveVide();
if (_imageProvider != null) {
Widget content;
if (_loadLivePhotoVideo && _videoPlayerController != null
&& _videoPlayerController.value.isInitialized) {
content = _getVideoPlayer();
} else {
content = PhotoView(
imageProvider: _imageProvider,
scaleStateChangedCallback: _scaleStateChangedCallback,
minScale: PhotoViewComputedScale.contained,
gaplessPlayback: true,
heroAttributes: PhotoViewHeroAttributes(
tag: widget.tagPrefix + _photo.tag(),
),
backgroundDecoration: widget.backgroundDecoration,
);
}
return GestureDetector(
onLongPressStart: (_) => {_onLongPressEvent(true)},
onLongPressEnd: (_) => {_onLongPressEvent(false)},
child: Stack(
children: [
content,
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: const EdgeInsets.only(right: 18, bottom: 14),
child: Icon(
Icons.featured_video,
size: 12,
color: Colors.white.withOpacity(0.5),
),
),
),
],
fit: StackFit.expand,
));
} else {
return loadWidget;
}
}
@override
void dispose() {
if (_videoPlayerController != null) {
_videoPlayerController.pause();
_videoPlayerController.dispose();
}
if (_chewieController != null) {
_chewieController.dispose();
}
super.dispose();
}
Widget _getVideoPlayer() {
_videoPlayerController.seekTo(Duration.zero);
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
aspectRatio: _videoPlayerController.value.aspectRatio,
autoPlay: true,
autoInitialize: true,
looping: false,
allowFullScreen: false,
showControls: false
);
return Chewie(controller: _chewieController);
}
void _loadLiveVide() {
if (_videoPlayerController != null) {
return ;
}
getLiveVideo(widget.photo).then((file) {
if (file != null && file.existsSync()) {
_logger.fine("loading live from local");
_setVideoPlayerController(file: file);
} else {
_logger.fine("loading live from remote");
getFileFromServer(widget.photo, liveVideo: true).then((file) {
if (file != null && file.existsSync()) {
_setVideoPlayerController(file: file);
}
});
}
});
}
VideoPlayerController _setVideoPlayerController({io.File file}) {
var videoPlayerController = VideoPlayerController.file(file);
return _videoPlayerController = videoPlayerController
..initialize().whenComplete(() {
if (mounted) {
setState(() {});
}
});
}
void _loadNetworkImage() {
if (!_loadedSmallThumbnail && !_loadedFinalImage) {
final cachedThumbnail = ThumbnailLruCache.get(_photo);
if (cachedThumbnail != null) {
_imageProvider = Image.memory(cachedThumbnail).image;
_loadedSmallThumbnail = true;
} else {
getThumbnailFromServer(_photo).then((file) {
final imageProvider = Image.memory(file).image;
if (mounted) {
precacheImage(imageProvider, context).then((value) {
if (mounted) {
setState(() {
_imageProvider = imageProvider;
_loadedSmallThumbnail = true;
});
}
}).catchError((e) {
_logger.severe("Could not load image " + _photo.toString());
_loadedSmallThumbnail = true;
});
}
});
}
}
if (!_loadedFinalImage) {
getFileFromServer(_photo).then((file) {
_onFinalImageLoaded(Image.file(
file,
gaplessPlayback: true,
).image);
});
}
}
void _loadLocalImage(BuildContext context) {
if (!_loadedSmallThumbnail &&
!_loadedLargeThumbnail &&
!_loadedFinalImage) {
final cachedThumbnail =
ThumbnailLruCache.get(_photo, kThumbnailSmallSize);
if (cachedThumbnail != null) {
_imageProvider = Image.memory(cachedThumbnail).image;
_loadedSmallThumbnail = true;
}
}
if (!_loadingLargeThumbnail &&
!_loadedLargeThumbnail &&
!_loadedFinalImage) {
_loadingLargeThumbnail = true;
getThumbnailFromLocal(_photo, size: kThumbnailLargeSize, quality: 100)
.then((cachedThumbnail) {
if (cachedThumbnail != null) {
_onLargeThumbnailLoaded(Image.memory(cachedThumbnail).image, context);
}
});
}
if (!_loadingFinalImage && !_loadedFinalImage) {
_loadingFinalImage = true;
getFile(_photo).then((file) {
if (file != null && file.existsSync()) {
_onFinalImageLoaded(Image.file(file).image);
} else {
_logger.info("File was deleted " + _photo.toString());
if (_photo.uploadedFileID != null) {
_photo.localID = null;
FilesDB.instance.update(_photo);
_loadNetworkImage();
} else {
FilesDB.instance.deleteLocalFile(_photo);
Bus.instance.fire(LocalPhotosUpdatedEvent([_photo]));
}
}
});
}
}
void _onLargeThumbnailLoaded(
ImageProvider imageProvider, BuildContext context) {
if (mounted && !_loadedFinalImage) {
precacheImage(imageProvider, context).then((value) {
if (mounted && !_loadedFinalImage) {
setState(() {
_imageProvider = imageProvider;
_loadedLargeThumbnail = true;
});
}
});
}
}
void _onFinalImageLoaded(ImageProvider imageProvider) {
if (mounted) {
precacheImage(imageProvider, context).then((value) {
if (mounted) {
setState(() {
_imageProvider = imageProvider;
_loadedFinalImage = true;
});
}
});
}
}
}