#290 slideshow: animated zoom effect

This commit is contained in:
Thibault Deckers 2022-10-04 19:10:15 +02:00
parent 1fa68f082f
commit 6df9456372
17 changed files with 115 additions and 18 deletions

View file

@ -14,7 +14,8 @@ All notable changes to this project will be documented in this file.
- Stats: top albums - Stats: top albums
- Stats: open full top listings - Stats: open full top listings
- Video: option for muted auto play - Video: option for muted auto play
- Slideshow: option for no transition - Slideshow / Screen saver: option for no transition
- Slideshow / Screen saver: animated zoom effect
- Widget: tap action setting - Widget: tap action setting
- Wallpaper: scroll effect option - Wallpaper: scroll effect option

View file

@ -715,6 +715,7 @@
"settingsSlideshowRepeat": "Repeat", "settingsSlideshowRepeat": "Repeat",
"settingsSlideshowShuffle": "Shuffle", "settingsSlideshowShuffle": "Shuffle",
"settingsSlideshowFillScreen": "Fill screen", "settingsSlideshowFillScreen": "Fill screen",
"settingsSlideshowAnimatedZoomEffect": "Animated zoom effect",
"settingsSlideshowTransitionTile": "Transition", "settingsSlideshowTransitionTile": "Transition",
"settingsSlideshowTransitionDialogTitle": "Transition", "settingsSlideshowTransitionDialogTitle": "Transition",
"settingsSlideshowIntervalTile": "Interval", "settingsSlideshowIntervalTile": "Interval",

View file

@ -534,6 +534,7 @@
"settingsSlideshowRepeat": "Répéter", "settingsSlideshowRepeat": "Répéter",
"settingsSlideshowShuffle": "Aléatoire", "settingsSlideshowShuffle": "Aléatoire",
"settingsSlideshowFillScreen": "Remplir lécran", "settingsSlideshowFillScreen": "Remplir lécran",
"settingsSlideshowAnimatedZoomEffect": "Effet de zoom animé",
"settingsSlideshowTransitionTile": "Transition", "settingsSlideshowTransitionTile": "Transition",
"settingsSlideshowTransitionDialogTitle": "Transition", "settingsSlideshowTransitionDialogTitle": "Transition",
"settingsSlideshowIntervalTile": "Intervalle", "settingsSlideshowIntervalTile": "Intervalle",

View file

@ -534,6 +534,7 @@
"settingsSlideshowRepeat": "반복", "settingsSlideshowRepeat": "반복",
"settingsSlideshowShuffle": "순서섞기", "settingsSlideshowShuffle": "순서섞기",
"settingsSlideshowFillScreen": "화면 채우기", "settingsSlideshowFillScreen": "화면 채우기",
"settingsSlideshowAnimatedZoomEffect": "애니메이션 확대/축소 효과",
"settingsSlideshowTransitionTile": "전환 효과", "settingsSlideshowTransitionTile": "전환 효과",
"settingsSlideshowTransitionDialogTitle": "전환 효과", "settingsSlideshowTransitionDialogTitle": "전환 효과",
"settingsSlideshowIntervalTile": "교체 주기", "settingsSlideshowIntervalTile": "교체 주기",

View file

@ -129,6 +129,7 @@ class SettingsDefaults {
static const slideshowRepeat = false; static const slideshowRepeat = false;
static const slideshowShuffle = false; static const slideshowShuffle = false;
static const slideshowFillScreen = false; static const slideshowFillScreen = false;
static const slideshowAnimatedZoomEffect = true;
static const slideshowTransition = ViewerTransition.fade; static const slideshowTransition = ViewerTransition.fade;
static const slideshowVideoPlayback = SlideshowVideoPlayback.playMuted; static const slideshowVideoPlayback = SlideshowVideoPlayback.playMuted;
static const slideshowInterval = SlideshowInterval.s5; static const slideshowInterval = SlideshowInterval.s5;

View file

@ -151,6 +151,7 @@ class Settings extends ChangeNotifier {
// screen saver // screen saver
static const screenSaverFillScreenKey = 'screen_saver_fill_screen'; static const screenSaverFillScreenKey = 'screen_saver_fill_screen';
static const screenSaverAnimatedZoomEffectKey = 'screen_saver_animated_zoom_effect';
static const screenSaverTransitionKey = 'screen_saver_transition'; static const screenSaverTransitionKey = 'screen_saver_transition';
static const screenSaverVideoPlaybackKey = 'screen_saver_video_playback'; static const screenSaverVideoPlaybackKey = 'screen_saver_video_playback';
static const screenSaverIntervalKey = 'screen_saver_interval'; static const screenSaverIntervalKey = 'screen_saver_interval';
@ -160,6 +161,7 @@ class Settings extends ChangeNotifier {
static const slideshowRepeatKey = 'slideshow_loop'; static const slideshowRepeatKey = 'slideshow_loop';
static const slideshowShuffleKey = 'slideshow_shuffle'; static const slideshowShuffleKey = 'slideshow_shuffle';
static const slideshowFillScreenKey = 'slideshow_fill_screen'; static const slideshowFillScreenKey = 'slideshow_fill_screen';
static const slideshowAnimatedZoomEffectKey = 'slideshow_animated_zoom_effect';
static const slideshowTransitionKey = 'slideshow_transition'; static const slideshowTransitionKey = 'slideshow_transition';
static const slideshowVideoPlaybackKey = 'slideshow_video_playback'; static const slideshowVideoPlaybackKey = 'slideshow_video_playback';
static const slideshowIntervalKey = 'slideshow_interval'; static const slideshowIntervalKey = 'slideshow_interval';
@ -649,6 +651,10 @@ class Settings extends ChangeNotifier {
set screenSaverFillScreen(bool newValue) => setAndNotify(screenSaverFillScreenKey, newValue); set screenSaverFillScreen(bool newValue) => setAndNotify(screenSaverFillScreenKey, newValue);
bool get screenSaverAnimatedZoomEffect => getBoolOrDefault(screenSaverAnimatedZoomEffectKey, SettingsDefaults.slideshowAnimatedZoomEffect);
set screenSaverAnimatedZoomEffect(bool newValue) => setAndNotify(screenSaverAnimatedZoomEffectKey, newValue);
ViewerTransition get screenSaverTransition => getEnumOrDefault(screenSaverTransitionKey, SettingsDefaults.slideshowTransition, ViewerTransition.values); ViewerTransition get screenSaverTransition => getEnumOrDefault(screenSaverTransitionKey, SettingsDefaults.slideshowTransition, ViewerTransition.values);
set screenSaverTransition(ViewerTransition newValue) => setAndNotify(screenSaverTransitionKey, newValue.toString()); set screenSaverTransition(ViewerTransition newValue) => setAndNotify(screenSaverTransitionKey, newValue.toString());
@ -679,6 +685,10 @@ class Settings extends ChangeNotifier {
set slideshowFillScreen(bool newValue) => setAndNotify(slideshowFillScreenKey, newValue); set slideshowFillScreen(bool newValue) => setAndNotify(slideshowFillScreenKey, newValue);
bool get slideshowAnimatedZoomEffect => getBoolOrDefault(slideshowAnimatedZoomEffectKey, SettingsDefaults.slideshowAnimatedZoomEffect);
set slideshowAnimatedZoomEffect(bool newValue) => setAndNotify(slideshowAnimatedZoomEffectKey, newValue);
ViewerTransition get slideshowTransition => getEnumOrDefault(slideshowTransitionKey, SettingsDefaults.slideshowTransition, ViewerTransition.values); ViewerTransition get slideshowTransition => getEnumOrDefault(slideshowTransitionKey, SettingsDefaults.slideshowTransition, ViewerTransition.values);
set slideshowTransition(ViewerTransition newValue) => setAndNotify(slideshowTransitionKey, newValue.toString()); set slideshowTransition(ViewerTransition newValue) => setAndNotify(slideshowTransitionKey, newValue.toString());
@ -880,9 +890,11 @@ class Settings extends ChangeNotifier {
case saveSearchHistoryKey: case saveSearchHistoryKey:
case filePickerShowHiddenFilesKey: case filePickerShowHiddenFilesKey:
case screenSaverFillScreenKey: case screenSaverFillScreenKey:
case screenSaverAnimatedZoomEffectKey:
case slideshowRepeatKey: case slideshowRepeatKey:
case slideshowShuffleKey: case slideshowShuffleKey:
case slideshowFillScreenKey: case slideshowFillScreenKey:
case slideshowAnimatedZoomEffectKey:
if (newValue is bool) { if (newValue is bool) {
settingsStore.setBool(key, newValue); settingsStore.setBool(key, newValue);
} else { } else {

View file

@ -41,6 +41,7 @@ class Durations {
static const thumbnailScrollerShadeAnimation = Duration(milliseconds: 150); static const thumbnailScrollerShadeAnimation = Duration(milliseconds: 150);
static const viewerVideoPlayerTransition = Duration(milliseconds: 500); static const viewerVideoPlayerTransition = Duration(milliseconds: 500);
static const viewerActionFeedbackAnimation = Duration(milliseconds: 600); static const viewerActionFeedbackAnimation = Duration(milliseconds: 600);
static const viewerHorizontalPageAnimation = Duration(seconds: 1);
// info animations // info animations
static const mapStyleSwitchAnimation = Duration(milliseconds: 300); static const mapStyleSwitchAnimation = Duration(milliseconds: 300);

View file

@ -45,7 +45,7 @@ class ScaleBoundaries extends Equatable {
); );
} }
double _scaleForLevel(ScaleLevel level) { double scaleForLevel(ScaleLevel level) {
final factor = level.factor; final factor = level.factor;
switch (level.ref) { switch (level.ref) {
case ScaleReference.contained: case ScaleReference.contained:
@ -61,18 +61,18 @@ class ScaleBoundaries extends Equatable {
double get originalScale => 1.0 / window.devicePixelRatio; double get originalScale => 1.0 / window.devicePixelRatio;
double get minScale => { double get minScale => {
_scaleForLevel(_minScale), scaleForLevel(_minScale),
_allowOriginalScaleBeyondRange ? originalScale : double.infinity, _allowOriginalScaleBeyondRange ? originalScale : double.infinity,
initialScale, initialScale,
}.fold(double.infinity, min); }.fold(double.infinity, min);
double get maxScale => { double get maxScale => {
_scaleForLevel(_maxScale), scaleForLevel(_maxScale),
_allowOriginalScaleBeyondRange ? originalScale : double.negativeInfinity, _allowOriginalScaleBeyondRange ? originalScale : double.negativeInfinity,
initialScale, initialScale,
}.fold(0, max); }.fold(0, max);
double get initialScale => _scaleForLevel(_initialScale); double get initialScale => scaleForLevel(_initialScale);
Offset get _viewportCenter => viewportSize.center(Offset.zero); Offset get _viewportCenter => viewportSize.center(Offset.zero);

View file

@ -32,6 +32,11 @@ class ScreenSaverSettingsPage extends StatelessWidget {
onChanged: (v) => settings.screenSaverFillScreen = v, onChanged: (v) => settings.screenSaverFillScreen = v,
title: context.l10n.settingsSlideshowFillScreen, title: context.l10n.settingsSlideshowFillScreen,
), ),
SettingsSwitchListTile(
selector: (context, s) => s.screenSaverAnimatedZoomEffect,
onChanged: (v) => settings.screenSaverAnimatedZoomEffect = v,
title: context.l10n.settingsSlideshowAnimatedZoomEffect,
),
SettingsSelectionListTile<ViewerTransition>( SettingsSelectionListTile<ViewerTransition>(
values: ViewerTransition.values, values: ViewerTransition.values,
getName: (context, v) => v.getName(context), getName: (context, v) => v.getName(context),

View file

@ -36,6 +36,11 @@ class ViewerSlideshowPage extends StatelessWidget {
onChanged: (v) => settings.slideshowFillScreen = v, onChanged: (v) => settings.slideshowFillScreen = v,
title: context.l10n.settingsSlideshowFillScreen, title: context.l10n.settingsSlideshowFillScreen,
), ),
SettingsSwitchListTile(
selector: (context, s) => s.slideshowAnimatedZoomEffect,
onChanged: (v) => settings.slideshowAnimatedZoomEffect = v,
title: context.l10n.settingsSlideshowAnimatedZoomEffect,
),
SettingsSelectionListTile<ViewerTransition>( SettingsSelectionListTile<ViewerTransition>(
values: ViewerTransition.values, values: ViewerTransition.values,
getName: (context, v) => v.getName(context), getName: (context, v) => v.getName(context),

View file

@ -1,20 +1,25 @@
import 'dart:async'; import 'dart:async';
import 'dart:math';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/enums/enums.dart';
import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/common/magnifier/scale/scale_level.dart'; import 'package:aves/widgets/common/magnifier/scale/scale_level.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class ViewerController { class ViewerController {
final ValueNotifier<AvesEntry?> entryNotifier = ValueNotifier(null); final ValueNotifier<AvesEntry?> entryNotifier = ValueNotifier(null);
final ScaleLevel initialScale;
final ViewerTransition transition; final ViewerTransition transition;
final Duration? autopilotInterval; final Duration? autopilotInterval;
final bool autopilotAnimatedZoom;
final bool repeat; final bool repeat;
late final ScaleLevel _initialScale;
late final ValueNotifier<bool> _autopilotNotifier; late final ValueNotifier<bool> _autopilotNotifier;
Timer? _playTimer; Timer? _playTimer;
final StreamController _streamController = StreamController.broadcast(); final StreamController _streamController = StreamController.broadcast();
final Map<TickerProvider, AnimationController> _autopilotAnimationControllers = {};
ScaleLevel? _autopilotInitialScale;
Stream<dynamic> get _events => _streamController.stream; Stream<dynamic> get _events => _streamController.stream;
@ -26,13 +31,22 @@ class ViewerController {
set autopilot(bool enabled) => _autopilotNotifier.value = enabled; set autopilot(bool enabled) => _autopilotNotifier.value = enabled;
ScaleLevel get initialScale => _autopilotInitialScale ?? _initialScale;
static final _autopilotScaleTweens = [
Tween<double>(begin: 1, end: 1.2),
Tween<double>(begin: 1.2, end: 1),
];
ViewerController({ ViewerController({
this.initialScale = const ScaleLevel(ref: ScaleReference.contained), ScaleLevel initialScale = const ScaleLevel(ref: ScaleReference.contained),
this.transition = ViewerTransition.parallax, this.transition = ViewerTransition.parallax,
this.repeat = false, this.repeat = false,
bool autopilot = false, bool autopilot = false,
this.autopilotInterval, this.autopilotInterval,
this.autopilotAnimatedZoom = false,
}) { }) {
_initialScale = initialScale;
_autopilotNotifier = ValueNotifier(autopilot); _autopilotNotifier = ValueNotifier(autopilot);
_autopilotNotifier.addListener(_onAutopilotChange); _autopilotNotifier.addListener(_onAutopilotChange);
_onAutopilotChange(); _onAutopilotChange();
@ -40,21 +54,53 @@ class ViewerController {
void dispose() { void dispose() {
_autopilotNotifier.removeListener(_onAutopilotChange); _autopilotNotifier.removeListener(_onAutopilotChange);
_clearAutopilotAnimations();
_stopPlayTimer(); _stopPlayTimer();
_streamController.close(); _streamController.close();
} }
void _stopPlayTimer() {
_playTimer?.cancel();
}
void _onAutopilotChange() { void _onAutopilotChange() {
_clearAutopilotAnimations();
_stopPlayTimer(); _stopPlayTimer();
if (autopilot && autopilotInterval != null) { if (autopilot && autopilotInterval != null) {
_playTimer = Timer.periodic(autopilotInterval!, (_) => _streamController.add(ViewerShowNextEvent())); _playTimer = Timer.periodic(autopilotInterval!, (_) => _streamController.add(ViewerShowNextEvent()));
_streamController.add(const ViewerOverlayToggleEvent(visible: false)); _streamController.add(const ViewerOverlayToggleEvent(visible: false));
} }
} }
void _stopPlayTimer() => _playTimer?.cancel();
void _clearAutopilotAnimations() => _autopilotAnimationControllers.keys.toSet().forEach((v) => stopAutopilotAnimation(vsync: v));
void stopAutopilotAnimation({required TickerProvider vsync}) => _autopilotAnimationControllers.remove(vsync)?.dispose();
void startAutopilotAnimation({
required TickerProvider vsync,
required void Function({required ScaleLevel scaleLevel}) onUpdate,
}) {
stopAutopilotAnimation(vsync: vsync);
if (!autopilot || !autopilotAnimatedZoom) return;
final scaleLevelRef = _initialScale.ref;
final scaleFactorTween = _autopilotScaleTweens[Random().nextInt(_autopilotScaleTweens.length)];
_autopilotInitialScale = ScaleLevel(ref: scaleLevelRef, factor: scaleFactorTween.begin!);
final animationController = AnimationController(
duration: autopilotInterval,
vsync: vsync,
);
animationController.addListener(() => onUpdate.call(
scaleLevel: ScaleLevel(
ref: scaleLevelRef,
factor: scaleFactorTween.evaluate(CurvedAnimation(
parent: animationController,
curve: Curves.linear,
)),
),
));
_autopilotAnimationControllers[vsync] = animationController;
Future.delayed(Durations.viewerHorizontalPageAnimation).then((_) => _autopilotAnimationControllers[vsync]?.forward());
}
} }
@immutable @immutable

View file

@ -90,7 +90,7 @@ class _MultiEntryScrollerState extends State<MultiEntryScroller> with AutomaticK
key: const Key('image_view'), key: const Key('image_view'),
mainEntry: mainEntry, mainEntry: mainEntry,
pageEntry: pageEntry ?? mainEntry, pageEntry: pageEntry ?? mainEntry,
initialScale: viewerController.initialScale, viewerController: viewerController,
onDisposed: () => widget.onViewDisposed(mainEntry, pageEntry), onDisposed: () => widget.onViewDisposed(mainEntry, pageEntry),
); );
} }
@ -139,7 +139,7 @@ class _SingleEntryScrollerState extends State<SingleEntryScroller> with Automati
return EntryPageView( return EntryPageView(
mainEntry: mainEntry, mainEntry: mainEntry,
pageEntry: pageEntry ?? mainEntry, pageEntry: pageEntry ?? mainEntry,
initialScale: viewerController.initialScale, viewerController: widget.viewerController,
); );
} }

View file

@ -229,7 +229,7 @@ class _ViewerVerticalPageViewState extends State<ViewerVerticalPageView> {
if (animate) { if (animate) {
pageController.animateToPage( pageController.animateToPage(
target, target,
duration: const Duration(seconds: 1), duration: Durations.viewerHorizontalPageAnimation,
curve: Curves.easeInOutCubic, curve: Curves.easeInOutCubic,
); );
} else { } else {

View file

@ -43,6 +43,7 @@ class _ScreenSaverPageState extends State<ScreenSaverPage> with WidgetsBindingOb
repeat: true, repeat: true,
autopilot: true, autopilot: true,
autopilotInterval: settings.screenSaverInterval.getDuration(), autopilotInterval: settings.screenSaverInterval.getDuration(),
autopilotAnimatedZoom: settings.screenSaverAnimatedZoomEffect,
); );
source.stateNotifier.addListener(_onSourceStateChanged); source.stateNotifier.addListener(_onSourceStateChanged);
_initSlideshowCollection(); _initSlideshowCollection();

View file

@ -45,6 +45,7 @@ class _SlideshowPageState extends State<SlideshowPage> {
repeat: settings.slideshowRepeat, repeat: settings.slideshowRepeat,
autopilot: true, autopilot: true,
autopilotInterval: settings.slideshowInterval.getDuration(), autopilotInterval: settings.slideshowInterval.getDuration(),
autopilotAnimatedZoom: settings.slideshowAnimatedZoomEffect,
); );
_initSlideshowCollection(); _initSlideshowCollection();
} }

View file

@ -16,6 +16,7 @@ import 'package:aves/widgets/common/magnifier/scale/scale_boundaries.dart';
import 'package:aves/widgets/common/magnifier/scale/scale_level.dart'; import 'package:aves/widgets/common/magnifier/scale/scale_level.dart';
import 'package:aves/widgets/common/magnifier/scale/state.dart'; import 'package:aves/widgets/common/magnifier/scale/state.dart';
import 'package:aves/widgets/common/thumbnail/image.dart'; import 'package:aves/widgets/common/thumbnail/image.dart';
import 'package:aves/widgets/viewer/controller.dart';
import 'package:aves/widgets/viewer/hero.dart'; import 'package:aves/widgets/viewer/hero.dart';
import 'package:aves/widgets/viewer/notifications.dart'; import 'package:aves/widgets/viewer/notifications.dart';
import 'package:aves/widgets/viewer/video/conductor.dart'; import 'package:aves/widgets/viewer/video/conductor.dart';
@ -35,7 +36,7 @@ import 'package:tuple/tuple.dart';
class EntryPageView extends StatefulWidget { class EntryPageView extends StatefulWidget {
final AvesEntry mainEntry, pageEntry; final AvesEntry mainEntry, pageEntry;
final ScaleLevel initialScale; final ViewerController viewerController;
final VoidCallback? onDisposed; final VoidCallback? onDisposed;
static const decorationCheckSize = 20.0; static const decorationCheckSize = 20.0;
@ -44,7 +45,7 @@ class EntryPageView extends StatefulWidget {
super.key, super.key,
required this.mainEntry, required this.mainEntry,
required this.pageEntry, required this.pageEntry,
required this.initialScale, required this.viewerController,
this.onDisposed, this.onDisposed,
}); });
@ -52,7 +53,7 @@ class EntryPageView extends StatefulWidget {
State<EntryPageView> createState() => _EntryPageViewState(); State<EntryPageView> createState() => _EntryPageViewState();
} }
class _EntryPageViewState extends State<EntryPageView> { class _EntryPageViewState extends State<EntryPageView> with SingleTickerProviderStateMixin {
late ValueNotifier<ViewState> _viewStateNotifier; late ValueNotifier<ViewState> _viewStateNotifier;
late MagnifierController _magnifierController; late MagnifierController _magnifierController;
final List<StreamSubscription> _subscriptions = []; final List<StreamSubscription> _subscriptions = [];
@ -72,6 +73,8 @@ class _EntryPageViewState extends State<EntryPageView> {
AvesEntry get entry => widget.pageEntry; AvesEntry get entry => widget.pageEntry;
ViewerController get viewerController => widget.viewerController;
// use the high res photo as cover for the video part of a motion photo // use the high res photo as cover for the video part of a motion photo
ImageProvider get videoCoverUriImage => mainEntry.isMotionPhoto ? mainEntry.uriImage : entry.uriImage; ImageProvider get videoCoverUriImage => mainEntry.isMotionPhoto ? mainEntry.uriImage : entry.uriImage;
@ -112,9 +115,16 @@ class _EntryPageViewState extends State<EntryPageView> {
_videoCoverStream = videoCoverUriImage.resolve(ImageConfiguration.empty); _videoCoverStream = videoCoverUriImage.resolve(ImageConfiguration.empty);
_videoCoverStream!.addListener(_videoCoverStreamListener); _videoCoverStream!.addListener(_videoCoverStreamListener);
} }
viewerController.startAutopilotAnimation(
vsync: this,
onUpdate: ({required scaleLevel}) {
final scale = _magnifierController.scaleBoundaries.scaleForLevel(scaleLevel);
_magnifierController.update(scale: scale, source: ChangeSource.animation);
});
} }
void _unregisterWidget(EntryPageView oldWidget) { void _unregisterWidget(EntryPageView oldWidget) {
viewerController.stopAutopilotAnimation(vsync: this);
_videoCoverStream?.removeListener(_videoCoverStreamListener); _videoCoverStream?.removeListener(_videoCoverStreamListener);
_videoCoverStream = null; _videoCoverStream = null;
_videoCoverInfoNotifier.value = null; _videoCoverInfoNotifier.value = null;
@ -385,7 +395,7 @@ class _EntryPageViewState extends State<EntryPageView> {
allowOriginalScaleBeyondRange: !isWallpaperMode, allowOriginalScaleBeyondRange: !isWallpaperMode,
minScale: minScale, minScale: minScale,
maxScale: maxScale, maxScale: maxScale,
initialScale: widget.initialScale, initialScale: viewerController.initialScale,
scaleStateCycle: scaleStateCycle, scaleStateCycle: scaleStateCycle,
applyScale: applyScale, applyScale: applyScale,
onTap: (c, s, a, p) => _onTap(alignment: a), onTap: (c, s, a, p) => _onTap(alignment: a),

View file

@ -9,6 +9,7 @@
"albumGroupType", "albumGroupType",
"albumMimeTypeMixed", "albumMimeTypeMixed",
"settingsDisabled", "settingsDisabled",
"settingsSlideshowAnimatedZoomEffect",
"settingsWidgetOpenPage", "settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle", "statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect" "wallpaperUseScrollEffect"
@ -24,6 +25,7 @@
"albumGroupType", "albumGroupType",
"albumMimeTypeMixed", "albumMimeTypeMixed",
"settingsDisabled", "settingsDisabled",
"settingsSlideshowAnimatedZoomEffect",
"settingsWidgetOpenPage", "settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle", "statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect" "wallpaperUseScrollEffect"
@ -54,6 +56,7 @@
"searchMetadataSectionTitle", "searchMetadataSectionTitle",
"settingsDisabled", "settingsDisabled",
"settingsConfirmationAfterMoveToBinItems", "settingsConfirmationAfterMoveToBinItems",
"settingsSlideshowAnimatedZoomEffect",
"settingsWidgetOpenPage", "settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle", "statsTopAlbumsSectionTitle",
"viewerInfoLabelDescription", "viewerInfoLabelDescription",
@ -74,6 +77,7 @@
"albumGroupType", "albumGroupType",
"albumMimeTypeMixed", "albumMimeTypeMixed",
"settingsDisabled", "settingsDisabled",
"settingsSlideshowAnimatedZoomEffect",
"settingsWidgetOpenPage", "settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle", "statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect" "wallpaperUseScrollEffect"
@ -89,6 +93,7 @@
"albumGroupType", "albumGroupType",
"albumMimeTypeMixed", "albumMimeTypeMixed",
"settingsDisabled", "settingsDisabled",
"settingsSlideshowAnimatedZoomEffect",
"settingsWidgetOpenPage", "settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle", "statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect" "wallpaperUseScrollEffect"
@ -120,6 +125,7 @@
"settingsDisabled", "settingsDisabled",
"settingsConfirmationAfterMoveToBinItems", "settingsConfirmationAfterMoveToBinItems",
"settingsViewerGestureSideTapNext", "settingsViewerGestureSideTapNext",
"settingsSlideshowAnimatedZoomEffect",
"settingsWidgetOpenPage", "settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle", "statsTopAlbumsSectionTitle",
"viewerInfoLabelDescription", "viewerInfoLabelDescription",
@ -140,6 +146,7 @@
"albumGroupType", "albumGroupType",
"albumMimeTypeMixed", "albumMimeTypeMixed",
"settingsDisabled", "settingsDisabled",
"settingsSlideshowAnimatedZoomEffect",
"settingsWidgetOpenPage", "settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle", "statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect" "wallpaperUseScrollEffect"
@ -155,6 +162,7 @@
"albumGroupType", "albumGroupType",
"albumMimeTypeMixed", "albumMimeTypeMixed",
"settingsDisabled", "settingsDisabled",
"settingsSlideshowAnimatedZoomEffect",
"settingsWidgetOpenPage", "settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle", "statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect" "wallpaperUseScrollEffect"
@ -170,6 +178,7 @@
"albumGroupType", "albumGroupType",
"albumMimeTypeMixed", "albumMimeTypeMixed",
"settingsDisabled", "settingsDisabled",
"settingsSlideshowAnimatedZoomEffect",
"settingsWidgetOpenPage", "settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle", "statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect" "wallpaperUseScrollEffect"
@ -220,6 +229,7 @@
"settingsSlideshowRepeat", "settingsSlideshowRepeat",
"settingsSlideshowShuffle", "settingsSlideshowShuffle",
"settingsSlideshowFillScreen", "settingsSlideshowFillScreen",
"settingsSlideshowAnimatedZoomEffect",
"settingsSlideshowTransitionTile", "settingsSlideshowTransitionTile",
"settingsSlideshowTransitionDialogTitle", "settingsSlideshowTransitionDialogTitle",
"settingsSlideshowIntervalTile", "settingsSlideshowIntervalTile",
@ -245,6 +255,7 @@
"albumGroupType", "albumGroupType",
"albumMimeTypeMixed", "albumMimeTypeMixed",
"settingsDisabled", "settingsDisabled",
"settingsSlideshowAnimatedZoomEffect",
"settingsWidgetOpenPage", "settingsWidgetOpenPage",
"statsTopAlbumsSectionTitle", "statsTopAlbumsSectionTitle",
"wallpaperUseScrollEffect" "wallpaperUseScrollEffect"