This commit is contained in:
Thibault Deckers 2023-03-29 16:03:10 +02:00
parent b5aaad4df8
commit 0584e8ffa7
175 changed files with 1142 additions and 1051 deletions

3
lib/convert/convert.dart Normal file
View file

@ -0,0 +1,3 @@
export 'metadata/date_field_source.dart';
export 'metadata/fields.dart';
export 'metadata/metadata_type.dart';

View file

@ -0,0 +1,18 @@
import 'package:aves_model/aves_model.dart';
extension ExtraDateFieldSourceConvert on DateFieldSource {
MetadataField? toMetadataField() {
switch (this) {
case DateFieldSource.fileModifiedDate:
return null;
case DateFieldSource.exifDate:
return MetadataField.exifDate;
case DateFieldSource.exifDateOriginal:
return MetadataField.exifDateOriginal;
case DateFieldSource.exifDateDigitized:
return MetadataField.exifDateDigitized;
case DateFieldSource.exifGpsDate:
return MetadataField.exifGpsDatestamp;
}
}
}

View file

@ -1,87 +1,6 @@
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
enum MetadataField { extension ExtraMetadataFieldConvert on MetadataField {
exifDate,
exifDateOriginal,
exifDateDigitized,
exifGpsAltitude,
exifGpsAltitudeRef,
exifGpsAreaInformation,
exifGpsDatestamp,
exifGpsDestBearing,
exifGpsDestBearingRef,
exifGpsDestDistance,
exifGpsDestDistanceRef,
exifGpsDestLatitude,
exifGpsDestLatitudeRef,
exifGpsDestLongitude,
exifGpsDestLongitudeRef,
exifGpsDifferential,
exifGpsDOP,
exifGpsHPositioningError,
exifGpsImgDirection,
exifGpsImgDirectionRef,
exifGpsLatitude,
exifGpsLatitudeRef,
exifGpsLongitude,
exifGpsLongitudeRef,
exifGpsMapDatum,
exifGpsMeasureMode,
exifGpsProcessingMethod,
exifGpsSatellites,
exifGpsSpeed,
exifGpsSpeedRef,
exifGpsStatus,
exifGpsTimestamp,
exifGpsTrack,
exifGpsTrackRef,
exifGpsVersionId,
exifImageDescription,
exifUserComment,
mp4GpsCoordinates,
mp4RotationDegrees,
mp4Xmp,
xmpXmpCreateDate,
}
class MetadataFields {
static const Set<MetadataField> exifGpsFields = {
MetadataField.exifGpsAltitude,
MetadataField.exifGpsAltitudeRef,
MetadataField.exifGpsAreaInformation,
MetadataField.exifGpsDatestamp,
MetadataField.exifGpsDestBearing,
MetadataField.exifGpsDestBearingRef,
MetadataField.exifGpsDestDistance,
MetadataField.exifGpsDestDistanceRef,
MetadataField.exifGpsDestLatitude,
MetadataField.exifGpsDestLatitudeRef,
MetadataField.exifGpsDestLongitude,
MetadataField.exifGpsDestLongitudeRef,
MetadataField.exifGpsDifferential,
MetadataField.exifGpsDOP,
MetadataField.exifGpsHPositioningError,
MetadataField.exifGpsImgDirection,
MetadataField.exifGpsImgDirectionRef,
MetadataField.exifGpsLatitude,
MetadataField.exifGpsLatitudeRef,
MetadataField.exifGpsLongitude,
MetadataField.exifGpsLongitudeRef,
MetadataField.exifGpsMapDatum,
MetadataField.exifGpsMeasureMode,
MetadataField.exifGpsProcessingMethod,
MetadataField.exifGpsSatellites,
MetadataField.exifGpsSpeed,
MetadataField.exifGpsSpeedRef,
MetadataField.exifGpsStatus,
MetadataField.exifGpsTimestamp,
MetadataField.exifGpsTrack,
MetadataField.exifGpsTrackRef,
MetadataField.exifGpsVersionId,
};
}
extension ExtraMetadataField on MetadataField {
MetadataType get type { MetadataType get type {
switch (this) { switch (this) {
case MetadataField.exifDate: case MetadataField.exifDate:
@ -228,21 +147,4 @@ extension ExtraMetadataField on MetadataField {
return null; return null;
} }
} }
String get title {
switch (this) {
case MetadataField.exifDate:
return 'Exif date';
case MetadataField.exifDateOriginal:
return 'Exif original date';
case MetadataField.exifDateDigitized:
return 'Exif digitized date';
case MetadataField.exifGpsDatestamp:
return 'Exif GPS date';
case MetadataField.xmpXmpCreateDate:
return 'XMP xmp:CreateDate';
default:
return name;
}
}
} }

View file

@ -0,0 +1,28 @@
import 'package:aves_model/aves_model.dart';
extension ExtraMetadataTypeConvert on MetadataType {
String get toPlatform {
switch (this) {
case MetadataType.comment:
return 'comment';
case MetadataType.exif:
return 'exif';
case MetadataType.iccProfile:
return 'icc_profile';
case MetadataType.iptc:
return 'iptc';
case MetadataType.jfif:
return 'jfif';
case MetadataType.jpegAdobe:
return 'jpeg_adobe';
case MetadataType.jpegDucky:
return 'jpeg_ducky';
case MetadataType.mp4:
return 'mp4';
case MetadataType.photoshopIrb:
return 'photoshop_irb';
case MetadataType.xmp:
return 'xmp';
}
}
}

View file

@ -5,10 +5,10 @@ import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/model/vaults/vaults.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';

View file

@ -1,8 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:aves/model/storage/relative_dir.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
final entryDirRepo = EntryDirRepo._private(); final entryDirRepo = EntryDirRepo._private();
@ -52,7 +52,7 @@ class EntryDir {
} }
String? _resolve() { String? _resolve() {
final vrl = VolumeRelativeDirectory.fromPath(asIs!); final vrl = androidFileUtils.relativeDirectoryFromPath(asIs!);
if (vrl == null || vrl.relativeDir.isEmpty) return asIs; if (vrl == null || vrl.relativeDir.isEmpty) return asIs;
var resolved = vrl.volumePath; var resolved = vrl.volumePath;

View file

@ -7,7 +7,7 @@ import 'package:aves/model/entry/cache.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/utils/math_utils.dart'; import 'package:aves/utils/math_utils.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/painting.dart';
extension ExtraAvesEntryImages on AvesEntry { extension ExtraAvesEntryImages on AvesEntry {
bool isThumbnailReady({double extent = 0}) => _isReady(_getThumbnailProviderKey(extent)); bool isThumbnailReady({double extent = 0}) => _isReady(_getThumbnailProviderKey(extent));

View file

@ -1,15 +1,15 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:aves/convert/convert.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/catalog.dart'; import 'package:aves/model/entry/extensions/catalog.dart';
import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/model/metadata/date_modifier.dart';
import 'package:aves/model/metadata/enums/date_field_source.dart'; import 'package:aves/ref/metadata/exif.dart';
import 'package:aves/model/metadata/fields.dart'; import 'package:aves/ref/metadata/iptc.dart';
import 'package:aves/ref/exif.dart';
import 'package:aves/ref/iptc.dart';
import 'package:aves/ref/mime_types.dart'; import 'package:aves/ref/mime_types.dart';
import 'package:aves/ref/metadata/xmp.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/services/metadata/xmp.dart'; import 'package:aves/services/metadata/xmp.dart';
import 'package:aves/utils/time_utils.dart'; import 'package:aves/utils/time_utils.dart';
@ -54,7 +54,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
editCreateDateXmp(descriptions, appliedModifier.setDateTime); editCreateDateXmp(descriptions, appliedModifier.setDateTime);
break; break;
case DateEditAction.shift: case DateEditAction.shift:
final xmpDate = XMP.getString(descriptions, XMP.xmpCreateDate, namespace: Namespaces.xmp); final xmpDate = XMP.getString(descriptions, XmpAttributes.xmpCreateDate, namespace: XmpNamespaces.xmp);
if (xmpDate != null) { if (xmpDate != null) {
final date = DateTime.tryParse(xmpDate); final date = DateTime.tryParse(xmpDate);
if (date != null) { if (date != null) {
@ -262,18 +262,18 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
if (editTitle) { if (editTitle) {
modified |= XMP.setAttribute( modified |= XMP.setAttribute(
descriptions, descriptions,
XMP.dcTitle, XmpElements.dcTitle,
title, title,
namespace: Namespaces.dc, namespace: XmpNamespaces.dc,
strat: XmpEditStrategy.always, strat: XmpEditStrategy.always,
); );
} }
if (editDescription) { if (editDescription) {
modified |= XMP.setAttribute( modified |= XMP.setAttribute(
descriptions, descriptions,
XMP.dcDescription, XmpElements.dcDescription,
description, description,
namespace: Namespaces.dc, namespace: XmpNamespaces.dc,
strat: XmpEditStrategy.always, strat: XmpEditStrategy.always,
); );
} }
@ -417,9 +417,9 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
static bool editCreateDateXmp(List<XmlNode> descriptions, DateTime? date) { static bool editCreateDateXmp(List<XmlNode> descriptions, DateTime? date) {
return XMP.setAttribute( return XMP.setAttribute(
descriptions, descriptions,
XMP.xmpCreateDate, XmpAttributes.xmpCreateDate,
date != null ? XMP.toXmpDate(date) : null, date != null ? XMP.toXmpDate(date) : null,
namespace: Namespaces.xmp, namespace: XmpNamespaces.xmp,
strat: XmpEditStrategy.always, strat: XmpEditStrategy.always,
); );
} }
@ -428,9 +428,9 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
static bool editTagsXmp(List<XmlNode> descriptions, Set<String> tags) { static bool editTagsXmp(List<XmlNode> descriptions, Set<String> tags) {
return XMP.setStringBag( return XMP.setStringBag(
descriptions, descriptions,
XMP.dcSubject, XmpElements.dcSubject,
tags, tags,
namespace: Namespaces.dc, namespace: XmpNamespaces.dc,
strat: XmpEditStrategy.always, strat: XmpEditStrategy.always,
); );
} }
@ -441,17 +441,17 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
modified |= XMP.setAttribute( modified |= XMP.setAttribute(
descriptions, descriptions,
XMP.xmpRating, XmpElements.xmpRating,
(rating ?? 0) == 0 ? null : '$rating', (rating ?? 0) == 0 ? null : '$rating',
namespace: Namespaces.xmp, namespace: XmpNamespaces.xmp,
strat: XmpEditStrategy.always, strat: XmpEditStrategy.always,
); );
modified |= XMP.setAttribute( modified |= XMP.setAttribute(
descriptions, descriptions,
XMP.msPhotoRating, XmpElements.msPhotoRating,
XMP.toMsPhotoRating(rating), XMP.toMsPhotoRating(rating),
namespace: Namespaces.microsoftPhoto, namespace: XmpNamespaces.microsoftPhoto,
strat: XmpEditStrategy.updateIfPresent, strat: XmpEditStrategy.updateIfPresent,
); );
@ -464,23 +464,23 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
modified |= XMP.removeElements( modified |= XMP.removeElements(
descriptions, descriptions,
XMP.containerDirectory, XmpElements.containerDirectory,
Namespaces.gContainer, XmpNamespaces.gContainer,
); );
modified |= [ modified |= [
XMP.gCameraMicroVideo, XmpAttributes.gCameraMicroVideo,
XMP.gCameraMicroVideoVersion, XmpAttributes.gCameraMicroVideoVersion,
XMP.gCameraMicroVideoOffset, XmpAttributes.gCameraMicroVideoOffset,
XMP.gCameraMicroVideoPresentationTimestampUs, XmpAttributes.gCameraMicroVideoPresentationTimestampUs,
XMP.gCameraMotionPhoto, XmpAttributes.gCameraMotionPhoto,
XMP.gCameraMotionPhotoVersion, XmpAttributes.gCameraMotionPhotoVersion,
XMP.gCameraMotionPhotoPresentationTimestampUs, XmpAttributes.gCameraMotionPhotoPresentationTimestampUs,
].fold<bool>(modified, (prev, name) { ].fold<bool>(modified, (prev, name) {
return prev |= XMP.removeElements( return prev |= XMP.removeElements(
descriptions, descriptions,
name, name,
Namespaces.gCamera, XmpNamespaces.gCamera,
); );
}); });

View file

@ -1,11 +1,11 @@
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/storage/volume.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/foundation.dart';
final Favourites favourites = Favourites._private(); final Favourites favourites = Favourites._private();

View file

@ -1,10 +1,10 @@
import 'package:aves/model/covers.dart'; import 'package:aves/model/covers.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.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/identity/aves_icons.dart'; import 'package:aves/widgets/common/identity/aves_icons.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -4,7 +4,7 @@ import 'dart:ui' as ui;
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/images.dart'; import 'package:aves/model/entry/extensions/images.dart';
import 'package:aves/ref/geotiff.dart'; import 'package:aves/ref/metadata/geotiff.dart';
import 'package:aves/utils/math_utils.dart'; import 'package:aves/utils/math_utils.dart';
import 'package:aves_map/aves_map.dart'; import 'package:aves_map/aves_map.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';

View file

@ -1,6 +1,6 @@
import 'package:event_bus/event_bus.dart'; import 'package:event_bus/event_bus.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/painting.dart';
class HighlightInfo extends ChangeNotifier { class HighlightInfo extends ChangeNotifier {
final EventBus eventBus = EventBus(); final EventBus eventBus = EventBus();

View file

@ -1,7 +1,6 @@
import 'package:aves/geo/states.dart'; import 'package:aves/geo/states.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@immutable @immutable
class AddressDetails extends Equatable { class AddressDetails extends Equatable {

View file

@ -1,8 +1,6 @@
import 'package:aves/model/metadata/fields.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
@immutable @immutable
class DateModifier extends Equatable { class DateModifier extends Equatable {

View file

@ -1,76 +0,0 @@
import 'package:aves_model/aves_model.dart';
class MetadataTypes {
static const main = {
MetadataType.exif,
MetadataType.xmp,
};
static const common = {
MetadataType.exif,
MetadataType.xmp,
MetadataType.comment,
MetadataType.iccProfile,
MetadataType.iptc,
MetadataType.photoshopIrb,
};
static const jpeg = {
MetadataType.jfif,
MetadataType.jpegAdobe,
MetadataType.jpegDucky,
};
}
extension ExtraMetadataType on MetadataType {
// match `metadata-extractor` directory names
String getText() {
switch (this) {
case MetadataType.comment:
return 'Comment';
case MetadataType.exif:
return 'Exif';
case MetadataType.iccProfile:
return 'ICC Profile';
case MetadataType.iptc:
return 'IPTC';
case MetadataType.jfif:
return 'JFIF';
case MetadataType.jpegAdobe:
return 'Adobe JPEG';
case MetadataType.jpegDucky:
return 'Ducky';
case MetadataType.mp4:
return 'MP4';
case MetadataType.photoshopIrb:
return 'Photoshop';
case MetadataType.xmp:
return 'XMP';
}
}
String get toPlatform {
switch (this) {
case MetadataType.comment:
return 'comment';
case MetadataType.exif:
return 'exif';
case MetadataType.iccProfile:
return 'icc_profile';
case MetadataType.iptc:
return 'iptc';
case MetadataType.jfif:
return 'jfif';
case MetadataType.jpegAdobe:
return 'jpeg_adobe';
case MetadataType.jpegDucky:
return 'jpeg_ducky';
case MetadataType.mp4:
return 'mp4';
case MetadataType.photoshopIrb:
return 'photoshop_irb';
case MetadataType.xmp:
return 'xmp';
}
}
}

View file

@ -2,7 +2,6 @@ import 'dart:ui';
import 'package:aves/model/filters/recent.dart'; import 'package:aves/model/filters/recent.dart';
import 'package:aves/model/naming_pattern.dart'; import 'package:aves/model/naming_pattern.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/ref/mime_types.dart'; import 'package:aves/ref/mime_types.dart';
import 'package:aves/utils/colors.dart'; import 'package:aves/utils/colors.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart';

View file

@ -1,5 +1,5 @@
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/painting.dart';
extension ExtraSubtitlePosition on SubtitlePosition { extension ExtraSubtitlePosition on SubtitlePosition {
TextAlignVertical toTextAlignVertical() { TextAlignVertical toTextAlignVertical() {

View file

@ -1,15 +0,0 @@
import 'package:aves/theme/icons.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart';
extension ExtraThumbnailOverlayLocationIcon on ThumbnailOverlayLocationIcon {
IconData getIcon(BuildContext context) {
switch (this) {
case ThumbnailOverlayLocationIcon.unlocated:
return AIcons.locationUnlocated;
case ThumbnailOverlayLocationIcon.located:
case ThumbnailOverlayLocationIcon.none:
return AIcons.location;
}
}
}

View file

@ -1,16 +0,0 @@
import 'package:aves/theme/icons.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart';
extension ExtraThumbnailOverlayTagIcon on ThumbnailOverlayTagIcon {
IconData getIcon(BuildContext context) {
switch (this) {
case ThumbnailOverlayTagIcon.tagged:
return AIcons.tag;
case ThumbnailOverlayTagIcon.untagged:
return AIcons.tagUntagged;
case ThumbnailOverlayTagIcon.none:
return AIcons.tag;
}
}
}

View file

@ -1,6 +1,6 @@
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/painting.dart';
extension ExtraWidgetShape on WidgetShape { extension ExtraWidgetShape on WidgetShape {
Path path(Size widgetSize, double devicePixelRatio) { Path path(Size widgetSize, double devicePixelRatio) {

View file

@ -9,7 +9,6 @@ import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/filters/mime.dart';
import 'package:aves/model/settings/defaults.dart'; import 'package:aves/model/settings/defaults.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/ref/bursts.dart'; import 'package:aves/ref/bursts.dart';
import 'package:aves/services/accessibility_service.dart'; import 'package:aves/services/accessibility_service.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';

View file

@ -2,13 +2,12 @@ import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/album.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/storage/relative_dir.dart';
import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/model/vaults/vaults.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/collection_utils.dart'; import 'package:aves/utils/collection_utils.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/view/view.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -175,27 +174,13 @@ mixin AlbumMixin on SourceBase {
final type = androidFileUtils.getAlbumType(dirPath); final type = androidFileUtils.getAlbumType(dirPath);
if (context != null) { if (context != null) {
switch (type) { final name = type.getName(context);
case AlbumType.camera: if (name != null) return name;
return context.l10n.albumCamera;
case AlbumType.download:
return context.l10n.albumDownload;
case AlbumType.screenshots:
return context.l10n.albumScreenshots;
case AlbumType.screenRecordings:
return context.l10n.albumScreenRecordings;
case AlbumType.videoCaptures:
return context.l10n.albumVideoCaptures;
case AlbumType.regular:
case AlbumType.vault:
case AlbumType.app:
break;
}
} }
if (type == AlbumType.vault) return pContext.basename(dirPath); if (type == AlbumType.vault) return pContext.basename(dirPath);
final dir = VolumeRelativeDirectory.fromPath(dirPath); final dir = androidFileUtils.relativeDirectoryFromPath(dirPath);
if (dir == null) return dirPath; if (dir == null) return dirPath;
final relativeDir = dir.relativeDir; final relativeDir = dir.relativeDir;

View file

@ -14,7 +14,6 @@ import 'package:aves/model/filters/rating.dart';
import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/filters/trash.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/events.dart'; import 'package:aves/model/source/events.dart';
import 'package:aves/model/source/location/location.dart'; import 'package:aves/model/source/location/location.dart';
import 'package:aves/model/source/section_keys.dart'; import 'package:aves/model/source/section_keys.dart';

View file

@ -15,7 +15,6 @@ import 'package:aves/model/metadata/trash.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/album.dart'; import 'package:aves/model/source/album.dart';
import 'package:aves/model/source/analysis_controller.dart'; import 'package:aves/model/source/analysis_controller.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/events.dart'; import 'package:aves/model/source/events.dart';
import 'package:aves/model/source/location/country.dart'; import 'package:aves/model/source/location/country.dart';
import 'package:aves/model/source/location/location.dart'; import 'package:aves/model/source/location/location.dart';

View file

@ -7,11 +7,11 @@ import 'package:aves/model/filters/location.dart';
import 'package:aves/model/metadata/address.dart'; import 'package:aves/model/metadata/address.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/analysis_controller.dart'; import 'package:aves/model/source/analysis_controller.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/location/country.dart'; import 'package:aves/model/source/location/country.dart';
import 'package:aves/model/source/location/place.dart'; import 'package:aves/model/source/location/place.dart';
import 'package:aves/model/source/location/state.dart'; import 'package:aves/model/source/location/state.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';

View file

@ -8,10 +8,10 @@ import 'package:aves/model/favourites.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/analysis_controller.dart'; import 'package:aves/model/source/analysis_controller.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/model/vaults/vaults.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';

View file

@ -4,9 +4,9 @@ import 'package:aves/model/filters/tag.dart';
import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/metadata/catalog.dart';
import 'package:aves/model/source/analysis_controller.dart'; import 'package:aves/model/source/analysis_controller.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/collection_utils.dart'; import 'package:aves/utils/collection_utils.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';

View file

@ -1,49 +0,0 @@
import 'package:aves/utils/android_file_utils.dart';
import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
@immutable
class VolumeRelativeDirectory extends Equatable {
final String volumePath, relativeDir;
@override
List<Object?> get props => [volumePath, relativeDir];
String get dirPath => '$volumePath$relativeDir';
const VolumeRelativeDirectory({
required this.volumePath,
required this.relativeDir,
});
static VolumeRelativeDirectory fromMap(Map map) {
return VolumeRelativeDirectory(
volumePath: map['volumePath'] ?? '',
relativeDir: map['relativeDir'] ?? '',
);
}
Map<String, dynamic> toMap() => {
'volumePath': volumePath,
'relativeDir': relativeDir,
};
// prefer static method over a null returning factory constructor
static VolumeRelativeDirectory? fromPath(String dirPath) {
final volume = androidFileUtils.getStorageVolume(dirPath);
if (volume == null) return null;
final root = volume.path;
final rootLength = root.length;
return VolumeRelativeDirectory(
volumePath: root,
relativeDir: dirPath.length < rootLength ? '' : dirPath.substring(rootLength),
);
}
String getVolumeDescription(BuildContext context) {
final volume = androidFileUtils.storageVolumes.firstWhereOrNull((volume) => volume.path == volumePath);
return volume?.getDescription(context) ?? volumePath;
}
}

View file

@ -1,41 +0,0 @@
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
@immutable
class StorageVolume extends Equatable {
final String? _description;
final String path, state;
final bool isPrimary, isRemovable;
@override
List<Object?> get props => [_description, path, state, isPrimary, isRemovable];
const StorageVolume({
required String? description,
required this.isPrimary,
required this.isRemovable,
required this.path,
required this.state,
}) : _description = description;
String getDescription(BuildContext? context) {
if (_description != null) return _description!;
// ideally, the context should always be provided, but in some cases (e.g. album comparison),
// this would require numerous additional methods to have the context as argument
// for such a minor benefit: fallback volume description on Android < N
if (isPrimary) return context?.l10n.storageVolumeDescriptionFallbackPrimary ?? 'Internal Storage';
return context?.l10n.storageVolumeDescriptionFallbackNonPrimary ?? 'SD card';
}
factory StorageVolume.fromMap(Map map) {
final isPrimary = map['isPrimary'] ?? false;
return StorageVolume(
description: map['description'],
isPrimary: isPrimary,
isRemovable: map['isRemovable'] ?? false,
path: map['path'] ?? '',
state: map['state'] ?? '',
);
}
}

View file

@ -1,6 +1,6 @@
import 'package:aves/model/vaults/enums.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/collection_utils.dart'; import 'package:aves/utils/collection_utils.dart';
import 'package:aves_model/aves_model.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';

View file

@ -2,18 +2,9 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:aves/model/vaults/details.dart'; import 'package:aves/model/vaults/details.dart';
import 'package:aves/model/vaults/enums.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/dialogs/aves_dialog.dart';
import 'package:aves/widgets/dialogs/filter_editors/password_dialog.dart';
import 'package:aves/widgets/dialogs/filter_editors/pattern_dialog.dart';
import 'package:aves/widgets/dialogs/filter_editors/pin_dialog.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:local_auth/error_codes.dart' as auth_error;
import 'package:local_auth/local_auth.dart';
import 'package:screen_state/screen_state.dart'; import 'package:screen_state/screen_state.dart';
final Vaults vaults = Vaults._private(); final Vaults vaults = Vaults._private();
@ -44,7 +35,7 @@ class Vaults extends ChangeNotifier {
Set<VaultDetails> get all => Set.unmodifiable(_rows); Set<VaultDetails> get all => Set.unmodifiable(_rows);
VaultDetails? _detailsForPath(String dirPath) => _rows.firstWhereOrNull((v) => v.path == dirPath); VaultDetails? detailsForPath(String dirPath) => _rows.firstWhereOrNull((v) => v.path == dirPath);
Future<void> create(VaultDetails details) async { Future<void> create(VaultDetails details) async {
await metadataDb.addVaults({details}); await metadataDb.addVaults({details});
@ -56,7 +47,7 @@ class Vaults extends ChangeNotifier {
} }
Future<void> remove(Set<String> dirPaths) async { Future<void> remove(Set<String> dirPaths) async {
final details = dirPaths.map(_detailsForPath).whereNotNull().toSet(); final details = dirPaths.map(detailsForPath).whereNotNull().toSet();
if (details.isEmpty) return; if (details.isEmpty) return;
await metadataDb.removeVaults(details); await metadataDb.removeVaults(details);
@ -70,7 +61,7 @@ class Vaults extends ChangeNotifier {
} }
Future<void> rename(String oldDirPath, String newDirPath) async { Future<void> rename(String oldDirPath, String newDirPath) async {
final oldDetails = _detailsForPath(oldDirPath); final oldDetails = detailsForPath(oldDirPath);
if (oldDetails == null) return; if (oldDetails == null) return;
final newName = VaultDetails.nameFromPath(newDirPath); final newName = VaultDetails.nameFromPath(newDirPath);
@ -96,7 +87,7 @@ class Vaults extends ChangeNotifier {
// update details, except name // update details, except name
Future<void> update(VaultDetails newDetails) async { Future<void> update(VaultDetails newDetails) async {
final oldDetails = _detailsForPath(newDetails.path); final oldDetails = detailsForPath(newDetails.path);
if (oldDetails == null) return; if (oldDetails == null) return;
await metadataDb.updateVault(newDetails.name, newDetails); await metadataDb.updateVault(newDetails.name, newDetails);
@ -141,119 +132,11 @@ class Vaults extends ChangeNotifier {
_onLockStateChanged(); _onLockStateChanged();
} }
Future<bool> tryUnlock(String dirPath, BuildContext context) async { void unlock(String dirPath) {
if (!isVault(dirPath) || !isLocked(dirPath)) return true; if (!vaults.isVault(dirPath) || !vaults.isLocked(dirPath)) return;
final details = _detailsForPath(dirPath);
if (details == null) return false;
bool? confirmed;
switch (details.lockType) {
case VaultLockType.system:
try {
confirmed = await LocalAuthentication().authenticate(
localizedReason: context.l10n.authenticateToUnlockVault,
);
} on PlatformException catch (e, stack) {
if (e.code != 'auth_in_progress') {
// `auth_in_progress`: `Authentication in progress`
await reportService.recordError(e, stack);
}
}
break;
case VaultLockType.pattern:
final pattern = await showDialog<String>(
context: context,
builder: (context) => const PatternDialog(needConfirmation: false),
routeSettings: const RouteSettings(name: PatternDialog.routeName),
);
if (pattern != null) {
confirmed = pattern == await securityService.readValue(details.passKey);
}
break;
case VaultLockType.pin:
final pin = await showDialog<String>(
context: context,
builder: (context) => const PinDialog(needConfirmation: false),
routeSettings: const RouteSettings(name: PinDialog.routeName),
);
if (pin != null) {
confirmed = pin == await securityService.readValue(details.passKey);
}
break;
case VaultLockType.password:
final password = await showDialog<String>(
context: context,
builder: (context) => const PasswordDialog(needConfirmation: false),
routeSettings: const RouteSettings(name: PasswordDialog.routeName),
);
if (password != null) {
confirmed = password == await securityService.readValue(details.passKey);
}
break;
}
if (confirmed == null || !confirmed) return false;
_unlockedDirPaths.add(dirPath); _unlockedDirPaths.add(dirPath);
_onLockStateChanged(); _onLockStateChanged();
return true;
}
Future<bool> setPass(BuildContext context, VaultDetails details) async {
switch (details.lockType) {
case VaultLockType.system:
final l10n = context.l10n;
try {
return await LocalAuthentication().authenticate(
localizedReason: l10n.authenticateToConfigureVault,
);
} on PlatformException catch (e, stack) {
await showDialog(
context: context,
builder: (context) => AvesDialog(
content: Text(e.message ?? l10n.genericFailureFeedback),
actions: const [OkButton()],
),
routeSettings: const RouteSettings(name: AvesDialog.warningRouteName),
);
if (e.code != auth_error.notAvailable) {
await reportService.recordError(e, stack);
}
}
break;
case VaultLockType.pattern:
final pattern = await showDialog<String>(
context: context,
builder: (context) => const PatternDialog(needConfirmation: true),
routeSettings: const RouteSettings(name: PatternDialog.routeName),
);
if (pattern != null) {
return await securityService.writeValue(details.passKey, pattern);
}
break;
case VaultLockType.pin:
final pin = await showDialog<String>(
context: context,
builder: (context) => const PinDialog(needConfirmation: true),
routeSettings: const RouteSettings(name: PinDialog.routeName),
);
if (pin != null) {
return await securityService.writeValue(details.passKey, pin);
}
break;
case VaultLockType.password:
final password = await showDialog<String>(
context: context,
builder: (context) => const PasswordDialog(needConfirmation: true),
routeSettings: const RouteSettings(name: PasswordDialog.routeName),
);
if (password != null) {
return await securityService.writeValue(details.passKey, password);
}
break;
}
return false;
} }
void _onScreenOff() => lock(all.where((v) => v.autoLockScreenOff).map((v) => v.path).toSet()); void _onScreenOff() => lock(all.where((v) => v.autoLockScreenOff).map((v) => v.path).toSet());

107
lib/ref/metadata/xmp.dart Normal file
View file

@ -0,0 +1,107 @@
class XmpNamespaces {
static const acdsee = 'http://ns.acdsee.com/iptc/1.0/';
static const adsmlat = 'http://adsml.org/xmlns/';
static const avm = 'http://www.communicatingastronomy.org/avm/1.0/';
static const camera = 'http://pix4d.com/camera/1.0/';
static const cc = 'http://creativecommons.org/ns#';
static const creatorAtom = 'http://ns.adobe.com/creatorAtom/1.0/';
static const crd = 'http://ns.adobe.com/camera-raw-defaults/1.0/';
static const crlcp = 'http://ns.adobe.com/camera-raw-embedded-lens-profile/1.0/';
static const crs = 'http://ns.adobe.com/camera-raw-settings/1.0/';
static const crss = 'http://ns.adobe.com/camera-raw-saved-settings/1.0/';
static const darktable = 'http://darktable.sf.net/';
static const dc = 'http://purl.org/dc/elements/1.1/';
static const dcterms = 'http://purl.org/dc/terms/';
static const dicom = 'http://ns.adobe.com/DICOM/';
static const digiKam = 'http://www.digikam.org/ns/1.0/';
static const droneDji = 'http://www.dji.com/drone-dji/1.0/';
static const dwc = 'http://rs.tdwg.org/dwc/index.htm';
static const dwciri = 'http://rs.tdwg.org/dwc/iri/';
static const exif = 'http://ns.adobe.com/exif/1.0/';
static const exifAux = 'http://ns.adobe.com/exif/1.0/aux/';
static const exifEx = 'http://cipa.jp/exif/1.0/';
static const fstop = 'http://www.fstopapp.com/xmp/';
static const gAudio = 'http://ns.google.com/photos/1.0/audio/';
static const gCamera = 'http://ns.google.com/photos/1.0/camera/';
static const gContainer = 'http://ns.google.com/photos/1.0/container/';
static const gCreations = 'http://ns.google.com/photos/1.0/creations/';
static const gDepth = 'http://ns.google.com/photos/1.0/depthmap/';
static const gDevice = 'http://ns.google.com/photos/dd/1.0/device/';
static const gDeviceCamera = 'http://ns.google.com/photos/dd/1.0/camera/';
static const gDeviceContainer = 'http://ns.google.com/photos/dd/1.0/container/';
static const gDeviceItem = 'http://ns.google.com/photos/dd/1.0/item/';
static const gFocus = 'http://ns.google.com/photos/1.0/focus/';
static const gImage = 'http://ns.google.com/photos/1.0/image/';
static const gPano = 'http://ns.google.com/photos/1.0/panorama/';
static const gSpherical = 'http://ns.google.com/videos/1.0/spherical/';
static const gettyImagesGift = 'http://xmp.gettyimages.com/gift/1.0/';
static const gimp210 = 'http://www.gimp.org/ns/2.10/';
static const gimpXmp = 'http://www.gimp.org/xmp/';
static const illustrator = 'http://ns.adobe.com/illustrator/1.0/';
static const iptc4xmpCore = 'http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/';
static const iptc4xmpExt = 'http://iptc.org/std/Iptc4xmpExt/2008-02-29/';
static const lr = 'http://ns.adobe.com/lightroom/1.0/';
static const mediapro = 'http://ns.iview-multimedia.com/mediapro/1.0/';
static const miCamera = 'http://ns.xiaomi.com/photos/1.0/camera/';
// also seen in the wild for prefix `MicrosoftPhoto`: 'http://ns.microsoft.com/photo/1.0'
static const microsoftPhoto = 'http://ns.microsoft.com/photo/1.0/';
static const mp1 = 'http://ns.microsoft.com/photo/1.1';
static const mp = 'http://ns.microsoft.com/photo/1.2/';
static const mpri = 'http://ns.microsoft.com/photo/1.2/t/RegionInfo#';
static const mpreg = 'http://ns.microsoft.com/photo/1.2/t/Region#';
static const mwgrs = 'http://www.metadataworkinggroup.com/schemas/regions/';
static const nga = 'https://standards.nga.gov/metadata/media/image/artobject/1.0';
static const opMedia = 'http://ns.oneplus.com/media/1.0/';
static const panorama = 'http://ns.adobe.com/photoshop/1.0/panorama-profile';
static const panoStudio = 'http://www.tshsoft.com/xmlns';
static const pdf = 'http://ns.adobe.com/pdf/1.3/';
static const pdfX = 'http://ns.adobe.com/pdfx/1.3/';
static const photoMechanic = 'http://ns.camerabits.com/photomechanic/1.0/';
static const photoshop = 'http://ns.adobe.com/photoshop/1.0/';
static const plus = 'http://ns.useplus.org/ldf/xmp/1.0/';
static const pmtm = 'http://www.hdrsoft.com/photomatix_settings01';
static const rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
static const stCamera = 'http://ns.adobe.com/photoshop/1.0/camera-profile';
static const stEvt = 'http://ns.adobe.com/xap/1.0/sType/ResourceEvent#';
static const stRef = 'http://ns.adobe.com/xap/1.0/sType/ResourceRef#';
static const tiff = 'http://ns.adobe.com/tiff/1.0/';
static const x = 'adobe:ns:meta/';
static const xmp = 'http://ns.adobe.com/xap/1.0/';
static const xmpBJ = 'http://ns.adobe.com/xap/1.0/bj/';
static const xmpDM = 'http://ns.adobe.com/xmp/1.0/DynamicMedia/';
static const xmpGImg = 'http://ns.adobe.com/xap/1.0/g/img/';
static const xmpMM = 'http://ns.adobe.com/xap/1.0/mm/';
static const xmpNote = 'http://ns.adobe.com/xmp/note/';
static const xmpRights = 'http://ns.adobe.com/xap/1.0/rights/';
static const xmpTPg = 'http://ns.adobe.com/xap/1.0/t/pg/';
static const xperiaCamera = 'http://xmlns.sony.net/xperia/camera/1.0/';
}
class XmpElements {
static const xXmpmeta = 'xmpmeta';
static const rdfRoot = 'RDF';
static const rdfDescription = 'Description';
static const containerDirectory = 'Directory';
static const dcDescription = 'description';
static const dcSubject = 'subject';
static const dcTitle = 'title';
static const msPhotoRating = 'Rating';
static const xmpRating = 'Rating';
}
class XmpAttributes {
static const xXmptk = 'xmptk';
static const rdfAbout = 'about';
static const gCameraMicroVideo = 'MicroVideo';
static const gCameraMicroVideoVersion = 'MicroVideoVersion';
static const gCameraMicroVideoOffset = 'MicroVideoOffset';
static const gCameraMicroVideoPresentationTimestampUs = 'MicroVideoPresentationTimestampUs';
static const gCameraMotionPhoto = 'MotionPhoto';
static const gCameraMotionPhotoVersion = 'MotionPhotoVersion';
static const gCameraMotionPhotoPresentationTimestampUs = 'MotionPhotoPresentationTimestampUs';
static const xmpCreateDate = 'CreateDate';
static const xmpMetadataDate = 'MetadataDate';
static const xmpModifyDate = 'ModifyDate';
static const xmpNoteHasExtendedXMP = 'HasExtendedXMP';
}

View file

@ -5,11 +5,11 @@ import 'package:aves/l10n/l10n.dart';
import 'package:aves/model/device.dart'; import 'package:aves/model/device.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/analysis_controller.dart'; import 'package:aves/model/source/analysis_controller.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/media_store_source.dart'; import 'package:aves/model/source/media_store_source.dart';
import 'package:aves/model/source/source_state.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/view/view.dart';
import 'package:aves_model/aves_model.dart';
import 'package:fijkplayer/fijkplayer.dart'; import 'package:fijkplayer/fijkplayer.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';

View file

@ -1,10 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/convert/convert.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/model/metadata/date_modifier.dart';
import 'package:aves/model/metadata/enums/metadata_type.dart';
import 'package:aves/model/metadata/fields.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';

View file

@ -1,9 +1,9 @@
import 'package:aves/convert/convert.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/entry/extensions/multipage.dart';
import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/geotiff.dart'; import 'package:aves/model/geotiff.dart';
import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/metadata/catalog.dart';
import 'package:aves/model/metadata/fields.dart';
import 'package:aves/model/metadata/overlay.dart'; import 'package:aves/model/metadata/overlay.dart';
import 'package:aves/model/multipage.dart'; import 'package:aves/model/multipage.dart';
import 'package:aves/model/panorama.dart'; import 'package:aves/model/panorama.dart';
@ -12,6 +12,7 @@ import 'package:aves/services/common/service_policy.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/services/metadata/xmp.dart'; import 'package:aves/services/metadata/xmp.dart';
import 'package:aves/utils/time_utils.dart'; import 'package:aves/utils/time_utils.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';

View file

@ -1,9 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/model/storage/relative_dir.dart';
import 'package:aves/model/storage/volume.dart';
import 'package:aves/services/common/output_buffer.dart'; import 'package:aves/services/common/output_buffer.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:streams_channel/streams_channel.dart'; import 'package:streams_channel/streams_channel.dart';

View file

@ -1,5 +1,5 @@
import 'package:aves/model/wallpaper_target.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
class WallpaperService { class WallpaperService {

View file

@ -1,8 +1,7 @@
import 'package:aves/model/apps.dart'; import 'package:aves/model/apps.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/storage/volume.dart';
import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/model/vaults/vaults.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -82,6 +81,19 @@ class AndroidFileUtils {
return volume != null || path.endsWith(separator) ? volume : getStorageVolume('$path$separator'); return volume != null || path.endsWith(separator) ? volume : getStorageVolume('$path$separator');
} }
// prefer static method over a null returning factory constructor
VolumeRelativeDirectory? relativeDirectoryFromPath(String dirPath) {
final volume = getStorageVolume(dirPath);
if (volume == null) return null;
final root = volume.path;
final rootLength = root.length;
return VolumeRelativeDirectory(
volumePath: root,
relativeDir: dirPath.length < rootLength ? '' : dirPath.substring(rootLength),
);
}
bool isOnRemovableStorage(String path) => getStorageVolume(path)?.isRemovable ?? false; bool isOnRemovableStorage(String path) => getStorageVolume(path)?.isRemovable ?? false;
AlbumType getAlbumType(String dirPath) { AlbumType getAlbumType(String dirPath) {

View file

@ -1,194 +1,29 @@
import 'package:aves/ref/metadata/xmp.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:xml/xml.dart'; import 'package:xml/xml.dart';
class Namespaces {
static const acdsee = 'http://ns.acdsee.com/iptc/1.0/';
static const adsmlat = 'http://adsml.org/xmlns/';
static const avm = 'http://www.communicatingastronomy.org/avm/1.0/';
static const camera = 'http://pix4d.com/camera/1.0/';
static const cc = 'http://creativecommons.org/ns#';
static const creatorAtom = 'http://ns.adobe.com/creatorAtom/1.0/';
static const crd = 'http://ns.adobe.com/camera-raw-defaults/1.0/';
static const crlcp = 'http://ns.adobe.com/camera-raw-embedded-lens-profile/1.0/';
static const crs = 'http://ns.adobe.com/camera-raw-settings/1.0/';
static const crss = 'http://ns.adobe.com/camera-raw-saved-settings/1.0/';
static const darktable = 'http://darktable.sf.net/';
static const dc = 'http://purl.org/dc/elements/1.1/';
static const dcterms = 'http://purl.org/dc/terms/';
static const dicom = 'http://ns.adobe.com/DICOM/';
static const digiKam = 'http://www.digikam.org/ns/1.0/';
static const droneDji = 'http://www.dji.com/drone-dji/1.0/';
static const dwc = 'http://rs.tdwg.org/dwc/index.htm';
static const dwciri = 'http://rs.tdwg.org/dwc/iri/';
static const exif = 'http://ns.adobe.com/exif/1.0/';
static const exifAux = 'http://ns.adobe.com/exif/1.0/aux/';
static const exifEx = 'http://cipa.jp/exif/1.0/';
static const fstop = 'http://www.fstopapp.com/xmp/';
static const gAudio = 'http://ns.google.com/photos/1.0/audio/';
static const gCamera = 'http://ns.google.com/photos/1.0/camera/';
static const gContainer = 'http://ns.google.com/photos/1.0/container/';
static const gCreations = 'http://ns.google.com/photos/1.0/creations/';
static const gDepth = 'http://ns.google.com/photos/1.0/depthmap/';
static const gDevice = 'http://ns.google.com/photos/dd/1.0/device/';
static const gDeviceCamera = 'http://ns.google.com/photos/dd/1.0/camera/';
static const gDeviceContainer = 'http://ns.google.com/photos/dd/1.0/container/';
static const gDeviceItem = 'http://ns.google.com/photos/dd/1.0/item/';
static const gFocus = 'http://ns.google.com/photos/1.0/focus/';
static const gImage = 'http://ns.google.com/photos/1.0/image/';
static const gPano = 'http://ns.google.com/photos/1.0/panorama/';
static const gSpherical = 'http://ns.google.com/videos/1.0/spherical/';
static const gettyImagesGift = 'http://xmp.gettyimages.com/gift/1.0/';
static const gimp210 = 'http://www.gimp.org/ns/2.10/';
static const gimpXmp = 'http://www.gimp.org/xmp/';
static const illustrator = 'http://ns.adobe.com/illustrator/1.0/';
static const iptc4xmpCore = 'http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/';
static const iptc4xmpExt = 'http://iptc.org/std/Iptc4xmpExt/2008-02-29/';
static const lr = 'http://ns.adobe.com/lightroom/1.0/';
static const mediapro = 'http://ns.iview-multimedia.com/mediapro/1.0/';
static const miCamera = 'http://ns.xiaomi.com/photos/1.0/camera/';
// also seen in the wild for prefix `MicrosoftPhoto`: 'http://ns.microsoft.com/photo/1.0'
static const microsoftPhoto = 'http://ns.microsoft.com/photo/1.0/';
static const mp1 = 'http://ns.microsoft.com/photo/1.1';
static const mp = 'http://ns.microsoft.com/photo/1.2/';
static const mpri = 'http://ns.microsoft.com/photo/1.2/t/RegionInfo#';
static const mpreg = 'http://ns.microsoft.com/photo/1.2/t/Region#';
static const mwgrs = 'http://www.metadataworkinggroup.com/schemas/regions/';
static const nga = 'https://standards.nga.gov/metadata/media/image/artobject/1.0';
static const opMedia = 'http://ns.oneplus.com/media/1.0/';
static const panorama = 'http://ns.adobe.com/photoshop/1.0/panorama-profile';
static const panoStudio = 'http://www.tshsoft.com/xmlns';
static const pdf = 'http://ns.adobe.com/pdf/1.3/';
static const pdfX = 'http://ns.adobe.com/pdfx/1.3/';
static const photoMechanic = 'http://ns.camerabits.com/photomechanic/1.0/';
static const photoshop = 'http://ns.adobe.com/photoshop/1.0/';
static const plus = 'http://ns.useplus.org/ldf/xmp/1.0/';
static const pmtm = 'http://www.hdrsoft.com/photomatix_settings01';
static const rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
static const stCamera = 'http://ns.adobe.com/photoshop/1.0/camera-profile';
static const stEvt = 'http://ns.adobe.com/xap/1.0/sType/ResourceEvent#';
static const stRef = 'http://ns.adobe.com/xap/1.0/sType/ResourceRef#';
static const tiff = 'http://ns.adobe.com/tiff/1.0/';
static const x = 'adobe:ns:meta/';
static const xmp = 'http://ns.adobe.com/xap/1.0/';
static const xmpBJ = 'http://ns.adobe.com/xap/1.0/bj/';
static const xmpDM = 'http://ns.adobe.com/xmp/1.0/DynamicMedia/';
static const xmpGImg = 'http://ns.adobe.com/xap/1.0/g/img/';
static const xmpMM = 'http://ns.adobe.com/xap/1.0/mm/';
static const xmpNote = 'http://ns.adobe.com/xmp/note/';
static const xmpRights = 'http://ns.adobe.com/xap/1.0/rights/';
static const xmpTPg = 'http://ns.adobe.com/xap/1.0/t/pg/';
static const xperiaCamera = 'http://xmlns.sony.net/xperia/camera/1.0/';
// cf https://exiftool.org/TagNames/XMP.html
static const Map<String, String> nsTitles = {
acdsee: 'ACDSee',
adsmlat: 'AdsML',
exifAux: 'Exif Aux',
avm: 'Astronomy Visualization',
camera: 'Pix4D Camera',
cc: 'Creative Commons',
crd: 'Camera Raw Defaults',
creatorAtom: 'After Effects',
crs: 'Camera Raw Settings',
crss: 'Camera Raw Saved Settings',
darktable: 'darktable',
dc: 'Dublin Core',
digiKam: 'digiKam',
droneDji: 'DJI Drone',
dwc: 'Darwin Core',
exif: 'Exif',
exifEx: 'Exif Ex',
fstop: 'F-Stop',
gAudio: 'Google Audio',
gCamera: 'Google Camera',
gContainer: 'Google Container',
gCreations: 'Google Creations',
gDepth: 'Google Depth',
gDevice: 'Google Device',
gFocus: 'Google Focus',
gImage: 'Google Image',
gPano: 'Google Panorama',
gSpherical: 'Google Spherical',
gettyImagesGift: 'Getty Images',
gimp210: 'GIMP 2.10',
gimpXmp: 'GIMP',
illustrator: 'Illustrator',
iptc4xmpCore: 'IPTC Core',
iptc4xmpExt: 'IPTC Extension',
lr: 'Lightroom',
mediapro: 'MediaPro',
miCamera: 'Mi Camera',
microsoftPhoto: 'Microsoft Photo 1.0',
mp1: 'Microsoft Photo 1.1',
mp: 'Microsoft Photo 1.2',
mwgrs: 'Regions',
nga: 'National Gallery of Art',
opMedia: 'OnePlus Media',
panorama: 'Panorama',
panoStudio: 'PanoramaStudio',
pdf: 'PDF',
pdfX: 'PDF/X',
photoMechanic: 'Photo Mechanic',
photoshop: 'Photoshop',
plus: 'PLUS',
pmtm: 'Photomatix',
tiff: 'TIFF',
xmp: 'Basic',
xmpBJ: 'Basic Job Ticket',
xmpDM: 'Dynamic Media',
xmpMM: 'Media Management',
xmpNote: 'Note',
xmpRights: 'Rights Management',
xmpTPg: 'Paged-Text',
xperiaCamera: 'Xperia Camera',
};
static final defaultPrefixes = {
gContainer: 'Container',
dc: 'dc',
gCamera: 'GCamera',
microsoftPhoto: 'MicrosoftPhoto',
rdf: 'rdf',
x: 'x',
xmp: 'xmp',
xmpGImg: 'xmpGImg',
xmpNote: 'xmpNote',
};
}
class XMP { class XMP {
static const xmlnsPrefix = 'xmlns'; static const xmlnsPrefix = 'xmlns';
static const propNamespaceSeparator = ':'; static const propNamespaceSeparator = ':';
static const structFieldSeparator = '/'; static const structFieldSeparator = '/';
static String prefixOf(String ns) => Namespaces.defaultPrefixes[ns] ?? ''; static final _defaultPrefixes = {
XmpNamespaces.gContainer: 'Container',
XmpNamespaces.dc: 'dc',
XmpNamespaces.gCamera: 'GCamera',
XmpNamespaces.microsoftPhoto: 'MicrosoftPhoto',
XmpNamespaces.rdf: 'rdf',
XmpNamespaces.x: 'x',
XmpNamespaces.xmp: 'xmp',
XmpNamespaces.xmpGImg: 'xmpGImg',
XmpNamespaces.xmpNote: 'xmpNote',
};
// elements static String prefixOf(String ns) => _defaultPrefixes[ns] ?? '';
static const xXmpmeta = 'xmpmeta';
static const rdfRoot = 'RDF';
static const rdfDescription = 'Description';
static const containerDirectory = 'Directory';
static const dcDescription = 'description';
static const dcSubject = 'subject';
static const dcTitle = 'title';
static const msPhotoRating = 'Rating';
static const xmpRating = 'Rating';
// attributes static const nsRdf = XmpNamespaces.rdf;
static const xXmptk = 'xmptk'; static const nsX = XmpNamespaces.x;
static const rdfAbout = 'about'; static const nsXmp = XmpNamespaces.xmp;
static const gCameraMicroVideo = 'MicroVideo';
static const gCameraMicroVideoVersion = 'MicroVideoVersion';
static const gCameraMicroVideoOffset = 'MicroVideoOffset';
static const gCameraMicroVideoPresentationTimestampUs = 'MicroVideoPresentationTimestampUs';
static const gCameraMotionPhoto = 'MotionPhoto';
static const gCameraMotionPhotoVersion = 'MotionPhotoVersion';
static const gCameraMotionPhotoPresentationTimestampUs = 'MotionPhotoPresentationTimestampUs';
static const xmpCreateDate = 'CreateDate';
static const xmpMetadataDate = 'MetadataDate';
static const xmpModifyDate = 'ModifyDate';
static const xmpNoteHasExtendedXMP = 'HasExtendedXMP';
// for `rdf:Description` node only // for `rdf:Description` node only
static bool _hasMeaningfulChildren(XmlNode node) => node.children.any((v) => v.nodeType != XmlNodeType.TEXT || v.text.trim().isNotEmpty); static bool _hasMeaningfulChildren(XmlNode node) => node.children.any((v) => v.nodeType != XmlNodeType.TEXT || v.text.trim().isNotEmpty);
@ -197,9 +32,9 @@ class XMP {
static bool _hasMeaningfulAttributes(XmlNode description) { static bool _hasMeaningfulAttributes(XmlNode description) {
final hasMeaningfulAttributes = description.attributes.any((v) { final hasMeaningfulAttributes = description.attributes.any((v) {
switch (v.name.local) { switch (v.name.local) {
case rdfAbout: case XmpAttributes.rdfAbout:
case xmpMetadataDate: case XmpAttributes.xmpMetadataDate:
case xmpModifyDate: case XmpAttributes.xmpModifyDate:
return false; return false;
default: default:
switch (v.name.prefix) { switch (v.name.prefix) {
@ -338,10 +173,10 @@ class XMP {
node.children.add(rootBuilder.buildFragment()); node.children.add(rootBuilder.buildFragment());
final bagBuilder = XmlBuilder(); final bagBuilder = XmlBuilder();
bagBuilder.namespace(Namespaces.rdf, prefixOf(Namespaces.rdf)); bagBuilder.namespace(nsRdf, prefixOf(nsRdf));
bagBuilder.element('Bag', namespace: Namespaces.rdf, nest: () { bagBuilder.element('Bag', namespace: nsRdf, nest: () {
values.forEach((v) { values.forEach((v) {
bagBuilder.element('li', namespace: Namespaces.rdf, nest: v); bagBuilder.element('li', namespace: nsRdf, nest: v);
}); });
}); });
node.children.last.children.add(bagBuilder.buildFragment()); node.children.last.children.add(bagBuilder.buildFragment());
@ -363,42 +198,42 @@ class XMP {
} }
if (xmpDoc == null) { if (xmpDoc == null) {
final builder = XmlBuilder(); final builder = XmlBuilder();
builder.namespace(Namespaces.x, prefixOf(Namespaces.x)); builder.namespace(nsX, prefixOf(nsX));
builder.element(xXmpmeta, namespace: Namespaces.x, namespaces: { builder.element(XmpElements.xXmpmeta, namespace: nsX, namespaces: {
Namespaces.x: prefixOf(Namespaces.x), nsX: prefixOf(nsX),
}, attributes: { }, attributes: {
'${prefixOf(Namespaces.x)}$propNamespaceSeparator$xXmptk': await toolkit(), '${prefixOf(nsX)}$propNamespaceSeparator${XmpAttributes.xXmptk}': await toolkit(),
}); });
xmpDoc = builder.buildDocument(); xmpDoc = builder.buildDocument();
} }
final root = xmpDoc.rootElement; final root = xmpDoc.rootElement;
XmlNode? rdf = root.getElement(rdfRoot, namespace: Namespaces.rdf); XmlNode? rdf = root.getElement(XmpElements.rdfRoot, namespace: nsRdf);
if (rdf == null) { if (rdf == null) {
final builder = XmlBuilder(); final builder = XmlBuilder();
builder.namespace(Namespaces.rdf, prefixOf(Namespaces.rdf)); builder.namespace(nsRdf, prefixOf(nsRdf));
builder.element(rdfRoot, namespace: Namespaces.rdf, namespaces: { builder.element(XmpElements.rdfRoot, namespace: nsRdf, namespaces: {
Namespaces.rdf: prefixOf(Namespaces.rdf), nsRdf: prefixOf(nsRdf),
}); });
// get element because doc fragment cannot be used to edit // get element because doc fragment cannot be used to edit
root.children.add(builder.buildFragment()); root.children.add(builder.buildFragment());
rdf = root.getElement(rdfRoot, namespace: Namespaces.rdf)!; rdf = root.getElement(XmpElements.rdfRoot, namespace: nsRdf)!;
} }
// content can be split in multiple `rdf:Description` elements // content can be split in multiple `rdf:Description` elements
List<XmlNode> descriptions = rdf.children.where((node) { List<XmlNode> descriptions = rdf.children.where((node) {
return node is XmlElement && node.name.local == rdfDescription && node.name.namespaceUri == Namespaces.rdf; return node is XmlElement && node.name.local == XmpElements.rdfDescription && node.name.namespaceUri == nsRdf;
}).toList(); }).toList();
if (descriptions.isEmpty) { if (descriptions.isEmpty) {
final builder = XmlBuilder(); final builder = XmlBuilder();
builder.namespace(Namespaces.rdf, prefixOf(Namespaces.rdf)); builder.namespace(nsRdf, prefixOf(nsRdf));
builder.element(rdfDescription, namespace: Namespaces.rdf, attributes: { builder.element(XmpElements.rdfDescription, namespace: nsRdf, attributes: {
'${prefixOf(Namespaces.rdf)}$propNamespaceSeparator$rdfAbout': '', '${prefixOf(nsRdf)}$propNamespaceSeparator${XmpAttributes.rdfAbout}': '',
}); });
rdf.children.add(builder.buildFragment()); rdf.children.add(builder.buildFragment());
// get element because doc fragment cannot be used to edit // get element because doc fragment cannot be used to edit
descriptions.add(rdf.getElement(rdfDescription, namespace: Namespaces.rdf)!); descriptions.add(rdf.getElement(XmpElements.rdfDescription, namespace: nsRdf)!);
} }
final modified = apply(descriptions); final modified = apply(descriptions);
@ -410,10 +245,10 @@ class XMP {
if (rdf.children.isNotEmpty) { if (rdf.children.isNotEmpty) {
if (modified) { if (modified) {
_addNamespaces(descriptions.first, {Namespaces.xmp: prefixOf(Namespaces.xmp)}); _addNamespaces(descriptions.first, {nsXmp: prefixOf(nsXmp)});
final xmpDate = toXmpDate(modifyDate ?? DateTime.now()); final xmpDate = toXmpDate(modifyDate ?? DateTime.now());
setAttribute(descriptions, xmpMetadataDate, xmpDate, namespace: Namespaces.xmp, strat: XmpEditStrategy.always); setAttribute(descriptions, XmpAttributes.xmpMetadataDate, xmpDate, namespace: nsXmp, strat: XmpEditStrategy.always);
setAttribute(descriptions, xmpModifyDate, xmpDate, namespace: Namespaces.xmp, strat: XmpEditStrategy.always); setAttribute(descriptions, XmpAttributes.xmpModifyDate, xmpDate, namespace: nsXmp, strat: XmpEditStrategy.always);
} }
} else { } else {
// clear XMP if there are no attributes or elements worth preserving // clear XMP if there are no attributes or elements worth preserving

View file

@ -3,7 +3,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraChipAction on ChipAction { extension ExtraChipActionView on ChipAction {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
case ChipAction.goToAlbumPage: case ChipAction.goToAlbumPage:

View file

@ -3,7 +3,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
extension ExtraChipSetAction on ChipSetAction { extension ExtraChipSetActionView on ChipSetAction {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
// general // general

View file

@ -4,7 +4,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraEntryAction on EntryAction { extension ExtraEntryActionView on EntryAction {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
case EntryAction.info: case EntryAction.info:

View file

@ -3,7 +3,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
extension ExtraEntrySetAction on EntrySetAction { extension ExtraEntrySetActionView on EntrySetAction {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
// general // general

View file

@ -3,7 +3,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraMapAction on MapAction { extension ExtraMapActionView on MapAction {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
case MapAction.selectStyle: case MapAction.selectStyle:

View file

@ -3,7 +3,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraMapClusterAction on MapClusterAction { extension ExtraMapClusterActionView on MapClusterAction {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
case MapClusterAction.editLocation: case MapClusterAction.editLocation:

View file

@ -3,7 +3,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraShareAction on ShareAction { extension ExtraShareActionView on ShareAction {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
case ShareAction.imageOnly: case ShareAction.imageOnly:

View file

@ -3,7 +3,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraSlideshowAction on SlideshowAction { extension ExtraSlideshowActionView on SlideshowAction {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
case SlideshowAction.resume: case SlideshowAction.resume:

View file

@ -2,7 +2,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraDateEditAction on DateEditAction { extension ExtraDateEditActionView on DateEditAction {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
case DateEditAction.setCustom: case DateEditAction.setCustom:

View file

@ -1,9 +1,8 @@
import 'package:aves/model/metadata/fields.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraDateFieldSource on DateFieldSource { extension ExtraDateFieldSourceView on DateFieldSource {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
case DateFieldSource.fileModifiedDate: case DateFieldSource.fileModifiedDate:
@ -18,19 +17,4 @@ extension ExtraDateFieldSource on DateFieldSource {
return 'Exif GPS date'; return 'Exif GPS date';
} }
} }
MetadataField? toMetadataField() {
switch (this) {
case DateFieldSource.fileModifiedDate:
return null;
case DateFieldSource.exifDate:
return MetadataField.exifDate;
case DateFieldSource.exifDateOriginal:
return MetadataField.exifDateOriginal;
case DateFieldSource.exifDateDigitized:
return MetadataField.exifDateDigitized;
case DateFieldSource.exifGpsDate:
return MetadataField.exifGpsDatestamp;
}
}
} }

View file

@ -0,0 +1,20 @@
import 'package:aves_model/aves_model.dart';
extension ExtraMetadataFieldView on MetadataField {
String get title {
switch (this) {
case MetadataField.exifDate:
return 'Exif date';
case MetadataField.exifDateOriginal:
return 'Exif original date';
case MetadataField.exifDateDigitized:
return 'Exif digitized date';
case MetadataField.exifGpsDatestamp:
return 'Exif GPS date';
case MetadataField.xmpXmpCreateDate:
return 'XMP xmp:CreateDate';
default:
return name;
}
}
}

View file

@ -2,7 +2,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraLengthUnit on LengthUnit { extension ExtraLengthUnitView on LengthUnit {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
case LengthUnit.px: case LengthUnit.px:

View file

@ -2,7 +2,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraLocationEditAction on LocationEditAction { extension ExtraLocationEditActionView on LocationEditAction {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
case LocationEditAction.chooseOnMap: case LocationEditAction.chooseOnMap:

View file

@ -0,0 +1,29 @@
import 'package:aves_model/aves_model.dart';
extension ExtraMetadataTypeView on MetadataType {
// match `metadata-extractor` directory names
String getText() {
switch (this) {
case MetadataType.comment:
return 'Comment';
case MetadataType.exif:
return 'Exif';
case MetadataType.iccProfile:
return 'ICC Profile';
case MetadataType.iptc:
return 'IPTC';
case MetadataType.jfif:
return 'JFIF';
case MetadataType.jpegAdobe:
return 'Adobe JPEG';
case MetadataType.jpegDucky:
return 'Ducky';
case MetadataType.mp4:
return 'MP4';
case MetadataType.photoshopIrb:
return 'Photoshop';
case MetadataType.xmp:
return 'XMP';
}
}
}

View file

@ -3,7 +3,7 @@ import 'package:aves_map/aves_map.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraAccessibilityAnimationsName on AccessibilityAnimations { extension ExtraAccessibilityAnimationsView on AccessibilityAnimations {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case AccessibilityAnimations.system: case AccessibilityAnimations.system:
@ -16,7 +16,7 @@ extension ExtraAccessibilityAnimationsName on AccessibilityAnimations {
} }
} }
extension ExtraAccessibilityTimeoutName on AccessibilityTimeout { extension ExtraAccessibilityTimeoutView on AccessibilityTimeout {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case AccessibilityTimeout.system: case AccessibilityTimeout.system:
@ -35,7 +35,7 @@ extension ExtraAccessibilityTimeoutName on AccessibilityTimeout {
} }
} }
extension ExtraAvesThemeBrightnessName on AvesThemeBrightness { extension ExtraAvesThemeBrightnessView on AvesThemeBrightness {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case AvesThemeBrightness.system: case AvesThemeBrightness.system:
@ -50,7 +50,7 @@ extension ExtraAvesThemeBrightnessName on AvesThemeBrightness {
} }
} }
extension ExtraCoordinateFormatName on CoordinateFormat { extension ExtraCoordinateFormatView on CoordinateFormat {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case CoordinateFormat.dms: case CoordinateFormat.dms:
@ -61,7 +61,7 @@ extension ExtraCoordinateFormatName on CoordinateFormat {
} }
} }
extension ExtraDisplayRefreshRateModeName on DisplayRefreshRateMode { extension ExtraDisplayRefreshRateModeView on DisplayRefreshRateMode {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case DisplayRefreshRateMode.auto: case DisplayRefreshRateMode.auto:
@ -74,7 +74,7 @@ extension ExtraDisplayRefreshRateModeName on DisplayRefreshRateMode {
} }
} }
extension ExtraEntryMapStyleName on EntryMapStyle { extension ExtraEntryMapStyleView on EntryMapStyle {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case EntryMapStyle.googleNormal: case EntryMapStyle.googleNormal:
@ -97,7 +97,7 @@ extension ExtraEntryMapStyleName on EntryMapStyle {
} }
} }
extension ExtraHomePageSettingName on HomePageSetting { extension ExtraHomePageSettingView on HomePageSetting {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case HomePageSetting.collection: case HomePageSetting.collection:
@ -108,7 +108,7 @@ extension ExtraHomePageSettingName on HomePageSetting {
} }
} }
extension ExtraKeepScreenOnName on KeepScreenOn { extension ExtraKeepScreenOnView on KeepScreenOn {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case KeepScreenOn.never: case KeepScreenOn.never:
@ -123,7 +123,7 @@ extension ExtraKeepScreenOnName on KeepScreenOn {
} }
} }
extension ExtraSlideshowVideoPlaybackName on SlideshowVideoPlayback { extension ExtraSlideshowVideoPlaybackView on SlideshowVideoPlayback {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case SlideshowVideoPlayback.skip: case SlideshowVideoPlayback.skip:
@ -136,7 +136,7 @@ extension ExtraSlideshowVideoPlaybackName on SlideshowVideoPlayback {
} }
} }
extension ExtraSubtitlePositionName on SubtitlePosition { extension ExtraSubtitlePositionView on SubtitlePosition {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case SubtitlePosition.top: case SubtitlePosition.top:
@ -147,33 +147,7 @@ extension ExtraSubtitlePositionName on SubtitlePosition {
} }
} }
extension ExtraThumbnailOverlayLocationIconName on ThumbnailOverlayLocationIcon { extension ExtraUnitSystemView on UnitSystem {
String getName(BuildContext context) {
switch (this) {
case ThumbnailOverlayLocationIcon.located:
return context.l10n.filterLocatedLabel;
case ThumbnailOverlayLocationIcon.unlocated:
return context.l10n.filterNoLocationLabel;
case ThumbnailOverlayLocationIcon.none:
return context.l10n.settingsDisabled;
}
}
}
extension ExtraThumbnailOverlayTagIconName on ThumbnailOverlayTagIcon {
String getName(BuildContext context) {
switch (this) {
case ThumbnailOverlayTagIcon.tagged:
return context.l10n.filterTaggedLabel;
case ThumbnailOverlayTagIcon.untagged:
return context.l10n.filterNoTagLabel;
case ThumbnailOverlayTagIcon.none:
return context.l10n.settingsDisabled;
}
}
}
extension ExtraUnitSystemName on UnitSystem {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case UnitSystem.metric: case UnitSystem.metric:
@ -184,7 +158,7 @@ extension ExtraUnitSystemName on UnitSystem {
} }
} }
extension ExtraVideoAutoPlayModeName on VideoAutoPlayMode { extension ExtraVideoAutoPlayModeView on VideoAutoPlayMode {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case VideoAutoPlayMode.disabled: case VideoAutoPlayMode.disabled:
@ -197,7 +171,7 @@ extension ExtraVideoAutoPlayModeName on VideoAutoPlayMode {
} }
} }
extension ExtraVideoBackgroundModeName on VideoBackgroundMode { extension ExtraVideoBackgroundModeView on VideoBackgroundMode {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case VideoBackgroundMode.disabled: case VideoBackgroundMode.disabled:
@ -208,7 +182,7 @@ extension ExtraVideoBackgroundModeName on VideoBackgroundMode {
} }
} }
extension ExtraVideoControlsName on VideoControls { extension ExtraVideoControlsView on VideoControls {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case VideoControls.play: case VideoControls.play:
@ -223,7 +197,7 @@ extension ExtraVideoControlsName on VideoControls {
} }
} }
extension ExtraVideoLoopModeName on VideoLoopMode { extension ExtraVideoLoopModeView on VideoLoopMode {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case VideoLoopMode.never: case VideoLoopMode.never:
@ -236,7 +210,7 @@ extension ExtraVideoLoopModeName on VideoLoopMode {
} }
} }
extension ExtraViewerTransitionName on ViewerTransition { extension ExtraViewerTransitionView on ViewerTransition {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case ViewerTransition.slide: case ViewerTransition.slide:
@ -253,7 +227,7 @@ extension ExtraViewerTransitionName on ViewerTransition {
} }
} }
extension ExtraWidgetDisplayedItemName on WidgetDisplayedItem { extension ExtraWidgetDisplayedItemView on WidgetDisplayedItem {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case WidgetDisplayedItem.random: case WidgetDisplayedItem.random:
@ -264,7 +238,7 @@ extension ExtraWidgetDisplayedItemName on WidgetDisplayedItem {
} }
} }
extension ExtraWidgetOpenPageName on WidgetOpenPage { extension ExtraWidgetOpenPageView on WidgetOpenPage {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case WidgetOpenPage.home: case WidgetOpenPage.home:

View file

@ -0,0 +1,27 @@
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart';
extension ExtraThumbnailOverlayLocationIconView on ThumbnailOverlayLocationIcon {
String getName(BuildContext context) {
switch (this) {
case ThumbnailOverlayLocationIcon.located:
return context.l10n.filterLocatedLabel;
case ThumbnailOverlayLocationIcon.unlocated:
return context.l10n.filterNoLocationLabel;
case ThumbnailOverlayLocationIcon.none:
return context.l10n.settingsDisabled;
}
}
IconData getIcon(BuildContext context) {
switch (this) {
case ThumbnailOverlayLocationIcon.unlocated:
return AIcons.locationUnlocated;
case ThumbnailOverlayLocationIcon.located:
case ThumbnailOverlayLocationIcon.none:
return AIcons.location;
}
}
}

View file

@ -0,0 +1,28 @@
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart';
extension ExtraThumbnailOverlayTagIconView on ThumbnailOverlayTagIcon {
String getName(BuildContext context) {
switch (this) {
case ThumbnailOverlayTagIcon.tagged:
return context.l10n.filterTaggedLabel;
case ThumbnailOverlayTagIcon.untagged:
return context.l10n.filterNoTagLabel;
case ThumbnailOverlayTagIcon.none:
return context.l10n.settingsDisabled;
}
}
IconData getIcon(BuildContext context) {
switch (this) {
case ThumbnailOverlayTagIcon.tagged:
return AIcons.tag;
case ThumbnailOverlayTagIcon.untagged:
return AIcons.tagUntagged;
case ThumbnailOverlayTagIcon.none:
return AIcons.tag;
}
}
}

View file

@ -0,0 +1,24 @@
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart';
extension ExtraAlbumTypeView on AlbumType {
String? getName(BuildContext context) {
switch (this) {
case AlbumType.camera:
return context.l10n.albumCamera;
case AlbumType.download:
return context.l10n.albumDownload;
case AlbumType.screenshots:
return context.l10n.albumScreenshots;
case AlbumType.screenRecordings:
return context.l10n.albumScreenRecordings;
case AlbumType.videoCaptures:
return context.l10n.albumVideoCaptures;
case AlbumType.regular:
case AlbumType.vault:
case AlbumType.app:
return null;
}
}
}

View file

@ -0,0 +1,62 @@
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart';
extension ExtraEntryGroupFactorView on EntryGroupFactor {
String getName(BuildContext context) {
final l10n = context.l10n;
switch (this) {
case EntryGroupFactor.album:
return l10n.collectionGroupAlbum;
case EntryGroupFactor.month:
return l10n.collectionGroupMonth;
case EntryGroupFactor.day:
return l10n.collectionGroupDay;
case EntryGroupFactor.none:
return l10n.collectionGroupNone;
}
}
IconData get icon {
switch (this) {
case EntryGroupFactor.album:
return AIcons.album;
case EntryGroupFactor.month:
return AIcons.dateByMonth;
case EntryGroupFactor.day:
return AIcons.dateByDay;
case EntryGroupFactor.none:
return AIcons.clear;
}
}
}
extension ExtraAlbumChipGroupFactorView on AlbumChipGroupFactor {
String getName(BuildContext context) {
final l10n = context.l10n;
switch (this) {
case AlbumChipGroupFactor.importance:
return l10n.albumGroupTier;
case AlbumChipGroupFactor.mimeType:
return l10n.albumGroupType;
case AlbumChipGroupFactor.volume:
return l10n.albumGroupVolume;
case AlbumChipGroupFactor.none:
return l10n.albumGroupNone;
}
}
IconData get icon {
switch (this) {
case AlbumChipGroupFactor.importance:
return AIcons.important;
case AlbumChipGroupFactor.mimeType:
return AIcons.mimeType;
case AlbumChipGroupFactor.volume:
return AIcons.removableStorage;
case AlbumChipGroupFactor.none:
return AIcons.clear;
}
}
}

View file

@ -0,0 +1,29 @@
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart';
extension ExtraTileLayoutView on TileLayout {
String getName(BuildContext context) {
final l10n = context.l10n;
switch (this) {
case TileLayout.mosaic:
return l10n.tileLayoutMosaic;
case TileLayout.grid:
return l10n.tileLayoutGrid;
case TileLayout.list:
return l10n.tileLayoutList;
}
}
IconData get icon {
switch (this) {
case TileLayout.mosaic:
return AIcons.layoutMosaic;
case TileLayout.grid:
return AIcons.layoutGrid;
case TileLayout.list:
return AIcons.layoutList;
}
}
}

View file

@ -1,9 +1,9 @@
import 'package:aves/model/source/enums/enums.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';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraEntrySortFactor on EntrySortFactor { extension ExtraEntrySortFactorView on EntrySortFactor {
String getName(BuildContext context) { String getName(BuildContext context) {
final l10n = context.l10n; final l10n = context.l10n;
switch (this) { switch (this) {
@ -46,7 +46,7 @@ extension ExtraEntrySortFactor on EntrySortFactor {
} }
} }
extension ExtraChipSortFactor on ChipSortFactor { extension ExtraChipSortFactorView on ChipSortFactor {
String getName(BuildContext context) { String getName(BuildContext context) {
final l10n = context.l10n; final l10n = context.l10n;
switch (this) { switch (this) {
@ -87,86 +87,3 @@ extension ExtraChipSortFactor on ChipSortFactor {
} }
} }
} }
extension ExtraEntryGroupFactor on EntryGroupFactor {
String getName(BuildContext context) {
final l10n = context.l10n;
switch (this) {
case EntryGroupFactor.album:
return l10n.collectionGroupAlbum;
case EntryGroupFactor.month:
return l10n.collectionGroupMonth;
case EntryGroupFactor.day:
return l10n.collectionGroupDay;
case EntryGroupFactor.none:
return l10n.collectionGroupNone;
}
}
IconData get icon {
switch (this) {
case EntryGroupFactor.album:
return AIcons.album;
case EntryGroupFactor.month:
return AIcons.dateByMonth;
case EntryGroupFactor.day:
return AIcons.dateByDay;
case EntryGroupFactor.none:
return AIcons.clear;
}
}
}
extension ExtraAlbumChipGroupFactor on AlbumChipGroupFactor {
String getName(BuildContext context) {
final l10n = context.l10n;
switch (this) {
case AlbumChipGroupFactor.importance:
return l10n.albumGroupTier;
case AlbumChipGroupFactor.mimeType:
return l10n.albumGroupType;
case AlbumChipGroupFactor.volume:
return l10n.albumGroupVolume;
case AlbumChipGroupFactor.none:
return l10n.albumGroupNone;
}
}
IconData get icon {
switch (this) {
case AlbumChipGroupFactor.importance:
return AIcons.important;
case AlbumChipGroupFactor.mimeType:
return AIcons.mimeType;
case AlbumChipGroupFactor.volume:
return AIcons.removableStorage;
case AlbumChipGroupFactor.none:
return AIcons.clear;
}
}
}
extension ExtraTileLayout on TileLayout {
String getName(BuildContext context) {
final l10n = context.l10n;
switch (this) {
case TileLayout.mosaic:
return l10n.tileLayoutMosaic;
case TileLayout.grid:
return l10n.tileLayoutGrid;
case TileLayout.list:
return l10n.tileLayoutList;
}
}
IconData get icon {
switch (this) {
case TileLayout.mosaic:
return AIcons.layoutMosaic;
case TileLayout.grid:
return AIcons.layoutGrid;
case TileLayout.list:
return AIcons.layoutList;
}
}
}

View file

@ -1,7 +1,7 @@
import 'package:aves/l10n/l10n.dart'; import 'package:aves/l10n/l10n.dart';
import 'package:aves/model/source/enums/enums.dart'; import 'package:aves_model/aves_model.dart';
extension ExtraSourceState on SourceState { extension ExtraSourceStateView on SourceState {
String? getName(AppLocalizations l10n) { String? getName(AppLocalizations l10n) {
switch (this) { switch (this) {
case SourceState.loading: case SourceState.loading:

View file

@ -1,9 +1,8 @@
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
enum VaultLockType { system, pattern, pin, password } extension ExtraVaultLockTypeView on VaultLockType {
extension ExtraVaultLockType on VaultLockType {
String getText(BuildContext context) { String getText(BuildContext context) {
switch (this) { switch (this) {
case VaultLockType.system: case VaultLockType.system:

View file

@ -0,0 +1,12 @@
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/view/view.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart';
extension ExtraVolumeRelativeDirectoryView on VolumeRelativeDirectory {
String getVolumeDescription(BuildContext context) {
final volume = androidFileUtils.storageVolumes.firstWhereOrNull((volume) => volume.path == volumePath);
return volume?.getDescription(context) ?? volumePath;
}
}

View file

@ -0,0 +1,15 @@
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart';
extension ExtraStorageVolumeView on StorageVolume {
String getDescription(BuildContext? context) {
if (description != null) return description!;
// ideally, the context should always be provided, but in some cases (e.g. album comparison),
// this would require numerous additional methods to have the context as argument
// for such a minor benefit: fallback volume description on Android < N
final l10n = context?.l10n;
if (isPrimary) return l10n?.storageVolumeDescriptionFallbackPrimary ?? 'Internal Storage';
return l10n?.storageVolumeDescriptionFallbackNonPrimary ?? 'SD card';
}
}

View file

@ -1,9 +1,8 @@
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
enum WallpaperTarget { home, lock, homeLock } extension ExtraWallpaperTargetView on WallpaperTarget {
extension ExtraWallpaperTarget on WallpaperTarget {
String getName(BuildContext context) { String getName(BuildContext context) {
switch (this) { switch (this) {
case WallpaperTarget.home: case WallpaperTarget.home:

67
lib/view/src/xmp.dart Normal file
View file

@ -0,0 +1,67 @@
import 'package:aves/ref/metadata/xmp.dart';
class XmpNamespaceView {
// cf https://exiftool.org/TagNames/XMP.html
static const Map<String, String> nsTitles = {
XmpNamespaces.acdsee: 'ACDSee',
XmpNamespaces.adsmlat: 'AdsML',
XmpNamespaces.exifAux: 'Exif Aux',
XmpNamespaces.avm: 'Astronomy Visualization',
XmpNamespaces.camera: 'Pix4D Camera',
XmpNamespaces.cc: 'Creative Commons',
XmpNamespaces.crd: 'Camera Raw Defaults',
XmpNamespaces.creatorAtom: 'After Effects',
XmpNamespaces.crs: 'Camera Raw Settings',
XmpNamespaces.crss: 'Camera Raw Saved Settings',
XmpNamespaces.darktable: 'darktable',
XmpNamespaces.dc: 'Dublin Core',
XmpNamespaces.digiKam: 'digiKam',
XmpNamespaces.droneDji: 'DJI Drone',
XmpNamespaces.dwc: 'Darwin Core',
XmpNamespaces.exif: 'Exif',
XmpNamespaces.exifEx: 'Exif Ex',
XmpNamespaces.fstop: 'F-Stop',
XmpNamespaces.gAudio: 'Google Audio',
XmpNamespaces.gCamera: 'Google Camera',
XmpNamespaces.gContainer: 'Google Container',
XmpNamespaces.gCreations: 'Google Creations',
XmpNamespaces.gDepth: 'Google Depth',
XmpNamespaces.gDevice: 'Google Device',
XmpNamespaces.gFocus: 'Google Focus',
XmpNamespaces.gImage: 'Google Image',
XmpNamespaces.gPano: 'Google Panorama',
XmpNamespaces.gSpherical: 'Google Spherical',
XmpNamespaces.gettyImagesGift: 'Getty Images',
XmpNamespaces.gimp210: 'GIMP 2.10',
XmpNamespaces.gimpXmp: 'GIMP',
XmpNamespaces.illustrator: 'Illustrator',
XmpNamespaces.iptc4xmpCore: 'IPTC Core',
XmpNamespaces.iptc4xmpExt: 'IPTC Extension',
XmpNamespaces.lr: 'Lightroom',
XmpNamespaces.mediapro: 'MediaPro',
XmpNamespaces.miCamera: 'Mi Camera',
XmpNamespaces.microsoftPhoto: 'Microsoft Photo 1.0',
XmpNamespaces.mp1: 'Microsoft Photo 1.1',
XmpNamespaces.mp: 'Microsoft Photo 1.2',
XmpNamespaces.mwgrs: 'Regions',
XmpNamespaces.nga: 'National Gallery of Art',
XmpNamespaces.opMedia: 'OnePlus Media',
XmpNamespaces.panorama: 'Panorama',
XmpNamespaces.panoStudio: 'PanoramaStudio',
XmpNamespaces.pdf: 'PDF',
XmpNamespaces.pdfX: 'PDF/X',
XmpNamespaces.photoMechanic: 'Photo Mechanic',
XmpNamespaces.photoshop: 'Photoshop',
XmpNamespaces.plus: 'PLUS',
XmpNamespaces.pmtm: 'Photomatix',
XmpNamespaces.tiff: 'TIFF',
XmpNamespaces.xmp: 'Basic',
XmpNamespaces.xmpBJ: 'Basic Job Ticket',
XmpNamespaces.xmpDM: 'Dynamic Media',
XmpNamespaces.xmpMM: 'Media Management',
XmpNamespaces.xmpNote: 'Note',
XmpNamespaces.xmpRights: 'Rights Management',
XmpNamespaces.xmpTPg: 'Paged-Text',
XmpNamespaces.xperiaCamera: 'Xperia Camera',
};
}

27
lib/view/view.dart Normal file
View file

@ -0,0 +1,27 @@
export 'src/actions/chip.dart';
export 'src/actions/chip_set.dart';
export 'src/actions/entry.dart';
export 'src/actions/entry_set.dart';
export 'src/actions/map.dart';
export 'src/actions/map_cluster.dart';
export 'src/actions/share.dart';
export 'src/actions/slideshow.dart';
export 'src/metadata/date_edit_action.dart';
export 'src/metadata/date_field_source.dart';
export 'src/metadata/fields.dart';
export 'src/metadata/length_unit.dart';
export 'src/metadata/location_edit_action.dart';
export 'src/metadata/metadata_type.dart';
export 'src/settings/enums.dart';
export 'src/settings/thumbnail_overlay_location_icon.dart';
export 'src/settings/thumbnail_overlay_tag_icon.dart';
export 'src/source/album.dart';
export 'src/source/group.dart';
export 'src/source/layout.dart';
export 'src/source/sort.dart';
export 'src/source/state.dart';
export 'src/source/vault.dart';
export 'src/storage/relative_dir.dart';
export 'src/storage/volume.dart';
export 'src/wallpaper_target.dart';
export 'src/xmp.dart';

View file

@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/app_mode.dart'; import 'package:aves/app_mode.dart';
import 'package:aves/model/actions/entry_set.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/query.dart'; import 'package:aves/model/filters/query.dart';
@ -11,10 +10,9 @@ import 'package:aves/model/selection.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/enums/view.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/collection/entry_set_action_delegate.dart'; import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
import 'package:aves/widgets/collection/filter_bar.dart'; import 'package:aves/widgets/collection/filter_bar.dart';

View file

@ -10,7 +10,6 @@ import 'package:aves/model/selection.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/section_keys.dart'; import 'package:aves/model/source/section_keys.dart';
import 'package:aves/ref/mime_types.dart'; import 'package:aves/ref/mime_types.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
@ -45,6 +44,7 @@ import 'package:aves/widgets/common/thumbnail/notifications.dart';
import 'package:aves/widgets/common/tile_extent_controller.dart'; import 'package:aves/widgets/common/tile_extent_controller.dart';
import 'package:aves/widgets/navigation/nav_bar/nav_bar.dart'; import 'package:aves/widgets/navigation/nav_bar/nav_bar.dart';
import 'package:aves/widgets/viewer/entry_viewer_page.dart'; import 'package:aves/widgets/viewer/entry_viewer_page.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View file

@ -2,11 +2,11 @@ import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/rating.dart'; import 'package:aves/model/filters/rating.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/utils/file_utils.dart'; import 'package:aves/utils/file_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/grid/draggable_thumb_label.dart'; import 'package:aves/widgets/common/grid/draggable_thumb_label.dart';
import 'package:aves/widgets/common/grid/sections/list_layout.dart'; import 'package:aves/widgets/common/grid/sections/list_layout.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -1,13 +1,13 @@
import 'package:aves/model/covers.dart'; import 'package:aves/model/covers.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/section_keys.dart'; import 'package:aves/model/source/section_keys.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_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/grid/header.dart'; import 'package:aves/widgets/common/grid/header.dart';
import 'package:aves/widgets/common/identity/aves_icons.dart'; import 'package:aves/widgets/common/identity/aves_icons.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class AlbumSectionHeader extends StatelessWidget { class AlbumSectionHeader extends StatelessWidget {

View file

@ -3,12 +3,12 @@ import 'dart:math';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/section_keys.dart'; import 'package:aves/model/source/section_keys.dart';
import 'package:aves/widgets/collection/grid/headers/album.dart'; import 'package:aves/widgets/collection/grid/headers/album.dart';
import 'package:aves/widgets/collection/grid/headers/date.dart'; import 'package:aves/widgets/collection/grid/headers/date.dart';
import 'package:aves/widgets/collection/grid/headers/rating.dart'; import 'package:aves/widgets/collection/grid/headers/rating.dart';
import 'package:aves/widgets/common/grid/header.dart'; import 'package:aves/widgets/common/grid/header.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CollectionSectionHeader extends StatelessWidget { class CollectionSectionHeader extends StatelessWidget {

View file

@ -2,13 +2,13 @@ import 'package:aves/app_mode.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/selection.dart'; import 'package:aves/model/selection.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/services/intent_service.dart'; import 'package:aves/services/intent_service.dart';
import 'package:aves/widgets/collection/grid/list_details.dart'; import 'package:aves/widgets/collection/grid/list_details.dart';
import 'package:aves/widgets/collection/grid/list_details_theme.dart'; import 'package:aves/widgets/collection/grid/list_details_theme.dart';
import 'package:aves/widgets/common/grid/scaling.dart'; import 'package:aves/widgets/common/grid/scaling.dart';
import 'package:aves/widgets/common/thumbnail/decorated.dart'; import 'package:aves/widgets/common/thumbnail/decorated.dart';
import 'package:aves/widgets/common/thumbnail/notifications.dart'; import 'package:aves/widgets/common/thumbnail/notifications.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -1,8 +1,8 @@
import 'package:aves/model/actions/entry.dart';
import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/album_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/album_chooser.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart';

View file

@ -1,4 +1,4 @@
import 'package:aves/model/actions/entry.dart'; import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/rate_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/rate_chooser.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';

View file

@ -1,6 +1,6 @@
import 'package:aves/model/actions/entry.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/entry/extensions/multipage.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/share_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/share_chooser.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';

View file

@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/model/actions/share.dart'; import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart';
import 'package:aves/widgets/common/basic/popup/menu_row.dart'; import 'package:aves/widgets/common/basic/popup/menu_row.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';

View file

@ -1,8 +1,8 @@
import 'package:aves/model/actions/entry.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/filters/tag.dart'; import 'package:aves/model/filters/tag.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/tag_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/tag_chooser.dart';

View file

@ -1,9 +1,11 @@
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/storage/relative_dir.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/view/view.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_dialog.dart'; import 'package:aves/widgets/dialogs/aves_dialog.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -25,7 +27,7 @@ mixin PermissionAwareMixin {
final uris = <String>[], mimeTypes = <String>[]; final uris = <String>[], mimeTypes = <String>[];
entries.where((entry) { entries.where((entry) {
final dir = entry.directory; final dir = entry.directory;
return dir != null && restrictedInaccessibleDirs.contains(VolumeRelativeDirectory.fromPath(dir)); return dir != null && restrictedInaccessibleDirs.contains(androidFileUtils.relativeDirectoryFromPath(dir));
}).forEach((entry) { }).forEach((entry) {
uris.add(entry.uri); uris.add(entry.uri);
mimeTypes.add(entry.mimeType); mimeTypes.add(entry.mimeType);

View file

@ -2,11 +2,11 @@ import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/storage/volume.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/collection_utils.dart'; import 'package:aves/utils/collection_utils.dart';
import 'package:aves/utils/file_utils.dart'; import 'package:aves/utils/file_utils.dart';
import 'package:aves/view/view.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_dialog.dart'; import 'package:aves/widgets/dialogs/aves_dialog.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';

View file

@ -1,13 +1,81 @@
import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/vaults/details.dart';
import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/model/vaults/vaults.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/action_mixins/feedback.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_dialog.dart';
import 'package:aves/widgets/dialogs/filter_editors/password_dialog.dart';
import 'package:aves/widgets/dialogs/filter_editors/pattern_dialog.dart';
import 'package:aves/widgets/dialogs/filter_editors/pin_dialog.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:local_auth/error_codes.dart' as auth_error;
import 'package:local_auth/local_auth.dart';
mixin VaultAwareMixin on FeedbackMixin { mixin VaultAwareMixin on FeedbackMixin {
Future<bool> _tryUnlock(String dirPath, BuildContext context) async {
if (!vaults.isVault(dirPath) || !vaults.isLocked(dirPath)) return true;
final details = vaults.detailsForPath(dirPath);
if (details == null) return false;
bool? confirmed;
switch (details.lockType) {
case VaultLockType.system:
try {
confirmed = await LocalAuthentication().authenticate(
localizedReason: context.l10n.authenticateToUnlockVault,
);
} on PlatformException catch (e, stack) {
if (e.code != 'auth_in_progress') {
// `auth_in_progress`: `Authentication in progress`
await reportService.recordError(e, stack);
}
}
break;
case VaultLockType.pattern:
final pattern = await showDialog<String>(
context: context,
builder: (context) => const PatternDialog(needConfirmation: false),
routeSettings: const RouteSettings(name: PatternDialog.routeName),
);
if (pattern != null) {
confirmed = pattern == await securityService.readValue(details.passKey);
}
break;
case VaultLockType.pin:
final pin = await showDialog<String>(
context: context,
builder: (context) => const PinDialog(needConfirmation: false),
routeSettings: const RouteSettings(name: PinDialog.routeName),
);
if (pin != null) {
confirmed = pin == await securityService.readValue(details.passKey);
}
break;
case VaultLockType.password:
final password = await showDialog<String>(
context: context,
builder: (context) => const PasswordDialog(needConfirmation: false),
routeSettings: const RouteSettings(name: PasswordDialog.routeName),
);
if (password != null) {
confirmed = password == await securityService.readValue(details.passKey);
}
break;
}
if (confirmed == null || !confirmed) return false;
vaults.unlock(dirPath);
return true;
}
Future<bool> unlockAlbum(BuildContext context, String dirPath) async { Future<bool> unlockAlbum(BuildContext context, String dirPath) async {
final success = await vaults.tryUnlock(dirPath, context); final success = await _tryUnlock(dirPath, context);
if (!success) { if (!success) {
showFeedback(context, context.l10n.genericFailureFeedback); showFeedback(context, context.l10n.genericFailureFeedback);
} }
@ -29,4 +97,60 @@ mixin VaultAwareMixin on FeedbackMixin {
} }
void lockFilters(Set<AlbumFilter> filters) => vaults.lock(filters.map((v) => v.album).toSet()); void lockFilters(Set<AlbumFilter> filters) => vaults.lock(filters.map((v) => v.album).toSet());
Future<bool> setVaultPass(BuildContext context, VaultDetails details) async {
switch (details.lockType) {
case VaultLockType.system:
final l10n = context.l10n;
try {
return await LocalAuthentication().authenticate(
localizedReason: l10n.authenticateToConfigureVault,
);
} on PlatformException catch (e, stack) {
await showDialog(
context: context,
builder: (context) => AvesDialog(
content: Text(e.message ?? l10n.genericFailureFeedback),
actions: const [OkButton()],
),
routeSettings: const RouteSettings(name: AvesDialog.warningRouteName),
);
if (e.code != auth_error.notAvailable) {
await reportService.recordError(e, stack);
}
}
break;
case VaultLockType.pattern:
final pattern = await showDialog<String>(
context: context,
builder: (context) => const PatternDialog(needConfirmation: true),
routeSettings: const RouteSettings(name: PatternDialog.routeName),
);
if (pattern != null) {
return await securityService.writeValue(details.passKey, pattern);
}
break;
case VaultLockType.pin:
final pin = await showDialog<String>(
context: context,
builder: (context) => const PinDialog(needConfirmation: true),
routeSettings: const RouteSettings(name: PinDialog.routeName),
);
if (pin != null) {
return await securityService.writeValue(details.passKey, pin);
}
break;
case VaultLockType.password:
final password = await showDialog<String>(
context: context,
builder: (context) => const PasswordDialog(needConfirmation: true),
routeSettings: const RouteSettings(name: PasswordDialog.routeName),
);
if (password != null) {
return await securityService.writeValue(details.passKey, password);
}
break;
}
return false;
}
} }

View file

@ -1,11 +1,11 @@
import 'dart:ui'; import 'dart:ui';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/events.dart'; import 'package:aves/model/source/events.dart';
import 'package:aves/model/source/source_state.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class SourceStateAwareAppBarTitle extends StatelessWidget { class SourceStateAwareAppBarTitle extends StatelessWidget {

View file

@ -2,9 +2,9 @@ import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:aves/model/highlight.dart'; import 'package:aves/model/highlight.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/common/grid/sections/list_layout.dart'; import 'package:aves/widgets/common/grid/sections/list_layout.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -1,11 +1,11 @@
import 'package:aves/model/highlight.dart'; import 'package:aves/model/highlight.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/widgets/common/behaviour/eager_scale_gesture_recognizer.dart'; import 'package:aves/widgets/common/behaviour/eager_scale_gesture_recognizer.dart';
import 'package:aves/widgets/common/grid/sections/fixed/scale_overlay.dart'; import 'package:aves/widgets/common/grid/sections/fixed/scale_overlay.dart';
import 'package:aves/widgets/common/grid/sections/mosaic/scale_overlay.dart'; import 'package:aves/widgets/common/grid/sections/mosaic/scale_overlay.dart';
import 'package:aves/widgets/common/grid/sections/section_layout_builder.dart'; import 'package:aves/widgets/common/grid/sections/section_layout_builder.dart';
import 'package:aves/widgets/common/grid/theme.dart'; import 'package:aves/widgets/common/grid/theme.dart';
import 'package:aves/widgets/common/tile_extent_controller.dart'; import 'package:aves/widgets/common/tile_extent_controller.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View file

@ -1,6 +1,6 @@
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:aves/model/source/enums/enums.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class FixedExtentGridPainter extends CustomPainter { class FixedExtentGridPainter extends CustomPainter {

View file

@ -1,8 +1,8 @@
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/utils/colors.dart'; import 'package:aves/utils/colors.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -1,9 +1,9 @@
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/section_keys.dart'; import 'package:aves/model/source/section_keys.dart';
import 'package:aves/widgets/common/grid/sections/fixed/section_layout_builder.dart'; import 'package:aves/widgets/common/grid/sections/fixed/section_layout_builder.dart';
import 'package:aves/widgets/common/grid/sections/list_layout.dart'; import 'package:aves/widgets/common/grid/sections/list_layout.dart';
import 'package:aves/widgets/common/grid/sections/mosaic/section_layout_builder.dart'; import 'package:aves/widgets/common/grid/sections/mosaic/section_layout_builder.dart';
import 'package:aves/widgets/common/grid/sections/section_layout_builder.dart'; import 'package:aves/widgets/common/grid/sections/section_layout_builder.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -1,8 +1,8 @@
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/source/section_keys.dart'; import 'package:aves/model/source/section_keys.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/common/grid/sections/list_layout.dart'; import 'package:aves/widgets/common/grid/sections/list_layout.dart';
import 'package:aves/widgets/common/grid/sections/section_layout.dart'; import 'package:aves/widgets/common/grid/sections/section_layout.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:aves/app_mode.dart'; import 'package:aves/app_mode.dart';
import 'package:aves/model/actions/chip.dart';
import 'package:aves/model/covers.dart'; import 'package:aves/model/covers.dart';
import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
@ -13,6 +12,7 @@ import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/colors.dart'; import 'package:aves/theme/colors.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/collection/filter_bar.dart'; import 'package:aves/widgets/collection/filter_bar.dart';
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart'; import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
import 'package:aves/widgets/common/basic/popup/menu_row.dart'; import 'package:aves/widgets/common/basic/popup/menu_row.dart';

View file

@ -3,11 +3,11 @@ import 'package:aves/model/covers.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/entry/extensions/multipage.dart';
import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/model/vaults/vaults.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';
import 'package:aves/widgets/common/grid/theme.dart'; import 'package:aves/widgets/common/grid/theme.dart';
import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View file

@ -1,7 +1,7 @@
import 'package:aves/model/actions/map.dart';
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/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/map/buttons/button.dart'; import 'package:aves/widgets/common/map/buttons/button.dart';
import 'package:aves/widgets/common/map/buttons/coordinate_filter.dart'; import 'package:aves/widgets/common/map/buttons/coordinate_filter.dart';

View file

@ -6,7 +6,6 @@ import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/images.dart'; import 'package:aves/model/entry/extensions/images.dart';
import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/entry/extensions/location.dart';
import 'package:aves/model/entry/sort.dart'; import 'package:aves/model/entry/sort.dart';
import 'package:aves/model/settings/enums/l10n.dart';
import 'package:aves/model/settings/enums/map_style.dart'; import 'package:aves/model/settings/enums/map_style.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/ref/poi.dart'; import 'package:aves/ref/poi.dart';
@ -14,6 +13,7 @@ import 'package:aves/services/common/services.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/utils/math_utils.dart'; import 'package:aves/utils/math_utils.dart';
import 'package:aves/view/view.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/buttons/overlay_button.dart'; import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
import 'package:aves/widgets/common/map/attribution.dart'; import 'package:aves/widgets/common/map/attribution.dart';

View file

@ -1,6 +1,6 @@
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/view/view.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/dialogs/selection_dialogs/common.dart'; import 'package:aves/widgets/dialogs/selection_dialogs/common.dart';
import 'package:aves/widgets/dialogs/selection_dialogs/single_selection.dart'; import 'package:aves/widgets/dialogs/selection_dialogs/single_selection.dart';

View file

@ -1,6 +1,7 @@
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/file_utils.dart'; import 'package:aves/utils/file_utils.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
import 'package:aves/widgets/viewer/info/common.dart'; import 'package:aves/widgets/viewer/info/common.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View file

@ -1,6 +1,5 @@
import 'package:aves/model/app/support.dart'; import 'package:aves/model/app/support.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/metadata/enums/length_unit.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/ref/mime_types.dart'; import 'package:aves/ref/mime_types.dart';
import 'package:aves/services/media/media_edit_service.dart'; import 'package:aves/services/media/media_edit_service.dart';
@ -8,6 +7,7 @@ import 'package:aves/theme/durations.dart';
import 'package:aves/theme/text.dart'; import 'package:aves/theme/text.dart';
import 'package:aves/theme/themes.dart'; import 'package:aves/theme/themes.dart';
import 'package:aves/utils/mime_utils.dart'; import 'package:aves/utils/mime_utils.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/basic/text_dropdown_button.dart'; import 'package:aves/widgets/common/basic/text_dropdown_button.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/fx/transitions.dart'; import 'package:aves/widgets/common/fx/transitions.dart';

View file

@ -1,14 +1,12 @@
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/model/metadata/date_modifier.dart';
import 'package:aves/model/metadata/enums/date_edit_action.dart';
import 'package:aves/model/metadata/enums/date_field_source.dart';
import 'package:aves/model/metadata/fields.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.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/theme/themes.dart'; import 'package:aves/theme/themes.dart';
import 'package:aves/utils/time_utils.dart'; import 'package:aves/utils/time_utils.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/basic/text_dropdown_button.dart'; import 'package:aves/widgets/common/basic/text_dropdown_button.dart';
import 'package:aves/widgets/common/basic/wheel.dart'; import 'package:aves/widgets/common/basic/wheel.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';

View file

@ -1,7 +1,6 @@
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/entry/extensions/location.dart';
import 'package:aves/model/entry/extensions/metadata_edition.dart'; import 'package:aves/model/entry/extensions/metadata_edition.dart';
import 'package:aves/model/metadata/enums/location_edit_action.dart';
import 'package:aves/model/settings/enums/coordinate_format.dart'; 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/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
@ -9,6 +8,7 @@ import 'package:aves/ref/poi.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/theme/themes.dart'; import 'package:aves/theme/themes.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/basic/text_dropdown_button.dart'; import 'package:aves/widgets/common/basic/text_dropdown_button.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/fx/transitions.dart'; import 'package:aves/widgets/common/fx/transitions.dart';

Some files were not shown because too many files have changed in this diff Show more