#1041 allow shifting dates by seconds
This commit is contained in:
parent
da4a7ae38f
commit
1578d2d944
7 changed files with 46 additions and 28 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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>();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue