diff --git a/CHANGELOG.md b/CHANGELOG.md index 89c02e39d..6ad529d83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file. - Viewer: allow zooming out small items to actual size - Collection: improved performance for sort/group by name - load previous top items on startup +- locale independent colors for known filters - upgraded Flutter to stable v2.10.1 ### Removed diff --git a/lib/model/actions/entry_actions.dart b/lib/model/actions/entry_actions.dart index e55748510..2528b62d7 100644 --- a/lib/model/actions/entry_actions.dart +++ b/lib/model/actions/entry_actions.dart @@ -1,5 +1,5 @@ +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/widgets.dart'; @@ -133,7 +133,7 @@ extension ExtraEntryAction on EntryAction { switch (this) { case EntryAction.debug: return ShaderMask( - shaderCallback: Themes.debugGradient.createShader, + shaderCallback: AColors.debugGradient.createShader, child: child, ); default: diff --git a/lib/model/actions/entry_info_actions.dart b/lib/model/actions/entry_info_actions.dart index 0105d2ce4..1498c8066 100644 --- a/lib/model/actions/entry_info_actions.dart +++ b/lib/model/actions/entry_info_actions.dart @@ -1,5 +1,5 @@ +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/widgets.dart'; @@ -55,7 +55,7 @@ extension ExtraEntryInfoAction on EntryInfoAction { switch (this) { case EntryInfoAction.debug: return ShaderMask( - shaderCallback: Themes.debugGradient.createShader, + shaderCallback: AColors.debugGradient.createShader, child: child, ); default: diff --git a/lib/model/entry_metadata_edition.dart b/lib/model/entry_metadata_edition.dart index ad14a3923..65707f3ea 100644 --- a/lib/model/entry_metadata_edition.dart +++ b/lib/model/entry_metadata_edition.dart @@ -22,7 +22,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry { final appliedModifier = await _applyDateModifierToEntry(userModifier); if (appliedModifier == null) { - await reportService.recordError('failed to get date for modifier=$userModifier, uri=$uri', null); + await reportService.recordError('failed to get date for modifier=$userModifier, entry=$this', null); return {}; } diff --git a/lib/model/filters/favourite.dart b/lib/model/filters/favourite.dart index 9805fdfc2..afa6116a6 100644 --- a/lib/model/filters/favourite.dart +++ b/lib/model/filters/favourite.dart @@ -1,4 +1,5 @@ import 'package:aves/model/filters/filters.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/foundation.dart'; @@ -32,7 +33,7 @@ class FavouriteFilter extends CollectionFilter { Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.favourite, size: size); @override - Future color(BuildContext context) => SynchronousFuture(Colors.red); + Future color(BuildContext context) => SynchronousFuture(AColors.favourite); @override String get category => type; diff --git a/lib/model/filters/mime.dart b/lib/model/filters/mime.dart index 0eab98db4..9bdc1c753 100644 --- a/lib/model/filters/mime.dart +++ b/lib/model/filters/mime.dart @@ -1,8 +1,11 @@ import 'package:aves/model/filters/filters.dart'; import 'package:aves/ref/mime_types.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; +import 'package:aves/utils/color_utils.dart'; import 'package:aves/utils/mime_utils.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; class MimeFilter extends CollectionFilter { @@ -12,6 +15,7 @@ class MimeFilter extends CollectionFilter { late final EntryFilter _test; late final String _label; late final IconData _icon; + late final Color _color; static final image = MimeFilter(MimeTypes.anyImage); static final video = MimeFilter(MimeTypes.anyVideo); @@ -21,6 +25,7 @@ class MimeFilter extends CollectionFilter { MimeFilter(this.mime) { IconData? icon; + Color? color; var lowMime = mime.toLowerCase(); if (lowMime.endsWith('/*')) { lowMime = lowMime.substring(0, lowMime.length - 2); @@ -28,14 +33,17 @@ class MimeFilter extends CollectionFilter { _label = lowMime.toUpperCase(); if (mime == MimeTypes.anyImage) { icon = AIcons.image; + color = AColors.image; } else if (mime == MimeTypes.anyVideo) { icon = AIcons.video; + color = AColors.video; } } else { _test = (entry) => entry.mimeType == lowMime; _label = MimeUtils.displayType(lowMime); } _icon = icon ?? AIcons.vector; + _color = color ?? stringToColor(_label); } MimeFilter.fromMap(Map json) @@ -70,6 +78,9 @@ class MimeFilter extends CollectionFilter { @override Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size); + @override + Future color(BuildContext context) => SynchronousFuture(_color); + @override String get category => type; diff --git a/lib/model/filters/type.dart b/lib/model/filters/type.dart index efc6420a2..1d2b986f0 100644 --- a/lib/model/filters/type.dart +++ b/lib/model/filters/type.dart @@ -1,6 +1,8 @@ import 'package:aves/model/filters/filters.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; class TypeFilter extends CollectionFilter { @@ -16,6 +18,7 @@ class TypeFilter extends CollectionFilter { final String itemType; late final EntryFilter _test; late final IconData _icon; + late final Color _color; static final animated = TypeFilter._private(_animated); static final geotiff = TypeFilter._private(_geotiff); @@ -32,26 +35,32 @@ class TypeFilter extends CollectionFilter { case _animated: _test = (entry) => entry.isAnimated; _icon = AIcons.animated; + _color = AColors.animated; break; case _geotiff: _test = (entry) => entry.isGeotiff; _icon = AIcons.geo; + _color = AColors.geotiff; break; case _motionPhoto: _test = (entry) => entry.isMotionPhoto; _icon = AIcons.motionPhoto; + _color = AColors.motionPhoto; break; case _panorama: _test = (entry) => entry.isImage && entry.is360; _icon = AIcons.threeSixty; + _color = AColors.panorama; break; case _raw: _test = (entry) => entry.isRaw; _icon = AIcons.raw; + _color = AColors.raw; break; case _sphericalVideo: _test = (entry) => entry.isVideo && entry.is360; _icon = AIcons.threeSixty; + _color = AColors.sphericalVideo; break; } } @@ -96,6 +105,9 @@ class TypeFilter extends CollectionFilter { @override Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size); + @override + Future color(BuildContext context) => SynchronousFuture(_color); + @override String get category => type; diff --git a/lib/model/source/album.dart b/lib/model/source/album.dart index 6fe52cc1d..4a7ec4bf5 100644 --- a/lib/model/source/album.dart +++ b/lib/model/source/album.dart @@ -152,17 +152,18 @@ mixin AlbumMixin on SourceBase { if (context != null) { final type = androidFileUtils.getAlbumType(dirPath); + final l10n = context.l10n; switch (type) { case AlbumType.camera: - return context.l10n.albumCamera; + return l10n.albumCamera; case AlbumType.download: - return context.l10n.albumDownload; + return l10n.albumDownload; case AlbumType.screenshots: - return context.l10n.albumScreenshots; + return l10n.albumScreenshots; case AlbumType.screenRecordings: - return context.l10n.albumScreenRecordings; + return l10n.albumScreenRecordings; case AlbumType.videoCaptures: - return context.l10n.albumVideoCaptures; + return l10n.albumVideoCaptures; case AlbumType.regular: case AlbumType.app: break; diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart new file mode 100644 index 000000000..087e4467c --- /dev/null +++ b/lib/theme/colors.dart @@ -0,0 +1,36 @@ +import 'package:aves/utils/color_utils.dart'; +import 'package:flutter/material.dart'; + +class AColors { + // mime + static final image = stringToColor('Image'); + static final video = stringToColor('Video'); + + // type + static const favourite = Colors.red; + static final animated = stringToColor('Animated'); + static final geotiff = stringToColor('GeoTIFF'); + static final motionPhoto = stringToColor('Motion Photo'); + static final panorama = stringToColor('Panorama'); + static final raw = stringToColor('Raw'); + static final sphericalVideo = stringToColor('360° Video'); + + // info + static final xmp = stringToColor('XMP'); + + // settings + static final accessibility = stringToColor('Accessibility'); + static final language = stringToColor('Language'); + static final navigation = stringToColor('Navigation'); + static final privacy = stringToColor('Privacy'); + static final thumbnails = stringToColor('Thumbnails'); + + static const debugGradient = LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Colors.red, + Colors.amber, + ], + ); +} diff --git a/lib/theme/themes.dart b/lib/theme/themes.dart index 5dd13fc3b..795c00371 100644 --- a/lib/theme/themes.dart +++ b/lib/theme/themes.dart @@ -5,15 +5,6 @@ import 'package:flutter/material.dart'; class Themes { static const _accentColor = Colors.indigoAccent; - static const debugGradient = LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [ - Colors.red, - Colors.amber, - ], - ); - static final darkTheme = ThemeData( brightness: Brightness.dark, // canvas color is used as background for the drawer and popups diff --git a/lib/widgets/common/favourite_toggler.dart b/lib/widgets/common/favourite_toggler.dart index caec381e2..319c1d8ac 100644 --- a/lib/widgets/common/favourite_toggler.dart +++ b/lib/widgets/common/favourite_toggler.dart @@ -1,5 +1,6 @@ import 'package:aves/model/entry.dart'; import 'package:aves/model/favourites.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/basic/menu.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; @@ -72,7 +73,7 @@ class _FavouriteTogglerState extends State { ), Sweeper( key: ValueKey(entries.length == 1 ? entries.first : entries.length), - builder: (context) => const Icon(AIcons.favourite, color: Colors.redAccent), + builder: (context) => const Icon(AIcons.favourite, color: AColors.favourite), toggledNotifier: isFavouriteNotifier, ), ], diff --git a/lib/widgets/drawer/tile.dart b/lib/widgets/drawer/tile.dart index 715ddd76b..fd4c97d8d 100644 --- a/lib/widgets/drawer/tile.dart +++ b/lib/widgets/drawer/tile.dart @@ -2,8 +2,8 @@ import 'package:aves/model/filters/favourite.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/filters/type.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/debug/app_debug_page.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart'; @@ -77,7 +77,7 @@ class DrawerPageIcon extends StatelessWidget { return const Icon(AIcons.tag); case AppDebugPage.routeName: return ShaderMask( - shaderCallback: Themes.debugGradient.createShader, + shaderCallback: AColors.debugGradient.createShader, child: const Icon(AIcons.debug), ); default: diff --git a/lib/widgets/map/map_page.dart b/lib/widgets/map/map_page.dart index 41de8297a..bc962480b 100644 --- a/lib/widgets/map/map_page.dart +++ b/lib/widgets/map/map_page.dart @@ -324,7 +324,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin } AvesEntry? _getRegionEntry(int? index) { - if (index != null && regionCollection != null) { + if (index != null && index >= 0 && regionCollection != null) { final regionEntries = regionCollection!.sortedEntries; if (index < regionEntries.length) { return regionEntries[index]; diff --git a/lib/widgets/settings/accessibility/accessibility.dart b/lib/widgets/settings/accessibility/accessibility.dart index 3d65b0417..e4fd0aadb 100644 --- a/lib/widgets/settings/accessibility/accessibility.dart +++ b/lib/widgets/settings/accessibility/accessibility.dart @@ -1,5 +1,5 @@ +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/color_utils.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/settings/accessibility/remove_animations.dart'; @@ -20,7 +20,7 @@ class AccessibilitySection extends StatelessWidget { return AvesExpansionTile( leading: SettingsTileLeading( icon: AIcons.accessibility, - color: stringToColor('Accessibility'), + color: AColors.accessibility, ), title: context.l10n.settingsSectionAccessibility, expandedNotifier: expandedNotifier, diff --git a/lib/widgets/settings/language/language.dart b/lib/widgets/settings/language/language.dart index 4baf94b13..b6f72e5e4 100644 --- a/lib/widgets/settings/language/language.dart +++ b/lib/widgets/settings/language/language.dart @@ -1,9 +1,9 @@ import 'package:aves/model/settings/enums/coordinate_format.dart'; import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/enums/unit_system.dart'; +import 'package:aves/model/settings/settings.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/color_utils.dart'; import 'package:aves/utils/constants.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; @@ -35,7 +35,7 @@ class LanguageSection extends StatelessWidget { value: 'language', leading: SettingsTileLeading( icon: AIcons.language, - color: stringToColor('Language'), + color: AColors.language, ), title: l10n.settingsSectionLanguage, expandedNotifier: expandedNotifier, diff --git a/lib/widgets/settings/navigation/navigation.dart b/lib/widgets/settings/navigation/navigation.dart index 15ed104e6..a7466c80c 100644 --- a/lib/widgets/settings/navigation/navigation.dart +++ b/lib/widgets/settings/navigation/navigation.dart @@ -2,8 +2,8 @@ import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/enums/home_page.dart'; import 'package:aves/model/settings/enums/screen_on.dart'; import 'package:aves/model/settings/settings.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/color_utils.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/dialogs/aves_selection_dialog.dart'; @@ -30,7 +30,7 @@ class NavigationSection extends StatelessWidget { return AvesExpansionTile( leading: SettingsTileLeading( icon: AIcons.home, - color: stringToColor('Navigation'), + color: AColors.navigation, ), title: context.l10n.settingsSectionNavigation, expandedNotifier: expandedNotifier, diff --git a/lib/widgets/settings/privacy/privacy.dart b/lib/widgets/settings/privacy/privacy.dart index 1a571dcd8..327326d67 100644 --- a/lib/widgets/settings/privacy/privacy.dart +++ b/lib/widgets/settings/privacy/privacy.dart @@ -1,8 +1,8 @@ import 'package:aves/app_flavor.dart'; import 'package:aves/model/device.dart'; import 'package:aves/model/settings/settings.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/color_utils.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/settings/common/tile_leading.dart'; @@ -26,7 +26,7 @@ class PrivacySection extends StatelessWidget { return AvesExpansionTile( leading: SettingsTileLeading( icon: AIcons.privacy, - color: stringToColor('Privacy'), + color: AColors.privacy, ), title: context.l10n.settingsSectionPrivacy, expandedNotifier: expandedNotifier, diff --git a/lib/widgets/settings/thumbnails/thumbnails.dart b/lib/widgets/settings/thumbnails/thumbnails.dart index 1ff395e94..4e5f00d4c 100644 --- a/lib/widgets/settings/thumbnails/thumbnails.dart +++ b/lib/widgets/settings/thumbnails/thumbnails.dart @@ -1,7 +1,7 @@ import 'package:aves/model/settings/settings.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/color_utils.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/common/identity/aves_icons.dart'; @@ -26,7 +26,7 @@ class ThumbnailsSection extends StatelessWidget { return AvesExpansionTile( leading: SettingsTileLeading( icon: AIcons.grid, - color: stringToColor('Thumbnails'), + color: AColors.thumbnails, ), title: context.l10n.settingsSectionThumbnails, expandedNotifier: expandedNotifier, diff --git a/lib/widgets/settings/video/video.dart b/lib/widgets/settings/video/video.dart index 9d2c48c1f..168a94154 100644 --- a/lib/widgets/settings/video/video.dart +++ b/lib/widgets/settings/video/video.dart @@ -1,10 +1,10 @@ import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/settings/enums/enums.dart'; -import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/enums/video_loop_mode.dart'; +import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/color_utils.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; @@ -77,7 +77,7 @@ class VideoSection extends StatelessWidget { : AvesExpansionTile( leading: SettingsTileLeading( icon: AIcons.video, - color: stringToColor('Video'), + color: AColors.video, ), title: context.l10n.settingsSectionVideo, expandedNotifier: expandedNotifier, diff --git a/lib/widgets/settings/viewer/viewer.dart b/lib/widgets/settings/viewer/viewer.dart index cb1531f60..3b684621a 100644 --- a/lib/widgets/settings/viewer/viewer.dart +++ b/lib/widgets/settings/viewer/viewer.dart @@ -1,8 +1,8 @@ import 'package:aves/model/settings/enums/enums.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/common/services.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/color_utils.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/settings/common/tile_leading.dart'; @@ -25,7 +25,7 @@ class ViewerSection extends StatelessWidget { return AvesExpansionTile( leading: SettingsTileLeading( icon: AIcons.image, - color: stringToColor('Image'), + color: AColors.image, ), title: context.l10n.settingsSectionViewer, expandedNotifier: expandedNotifier, diff --git a/lib/widgets/stats/filter_table.dart b/lib/widgets/stats/filter_table.dart index 703a54698..635b91e05 100644 --- a/lib/widgets/stats/filter_table.dart +++ b/lib/widgets/stats/filter_table.dart @@ -1,5 +1,4 @@ import 'package:aves/model/filters/filters.dart'; -import 'package:aves/utils/color_utils.dart'; import 'package:aves/utils/constants.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; @@ -53,7 +52,6 @@ class FilterTable extends StatelessWidget { return Table( children: displayedEntries.map((kv) { final filter = filterBuilder(kv.key); - final label = filter.getLabel(context); final count = kv.value; final percent = count / totalEntryCount; return TableRow( @@ -76,19 +74,25 @@ class FilterTable extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: lineHeight), child: ClipRRect( borderRadius: BorderRadius.all(barRadius), - child: LinearPercentIndicator( - percent: percent, - lineHeight: lineHeight, - backgroundColor: Colors.white24, - progressColor: stringToColor(label), - animation: true, - isRTL: isRtl, - barRadius: barRadius, - center: Text( - intl.NumberFormat.percentPattern().format(percent), - style: const TextStyle(shadows: Constants.embossShadows), - ), - padding: EdgeInsets.zero, + child: FutureBuilder( + future: filter.color(context), + builder: (context, snapshot) { + final color = snapshot.data; + return LinearPercentIndicator( + percent: percent, + lineHeight: lineHeight, + backgroundColor: Colors.white24, + progressColor: color, + animation: true, + isRTL: isRtl, + barRadius: barRadius, + center: Text( + intl.NumberFormat.percentPattern().format(percent), + style: const TextStyle(shadows: Constants.embossShadows), + ), + padding: EdgeInsets.zero, + ); + }, ), ), ), diff --git a/lib/widgets/viewer/info/metadata/xmp_tile.dart b/lib/widgets/viewer/info/metadata/xmp_tile.dart index 01dd5a393..44b4ae3e9 100644 --- a/lib/widgets/viewer/info/metadata/xmp_tile.dart +++ b/lib/widgets/viewer/info/metadata/xmp_tile.dart @@ -1,7 +1,7 @@ import 'dart:collection'; import 'package:aves/model/entry.dart'; -import 'package:aves/utils/color_utils.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/utils/xmp_utils.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/viewer/info/metadata/xmp_namespaces.dart'; @@ -43,7 +43,7 @@ class _XmpDirTileState extends State { return AvesExpansionTile( // title may contain parent to distinguish multiple XMP directories title: widget.title, - color: stringToColor('XMP'), + color: AColors.xmp, expandedNotifier: widget.expandedNotifier, initiallyExpanded: widget.initiallyExpanded, children: [