#1041 allow shifting dates by seconds

This commit is contained in:
Thibault Deckers 2024-06-08 20:05:14 +02:00
parent da4a7ae38f
commit 1578d2d944
7 changed files with 46 additions and 28 deletions

View file

@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
### Added ### Added
- Albums / Countries / Tags: show selection in Collection - Albums / Countries / Tags: show selection in Collection
- allow shifting dates by seconds
### Changed ### Changed

View file

@ -74,7 +74,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa
private fun editDate(call: MethodCall, result: MethodChannel.Result) { private fun editDate(call: MethodCall, result: MethodChannel.Result) {
val dateMillis = call.argument<Number>("dateMillis")?.toLong() val dateMillis = call.argument<Number>("dateMillis")?.toLong()
val shiftMinutes = call.argument<Number>("shiftMinutes")?.toLong() val shiftSeconds = call.argument<Number>("shiftSeconds")?.toLong()
val fields = call.argument<List<String>>("fields") val fields = call.argument<List<String>>("fields")
val entryMap = call.argument<FieldMap>("entry") val entryMap = call.argument<FieldMap>("entry")
if (entryMap == null || fields == null) { if (entryMap == null || fields == null) {
@ -97,7 +97,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa
} }
val callback = MetadataOpCallback("editDate", entryMap, result) 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) { private fun editMetadata(call: MethodCall, result: MethodChannel.Result) {

View file

@ -1026,7 +1026,7 @@ abstract class ImageProvider {
uri: Uri, uri: Uri,
mimeType: String, mimeType: String,
dateMillis: Long?, dateMillis: Long?,
shiftMinutes: Long?, shiftSeconds: Long?,
fields: List<String>, fields: List<String>,
callback: ImageOpCallback, callback: ImageOpCallback,
) { ) {
@ -1057,9 +1057,9 @@ abstract class ImageProvider {
} }
} }
shiftMinutes != null -> { shiftSeconds != null -> {
// shift // shift
val shiftMillis = shiftMinutes * 60000 val shiftMillis = shiftSeconds * 1000
listOf( listOf(
ExifInterface.TAG_DATETIME, ExifInterface.TAG_DATETIME,
ExifInterface.TAG_DATETIME_ORIGINAL, ExifInterface.TAG_DATETIME_ORIGINAL,

View file

@ -62,7 +62,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
final date = DateTime.tryParse(xmpDate); final date = DateTime.tryParse(xmpDate);
if (date != null) { if (date != null) {
// TODO TLAD [date] DateTime.tryParse converts to UTC time, losing the time zone offset // 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); editCreateDateXmp(descriptions, shiftedDate);
} else { } else {
reportService.recordError('failed to parse XMP date=$xmpDate', null); reportService.recordError('failed to parse XMP date=$xmpDate', null);

View file

@ -16,17 +16,17 @@ class DateModifier extends Equatable {
final Set<MetadataField> fields; final Set<MetadataField> fields;
final DateTime? setDateTime; final DateTime? setDateTime;
final DateFieldSource? copyFieldSource; final DateFieldSource? copyFieldSource;
final int? shiftMinutes; final int? shiftSeconds;
@override @override
List<Object?> get props => [action, fields, setDateTime, copyFieldSource, shiftMinutes]; List<Object?> get props => [action, fields, setDateTime, copyFieldSource, shiftSeconds];
const DateModifier._private( const DateModifier._private(
this.action, { this.action, {
this.fields = const {}, this.fields = const {},
this.setDateTime, this.setDateTime,
this.copyFieldSource, this.copyFieldSource,
this.shiftMinutes, this.shiftSeconds,
}); });
factory DateModifier.setCustom(Set<MetadataField> fields, DateTime dateTime) { factory DateModifier.setCustom(Set<MetadataField> fields, DateTime dateTime) {
@ -41,8 +41,8 @@ class DateModifier extends Equatable {
return const DateModifier._private(DateEditAction.extractFromTitle); return const DateModifier._private(DateEditAction.extractFromTitle);
} }
factory DateModifier.shift(Set<MetadataField> fields, int shiftMinutes) { factory DateModifier.shift(Set<MetadataField> fields, int shiftSeconds) {
return DateModifier._private(DateEditAction.shift, fields: fields, shiftMinutes: shiftMinutes); return DateModifier._private(DateEditAction.shift, fields: fields, shiftSeconds: shiftSeconds);
} }
factory DateModifier.remove(Set<MetadataField> fields) { factory DateModifier.remove(Set<MetadataField> fields) {

View file

@ -64,7 +64,7 @@ class PlatformMetadataEditService implements MetadataEditService {
final result = await _platform.invokeMethod('editDate', <String, dynamic>{ final result = await _platform.invokeMethod('editDate', <String, dynamic>{
'entry': entry.toPlatformEntryMap(), 'entry': entry.toPlatformEntryMap(),
'dateMillis': modifier.setDateTime?.millisecondsSinceEpoch, '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(), 'fields': modifier.fields.where((v) => v.type == MetadataType.exif).map((v) => v.toPlatform).whereNotNull().toList(),
}); });
if (result != null) return (result as Map).cast<String, dynamic>(); if (result != null) return (result as Map).cast<String, dynamic>();

View file

@ -42,7 +42,7 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
DateFieldSource _copyFieldSource = DateFieldSource.fileModifiedDate; DateFieldSource _copyFieldSource = DateFieldSource.fileModifiedDate;
late AvesEntry _copyItemSource; late AvesEntry _copyItemSource;
late DateTime _customDateTime; late DateTime _customDateTime;
late ValueNotifier<int> _shiftHour, _shiftMinute; late ValueNotifier<int> _shiftHour, _shiftMinute, _shiftSecond;
late ValueNotifier<String> _shiftSign; late ValueNotifier<String> _shiftSign;
bool _showOptions = false; bool _showOptions = false;
final Set<MetadataField> _fields = {...DateModifier.writableFields}; final Set<MetadataField> _fields = {...DateModifier.writableFields};
@ -50,12 +50,15 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
DateTime get copyItemDate => _copyItemSource.bestDate ?? DateTime.now(); DateTime get copyItemDate => _copyItemSource.bestDate ?? DateTime.now();
static const _positiveSign = '+';
static const _negativeSign = '-';
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_initCustom(); _initCustom();
_initCopyItem(); _initCopyItem();
_initShift(minutesInHour); _initShift();
_validate(); _validate();
} }
@ -64,6 +67,7 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
_isValidNotifier.dispose(); _isValidNotifier.dispose();
_shiftHour.dispose(); _shiftHour.dispose();
_shiftMinute.dispose(); _shiftMinute.dispose();
_shiftSecond.dispose();
_shiftSign.dispose(); _shiftSign.dispose();
super.dispose(); super.dispose();
} }
@ -76,11 +80,11 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
_copyItemSource = widget.entry; _copyItemSource = widget.entry;
} }
void _initShift(int initialMinutes) { void _initShift() {
final abs = initialMinutes.abs(); _shiftHour = ValueNotifier(1);
_shiftHour = ValueNotifier(abs ~/ minutesInHour); _shiftMinute = ValueNotifier(0);
_shiftMinute = ValueNotifier(abs % minutesInHour); _shiftSecond = ValueNotifier(0);
_shiftSign = ValueNotifier(initialMinutes.isNegative ? '-' : '+'); _shiftSign = ValueNotifier(_positiveSign);
} }
@override @override
@ -215,13 +219,15 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
Center(child: Text(l10n.durationDialogHours)), Center(child: Text(l10n.durationDialogHours)),
const SizedBox(width: 16), const SizedBox(width: 16),
Center(child: Text(l10n.durationDialogMinutes)), Center(child: Text(l10n.durationDialogMinutes)),
const SizedBox(width: 16),
Center(child: Text(l10n.durationDialogSeconds)),
], ],
), ),
TableRow( TableRow(
children: [ children: [
WheelSelector( WheelSelector(
valueNotifier: _shiftSign, valueNotifier: _shiftSign,
values: const ['+', '-'], values: const [_positiveSign, _negativeSign],
textStyle: textStyle, textStyle: textStyle,
textAlign: TextAlign.center, textAlign: TextAlign.center,
format: (v) => v, format: (v) => v,
@ -236,12 +242,9 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
format: timeComponentFormatter.format, format: timeComponentFormatter.format,
), ),
), ),
const Padding( const Text(
padding: EdgeInsets.only(bottom: 2), ':',
child: Text( style: textStyle,
':',
style: textStyle,
),
), ),
Align( Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
@ -253,6 +256,20 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
format: timeComponentFormatter.format, 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<EditEntryDateDialog> {
case DateEditAction.extractFromTitle: case DateEditAction.extractFromTitle:
return DateModifier.extractFromTitle(); return DateModifier.extractFromTitle();
case DateEditAction.shift: case DateEditAction.shift:
final shiftTotalMinutes = (_shiftHour.value * minutesInHour + _shiftMinute.value) * (_shiftSign.value == '+' ? 1 : -1); final shiftTotalSeconds = ((_shiftHour.value * minutesInHour + _shiftMinute.value) * secondsInMinute + _shiftSecond.value) * (_shiftSign.value == _positiveSign ? 1 : -1);
return DateModifier.shift(_fields, shiftTotalMinutes); return DateModifier.shift(_fields, shiftTotalSeconds);
case DateEditAction.remove: case DateEditAction.remove:
return DateModifier.remove(_fields); return DateModifier.remove(_fields);
} }