#130 motion photo auto play option
This commit is contained in:
parent
e4cfd82f34
commit
929b662d2a
12 changed files with 49 additions and 14 deletions
|
@ -6,12 +6,19 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Collection / Albums / Countries / Tags: list view (scalable like the grid view)
|
||||||
- moving, editing or deleting multiple items can be cancelled
|
- moving, editing or deleting multiple items can be cancelled
|
||||||
|
- Viewer: option to auto play motion photos (after a small delay to show first the high-res photo)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- upgraded Flutter to stable v2.8.1
|
- upgraded Flutter to stable v2.8.1
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Collection: more consistent scroll bar thumb position to match the viewport
|
||||||
|
- Settings: fixed file selection to import settings on older devices
|
||||||
|
|
||||||
## <a id="v1.5.7"></a>[v1.5.7] - 2021-12-01
|
## <a id="v1.5.7"></a>[v1.5.7] - 2021-12-01
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -565,6 +565,7 @@ class MediaStoreImageProvider : ImageProvider() {
|
||||||
throw Exception("unsupported Android version")
|
throw Exception("unsupported Android version")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.d(LOG_TAG, "rename content at uri=$mediaUri")
|
||||||
val uri = StorageUtils.getMediaStoreScopedStorageSafeUri(mediaUri, mimeType)
|
val uri = StorageUtils.getMediaStoreScopedStorageSafeUri(mediaUri, mimeType)
|
||||||
|
|
||||||
// `IS_PENDING` is necessary for `TITLE`, not for `DISPLAY_NAME`
|
// `IS_PENDING` is necessary for `TITLE`, not for `DISPLAY_NAME`
|
||||||
|
|
|
@ -816,6 +816,8 @@
|
||||||
"@settingsViewerUseCutout": {},
|
"@settingsViewerUseCutout": {},
|
||||||
"settingsViewerMaximumBrightness": "Maximum brightness",
|
"settingsViewerMaximumBrightness": "Maximum brightness",
|
||||||
"@settingsViewerMaximumBrightness": {},
|
"@settingsViewerMaximumBrightness": {},
|
||||||
|
"settingsMotionPhotoAutoPlay": "Auto play motion photos",
|
||||||
|
"@settingsMotionPhotoAutoPlay": {},
|
||||||
"settingsImageBackground": "Image background",
|
"settingsImageBackground": "Image background",
|
||||||
"@settingsImageBackground": {},
|
"@settingsImageBackground": {},
|
||||||
|
|
||||||
|
|
|
@ -381,6 +381,7 @@
|
||||||
"settingsSectionViewer": "Visionneuse",
|
"settingsSectionViewer": "Visionneuse",
|
||||||
"settingsViewerUseCutout": "Utiliser la zone d’encoche",
|
"settingsViewerUseCutout": "Utiliser la zone d’encoche",
|
||||||
"settingsViewerMaximumBrightness": "Luminosité maximale",
|
"settingsViewerMaximumBrightness": "Luminosité maximale",
|
||||||
|
"settingsMotionPhotoAutoPlay": "Lecture automatique des photos animées",
|
||||||
"settingsImageBackground": "Arrière-plan de l’image",
|
"settingsImageBackground": "Arrière-plan de l’image",
|
||||||
|
|
||||||
"settingsViewerQuickActionsTile": "Actions rapides",
|
"settingsViewerQuickActionsTile": "Actions rapides",
|
||||||
|
|
|
@ -381,6 +381,7 @@
|
||||||
"settingsSectionViewer": "뷰어",
|
"settingsSectionViewer": "뷰어",
|
||||||
"settingsViewerUseCutout": "컷아웃 영역 사용",
|
"settingsViewerUseCutout": "컷아웃 영역 사용",
|
||||||
"settingsViewerMaximumBrightness": "최대 밝기",
|
"settingsViewerMaximumBrightness": "최대 밝기",
|
||||||
|
"settingsMotionPhotoAutoPlay": "모션 포토 자동 재생",
|
||||||
"settingsImageBackground": "이미지 배경",
|
"settingsImageBackground": "이미지 배경",
|
||||||
|
|
||||||
"settingsViewerQuickActionsTile": "빠른 작업",
|
"settingsViewerQuickActionsTile": "빠른 작업",
|
||||||
|
|
|
@ -67,6 +67,7 @@ class SettingsDefaults {
|
||||||
static const enableOverlayBlurEffect = true; // `enableOverlayBlurEffect` has a contextual default value
|
static const enableOverlayBlurEffect = true; // `enableOverlayBlurEffect` has a contextual default value
|
||||||
static const viewerUseCutout = true;
|
static const viewerUseCutout = true;
|
||||||
static const viewerMaxBrightness = false;
|
static const viewerMaxBrightness = false;
|
||||||
|
static const enableMotionPhotoAutoPlay = false;
|
||||||
|
|
||||||
// video
|
// video
|
||||||
static const videoQuickActions = [
|
static const videoQuickActions = [
|
||||||
|
|
|
@ -83,6 +83,8 @@ class Settings extends ChangeNotifier {
|
||||||
static const enableOverlayBlurEffectKey = 'enable_overlay_blur_effect';
|
static const enableOverlayBlurEffectKey = 'enable_overlay_blur_effect';
|
||||||
static const viewerUseCutoutKey = 'viewer_use_cutout';
|
static const viewerUseCutoutKey = 'viewer_use_cutout';
|
||||||
static const viewerMaxBrightnessKey = 'viewer_max_brightness';
|
static const viewerMaxBrightnessKey = 'viewer_max_brightness';
|
||||||
|
static const enableMotionPhotoAutoPlayKey = 'motion_photo_auto_play';
|
||||||
|
static const imageBackgroundKey = 'image_background';
|
||||||
|
|
||||||
// video
|
// video
|
||||||
static const videoQuickActionsKey = 'video_quick_actions';
|
static const videoQuickActionsKey = 'video_quick_actions';
|
||||||
|
@ -104,9 +106,6 @@ class Settings extends ChangeNotifier {
|
||||||
static const coordinateFormatKey = 'coordinates_format';
|
static const coordinateFormatKey = 'coordinates_format';
|
||||||
static const unitSystemKey = 'unit_system';
|
static const unitSystemKey = 'unit_system';
|
||||||
|
|
||||||
// rendering
|
|
||||||
static const imageBackgroundKey = 'image_background';
|
|
||||||
|
|
||||||
// search
|
// search
|
||||||
static const saveSearchHistoryKey = 'save_search_history';
|
static const saveSearchHistoryKey = 'save_search_history';
|
||||||
static const searchHistoryKey = 'search_history';
|
static const searchHistoryKey = 'search_history';
|
||||||
|
@ -365,6 +364,14 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
set viewerMaxBrightness(bool newValue) => setAndNotify(viewerMaxBrightnessKey, newValue);
|
set viewerMaxBrightness(bool newValue) => setAndNotify(viewerMaxBrightnessKey, newValue);
|
||||||
|
|
||||||
|
bool get enableMotionPhotoAutoPlay => getBoolOrDefault(enableMotionPhotoAutoPlayKey, SettingsDefaults.enableMotionPhotoAutoPlay);
|
||||||
|
|
||||||
|
set enableMotionPhotoAutoPlay(bool newValue) => setAndNotify(enableMotionPhotoAutoPlayKey, newValue);
|
||||||
|
|
||||||
|
EntryBackground get imageBackground => getEnumOrDefault(imageBackgroundKey, SettingsDefaults.imageBackground, EntryBackground.values);
|
||||||
|
|
||||||
|
set imageBackground(EntryBackground newValue) => setAndNotify(imageBackgroundKey, newValue.toString());
|
||||||
|
|
||||||
// video
|
// video
|
||||||
|
|
||||||
List<VideoAction> get videoQuickActions => getEnumListOrDefault(videoQuickActionsKey, SettingsDefaults.videoQuickActions, VideoAction.values);
|
List<VideoAction> get videoQuickActions => getEnumListOrDefault(videoQuickActionsKey, SettingsDefaults.videoQuickActions, VideoAction.values);
|
||||||
|
@ -427,12 +434,6 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
set unitSystem(UnitSystem newValue) => setAndNotify(unitSystemKey, newValue.toString());
|
set unitSystem(UnitSystem newValue) => setAndNotify(unitSystemKey, newValue.toString());
|
||||||
|
|
||||||
// rendering
|
|
||||||
|
|
||||||
EntryBackground get imageBackground => getEnumOrDefault(imageBackgroundKey, SettingsDefaults.imageBackground, EntryBackground.values);
|
|
||||||
|
|
||||||
set imageBackground(EntryBackground newValue) => setAndNotify(imageBackgroundKey, newValue.toString());
|
|
||||||
|
|
||||||
// search
|
// search
|
||||||
|
|
||||||
bool get saveSearchHistory => getBoolOrDefault(saveSearchHistoryKey, SettingsDefaults.saveSearchHistory);
|
bool get saveSearchHistory => getBoolOrDefault(saveSearchHistoryKey, SettingsDefaults.saveSearchHistory);
|
||||||
|
@ -613,6 +614,7 @@ class Settings extends ChangeNotifier {
|
||||||
case enableOverlayBlurEffectKey:
|
case enableOverlayBlurEffectKey:
|
||||||
case viewerUseCutoutKey:
|
case viewerUseCutoutKey:
|
||||||
case viewerMaxBrightnessKey:
|
case viewerMaxBrightnessKey:
|
||||||
|
case enableMotionPhotoAutoPlayKey:
|
||||||
case enableVideoHardwareAccelerationKey:
|
case enableVideoHardwareAccelerationKey:
|
||||||
case enableVideoAutoPlayKey:
|
case enableVideoAutoPlayKey:
|
||||||
case subtitleShowOutlineKey:
|
case subtitleShowOutlineKey:
|
||||||
|
@ -633,12 +635,12 @@ class Settings extends ChangeNotifier {
|
||||||
case albumSortFactorKey:
|
case albumSortFactorKey:
|
||||||
case countrySortFactorKey:
|
case countrySortFactorKey:
|
||||||
case tagSortFactorKey:
|
case tagSortFactorKey:
|
||||||
|
case imageBackgroundKey:
|
||||||
case videoLoopModeKey:
|
case videoLoopModeKey:
|
||||||
case subtitleTextAlignmentKey:
|
case subtitleTextAlignmentKey:
|
||||||
case infoMapStyleKey:
|
case infoMapStyleKey:
|
||||||
case coordinateFormatKey:
|
case coordinateFormatKey:
|
||||||
case unitSystemKey:
|
case unitSystemKey:
|
||||||
case imageBackgroundKey:
|
|
||||||
case accessibilityAnimationsKey:
|
case accessibilityAnimationsKey:
|
||||||
case timeToTakeActionKey:
|
case timeToTakeActionKey:
|
||||||
if (value is String) {
|
if (value is String) {
|
||||||
|
|
|
@ -59,6 +59,7 @@ class Durations {
|
||||||
static const collectionScrollMonitoringTimerDelay = Duration(milliseconds: 100);
|
static const collectionScrollMonitoringTimerDelay = Duration(milliseconds: 100);
|
||||||
static const highlightJumpDelay = Duration(milliseconds: 400);
|
static const highlightJumpDelay = Duration(milliseconds: 400);
|
||||||
static const highlightScrollInitDelay = Duration(milliseconds: 800);
|
static const highlightScrollInitDelay = Duration(milliseconds: 800);
|
||||||
|
static const motionPhotoAutoPlayDelay = Duration(milliseconds: 700);
|
||||||
static const videoOverlayHideDelay = Duration(milliseconds: 500);
|
static const videoOverlayHideDelay = Duration(milliseconds: 500);
|
||||||
static const videoProgressTimerInterval = Duration(milliseconds: 300);
|
static const videoProgressTimerInterval = Duration(milliseconds: 300);
|
||||||
static const doubleBackTimerDelay = Duration(milliseconds: 1000);
|
static const doubleBackTimerDelay = Duration(milliseconds: 1000);
|
||||||
|
|
|
@ -42,6 +42,14 @@ class ViewerSection extends StatelessWidget {
|
||||||
title: Text(context.l10n.settingsViewerMaximumBrightness),
|
title: Text(context.l10n.settingsViewerMaximumBrightness),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Selector<Settings, bool>(
|
||||||
|
selector: (context, s) => s.enableMotionPhotoAutoPlay,
|
||||||
|
builder: (context, current, child) => SwitchListTile(
|
||||||
|
value: current,
|
||||||
|
onChanged: (v) => settings.enableMotionPhotoAutoPlay = v,
|
||||||
|
title: Text(context.l10n.settingsMotionPhotoAutoPlay),
|
||||||
|
),
|
||||||
|
),
|
||||||
Selector<Settings, EntryBackground>(
|
Selector<Settings, EntryBackground>(
|
||||||
selector: (context, s) => s.imageBackground,
|
selector: (context, s) => s.imageBackground,
|
||||||
builder: (context, current, child) => ListTile(
|
builder: (context, current, child) => ListTile(
|
||||||
|
|
|
@ -630,7 +630,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with FeedbackMixin,
|
||||||
// auto play/pause when changing page
|
// auto play/pause when changing page
|
||||||
Future<void> _onPageChange() async {
|
Future<void> _onPageChange() async {
|
||||||
await _pauseVideoControllers();
|
await _pauseVideoControllers();
|
||||||
if (settings.enableVideoAutoPlay) {
|
if (settings.enableVideoAutoPlay || (entry.isMotionPhoto && settings.enableMotionPhotoAutoPlay)) {
|
||||||
final page = multiPageController.page;
|
final page = multiPageController.page;
|
||||||
final pageInfo = multiPageInfo.getByIndex(page)!;
|
final pageInfo = multiPageInfo.getByIndex(page)!;
|
||||||
if (pageInfo.isVideo) {
|
if (pageInfo.isVideo) {
|
||||||
|
@ -644,6 +644,13 @@ class _EntryViewerStackState extends State<EntryViewerStack> with FeedbackMixin,
|
||||||
_multiPageControllerPageListeners[multiPageController] = _onPageChange;
|
_multiPageControllerPageListeners[multiPageController] = _onPageChange;
|
||||||
multiPageController.pageNotifier.addListener(_onPageChange);
|
multiPageController.pageNotifier.addListener(_onPageChange);
|
||||||
await _onPageChange();
|
await _onPageChange();
|
||||||
|
|
||||||
|
if (entry.isMotionPhoto && settings.enableMotionPhotoAutoPlay) {
|
||||||
|
await Future.delayed(Durations.motionPhotoAutoPlayDelay);
|
||||||
|
if (entry == _entryNotifier.value) {
|
||||||
|
multiPageController.page = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,9 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
|
|
||||||
AvesEntry get entry => widget.pageEntry;
|
AvesEntry get entry => widget.pageEntry;
|
||||||
|
|
||||||
|
// use the high res photo as cover for the video part of a motion photo
|
||||||
|
ImageProvider get videoCoverUriImage => mainEntry.isMotionPhoto ? mainEntry.uriImage : entry.uriImage;
|
||||||
|
|
||||||
static const initialScale = ScaleLevel(ref: ScaleReference.contained);
|
static const initialScale = ScaleLevel(ref: ScaleReference.contained);
|
||||||
static const minScale = ScaleLevel(ref: ScaleReference.contained);
|
static const minScale = ScaleLevel(ref: ScaleReference.contained);
|
||||||
static const maxScale = ScaleLevel(factor: 2.0);
|
static const maxScale = ScaleLevel(factor: 2.0);
|
||||||
|
@ -98,7 +101,7 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
_subscriptions.add(_magnifierController.scaleBoundariesStream.listen(_onViewScaleBoundariesChanged));
|
_subscriptions.add(_magnifierController.scaleBoundariesStream.listen(_onViewScaleBoundariesChanged));
|
||||||
if (entry.isVideo) {
|
if (entry.isVideo) {
|
||||||
_videoCoverStreamListener = ImageStreamListener((image, _) => _videoCoverInfoNotifier.value = image);
|
_videoCoverStreamListener = ImageStreamListener((image, _) => _videoCoverInfoNotifier.value = image);
|
||||||
_videoCoverStream = entry.uriImage.resolve(ImageConfiguration.empty);
|
_videoCoverStream = videoCoverUriImage.resolve(ImageConfiguration.empty);
|
||||||
_videoCoverStream!.addListener(_videoCoverStreamListener);
|
_videoCoverStream!.addListener(_videoCoverStreamListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,7 +251,7 @@ class _EntryPageViewState extends State<EntryPageView> {
|
||||||
controller: coverController,
|
controller: coverController,
|
||||||
displaySize: coverSize,
|
displaySize: coverSize,
|
||||||
child: Image(
|
child: Image(
|
||||||
image: entry.uriImage,
|
image: videoCoverUriImage,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"menuActionConfigureView",
|
"menuActionConfigureView",
|
||||||
"viewDialogTabLayout",
|
"viewDialogTabLayout",
|
||||||
"tileLayoutGrid",
|
"tileLayoutGrid",
|
||||||
"tileLayoutList"
|
"tileLayoutList",
|
||||||
|
"settingsMotionPhotoAutoPlay"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue