Add widget for live images
This commit is contained in:
parent
3530c36d50
commit
acffc4f269
|
@ -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
|
||||
|
|
283
lib/ui/zoomable_live_image.dart
Normal file
283
lib/ui/zoomable_live_image.dart
Normal 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;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue