#130 motion photo auto play option

This commit is contained in:
Thibault Deckers 2021-12-20 16:34:33 +09:00
parent e4cfd82f34
commit 929b662d2a
12 changed files with 49 additions and 14 deletions

View file

@ -6,12 +6,19 @@ All notable changes to this project will be documented in this file.
### Added
- Collection / Albums / Countries / Tags: list view (scalable like the grid view)
- 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
- 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
### Added

View file

@ -565,6 +565,7 @@ class MediaStoreImageProvider : ImageProvider() {
throw Exception("unsupported Android version")
}
Log.d(LOG_TAG, "rename content at uri=$mediaUri")
val uri = StorageUtils.getMediaStoreScopedStorageSafeUri(mediaUri, mimeType)
// `IS_PENDING` is necessary for `TITLE`, not for `DISPLAY_NAME`

View file

@ -816,6 +816,8 @@
"@settingsViewerUseCutout": {},
"settingsViewerMaximumBrightness": "Maximum brightness",
"@settingsViewerMaximumBrightness": {},
"settingsMotionPhotoAutoPlay": "Auto play motion photos",
"@settingsMotionPhotoAutoPlay": {},
"settingsImageBackground": "Image background",
"@settingsImageBackground": {},

View file

@ -381,6 +381,7 @@
"settingsSectionViewer": "Visionneuse",
"settingsViewerUseCutout": "Utiliser la zone dencoche",
"settingsViewerMaximumBrightness": "Luminosité maximale",
"settingsMotionPhotoAutoPlay": "Lecture automatique des photos animées",
"settingsImageBackground": "Arrière-plan de limage",
"settingsViewerQuickActionsTile": "Actions rapides",

View file

@ -381,6 +381,7 @@
"settingsSectionViewer": "뷰어",
"settingsViewerUseCutout": "컷아웃 영역 사용",
"settingsViewerMaximumBrightness": "최대 밝기",
"settingsMotionPhotoAutoPlay": "모션 포토 자동 재생",
"settingsImageBackground": "이미지 배경",
"settingsViewerQuickActionsTile": "빠른 작업",

View file

@ -67,6 +67,7 @@ class SettingsDefaults {
static const enableOverlayBlurEffect = true; // `enableOverlayBlurEffect` has a contextual default value
static const viewerUseCutout = true;
static const viewerMaxBrightness = false;
static const enableMotionPhotoAutoPlay = false;
// video
static const videoQuickActions = [

View file

@ -83,6 +83,8 @@ class Settings extends ChangeNotifier {
static const enableOverlayBlurEffectKey = 'enable_overlay_blur_effect';
static const viewerUseCutoutKey = 'viewer_use_cutout';
static const viewerMaxBrightnessKey = 'viewer_max_brightness';
static const enableMotionPhotoAutoPlayKey = 'motion_photo_auto_play';
static const imageBackgroundKey = 'image_background';
// video
static const videoQuickActionsKey = 'video_quick_actions';
@ -104,9 +106,6 @@ class Settings extends ChangeNotifier {
static const coordinateFormatKey = 'coordinates_format';
static const unitSystemKey = 'unit_system';
// rendering
static const imageBackgroundKey = 'image_background';
// search
static const saveSearchHistoryKey = 'save_search_history';
static const searchHistoryKey = 'search_history';
@ -365,6 +364,14 @@ class Settings extends ChangeNotifier {
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
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());
// rendering
EntryBackground get imageBackground => getEnumOrDefault(imageBackgroundKey, SettingsDefaults.imageBackground, EntryBackground.values);
set imageBackground(EntryBackground newValue) => setAndNotify(imageBackgroundKey, newValue.toString());
// search
bool get saveSearchHistory => getBoolOrDefault(saveSearchHistoryKey, SettingsDefaults.saveSearchHistory);
@ -613,6 +614,7 @@ class Settings extends ChangeNotifier {
case enableOverlayBlurEffectKey:
case viewerUseCutoutKey:
case viewerMaxBrightnessKey:
case enableMotionPhotoAutoPlayKey:
case enableVideoHardwareAccelerationKey:
case enableVideoAutoPlayKey:
case subtitleShowOutlineKey:
@ -633,12 +635,12 @@ class Settings extends ChangeNotifier {
case albumSortFactorKey:
case countrySortFactorKey:
case tagSortFactorKey:
case imageBackgroundKey:
case videoLoopModeKey:
case subtitleTextAlignmentKey:
case infoMapStyleKey:
case coordinateFormatKey:
case unitSystemKey:
case imageBackgroundKey:
case accessibilityAnimationsKey:
case timeToTakeActionKey:
if (value is String) {

View file

@ -59,6 +59,7 @@ class Durations {
static const collectionScrollMonitoringTimerDelay = Duration(milliseconds: 100);
static const highlightJumpDelay = Duration(milliseconds: 400);
static const highlightScrollInitDelay = Duration(milliseconds: 800);
static const motionPhotoAutoPlayDelay = Duration(milliseconds: 700);
static const videoOverlayHideDelay = Duration(milliseconds: 500);
static const videoProgressTimerInterval = Duration(milliseconds: 300);
static const doubleBackTimerDelay = Duration(milliseconds: 1000);

View file

@ -42,6 +42,14 @@ class ViewerSection extends StatelessWidget {
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: (context, s) => s.imageBackground,
builder: (context, current, child) => ListTile(

View file

@ -630,7 +630,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with FeedbackMixin,
// auto play/pause when changing page
Future<void> _onPageChange() async {
await _pauseVideoControllers();
if (settings.enableVideoAutoPlay) {
if (settings.enableVideoAutoPlay || (entry.isMotionPhoto && settings.enableMotionPhotoAutoPlay)) {
final page = multiPageController.page;
final pageInfo = multiPageInfo.getByIndex(page)!;
if (pageInfo.isVideo) {
@ -644,6 +644,13 @@ class _EntryViewerStackState extends State<EntryViewerStack> with FeedbackMixin,
_multiPageControllerPageListeners[multiPageController] = _onPageChange;
multiPageController.pageNotifier.addListener(_onPageChange);
await _onPageChange();
if (entry.isMotionPhoto && settings.enableMotionPhotoAutoPlay) {
await Future.delayed(Durations.motionPhotoAutoPlayDelay);
if (entry == _entryNotifier.value) {
multiPageController.page = 1;
}
}
}
}

View file

@ -63,6 +63,9 @@ class _EntryPageViewState extends State<EntryPageView> {
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 minScale = ScaleLevel(ref: ScaleReference.contained);
static const maxScale = ScaleLevel(factor: 2.0);
@ -98,7 +101,7 @@ class _EntryPageViewState extends State<EntryPageView> {
_subscriptions.add(_magnifierController.scaleBoundariesStream.listen(_onViewScaleBoundariesChanged));
if (entry.isVideo) {
_videoCoverStreamListener = ImageStreamListener((image, _) => _videoCoverInfoNotifier.value = image);
_videoCoverStream = entry.uriImage.resolve(ImageConfiguration.empty);
_videoCoverStream = videoCoverUriImage.resolve(ImageConfiguration.empty);
_videoCoverStream!.addListener(_videoCoverStreamListener);
}
}
@ -248,7 +251,7 @@ class _EntryPageViewState extends State<EntryPageView> {
controller: coverController,
displaySize: coverSize,
child: Image(
image: entry.uriImage,
image: videoCoverUriImage,
),
);
}

View file

@ -3,6 +3,7 @@
"menuActionConfigureView",
"viewDialogTabLayout",
"tileLayoutGrid",
"tileLayoutList"
"tileLayoutList",
"settingsMotionPhotoAutoPlay"
]
}