#562 sony burst support
This commit is contained in:
parent
708f1310e4
commit
5b495e95e5
20 changed files with 507 additions and 181 deletions
|
@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Collection: optional support for Samsung and Sony burst patterns
|
||||||
- Info: improved state/place display (requires rescan, limited to AU/GB/EN)
|
- Info: improved state/place display (requires rescan, limited to AU/GB/EN)
|
||||||
- improved support for system font scale
|
- improved support for system font scale
|
||||||
|
|
||||||
|
|
|
@ -754,6 +754,9 @@
|
||||||
"settingsCollectionBrowsingQuickActionEditorBanner": "Touch and hold to move buttons and select which actions are displayed when browsing items.",
|
"settingsCollectionBrowsingQuickActionEditorBanner": "Touch and hold to move buttons and select which actions are displayed when browsing items.",
|
||||||
"settingsCollectionSelectionQuickActionEditorBanner": "Touch and hold to move buttons and select which actions are displayed when selecting items.",
|
"settingsCollectionSelectionQuickActionEditorBanner": "Touch and hold to move buttons and select which actions are displayed when selecting items.",
|
||||||
|
|
||||||
|
"settingsCollectionBurstPatternsTile": "Burst patterns",
|
||||||
|
"settingsCollectionBurstPatternsNone": "None",
|
||||||
|
|
||||||
"settingsViewerSectionTitle": "Viewer",
|
"settingsViewerSectionTitle": "Viewer",
|
||||||
"settingsViewerGestureSideTapNext": "Tap on screen edges to show previous/next item",
|
"settingsViewerGestureSideTapNext": "Tap on screen edges to show previous/next item",
|
||||||
"settingsViewerUseCutout": "Use cutout area",
|
"settingsViewerUseCutout": "Use cutout area",
|
||||||
|
|
|
@ -7,8 +7,6 @@ import 'package:aves/services/common/services.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
extension ExtraAvesEntryMultipage on AvesEntry {
|
extension ExtraAvesEntryMultipage on AvesEntry {
|
||||||
static final _burstFilenamePattern = RegExp(r'^(\d{8}_\d{6})_(\d+)$');
|
|
||||||
|
|
||||||
bool get isMultiPage => (catalogMetadata?.isMultiPage ?? false) || isBurst;
|
bool get isMultiPage => (catalogMetadata?.isMultiPage ?? false) || isBurst;
|
||||||
|
|
||||||
bool get isBurst => burstEntries?.isNotEmpty == true;
|
bool get isBurst => burstEntries?.isNotEmpty == true;
|
||||||
|
@ -18,11 +16,13 @@ extension ExtraAvesEntryMultipage on AvesEntry {
|
||||||
|
|
||||||
bool get isMotionPhoto => (catalogMetadata?.isMotionPhoto ?? false) || _isMotionPhotoLegacy;
|
bool get isMotionPhoto => (catalogMetadata?.isMotionPhoto ?? false) || _isMotionPhotoLegacy;
|
||||||
|
|
||||||
String? get burstKey {
|
String? getBurstKey(List<String> patterns) {
|
||||||
if (filenameWithoutExtension != null) {
|
if (filenameWithoutExtension != null) {
|
||||||
final match = _burstFilenamePattern.firstMatch(filenameWithoutExtension!);
|
for (final pattern in patterns) {
|
||||||
if (match != null) {
|
final match = RegExp(pattern).firstMatch(filenameWithoutExtension!);
|
||||||
return '$directory/${match.group(1)}';
|
if (match != null) {
|
||||||
|
return '$directory/${match.group(1)}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -13,8 +13,8 @@ import 'package:aves/model/settings/defaults.dart';
|
||||||
import 'package:aves/model/settings/enums/enums.dart';
|
import 'package:aves/model/settings/enums/enums.dart';
|
||||||
import 'package:aves/model/settings/enums/map_style.dart';
|
import 'package:aves/model/settings/enums/map_style.dart';
|
||||||
import 'package:aves/model/source/enums/enums.dart';
|
import 'package:aves/model/source/enums/enums.dart';
|
||||||
|
import 'package:aves/ref/bursts.dart';
|
||||||
import 'package:aves/services/accessibility_service.dart';
|
import 'package:aves/services/accessibility_service.dart';
|
||||||
import 'package:aves_utils/aves_utils.dart';
|
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/widgets/aves_app.dart';
|
import 'package:aves/widgets/aves_app.dart';
|
||||||
import 'package:aves/widgets/common/search/page.dart';
|
import 'package:aves/widgets/common/search/page.dart';
|
||||||
|
@ -23,7 +23,9 @@ import 'package:aves/widgets/filter_grids/countries_page.dart';
|
||||||
import 'package:aves/widgets/filter_grids/places_page.dart';
|
import 'package:aves/widgets/filter_grids/places_page.dart';
|
||||||
import 'package:aves/widgets/filter_grids/tags_page.dart';
|
import 'package:aves/widgets/filter_grids/tags_page.dart';
|
||||||
import 'package:aves_map/aves_map.dart';
|
import 'package:aves_map/aves_map.dart';
|
||||||
|
import 'package:aves_utils/aves_utils.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:latlong2/latlong.dart';
|
import 'package:latlong2/latlong.dart';
|
||||||
|
@ -90,6 +92,7 @@ class Settings extends ChangeNotifier {
|
||||||
static const drawerPageBookmarksKey = 'drawer_page_bookmarks';
|
static const drawerPageBookmarksKey = 'drawer_page_bookmarks';
|
||||||
|
|
||||||
// collection
|
// collection
|
||||||
|
static const collectionBurstPatternsKey = 'collection_burst_patterns';
|
||||||
static const collectionGroupFactorKey = 'collection_group_factor';
|
static const collectionGroupFactorKey = 'collection_group_factor';
|
||||||
static const collectionSortFactorKey = 'collection_sort_factor';
|
static const collectionSortFactorKey = 'collection_sort_factor';
|
||||||
static const collectionSortReverseKey = 'collection_sort_reverse';
|
static const collectionSortReverseKey = 'collection_sort_reverse';
|
||||||
|
@ -245,6 +248,10 @@ class Settings extends ChangeNotifier {
|
||||||
final performanceClass = await deviceService.getPerformanceClass();
|
final performanceClass = await deviceService.getPerformanceClass();
|
||||||
enableBlurEffect = performanceClass >= 29;
|
enableBlurEffect = performanceClass >= 29;
|
||||||
|
|
||||||
|
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||||
|
final pattern = BurstPatterns.byManufacturer[androidInfo.manufacturer];
|
||||||
|
collectionBurstPatterns = pattern != null ? [pattern] : [];
|
||||||
|
|
||||||
// availability
|
// availability
|
||||||
if (flavor.hasMapStyleDefault) {
|
if (flavor.hasMapStyleDefault) {
|
||||||
final defaultMapStyle = mobileServices.defaultMapStyle;
|
final defaultMapStyle = mobileServices.defaultMapStyle;
|
||||||
|
@ -495,6 +502,10 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
// collection
|
// collection
|
||||||
|
|
||||||
|
List<String> get collectionBurstPatterns => getStringList(collectionBurstPatternsKey) ?? [];
|
||||||
|
|
||||||
|
set collectionBurstPatterns(List<String> newValue) => _set(collectionBurstPatternsKey, newValue);
|
||||||
|
|
||||||
EntryGroupFactor get collectionSectionFactor => getEnumOrDefault(collectionGroupFactorKey, SettingsDefaults.collectionSectionFactor, EntryGroupFactor.values);
|
EntryGroupFactor get collectionSectionFactor => getEnumOrDefault(collectionGroupFactorKey, SettingsDefaults.collectionSectionFactor, EntryGroupFactor.values);
|
||||||
|
|
||||||
set collectionSectionFactor(EntryGroupFactor newValue) => _set(collectionGroupFactorKey, newValue.toString());
|
set collectionSectionFactor(EntryGroupFactor newValue) => _set(collectionGroupFactorKey, newValue.toString());
|
||||||
|
@ -1152,6 +1163,7 @@ class Settings extends ChangeNotifier {
|
||||||
case drawerTypeBookmarksKey:
|
case drawerTypeBookmarksKey:
|
||||||
case drawerAlbumBookmarksKey:
|
case drawerAlbumBookmarksKey:
|
||||||
case drawerPageBookmarksKey:
|
case drawerPageBookmarksKey:
|
||||||
|
case collectionBurstPatternsKey:
|
||||||
case pinnedFiltersKey:
|
case pinnedFiltersKey:
|
||||||
case hiddenFiltersKey:
|
case hiddenFiltersKey:
|
||||||
case collectionBrowsingQuickActionsKey:
|
case collectionBrowsingQuickActionsKey:
|
||||||
|
|
|
@ -28,6 +28,7 @@ import 'package:flutter/foundation.dart';
|
||||||
class CollectionLens with ChangeNotifier {
|
class CollectionLens with ChangeNotifier {
|
||||||
final CollectionSource source;
|
final CollectionSource source;
|
||||||
final Set<CollectionFilter> filters;
|
final Set<CollectionFilter> filters;
|
||||||
|
List<String> burstPatterns;
|
||||||
EntryGroupFactor sectionFactor;
|
EntryGroupFactor sectionFactor;
|
||||||
EntrySortFactor sortFactor;
|
EntrySortFactor sortFactor;
|
||||||
bool sortReverse;
|
bool sortReverse;
|
||||||
|
@ -50,6 +51,7 @@ class CollectionLens with ChangeNotifier {
|
||||||
this.fixedSort = false,
|
this.fixedSort = false,
|
||||||
this.fixedSelection,
|
this.fixedSelection,
|
||||||
}) : filters = (filters ?? {}).whereNotNull().toSet(),
|
}) : filters = (filters ?? {}).whereNotNull().toSet(),
|
||||||
|
burstPatterns = settings.collectionBurstPatterns,
|
||||||
sectionFactor = settings.collectionSectionFactor,
|
sectionFactor = settings.collectionSectionFactor,
|
||||||
sortFactor = settings.collectionSortFactor,
|
sortFactor = settings.collectionSortFactor,
|
||||||
sortReverse = settings.collectionSortReverse {
|
sortReverse = settings.collectionSortReverse {
|
||||||
|
@ -85,6 +87,7 @@ class CollectionLens with ChangeNotifier {
|
||||||
}
|
}
|
||||||
_subscriptions.add(settings.updateStream
|
_subscriptions.add(settings.updateStream
|
||||||
.where((event) => [
|
.where((event) => [
|
||||||
|
Settings.collectionBurstPatternsKey,
|
||||||
Settings.collectionSortFactorKey,
|
Settings.collectionSortFactorKey,
|
||||||
Settings.collectionGroupFactorKey,
|
Settings.collectionGroupFactorKey,
|
||||||
Settings.collectionSortReverseKey,
|
Settings.collectionSortReverseKey,
|
||||||
|
@ -188,7 +191,7 @@ class CollectionLens with ChangeNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _groupBursts() {
|
void _groupBursts() {
|
||||||
final byBurstKey = groupBy<AvesEntry, String?>(_filteredSortedEntries, (entry) => entry.burstKey).whereNotNullKey();
|
final byBurstKey = groupBy<AvesEntry, String?>(_filteredSortedEntries, (entry) => entry.getBurstKey(burstPatterns)).whereNotNullKey();
|
||||||
byBurstKey.forEach((burstKey, entries) {
|
byBurstKey.forEach((burstKey, entries) {
|
||||||
if (entries.length > 1) {
|
if (entries.length > 1) {
|
||||||
entries.sort(AvesEntrySort.compareByName);
|
entries.sort(AvesEntrySort.compareByName);
|
||||||
|
@ -287,13 +290,19 @@ class CollectionLens with ChangeNotifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSettingsChanged() {
|
void _onSettingsChanged() {
|
||||||
|
final newBurstPatterns = settings.collectionBurstPatterns;
|
||||||
final newSortFactor = settings.collectionSortFactor;
|
final newSortFactor = settings.collectionSortFactor;
|
||||||
final newSectionFactor = settings.collectionSectionFactor;
|
final newSectionFactor = settings.collectionSectionFactor;
|
||||||
final newSortReverse = settings.collectionSortReverse;
|
final newSortReverse = settings.collectionSortReverse;
|
||||||
|
|
||||||
final needSort = sortFactor != newSortFactor || sortReverse != newSortReverse;
|
final needFilter = burstPatterns != newBurstPatterns;
|
||||||
|
final needSort = needFilter || sortFactor != newSortFactor || sortReverse != newSortReverse;
|
||||||
final needSection = needSort || sectionFactor != newSectionFactor;
|
final needSection = needSort || sectionFactor != newSectionFactor;
|
||||||
|
|
||||||
|
if (needFilter) {
|
||||||
|
burstPatterns = newBurstPatterns;
|
||||||
|
_applyFilters();
|
||||||
|
}
|
||||||
if (needSort) {
|
if (needSort) {
|
||||||
sortFactor = newSortFactor;
|
sortFactor = newSortFactor;
|
||||||
sortReverse = newSortReverse;
|
sortReverse = newSortReverse;
|
||||||
|
@ -303,6 +312,10 @@ class CollectionLens with ChangeNotifier {
|
||||||
sectionFactor = newSectionFactor;
|
sectionFactor = newSectionFactor;
|
||||||
_applySection();
|
_applySection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (needFilter) {
|
||||||
|
filterChangeNotifier.notifyListeners();
|
||||||
|
}
|
||||||
if (needSort || needSection) {
|
if (needSort || needSection) {
|
||||||
sortSectionChangeNotifier.notifyListeners();
|
sortSectionChangeNotifier.notifyListeners();
|
||||||
}
|
}
|
||||||
|
@ -316,9 +329,9 @@ class CollectionLens with ChangeNotifier {
|
||||||
if (groupBursts) {
|
if (groupBursts) {
|
||||||
// find impacted burst groups
|
// find impacted burst groups
|
||||||
final obsoleteBurstEntries = <AvesEntry>{};
|
final obsoleteBurstEntries = <AvesEntry>{};
|
||||||
final burstKeys = entries.map((entry) => entry.burstKey).whereNotNull().toSet();
|
final burstKeys = entries.map((entry) => entry.getBurstKey(burstPatterns)).whereNotNull().toSet();
|
||||||
if (burstKeys.isNotEmpty) {
|
if (burstKeys.isNotEmpty) {
|
||||||
_filteredSortedEntries.where((entry) => entry.isBurst && burstKeys.contains(entry.burstKey)).forEach((mainEntry) {
|
_filteredSortedEntries.where((entry) => entry.isBurst && burstKeys.contains(entry.getBurstKey(burstPatterns))).forEach((mainEntry) {
|
||||||
final subEntries = mainEntry.burstEntries!;
|
final subEntries = mainEntry.burstEntries!;
|
||||||
// remove the deleted sub-entries
|
// remove the deleted sub-entries
|
||||||
subEntries.removeWhere(entries.contains);
|
subEntries.removeWhere(entries.contains);
|
||||||
|
|
42
lib/ref/bursts.dart
Normal file
42
lib/ref/bursts.dart
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
class BurstPatterns {
|
||||||
|
static const samsung = r'^(\d{8}_\d{6})_(\d+)$';
|
||||||
|
static const sony = r'^DSC_\d+_BURST(\d{17})(_COVER)?$';
|
||||||
|
|
||||||
|
static final options = [
|
||||||
|
BurstPatterns.samsung,
|
||||||
|
BurstPatterns.sony,
|
||||||
|
];
|
||||||
|
|
||||||
|
static String getName(String pattern) {
|
||||||
|
switch (pattern) {
|
||||||
|
case samsung:
|
||||||
|
return 'Samsung';
|
||||||
|
case sony:
|
||||||
|
return 'Sony';
|
||||||
|
default:
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getExample(String pattern) {
|
||||||
|
switch (pattern) {
|
||||||
|
case samsung:
|
||||||
|
return '20151021_072800_007';
|
||||||
|
case sony:
|
||||||
|
return 'DSC_0007_BURST20151021072800123';
|
||||||
|
default:
|
||||||
|
return '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const byManufacturer = {
|
||||||
|
_Manufacturers.samsung: samsung,
|
||||||
|
_Manufacturers.sony: sony,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// values as returned by `DeviceInfoPlugin().androidInfo`
|
||||||
|
class _Manufacturers {
|
||||||
|
static const samsung = 'samsung';
|
||||||
|
static const sony = 'sony';
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import 'package:aves/model/settings/enums/coordinate_format.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/theme/format.dart';
|
import 'package:aves/theme/format.dart';
|
||||||
import 'package:aves/theme/icons.dart';
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/utils/collection_utils.dart';
|
||||||
import 'package:aves/utils/constants.dart';
|
import 'package:aves/utils/constants.dart';
|
||||||
import 'package:aves/utils/file_utils.dart';
|
import 'package:aves/utils/file_utils.dart';
|
||||||
import 'package:aves/widgets/collection/grid/list_details_theme.dart';
|
import 'package:aves/widgets/collection/grid/list_details_theme.dart';
|
||||||
|
@ -80,7 +81,7 @@ class EntryListDetails extends StatelessWidget {
|
||||||
final date = entry.bestDate;
|
final date = entry.bestDate;
|
||||||
final dateText = date != null ? formatDateTime(date, locale, use24hour) : Constants.overlayUnknown;
|
final dateText = date != null ? formatDateTime(date, locale, use24hour) : Constants.overlayUnknown;
|
||||||
|
|
||||||
final size = entry.sizeBytes;
|
final size = entry.burstEntries?.map((v) => v.sizeBytes).sum ?? entry.sizeBytes;
|
||||||
final sizeText = size != null ? formatFileSize(locale, size) : Constants.overlayUnknown;
|
final sizeText = size != null ? formatFileSize(locale, size) : Constants.overlayUnknown;
|
||||||
|
|
||||||
return _buildRow(
|
return _buildRow(
|
||||||
|
|
|
@ -29,9 +29,9 @@ import 'package:aves/widgets/common/action_mixins/permission_aware.dart';
|
||||||
import 'package:aves/widgets/common/action_mixins/size_aware.dart';
|
import 'package:aves/widgets/common/action_mixins/size_aware.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_confirmation_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_confirmation_dialog.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
|
||||||
import 'package:aves/widgets/dialogs/convert_entry_dialog.dart';
|
import 'package:aves/widgets/dialogs/convert_entry_dialog.dart';
|
||||||
import 'package:aves/widgets/dialogs/pick_dialogs/album_pick_page.dart';
|
import 'package:aves/widgets/dialogs/pick_dialogs/album_pick_page.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/single_selection.dart';
|
||||||
import 'package:aves/widgets/viewer/controls/notifications.dart';
|
import 'package:aves/widgets/viewer/controls/notifications.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -172,13 +172,13 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
|
||||||
if (uniqueNames.length < names.length) {
|
if (uniqueNames.length < names.length) {
|
||||||
final value = await showDialog<NameConflictStrategy>(
|
final value = await showDialog<NameConflictStrategy>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AvesSelectionDialog<NameConflictStrategy>(
|
builder: (context) => AvesSingleSelectionDialog<NameConflictStrategy>(
|
||||||
initialValue: nameConflictStrategy,
|
initialValue: nameConflictStrategy,
|
||||||
options: Map.fromEntries(NameConflictStrategy.values.map((v) => MapEntry(v, v.getName(context)))),
|
options: Map.fromEntries(NameConflictStrategy.values.map((v) => MapEntry(v, v.getName(context)))),
|
||||||
message: originAlbums.length == 1 ? l10n.nameConflictDialogSingleSourceMessage : l10n.nameConflictDialogMultipleSourceMessage,
|
message: originAlbums.length == 1 ? l10n.nameConflictDialogSingleSourceMessage : l10n.nameConflictDialogMultipleSourceMessage,
|
||||||
confirmationButtonLabel: l10n.continueButtonLabel,
|
confirmationButtonLabel: l10n.continueButtonLabel,
|
||||||
),
|
),
|
||||||
routeSettings: const RouteSettings(name: AvesSelectionDialog.routeName),
|
routeSettings: const RouteSettings(name: AvesSingleSelectionDialog.routeName),
|
||||||
);
|
);
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
nameConflictStrategy = value;
|
nameConflictStrategy = value;
|
||||||
|
|
|
@ -21,7 +21,8 @@ import 'package:aves/widgets/common/map/buttons/panel.dart';
|
||||||
import 'package:aves/widgets/common/map/decorator.dart';
|
import 'package:aves/widgets/common/map/decorator.dart';
|
||||||
import 'package:aves/widgets/common/map/leaflet/map.dart';
|
import 'package:aves/widgets/common/map/leaflet/map.dart';
|
||||||
import 'package:aves/widgets/common/thumbnail/image.dart';
|
import 'package:aves/widgets/common/thumbnail/image.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
import 'package:aves/widgets/dialogs/selection_dialogs/common.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/single_selection.dart';
|
||||||
import 'package:aves_map/aves_map.dart';
|
import 'package:aves_map/aves_map.dart';
|
||||||
import 'package:aves_utils/aves_utils.dart';
|
import 'package:aves_utils/aves_utils.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
@ -212,7 +213,7 @@ class _GeoMapState extends State<GeoMap> {
|
||||||
child: OverlayTextButton(
|
child: OverlayTextButton(
|
||||||
onPressed: () => showSelectionDialog<EntryMapStyle>(
|
onPressed: () => showSelectionDialog<EntryMapStyle>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AvesSelectionDialog<EntryMapStyle?>(
|
builder: (context) => AvesSingleSelectionDialog<EntryMapStyle?>(
|
||||||
initialValue: settings.mapStyle,
|
initialValue: settings.mapStyle,
|
||||||
options: Map.fromEntries(availability.mapStyles.map((v) => MapEntry(v, v.getName(context)))),
|
options: Map.fromEntries(availability.mapStyles.map((v) => MapEntry(v, v.getName(context)))),
|
||||||
title: context.l10n.mapStyleDialogTitle,
|
title: context.l10n.mapStyleDialogTitle,
|
||||||
|
|
|
@ -3,7 +3,8 @@ import 'package:aves/model/settings/enums/l10n.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
import 'package:aves/widgets/dialogs/selection_dialogs/common.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/single_selection.dart';
|
||||||
import 'package:aves_map/aves_map.dart';
|
import 'package:aves_map/aves_map.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
@ -18,7 +19,7 @@ class MapActionDelegate {
|
||||||
case MapAction.selectStyle:
|
case MapAction.selectStyle:
|
||||||
showSelectionDialog<EntryMapStyle>(
|
showSelectionDialog<EntryMapStyle>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AvesSelectionDialog<EntryMapStyle?>(
|
builder: (context) => AvesSingleSelectionDialog<EntryMapStyle?>(
|
||||||
initialValue: settings.mapStyle,
|
initialValue: settings.mapStyle,
|
||||||
options: Map.fromEntries(availability.mapStyles.map((v) => MapEntry(v, v.getName(context)))),
|
options: Map.fromEntries(availability.mapStyles.map((v) => MapEntry(v, v.getName(context)))),
|
||||||
title: context.l10n.mapStyleDialogTitle,
|
title: context.l10n.mapStyleDialogTitle,
|
||||||
|
|
|
@ -1,154 +0,0 @@
|
||||||
import 'package:aves/theme/durations.dart';
|
|
||||||
import 'package:aves/widgets/common/basic/list_tiles/reselectable_radio.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/scheduler.dart';
|
|
||||||
|
|
||||||
import 'aves_dialog.dart';
|
|
||||||
|
|
||||||
Future<void> showSelectionDialog<T>({
|
|
||||||
required BuildContext context,
|
|
||||||
required WidgetBuilder builder,
|
|
||||||
required void Function(T value) onSelection,
|
|
||||||
}) async {
|
|
||||||
final value = await showDialog<T>(
|
|
||||||
context: context,
|
|
||||||
builder: builder,
|
|
||||||
routeSettings: const RouteSettings(name: AvesSelectionDialog.routeName),
|
|
||||||
);
|
|
||||||
// wait for the dialog to hide as applying the change may block the UI
|
|
||||||
await Future.delayed(Durations.dialogTransitionAnimation * timeDilation);
|
|
||||||
if (value != null) {
|
|
||||||
onSelection(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef TextBuilder<T> = String Function(T value);
|
|
||||||
|
|
||||||
class AvesSelectionDialog<T> extends StatefulWidget {
|
|
||||||
static const routeName = '/dialog/selection';
|
|
||||||
|
|
||||||
final T initialValue;
|
|
||||||
final Map<T, String> options;
|
|
||||||
final TextBuilder<T>? optionSubtitleBuilder;
|
|
||||||
final String? title, message, confirmationButtonLabel;
|
|
||||||
final bool? dense;
|
|
||||||
|
|
||||||
const AvesSelectionDialog({
|
|
||||||
super.key,
|
|
||||||
required this.initialValue,
|
|
||||||
required this.options,
|
|
||||||
this.optionSubtitleBuilder,
|
|
||||||
this.title,
|
|
||||||
this.message,
|
|
||||||
this.confirmationButtonLabel,
|
|
||||||
this.dense,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<AvesSelectionDialog<T>> createState() => _AvesSelectionDialogState<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AvesSelectionDialogState<T> extends State<AvesSelectionDialog<T>> {
|
|
||||||
late T _selectedValue;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_selectedValue = widget.initialValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final title = widget.title;
|
|
||||||
final message = widget.message;
|
|
||||||
final verticalPadding = (title == null && message == null) ? AvesDialog.cornerRadius.y / 2 : .0;
|
|
||||||
final confirmationButtonLabel = widget.confirmationButtonLabel;
|
|
||||||
final needConfirmation = confirmationButtonLabel != null;
|
|
||||||
return AvesDialog(
|
|
||||||
title: title,
|
|
||||||
scrollableContent: [
|
|
||||||
if (verticalPadding != 0) SizedBox(height: verticalPadding),
|
|
||||||
if (message != null)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Text(message),
|
|
||||||
),
|
|
||||||
...widget.options.entries.map((kv) {
|
|
||||||
final radioValue = kv.key;
|
|
||||||
final radioTitle = kv.value;
|
|
||||||
return SelectionRadioListTile(
|
|
||||||
value: radioValue,
|
|
||||||
title: radioTitle,
|
|
||||||
optionSubtitleBuilder: widget.optionSubtitleBuilder,
|
|
||||||
needConfirmation: needConfirmation,
|
|
||||||
dense: widget.dense,
|
|
||||||
getGroupValue: () => _selectedValue,
|
|
||||||
setGroupValue: (v) => setState(() => _selectedValue = v),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
if (verticalPadding != 0) SizedBox(height: verticalPadding),
|
|
||||||
],
|
|
||||||
actions: [
|
|
||||||
const CancelButton(),
|
|
||||||
if (needConfirmation)
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.maybeOf(context)?.pop(_selectedValue),
|
|
||||||
child: Text(confirmationButtonLabel),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SelectionRadioListTile<T> extends StatelessWidget {
|
|
||||||
final T value;
|
|
||||||
final String title;
|
|
||||||
final TextBuilder<T>? optionSubtitleBuilder;
|
|
||||||
final bool needConfirmation;
|
|
||||||
final bool? dense;
|
|
||||||
final T Function() getGroupValue;
|
|
||||||
final void Function(T value) setGroupValue;
|
|
||||||
|
|
||||||
const SelectionRadioListTile({
|
|
||||||
super.key,
|
|
||||||
required this.value,
|
|
||||||
required this.title,
|
|
||||||
this.optionSubtitleBuilder,
|
|
||||||
required this.needConfirmation,
|
|
||||||
this.dense,
|
|
||||||
required this.getGroupValue,
|
|
||||||
required this.setGroupValue,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final subtitle = optionSubtitleBuilder?.call(value);
|
|
||||||
return ReselectableRadioListTile<T>(
|
|
||||||
// key is expected by test driver
|
|
||||||
key: Key('$value'),
|
|
||||||
value: value,
|
|
||||||
groupValue: getGroupValue(),
|
|
||||||
onChanged: (v) {
|
|
||||||
if (needConfirmation) {
|
|
||||||
setGroupValue(v as T);
|
|
||||||
} else {
|
|
||||||
Navigator.maybeOf(context)?.pop(v);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
reselectable: true,
|
|
||||||
title: Text(
|
|
||||||
title,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 2,
|
|
||||||
),
|
|
||||||
subtitle: subtitle != null
|
|
||||||
? Text(
|
|
||||||
subtitle,
|
|
||||||
softWrap: false,
|
|
||||||
overflow: TextOverflow.fade,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
dense: dense,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,7 +9,8 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/identity/aves_caption.dart';
|
import 'package:aves/widgets/common/identity/aves_caption.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_confirmation_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_confirmation_dialog.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
import 'package:aves/widgets/dialogs/selection_dialogs/common.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/single_selection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ class _EditVaultDialogState extends State<EditVaultDialog> {
|
||||||
_unfocus();
|
_unfocus();
|
||||||
showSelectionDialog<VaultLockType>(
|
showSelectionDialog<VaultLockType>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AvesSelectionDialog<VaultLockType>(
|
builder: (context) => AvesSingleSelectionDialog<VaultLockType>(
|
||||||
initialValue: _lockType,
|
initialValue: _lockType,
|
||||||
options: Map.fromEntries(_lockTypeOptions.map((v) => MapEntry(v, v.getText(context)))),
|
options: Map.fromEntries(_lockTypeOptions.map((v) => MapEntry(v, v.getText(context)))),
|
||||||
),
|
),
|
||||||
|
|
23
lib/widgets/dialogs/selection_dialogs/common.dart
Normal file
23
lib/widgets/dialogs/selection_dialogs/common.dart
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import 'package:aves/theme/durations.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/single_selection.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/scheduler.dart';
|
||||||
|
|
||||||
|
Future<void> showSelectionDialog<T>({
|
||||||
|
required BuildContext context,
|
||||||
|
required WidgetBuilder builder,
|
||||||
|
required void Function(T value) onSelection,
|
||||||
|
}) async {
|
||||||
|
final value = await showDialog<T>(
|
||||||
|
context: context,
|
||||||
|
builder: builder,
|
||||||
|
routeSettings: const RouteSettings(name: AvesSingleSelectionDialog.routeName),
|
||||||
|
);
|
||||||
|
// wait for the dialog to hide as applying the change may block the UI
|
||||||
|
await Future.delayed(Durations.dialogTransitionAnimation * timeDilation);
|
||||||
|
if (value != null) {
|
||||||
|
onSelection(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef TextBuilder<T> = String Function(T value);
|
91
lib/widgets/dialogs/selection_dialogs/multi_selection.dart
Normal file
91
lib/widgets/dialogs/selection_dialogs/multi_selection.dart
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/common.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AvesMultiSelectionDialog<T> extends StatefulWidget {
|
||||||
|
static const routeName = '/dialog/multi_selection';
|
||||||
|
|
||||||
|
final Set<T> initialValue;
|
||||||
|
final Map<T, String> options;
|
||||||
|
final TextBuilder<T>? optionSubtitleBuilder;
|
||||||
|
final String? title, message;
|
||||||
|
final bool? dense;
|
||||||
|
|
||||||
|
const AvesMultiSelectionDialog({
|
||||||
|
super.key,
|
||||||
|
required this.initialValue,
|
||||||
|
required this.options,
|
||||||
|
this.optionSubtitleBuilder,
|
||||||
|
this.title,
|
||||||
|
this.message,
|
||||||
|
this.dense,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AvesMultiSelectionDialog<T>> createState() => _AvesMultiSelectionDialogState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AvesMultiSelectionDialogState<T> extends State<AvesMultiSelectionDialog<T>> {
|
||||||
|
late Set<T> _selectedValues;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_selectedValues = widget.initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final title = widget.title;
|
||||||
|
final message = widget.message;
|
||||||
|
final verticalPadding = (title == null && message == null) ? AvesDialog.cornerRadius.y / 2 : .0;
|
||||||
|
return AvesDialog(
|
||||||
|
title: title,
|
||||||
|
scrollableContent: [
|
||||||
|
if (verticalPadding != 0) SizedBox(height: verticalPadding),
|
||||||
|
if (message != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Text(message),
|
||||||
|
),
|
||||||
|
...widget.options.entries.map((kv) {
|
||||||
|
final value = kv.key;
|
||||||
|
final title = kv.value;
|
||||||
|
final subtitle = widget.optionSubtitleBuilder?.call(value);
|
||||||
|
return SwitchListTile(
|
||||||
|
value: _selectedValues.contains(value),
|
||||||
|
onChanged: (v) {
|
||||||
|
if (v) {
|
||||||
|
_selectedValues.add(value);
|
||||||
|
} else {
|
||||||
|
_selectedValues.remove(value);
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
title: Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(title),
|
||||||
|
),
|
||||||
|
subtitle: subtitle != null
|
||||||
|
? Text(
|
||||||
|
subtitle,
|
||||||
|
softWrap: false,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
dense: widget.dense,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
if (verticalPadding != 0) SizedBox(height: verticalPadding),
|
||||||
|
],
|
||||||
|
actions: [
|
||||||
|
const CancelButton(),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.maybeOf(context)?.pop(widget.options.keys.where(_selectedValues.contains).toList()),
|
||||||
|
child: Text(context.l10n.applyButtonLabel),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
56
lib/widgets/dialogs/selection_dialogs/radio_list_tile.dart
Normal file
56
lib/widgets/dialogs/selection_dialogs/radio_list_tile.dart
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import 'package:aves/widgets/common/basic/list_tiles/reselectable_radio.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/common.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SelectionRadioListTile<T> extends StatelessWidget {
|
||||||
|
final T value;
|
||||||
|
final String title;
|
||||||
|
final TextBuilder<T>? optionSubtitleBuilder;
|
||||||
|
final bool needConfirmation;
|
||||||
|
final bool? dense;
|
||||||
|
final T Function() getGroupValue;
|
||||||
|
final void Function(T value) setGroupValue;
|
||||||
|
|
||||||
|
const SelectionRadioListTile({
|
||||||
|
super.key,
|
||||||
|
required this.value,
|
||||||
|
required this.title,
|
||||||
|
this.optionSubtitleBuilder,
|
||||||
|
required this.needConfirmation,
|
||||||
|
this.dense,
|
||||||
|
required this.getGroupValue,
|
||||||
|
required this.setGroupValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final subtitle = optionSubtitleBuilder?.call(value);
|
||||||
|
return ReselectableRadioListTile<T>(
|
||||||
|
// key is expected by test driver
|
||||||
|
key: Key('$value'),
|
||||||
|
value: value,
|
||||||
|
groupValue: getGroupValue(),
|
||||||
|
onChanged: (v) {
|
||||||
|
if (needConfirmation) {
|
||||||
|
setGroupValue(v as T);
|
||||||
|
} else {
|
||||||
|
Navigator.maybeOf(context)?.pop(v);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reselectable: true,
|
||||||
|
title: Text(
|
||||||
|
title,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 2,
|
||||||
|
),
|
||||||
|
subtitle: subtitle != null
|
||||||
|
? Text(
|
||||||
|
subtitle,
|
||||||
|
softWrap: false,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
dense: dense,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
80
lib/widgets/dialogs/selection_dialogs/single_selection.dart
Normal file
80
lib/widgets/dialogs/selection_dialogs/single_selection.dart
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/common.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/radio_list_tile.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AvesSingleSelectionDialog<T> extends StatefulWidget {
|
||||||
|
static const routeName = '/dialog/selection';
|
||||||
|
|
||||||
|
final T initialValue;
|
||||||
|
final Map<T, String> options;
|
||||||
|
final TextBuilder<T>? optionSubtitleBuilder;
|
||||||
|
final String? title, message, confirmationButtonLabel;
|
||||||
|
final bool? dense;
|
||||||
|
|
||||||
|
const AvesSingleSelectionDialog({
|
||||||
|
super.key,
|
||||||
|
required this.initialValue,
|
||||||
|
required this.options,
|
||||||
|
this.optionSubtitleBuilder,
|
||||||
|
this.title,
|
||||||
|
this.message,
|
||||||
|
this.confirmationButtonLabel,
|
||||||
|
this.dense,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AvesSingleSelectionDialog<T>> createState() => _AvesSingleSelectionDialogState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AvesSingleSelectionDialogState<T> extends State<AvesSingleSelectionDialog<T>> {
|
||||||
|
late T _selectedValue;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_selectedValue = widget.initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final title = widget.title;
|
||||||
|
final message = widget.message;
|
||||||
|
final verticalPadding = (title == null && message == null) ? AvesDialog.cornerRadius.y / 2 : .0;
|
||||||
|
final confirmationButtonLabel = widget.confirmationButtonLabel;
|
||||||
|
final needConfirmation = confirmationButtonLabel != null;
|
||||||
|
return AvesDialog(
|
||||||
|
title: title,
|
||||||
|
scrollableContent: [
|
||||||
|
if (verticalPadding != 0) SizedBox(height: verticalPadding),
|
||||||
|
if (message != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Text(message),
|
||||||
|
),
|
||||||
|
...widget.options.entries.map((kv) {
|
||||||
|
final radioValue = kv.key;
|
||||||
|
final radioTitle = kv.value;
|
||||||
|
return SelectionRadioListTile(
|
||||||
|
value: radioValue,
|
||||||
|
title: radioTitle,
|
||||||
|
optionSubtitleBuilder: widget.optionSubtitleBuilder,
|
||||||
|
needConfirmation: needConfirmation,
|
||||||
|
dense: widget.dense,
|
||||||
|
getGroupValue: () => _selectedValue,
|
||||||
|
setGroupValue: (v) => setState(() => _selectedValue = v),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
if (verticalPadding != 0) SizedBox(height: verticalPadding),
|
||||||
|
],
|
||||||
|
actions: [
|
||||||
|
const CancelButton(),
|
||||||
|
if (needConfirmation)
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.maybeOf(context)?.pop(_selectedValue),
|
||||||
|
child: Text(confirmationButtonLabel),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
import 'package:aves/model/device.dart';
|
import 'package:aves/model/device.dart';
|
||||||
import 'package:aves/model/wallpaper_target.dart';
|
import 'package:aves/model/wallpaper_target.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/radio_list_tile.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
import 'aves_dialog.dart';
|
|
||||||
|
|
||||||
class WallpaperSettingsDialog extends StatefulWidget {
|
class WallpaperSettingsDialog extends StatefulWidget {
|
||||||
static const routeName = '/dialog/wallpaper_settings';
|
static const routeName = '/dialog/wallpaper_settings';
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
|
import 'package:aves/utils/constants.dart';
|
||||||
import 'package:aves/utils/time_utils.dart';
|
import 'package:aves/utils/time_utils.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/identity/aves_caption.dart';
|
import 'package:aves/widgets/common/identity/aves_caption.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_selection_dialog.dart';
|
|
||||||
import 'package:aves/widgets/dialogs/duration_dialog.dart';
|
import 'package:aves/widgets/dialogs/duration_dialog.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/common.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/multi_selection.dart';
|
||||||
|
import 'package:aves/widgets/dialogs/selection_dialogs/single_selection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
@ -82,7 +85,7 @@ class SettingsSwitchListTile extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsSelectionListTile<T extends Enum> extends StatelessWidget {
|
class SettingsSelectionListTile<T> extends StatelessWidget {
|
||||||
final List<T> values;
|
final List<T> values;
|
||||||
final String Function(BuildContext, T) getName;
|
final String Function(BuildContext, T) getName;
|
||||||
final T Function(BuildContext, Settings) selector;
|
final T Function(BuildContext, Settings) selector;
|
||||||
|
@ -123,7 +126,7 @@ class SettingsSelectionListTile<T extends Enum> extends StatelessWidget {
|
||||||
subtitle: AvesCaption(getName(context, current)),
|
subtitle: AvesCaption(getName(context, current)),
|
||||||
onTap: () => showSelectionDialog<T>(
|
onTap: () => showSelectionDialog<T>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AvesSelectionDialog<T>(
|
builder: (context) => AvesSingleSelectionDialog<T>(
|
||||||
initialValue: current,
|
initialValue: current,
|
||||||
options: Map.fromEntries(values.map((v) => MapEntry(v, getName(context, v)))),
|
options: Map.fromEntries(values.map((v) => MapEntry(v, getName(context, v)))),
|
||||||
optionSubtitleBuilder: optionSubtitleBuilder,
|
optionSubtitleBuilder: optionSubtitleBuilder,
|
||||||
|
@ -137,6 +140,62 @@ class SettingsSelectionListTile<T extends Enum> extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SettingsMultiSelectionListTile<T> extends StatelessWidget {
|
||||||
|
final List<T> values;
|
||||||
|
final String Function(BuildContext, T) getName;
|
||||||
|
final List<T> Function(BuildContext, Settings) selector;
|
||||||
|
final ValueChanged<List<T>> onSelection;
|
||||||
|
final String tileTitle, noneSubtitle;
|
||||||
|
final WidgetBuilder? trailingBuilder;
|
||||||
|
final String? dialogTitle;
|
||||||
|
final TextBuilder<T>? optionSubtitleBuilder;
|
||||||
|
|
||||||
|
const SettingsMultiSelectionListTile({
|
||||||
|
super.key,
|
||||||
|
required this.values,
|
||||||
|
required this.getName,
|
||||||
|
required this.selector,
|
||||||
|
required this.onSelection,
|
||||||
|
required this.tileTitle,
|
||||||
|
required this.noneSubtitle,
|
||||||
|
this.trailingBuilder,
|
||||||
|
this.dialogTitle,
|
||||||
|
this.optionSubtitleBuilder,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Selector<Settings, List<T>>(
|
||||||
|
selector: selector,
|
||||||
|
builder: (context, current, child) {
|
||||||
|
Widget titleWidget = Text(tileTitle);
|
||||||
|
if (trailingBuilder != null) {
|
||||||
|
titleWidget = Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: titleWidget),
|
||||||
|
trailingBuilder!(context),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ListTile(
|
||||||
|
title: titleWidget,
|
||||||
|
subtitle: AvesCaption(current.isEmpty ? noneSubtitle : current.map((v) => getName(context, v)).join(Constants.separator)),
|
||||||
|
onTap: () => showSelectionDialog<List<T>>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AvesMultiSelectionDialog<T>(
|
||||||
|
initialValue: current.toSet(),
|
||||||
|
options: Map.fromEntries(values.map((v) => MapEntry(v, getName(context, v)))),
|
||||||
|
optionSubtitleBuilder: optionSubtitleBuilder,
|
||||||
|
title: dialogTitle,
|
||||||
|
),
|
||||||
|
onSelection: onSelection,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SettingsDurationListTile extends StatelessWidget {
|
class SettingsDurationListTile extends StatelessWidget {
|
||||||
final int Function(BuildContext, Settings) selector;
|
final int Function(BuildContext, Settings) selector;
|
||||||
final ValueChanged<int> onChanged;
|
final ValueChanged<int> onChanged;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
import 'package:aves/ref/bursts.dart';
|
||||||
import 'package:aves/theme/colors.dart';
|
import 'package:aves/theme/colors.dart';
|
||||||
import 'package:aves/theme/icons.dart';
|
import 'package:aves/theme/icons.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
@ -27,6 +28,7 @@ class ThumbnailsSection extends SettingsSection {
|
||||||
List<SettingsTile> tiles(BuildContext context) => [
|
List<SettingsTile> tiles(BuildContext context) => [
|
||||||
if (!settings.useTvLayout) SettingsTileCollectionQuickActions(),
|
if (!settings.useTvLayout) SettingsTileCollectionQuickActions(),
|
||||||
SettingsTileThumbnailOverlay(),
|
SettingsTileThumbnailOverlay(),
|
||||||
|
SettingsTileBurstPatterns(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,3 +55,19 @@ class SettingsTileThumbnailOverlay extends SettingsTile {
|
||||||
builder: (context) => const ThumbnailOverlayPage(),
|
builder: (context) => const ThumbnailOverlayPage(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SettingsTileBurstPatterns extends SettingsTile {
|
||||||
|
@override
|
||||||
|
String title(BuildContext context) => context.l10n.settingsCollectionBurstPatternsTile;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => SettingsMultiSelectionListTile<String>(
|
||||||
|
values: BurstPatterns.options,
|
||||||
|
getName: (context, v) => BurstPatterns.getName(v),
|
||||||
|
selector: (context, s) => s.collectionBurstPatterns,
|
||||||
|
onSelection: (v) => settings.collectionBurstPatterns = v,
|
||||||
|
tileTitle: title(context),
|
||||||
|
noneSubtitle: context.l10n.settingsCollectionBurstPatternsNone,
|
||||||
|
optionSubtitleBuilder: BurstPatterns.getExample,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -443,6 +443,8 @@
|
||||||
"settingsCollectionQuickActionTabSelecting",
|
"settingsCollectionQuickActionTabSelecting",
|
||||||
"settingsCollectionBrowsingQuickActionEditorBanner",
|
"settingsCollectionBrowsingQuickActionEditorBanner",
|
||||||
"settingsCollectionSelectionQuickActionEditorBanner",
|
"settingsCollectionSelectionQuickActionEditorBanner",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerSectionTitle",
|
"settingsViewerSectionTitle",
|
||||||
"settingsViewerGestureSideTapNext",
|
"settingsViewerGestureSideTapNext",
|
||||||
"settingsViewerUseCutout",
|
"settingsViewerUseCutout",
|
||||||
|
@ -1001,6 +1003,8 @@
|
||||||
"settingsCollectionQuickActionTabSelecting",
|
"settingsCollectionQuickActionTabSelecting",
|
||||||
"settingsCollectionBrowsingQuickActionEditorBanner",
|
"settingsCollectionBrowsingQuickActionEditorBanner",
|
||||||
"settingsCollectionSelectionQuickActionEditorBanner",
|
"settingsCollectionSelectionQuickActionEditorBanner",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerSectionTitle",
|
"settingsViewerSectionTitle",
|
||||||
"settingsViewerGestureSideTapNext",
|
"settingsViewerGestureSideTapNext",
|
||||||
"settingsViewerUseCutout",
|
"settingsViewerUseCutout",
|
||||||
|
@ -1176,6 +1180,8 @@
|
||||||
|
|
||||||
"cs": [
|
"cs": [
|
||||||
"settingsVideoEnablePip",
|
"settingsVideoEnablePip",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle"
|
"settingsVideoBackgroundModeDialogTitle"
|
||||||
],
|
],
|
||||||
|
@ -1211,6 +1217,8 @@
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
"settingsConfirmationVaultDataLoss",
|
"settingsConfirmationVaultDataLoss",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle",
|
"settingsVideoBackgroundModeDialogTitle",
|
||||||
"settingsDisablingBinWarningDialogMessage"
|
"settingsDisablingBinWarningDialogMessage"
|
||||||
|
@ -1226,10 +1234,22 @@
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle"
|
"settingsVideoBackgroundModeDialogTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"es": [
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone"
|
||||||
|
],
|
||||||
|
|
||||||
|
"eu": [
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone"
|
||||||
|
],
|
||||||
|
|
||||||
"fa": [
|
"fa": [
|
||||||
"clearTooltip",
|
"clearTooltip",
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
|
@ -1533,6 +1553,8 @@
|
||||||
"settingsCollectionQuickActionTabSelecting",
|
"settingsCollectionQuickActionTabSelecting",
|
||||||
"settingsCollectionBrowsingQuickActionEditorBanner",
|
"settingsCollectionBrowsingQuickActionEditorBanner",
|
||||||
"settingsCollectionSelectionQuickActionEditorBanner",
|
"settingsCollectionSelectionQuickActionEditorBanner",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerSectionTitle",
|
"settingsViewerSectionTitle",
|
||||||
"settingsViewerGestureSideTapNext",
|
"settingsViewerGestureSideTapNext",
|
||||||
"settingsViewerUseCutout",
|
"settingsViewerUseCutout",
|
||||||
|
@ -1704,6 +1726,11 @@
|
||||||
"filePickerUseThisFolder"
|
"filePickerUseThisFolder"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"fr": [
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone"
|
||||||
|
],
|
||||||
|
|
||||||
"gl": [
|
"gl": [
|
||||||
"columnCount",
|
"columnCount",
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
|
@ -2037,6 +2064,8 @@
|
||||||
"settingsCollectionQuickActionTabSelecting",
|
"settingsCollectionQuickActionTabSelecting",
|
||||||
"settingsCollectionBrowsingQuickActionEditorBanner",
|
"settingsCollectionBrowsingQuickActionEditorBanner",
|
||||||
"settingsCollectionSelectionQuickActionEditorBanner",
|
"settingsCollectionSelectionQuickActionEditorBanner",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerSectionTitle",
|
"settingsViewerSectionTitle",
|
||||||
"settingsViewerGestureSideTapNext",
|
"settingsViewerGestureSideTapNext",
|
||||||
"settingsViewerUseCutout",
|
"settingsViewerUseCutout",
|
||||||
|
@ -2672,6 +2701,8 @@
|
||||||
"settingsCollectionQuickActionTabSelecting",
|
"settingsCollectionQuickActionTabSelecting",
|
||||||
"settingsCollectionBrowsingQuickActionEditorBanner",
|
"settingsCollectionBrowsingQuickActionEditorBanner",
|
||||||
"settingsCollectionSelectionQuickActionEditorBanner",
|
"settingsCollectionSelectionQuickActionEditorBanner",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerSectionTitle",
|
"settingsViewerSectionTitle",
|
||||||
"settingsViewerGestureSideTapNext",
|
"settingsViewerGestureSideTapNext",
|
||||||
"settingsViewerUseCutout",
|
"settingsViewerUseCutout",
|
||||||
|
@ -2845,6 +2876,11 @@
|
||||||
"filePickerUseThisFolder"
|
"filePickerUseThisFolder"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"id": [
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone"
|
||||||
|
],
|
||||||
|
|
||||||
"it": [
|
"it": [
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
"lengthUnitPixel",
|
"lengthUnitPixel",
|
||||||
|
@ -2857,6 +2893,8 @@
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle"
|
"settingsVideoBackgroundModeDialogTitle"
|
||||||
],
|
],
|
||||||
|
@ -2904,6 +2942,8 @@
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
"settingsModificationWarningDialogMessage",
|
"settingsModificationWarningDialogMessage",
|
||||||
"settingsConfirmationVaultDataLoss",
|
"settingsConfirmationVaultDataLoss",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerShowDescription",
|
"settingsViewerShowDescription",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle",
|
"settingsVideoBackgroundModeDialogTitle",
|
||||||
|
@ -2914,6 +2954,11 @@
|
||||||
"settingsWidgetDisplayedItem"
|
"settingsWidgetDisplayedItem"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"ko": [
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone"
|
||||||
|
],
|
||||||
|
|
||||||
"lt": [
|
"lt": [
|
||||||
"columnCount",
|
"columnCount",
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
|
@ -2951,6 +2996,8 @@
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
"settingsModificationWarningDialogMessage",
|
"settingsModificationWarningDialogMessage",
|
||||||
"settingsConfirmationVaultDataLoss",
|
"settingsConfirmationVaultDataLoss",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerShowDescription",
|
"settingsViewerShowDescription",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle",
|
"settingsVideoBackgroundModeDialogTitle",
|
||||||
|
@ -2965,6 +3012,8 @@
|
||||||
"settingsVideoEnablePip",
|
"settingsVideoEnablePip",
|
||||||
"patternDialogEnter",
|
"patternDialogEnter",
|
||||||
"patternDialogConfirm",
|
"patternDialogConfirm",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle"
|
"settingsVideoBackgroundModeDialogTitle"
|
||||||
],
|
],
|
||||||
|
@ -3017,6 +3066,8 @@
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
"settingsModificationWarningDialogMessage",
|
"settingsModificationWarningDialogMessage",
|
||||||
"settingsConfirmationVaultDataLoss",
|
"settingsConfirmationVaultDataLoss",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerShowRatingTags",
|
"settingsViewerShowRatingTags",
|
||||||
"settingsViewerShowDescription",
|
"settingsViewerShowDescription",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
|
@ -3241,6 +3292,8 @@
|
||||||
"settingsCollectionQuickActionTabSelecting",
|
"settingsCollectionQuickActionTabSelecting",
|
||||||
"settingsCollectionBrowsingQuickActionEditorBanner",
|
"settingsCollectionBrowsingQuickActionEditorBanner",
|
||||||
"settingsCollectionSelectionQuickActionEditorBanner",
|
"settingsCollectionSelectionQuickActionEditorBanner",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerSectionTitle",
|
"settingsViewerSectionTitle",
|
||||||
"settingsViewerGestureSideTapNext",
|
"settingsViewerGestureSideTapNext",
|
||||||
"settingsViewerUseCutout",
|
"settingsViewerUseCutout",
|
||||||
|
@ -3350,7 +3403,14 @@
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"pl": [
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone"
|
||||||
|
],
|
||||||
|
|
||||||
"pt": [
|
"pt": [
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsVideoBackgroundModeDialogTitle"
|
"settingsVideoBackgroundModeDialogTitle"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -3366,6 +3426,8 @@
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle"
|
"settingsVideoBackgroundModeDialogTitle"
|
||||||
],
|
],
|
||||||
|
@ -3383,6 +3445,8 @@
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
"settingsConfirmationVaultDataLoss",
|
"settingsConfirmationVaultDataLoss",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle",
|
"settingsVideoBackgroundModeDialogTitle",
|
||||||
"settingsVideoGestureVerticalDragBrightnessVolume"
|
"settingsVideoGestureVerticalDragBrightnessVolume"
|
||||||
|
@ -3638,6 +3702,8 @@
|
||||||
"settingsCollectionQuickActionTabSelecting",
|
"settingsCollectionQuickActionTabSelecting",
|
||||||
"settingsCollectionBrowsingQuickActionEditorBanner",
|
"settingsCollectionBrowsingQuickActionEditorBanner",
|
||||||
"settingsCollectionSelectionQuickActionEditorBanner",
|
"settingsCollectionSelectionQuickActionEditorBanner",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerSectionTitle",
|
"settingsViewerSectionTitle",
|
||||||
"settingsViewerGestureSideTapNext",
|
"settingsViewerGestureSideTapNext",
|
||||||
"settingsViewerUseCutout",
|
"settingsViewerUseCutout",
|
||||||
|
@ -3996,6 +4062,8 @@
|
||||||
"settingsCollectionQuickActionTabSelecting",
|
"settingsCollectionQuickActionTabSelecting",
|
||||||
"settingsCollectionBrowsingQuickActionEditorBanner",
|
"settingsCollectionBrowsingQuickActionEditorBanner",
|
||||||
"settingsCollectionSelectionQuickActionEditorBanner",
|
"settingsCollectionSelectionQuickActionEditorBanner",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerSectionTitle",
|
"settingsViewerSectionTitle",
|
||||||
"settingsViewerGestureSideTapNext",
|
"settingsViewerGestureSideTapNext",
|
||||||
"settingsViewerUseCutout",
|
"settingsViewerUseCutout",
|
||||||
|
@ -4200,11 +4268,18 @@
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
"settingsConfirmationVaultDataLoss",
|
"settingsConfirmationVaultDataLoss",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle",
|
"settingsVideoBackgroundModeDialogTitle",
|
||||||
"settingsDisablingBinWarningDialogMessage"
|
"settingsDisablingBinWarningDialogMessage"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"uk": [
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone"
|
||||||
|
],
|
||||||
|
|
||||||
"zh": [
|
"zh": [
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
"chipActionLock",
|
"chipActionLock",
|
||||||
|
@ -4240,6 +4315,8 @@
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
"settingsModificationWarningDialogMessage",
|
"settingsModificationWarningDialogMessage",
|
||||||
"settingsConfirmationVaultDataLoss",
|
"settingsConfirmationVaultDataLoss",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerShowDescription",
|
"settingsViewerShowDescription",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle",
|
"settingsVideoBackgroundModeDialogTitle",
|
||||||
|
@ -4285,6 +4362,8 @@
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
"settingsModificationWarningDialogMessage",
|
"settingsModificationWarningDialogMessage",
|
||||||
"settingsConfirmationVaultDataLoss",
|
"settingsConfirmationVaultDataLoss",
|
||||||
|
"settingsCollectionBurstPatternsTile",
|
||||||
|
"settingsCollectionBurstPatternsNone",
|
||||||
"settingsViewerShowDescription",
|
"settingsViewerShowDescription",
|
||||||
"settingsVideoBackgroundMode",
|
"settingsVideoBackgroundMode",
|
||||||
"settingsVideoBackgroundModeDialogTitle",
|
"settingsVideoBackgroundModeDialogTitle",
|
||||||
|
|
Loading…
Reference in a new issue