From 1578d2d944b5d3d6faab1b9aaed82cbc2c16d504 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sat, 8 Jun 2024 20:05:14 +0200 Subject: [PATCH] #1041 allow shifting dates by seconds --- CHANGELOG.md | 1 + .../aves/channel/calls/MetadataEditHandler.kt | 4 +- .../aves/model/provider/ImageProvider.kt | 6 +-- .../entry/extensions/metadata_edition.dart | 2 +- lib/model/metadata/date_modifier.dart | 10 ++-- .../metadata/metadata_edit_service.dart | 2 +- .../entry_editors/edit_date_dialog.dart | 49 +++++++++++++------ 7 files changed, 46 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 295400e2d..045018670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Albums / Countries / Tags: show selection in Collection +- allow shifting dates by seconds ### Changed diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataEditHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataEditHandler.kt index 9d1704b27..36ac14ff2 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataEditHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataEditHandler.kt @@ -74,7 +74,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa private fun editDate(call: MethodCall, result: MethodChannel.Result) { val dateMillis = call.argument("dateMillis")?.toLong() - val shiftMinutes = call.argument("shiftMinutes")?.toLong() + val shiftSeconds = call.argument("shiftSeconds")?.toLong() val fields = call.argument>("fields") val entryMap = call.argument("entry") if (entryMap == null || fields == null) { @@ -97,7 +97,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa } val callback = MetadataOpCallback("editDate", entryMap, result) - provider.editDate(contextWrapper, path, uri, mimeType, dateMillis, shiftMinutes, fields, callback) + provider.editDate(contextWrapper, path, uri, mimeType, dateMillis, shiftSeconds, fields, callback) } private fun editMetadata(call: MethodCall, result: MethodChannel.Result) { diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt index cc54bd459..d406f12fd 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt @@ -1026,7 +1026,7 @@ abstract class ImageProvider { uri: Uri, mimeType: String, dateMillis: Long?, - shiftMinutes: Long?, + shiftSeconds: Long?, fields: List, callback: ImageOpCallback, ) { @@ -1057,9 +1057,9 @@ abstract class ImageProvider { } } - shiftMinutes != null -> { + shiftSeconds != null -> { // shift - val shiftMillis = shiftMinutes * 60000 + val shiftMillis = shiftSeconds * 1000 listOf( ExifInterface.TAG_DATETIME, ExifInterface.TAG_DATETIME_ORIGINAL, diff --git a/lib/model/entry/extensions/metadata_edition.dart b/lib/model/entry/extensions/metadata_edition.dart index 1a75e9fd3..6ad3b2dce 100644 --- a/lib/model/entry/extensions/metadata_edition.dart +++ b/lib/model/entry/extensions/metadata_edition.dart @@ -62,7 +62,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry { final date = DateTime.tryParse(xmpDate); if (date != null) { // TODO TLAD [date] DateTime.tryParse converts to UTC time, losing the time zone offset - final shiftedDate = date.add(Duration(minutes: appliedModifier.shiftMinutes!)); + final shiftedDate = date.add(Duration(seconds: appliedModifier.shiftSeconds!)); editCreateDateXmp(descriptions, shiftedDate); } else { reportService.recordError('failed to parse XMP date=$xmpDate', null); diff --git a/lib/model/metadata/date_modifier.dart b/lib/model/metadata/date_modifier.dart index 4fd675122..b34a86e82 100644 --- a/lib/model/metadata/date_modifier.dart +++ b/lib/model/metadata/date_modifier.dart @@ -16,17 +16,17 @@ class DateModifier extends Equatable { final Set fields; final DateTime? setDateTime; final DateFieldSource? copyFieldSource; - final int? shiftMinutes; + final int? shiftSeconds; @override - List get props => [action, fields, setDateTime, copyFieldSource, shiftMinutes]; + List get props => [action, fields, setDateTime, copyFieldSource, shiftSeconds]; const DateModifier._private( this.action, { this.fields = const {}, this.setDateTime, this.copyFieldSource, - this.shiftMinutes, + this.shiftSeconds, }); factory DateModifier.setCustom(Set fields, DateTime dateTime) { @@ -41,8 +41,8 @@ class DateModifier extends Equatable { return const DateModifier._private(DateEditAction.extractFromTitle); } - factory DateModifier.shift(Set fields, int shiftMinutes) { - return DateModifier._private(DateEditAction.shift, fields: fields, shiftMinutes: shiftMinutes); + factory DateModifier.shift(Set fields, int shiftSeconds) { + return DateModifier._private(DateEditAction.shift, fields: fields, shiftSeconds: shiftSeconds); } factory DateModifier.remove(Set fields) { diff --git a/lib/services/metadata/metadata_edit_service.dart b/lib/services/metadata/metadata_edit_service.dart index 447a05085..d19d7dc06 100644 --- a/lib/services/metadata/metadata_edit_service.dart +++ b/lib/services/metadata/metadata_edit_service.dart @@ -64,7 +64,7 @@ class PlatformMetadataEditService implements MetadataEditService { final result = await _platform.invokeMethod('editDate', { 'entry': entry.toPlatformEntryMap(), 'dateMillis': modifier.setDateTime?.millisecondsSinceEpoch, - 'shiftMinutes': modifier.shiftMinutes, + 'shiftSeconds': modifier.shiftSeconds, 'fields': modifier.fields.where((v) => v.type == MetadataType.exif).map((v) => v.toPlatform).whereNotNull().toList(), }); if (result != null) return (result as Map).cast(); diff --git a/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart index a9e157784..fa23a57eb 100644 --- a/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart @@ -42,7 +42,7 @@ class _EditEntryDateDialogState extends State { DateFieldSource _copyFieldSource = DateFieldSource.fileModifiedDate; late AvesEntry _copyItemSource; late DateTime _customDateTime; - late ValueNotifier _shiftHour, _shiftMinute; + late ValueNotifier _shiftHour, _shiftMinute, _shiftSecond; late ValueNotifier _shiftSign; bool _showOptions = false; final Set _fields = {...DateModifier.writableFields}; @@ -50,12 +50,15 @@ class _EditEntryDateDialogState extends State { DateTime get copyItemDate => _copyItemSource.bestDate ?? DateTime.now(); + static const _positiveSign = '+'; + static const _negativeSign = '-'; + @override void initState() { super.initState(); _initCustom(); _initCopyItem(); - _initShift(minutesInHour); + _initShift(); _validate(); } @@ -64,6 +67,7 @@ class _EditEntryDateDialogState extends State { _isValidNotifier.dispose(); _shiftHour.dispose(); _shiftMinute.dispose(); + _shiftSecond.dispose(); _shiftSign.dispose(); super.dispose(); } @@ -76,11 +80,11 @@ class _EditEntryDateDialogState extends State { _copyItemSource = widget.entry; } - void _initShift(int initialMinutes) { - final abs = initialMinutes.abs(); - _shiftHour = ValueNotifier(abs ~/ minutesInHour); - _shiftMinute = ValueNotifier(abs % minutesInHour); - _shiftSign = ValueNotifier(initialMinutes.isNegative ? '-' : '+'); + void _initShift() { + _shiftHour = ValueNotifier(1); + _shiftMinute = ValueNotifier(0); + _shiftSecond = ValueNotifier(0); + _shiftSign = ValueNotifier(_positiveSign); } @override @@ -215,13 +219,15 @@ class _EditEntryDateDialogState extends State { Center(child: Text(l10n.durationDialogHours)), const SizedBox(width: 16), Center(child: Text(l10n.durationDialogMinutes)), + const SizedBox(width: 16), + Center(child: Text(l10n.durationDialogSeconds)), ], ), TableRow( children: [ WheelSelector( valueNotifier: _shiftSign, - values: const ['+', '-'], + values: const [_positiveSign, _negativeSign], textStyle: textStyle, textAlign: TextAlign.center, format: (v) => v, @@ -236,12 +242,9 @@ class _EditEntryDateDialogState extends State { format: timeComponentFormatter.format, ), ), - const Padding( - padding: EdgeInsets.only(bottom: 2), - child: Text( - ':', - style: textStyle, - ), + const Text( + ':', + style: textStyle, ), Align( alignment: Alignment.centerLeft, @@ -253,6 +256,20 @@ class _EditEntryDateDialogState extends State { format: timeComponentFormatter.format, ), ), + const Text( + ':', + style: textStyle, + ), + Align( + alignment: Alignment.centerLeft, + child: WheelSelector( + valueNotifier: _shiftSecond, + values: List.generate(secondsInMinute, (i) => i), + textStyle: textStyle, + textAlign: digitsAlign, + format: timeComponentFormatter.format, + ), + ), ], ) ], @@ -371,8 +388,8 @@ class _EditEntryDateDialogState extends State { case DateEditAction.extractFromTitle: return DateModifier.extractFromTitle(); case DateEditAction.shift: - final shiftTotalMinutes = (_shiftHour.value * minutesInHour + _shiftMinute.value) * (_shiftSign.value == '+' ? 1 : -1); - return DateModifier.shift(_fields, shiftTotalMinutes); + final shiftTotalSeconds = ((_shiftHour.value * minutesInHour + _shiftMinute.value) * secondsInMinute + _shiftSecond.value) * (_shiftSign.value == _positiveSign ? 1 : -1); + return DateModifier.shift(_fields, shiftTotalSeconds); case DateEditAction.remove: return DateModifier.remove(_fields); }