#526 converter: write metadata
This commit is contained in:
parent
64ae4e7614
commit
ef6eb53eb2
15 changed files with 285 additions and 84 deletions
|
@ -6,7 +6,8 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Collection: bulk converting
|
- Export: bulk converting
|
||||||
|
- Export: write metadata when converting
|
||||||
- Places: page & navigation entry
|
- Places: page & navigation entry
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -132,8 +132,9 @@ class ImageOpStreamHandler(private val activity: Activity, private val arguments
|
||||||
val lengthUnit = arguments["lengthUnit"] as String?
|
val lengthUnit = arguments["lengthUnit"] as String?
|
||||||
val width = (arguments["width"] as Number?)?.toInt()
|
val width = (arguments["width"] as Number?)?.toInt()
|
||||||
val height = (arguments["height"] as Number?)?.toInt()
|
val height = (arguments["height"] as Number?)?.toInt()
|
||||||
|
val writeMetadata = arguments["writeMetadata"] as Boolean?
|
||||||
val nameConflictStrategy = NameConflictStrategy.get(arguments["nameConflictStrategy"] as String?)
|
val nameConflictStrategy = NameConflictStrategy.get(arguments["nameConflictStrategy"] as String?)
|
||||||
if (destinationDir == null || mimeType == null || lengthUnit == null || width == null || height == null || nameConflictStrategy == null) {
|
if (destinationDir == null || mimeType == null || lengthUnit == null || width == null || height == null || writeMetadata == null || nameConflictStrategy == null) {
|
||||||
error("convert-args", "missing arguments", null)
|
error("convert-args", "missing arguments", null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -148,10 +149,21 @@ class ImageOpStreamHandler(private val activity: Activity, private val arguments
|
||||||
|
|
||||||
destinationDir = ensureTrailingSeparator(destinationDir)
|
destinationDir = ensureTrailingSeparator(destinationDir)
|
||||||
val entries = entryMapList.map(::AvesEntry)
|
val entries = entryMapList.map(::AvesEntry)
|
||||||
provider.convertMultiple(activity, mimeType, destinationDir, entries, lengthUnit, width, height, nameConflictStrategy, object : ImageOpCallback {
|
provider.convertMultiple(
|
||||||
override fun onSuccess(fields: FieldMap) = success(fields)
|
activity = activity,
|
||||||
override fun onFailure(throwable: Throwable) = error("convert-failure", "failed to convert entries", throwable)
|
imageExportMimeType = mimeType,
|
||||||
})
|
targetDir = destinationDir,
|
||||||
|
entries = entries,
|
||||||
|
lengthUnit = lengthUnit,
|
||||||
|
width = width,
|
||||||
|
height = height,
|
||||||
|
writeMetadata = writeMetadata,
|
||||||
|
nameConflictStrategy = nameConflictStrategy,
|
||||||
|
callback = object : ImageOpCallback {
|
||||||
|
override fun onSuccess(fields: FieldMap) = success(fields)
|
||||||
|
override fun onFailure(throwable: Throwable) = error("convert-failure", "failed to convert entries", throwable)
|
||||||
|
},
|
||||||
|
)
|
||||||
endOfStream()
|
endOfStream()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,10 +195,17 @@ class ImageOpStreamHandler(private val activity: Activity, private val arguments
|
||||||
// always use Media Store (as we move from or to it)
|
// always use Media Store (as we move from or to it)
|
||||||
val provider = MediaStoreImageProvider()
|
val provider = MediaStoreImageProvider()
|
||||||
|
|
||||||
provider.moveMultiple(activity, copy, nameConflictStrategy, entriesByTargetDir, ::isCancelledOp, object : ImageOpCallback {
|
provider.moveMultiple(
|
||||||
override fun onSuccess(fields: FieldMap) = success(fields)
|
activity = activity,
|
||||||
override fun onFailure(throwable: Throwable) = error("move-failure", "failed to move entries", throwable)
|
copy = copy,
|
||||||
})
|
nameConflictStrategy = nameConflictStrategy,
|
||||||
|
entriesByTargetDir = entriesByTargetDir,
|
||||||
|
isCancelledOp = ::isCancelledOp,
|
||||||
|
callback = object : ImageOpCallback {
|
||||||
|
override fun onSuccess(fields: FieldMap) = success(fields)
|
||||||
|
override fun onFailure(throwable: Throwable) = error("move-failure", "failed to move entries", throwable)
|
||||||
|
},
|
||||||
|
)
|
||||||
endOfStream()
|
endOfStream()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,10 +237,15 @@ class ImageOpStreamHandler(private val activity: Activity, private val arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
val entryMap = mapOf(*entryList.map { Pair(it.key, it.value) }.toTypedArray())
|
val entryMap = mapOf(*entryList.map { Pair(it.key, it.value) }.toTypedArray())
|
||||||
provider.renameMultiple(activity, entryMap, ::isCancelledOp, object : ImageOpCallback {
|
provider.renameMultiple(
|
||||||
override fun onSuccess(fields: FieldMap) = success(fields)
|
activity = activity,
|
||||||
override fun onFailure(throwable: Throwable) = error("rename-failure", "failed to rename", throwable.message)
|
entriesToNewName = entryMap,
|
||||||
})
|
isCancelledOp = ::isCancelledOp,
|
||||||
|
callback = object : ImageOpCallback {
|
||||||
|
override fun onSuccess(fields: FieldMap) = success(fields)
|
||||||
|
override fun onFailure(throwable: Throwable) = error("rename-failure", "failed to rename", throwable.message)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
endOfStream()
|
endOfStream()
|
||||||
|
|
|
@ -15,15 +15,9 @@ class AvesEntry(map: FieldMap) {
|
||||||
val trashed = map["trashed"] as Boolean
|
val trashed = map["trashed"] as Boolean
|
||||||
val trashPath = map["trashPath"] as String?
|
val trashPath = map["trashPath"] as String?
|
||||||
|
|
||||||
private val isRotated: Boolean
|
val isRotated: Boolean
|
||||||
get() = rotationDegrees % 180 == 90
|
get() = rotationDegrees % 180 == 90
|
||||||
|
|
||||||
val displayWidth: Int
|
|
||||||
get() = if (isRotated) height else width
|
|
||||||
|
|
||||||
val displayHeight: Int
|
|
||||||
get() = if (isRotated) width else height
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// convenience method
|
// convenience method
|
||||||
private fun toLong(o: Any?): Long? = when (o) {
|
private fun toLong(o: Any?): Long? = when (o) {
|
||||||
|
|
|
@ -38,9 +38,13 @@ import deckers.thibault.aves.utils.FileUtils.transferTo
|
||||||
import deckers.thibault.aves.utils.MimeTypes.canEditExif
|
import deckers.thibault.aves.utils.MimeTypes.canEditExif
|
||||||
import deckers.thibault.aves.utils.MimeTypes.canEditIptc
|
import deckers.thibault.aves.utils.MimeTypes.canEditIptc
|
||||||
import deckers.thibault.aves.utils.MimeTypes.canEditXmp
|
import deckers.thibault.aves.utils.MimeTypes.canEditXmp
|
||||||
|
import deckers.thibault.aves.utils.MimeTypes.canReadWithExifInterface
|
||||||
|
import deckers.thibault.aves.utils.MimeTypes.canReadWithPixyMeta
|
||||||
import deckers.thibault.aves.utils.MimeTypes.canRemoveMetadata
|
import deckers.thibault.aves.utils.MimeTypes.canRemoveMetadata
|
||||||
import deckers.thibault.aves.utils.MimeTypes.extensionFor
|
import deckers.thibault.aves.utils.MimeTypes.extensionFor
|
||||||
import deckers.thibault.aves.utils.MimeTypes.isVideo
|
import deckers.thibault.aves.utils.MimeTypes.isVideo
|
||||||
|
import pixy.meta.meta.Metadata
|
||||||
|
import pixy.meta.meta.MetadataType
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.nio.channels.Channels
|
import java.nio.channels.Channels
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -177,6 +181,7 @@ abstract class ImageProvider {
|
||||||
lengthUnit: String,
|
lengthUnit: String,
|
||||||
width: Int,
|
width: Int,
|
||||||
height: Int,
|
height: Int,
|
||||||
|
writeMetadata: Boolean,
|
||||||
nameConflictStrategy: NameConflictStrategy,
|
nameConflictStrategy: NameConflictStrategy,
|
||||||
callback: ImageOpCallback,
|
callback: ImageOpCallback,
|
||||||
) {
|
) {
|
||||||
|
@ -204,7 +209,7 @@ abstract class ImageProvider {
|
||||||
val sourceMimeType = entry.mimeType
|
val sourceMimeType = entry.mimeType
|
||||||
val exportMimeType = if (isVideo(sourceMimeType)) sourceMimeType else imageExportMimeType
|
val exportMimeType = if (isVideo(sourceMimeType)) sourceMimeType else imageExportMimeType
|
||||||
try {
|
try {
|
||||||
val newFields = exportSingle(
|
val newFields = convertSingle(
|
||||||
activity = activity,
|
activity = activity,
|
||||||
sourceEntry = entry,
|
sourceEntry = entry,
|
||||||
targetDir = targetDir,
|
targetDir = targetDir,
|
||||||
|
@ -212,19 +217,20 @@ abstract class ImageProvider {
|
||||||
lengthUnit = lengthUnit,
|
lengthUnit = lengthUnit,
|
||||||
width = width,
|
width = width,
|
||||||
height = height,
|
height = height,
|
||||||
|
writeMetadata = writeMetadata,
|
||||||
nameConflictStrategy = nameConflictStrategy,
|
nameConflictStrategy = nameConflictStrategy,
|
||||||
exportMimeType = exportMimeType,
|
exportMimeType = exportMimeType,
|
||||||
)
|
)
|
||||||
result["newFields"] = newFields
|
result["newFields"] = newFields
|
||||||
result["success"] = true
|
result["success"] = true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(LOG_TAG, "failed to export to targetDir=$targetDir entry with sourcePath=$sourcePath pageId=$pageId", e)
|
Log.w(LOG_TAG, "failed to convert to targetDir=$targetDir entry with sourcePath=$sourcePath pageId=$pageId", e)
|
||||||
}
|
}
|
||||||
callback.onSuccess(result)
|
callback.onSuccess(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun exportSingle(
|
private suspend fun convertSingle(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
sourceEntry: AvesEntry,
|
sourceEntry: AvesEntry,
|
||||||
targetDir: String,
|
targetDir: String,
|
||||||
|
@ -232,6 +238,7 @@ abstract class ImageProvider {
|
||||||
lengthUnit: String,
|
lengthUnit: String,
|
||||||
width: Int,
|
width: Int,
|
||||||
height: Int,
|
height: Int,
|
||||||
|
writeMetadata: Boolean,
|
||||||
nameConflictStrategy: NameConflictStrategy,
|
nameConflictStrategy: NameConflictStrategy,
|
||||||
exportMimeType: String,
|
exportMimeType: String,
|
||||||
): FieldMap {
|
): FieldMap {
|
||||||
|
@ -269,17 +276,11 @@ abstract class ImageProvider {
|
||||||
sourceDocFile.copyTo(output)
|
sourceDocFile.copyTo(output)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val targetWidthPx: Int
|
var targetWidthPx: Int = if (sourceEntry.isRotated) height else width
|
||||||
val targetHeightPx: Int
|
var targetHeightPx: Int = if (sourceEntry.isRotated) width else height
|
||||||
when (lengthUnit) {
|
if (lengthUnit == LENGTH_UNIT_PERCENT) {
|
||||||
LENGTH_UNIT_PERCENT -> {
|
targetWidthPx = sourceEntry.width * targetWidthPx / 100
|
||||||
targetWidthPx = sourceEntry.displayWidth * width / 100
|
targetHeightPx = sourceEntry.height * targetHeightPx / 100
|
||||||
targetHeightPx = sourceEntry.displayHeight * height / 100
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
targetWidthPx = width
|
|
||||||
targetHeightPx = height
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val model: Any = if (MimeTypes.isHeic(sourceMimeType) && pageId != null) {
|
val model: Any = if (MimeTypes.isHeic(sourceMimeType) && pageId != null) {
|
||||||
|
@ -344,13 +345,108 @@ abstract class ImageProvider {
|
||||||
targetNameWithoutExtension = targetNameWithoutExtension,
|
targetNameWithoutExtension = targetNameWithoutExtension,
|
||||||
write = write,
|
write = write,
|
||||||
)
|
)
|
||||||
return scanNewPath(activity, targetPath, exportMimeType)
|
|
||||||
|
val newFields = scanNewPath(activity, targetPath, exportMimeType)
|
||||||
|
val targetUri = Uri.parse(newFields["uri"] as String)
|
||||||
|
if (writeMetadata) {
|
||||||
|
copyMetadata(
|
||||||
|
context = activity,
|
||||||
|
sourceMimeType = sourceMimeType,
|
||||||
|
sourceUri = sourceUri,
|
||||||
|
targetMimeType = targetMimeType,
|
||||||
|
targetUri = targetUri,
|
||||||
|
targetPath = targetPath,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newFields
|
||||||
} finally {
|
} finally {
|
||||||
// clearing Glide target should happen after effectively writing the bitmap
|
// clearing Glide target should happen after effectively writing the bitmap
|
||||||
Glide.with(activity).clear(target)
|
Glide.with(activity).clear(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun copyMetadata(
|
||||||
|
context: Context,
|
||||||
|
sourceMimeType: String,
|
||||||
|
sourceUri: Uri,
|
||||||
|
targetMimeType: String,
|
||||||
|
targetUri: Uri,
|
||||||
|
targetPath: String,
|
||||||
|
) {
|
||||||
|
val editableFile = File.createTempFile("aves", null).apply {
|
||||||
|
deleteOnExit()
|
||||||
|
// copy original file to a temporary file for editing
|
||||||
|
val inputStream = StorageUtils.openInputStream(context, targetUri)
|
||||||
|
transferFrom(inputStream, File(targetPath).length())
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy IPTC / XMP via PixyMeta
|
||||||
|
|
||||||
|
var pixyIptc: pixy.meta.meta.iptc.IPTC? = null
|
||||||
|
var pixyXmp: pixy.meta.meta.xmp.XMP? = null
|
||||||
|
if (canReadWithPixyMeta(sourceMimeType)) {
|
||||||
|
StorageUtils.openInputStream(context, sourceUri)?.use { input ->
|
||||||
|
val metadata = Metadata.readMetadata(input)
|
||||||
|
if (canEditIptc(targetMimeType)) {
|
||||||
|
pixyIptc = metadata[MetadataType.IPTC] as pixy.meta.meta.iptc.IPTC?
|
||||||
|
}
|
||||||
|
if (canEditXmp(targetMimeType)) {
|
||||||
|
pixyXmp = metadata[MetadataType.XMP] as pixy.meta.meta.xmp.XMP?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pixyIptc != null || pixyXmp != null) {
|
||||||
|
editableFile.outputStream().use { output ->
|
||||||
|
if (pixyIptc != null) {
|
||||||
|
// reopen input to read from start
|
||||||
|
StorageUtils.openInputStream(context, targetUri)?.use { input ->
|
||||||
|
val iptcs = pixyIptc!!.dataSets.flatMap { it.value }
|
||||||
|
Metadata.insertIPTC(input, output, iptcs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pixyXmp != null) {
|
||||||
|
// reopen input to read from start
|
||||||
|
StorageUtils.openInputStream(context, targetUri)?.use { input ->
|
||||||
|
val xmpString = pixyXmp!!.xmpDocString()
|
||||||
|
val extendedXmp = if (pixyXmp!!.hasExtendedXmp()) pixyXmp!!.extendedXmpDocString() else null
|
||||||
|
PixyMetaHelper.setXmp(input, output, xmpString, if (targetMimeType == MimeTypes.JPEG) extendedXmp else null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy Exif via ExifInterface
|
||||||
|
|
||||||
|
val exif = HashMap<String, String?>()
|
||||||
|
val skippedTags = listOf(
|
||||||
|
ExifInterface.TAG_IMAGE_LENGTH,
|
||||||
|
ExifInterface.TAG_IMAGE_WIDTH,
|
||||||
|
ExifInterface.TAG_ORIENTATION,
|
||||||
|
)
|
||||||
|
if (canReadWithExifInterface(sourceMimeType) && canEditExif(targetMimeType)) {
|
||||||
|
StorageUtils.openInputStream(context, sourceUri)?.use { input ->
|
||||||
|
ExifInterface(input).apply {
|
||||||
|
ExifInterfaceHelper.allTags.keys.filterNot { skippedTags.contains(it) }.filter { hasAttribute(it) }.forEach { tag ->
|
||||||
|
exif[tag] = getAttribute(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (exif.isNotEmpty()) {
|
||||||
|
ExifInterface(editableFile).apply {
|
||||||
|
exif.entries.forEach { (tag, value) ->
|
||||||
|
setAttribute(tag, value)
|
||||||
|
}
|
||||||
|
saveAttributes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the edited temporary file back to the original
|
||||||
|
editableFile.transferTo(outputStream(context, targetMimeType, targetUri, targetPath))
|
||||||
|
editableFile.delete()
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
suspend fun captureFrame(
|
suspend fun captureFrame(
|
||||||
contextWrapper: ContextWrapper,
|
contextWrapper: ContextWrapper,
|
||||||
|
|
|
@ -422,6 +422,7 @@
|
||||||
"exportEntryDialogFormat": "Format:",
|
"exportEntryDialogFormat": "Format:",
|
||||||
"exportEntryDialogWidth": "Width",
|
"exportEntryDialogWidth": "Width",
|
||||||
"exportEntryDialogHeight": "Height",
|
"exportEntryDialogHeight": "Height",
|
||||||
|
"exportEntryDialogWriteMetadata": "Write metadata",
|
||||||
|
|
||||||
"renameEntryDialogLabel": "New name",
|
"renameEntryDialogLabel": "New name",
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ 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/settings/enums/enums.dart';
|
import 'package:aves/model/settings/enums/enums.dart';
|
||||||
import 'package:aves/model/source/enums/enums.dart';
|
import 'package:aves/model/source/enums/enums.dart';
|
||||||
|
import 'package:aves/ref/mime_types.dart';
|
||||||
import 'package:aves/widgets/filter_grids/albums_page.dart';
|
import 'package:aves/widgets/filter_grids/albums_page.dart';
|
||||||
import 'package:aves/widgets/filter_grids/countries_page.dart';
|
import 'package:aves/widgets/filter_grids/countries_page.dart';
|
||||||
import 'package:aves/widgets/filter_grids/places_page.dart';
|
|
||||||
import 'package:aves/widgets/filter_grids/tags_page.dart';
|
import 'package:aves/widgets/filter_grids/tags_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
@ -41,7 +41,6 @@ class SettingsDefaults {
|
||||||
static const drawerPageBookmarks = [
|
static const drawerPageBookmarks = [
|
||||||
AlbumListPage.routeName,
|
AlbumListPage.routeName,
|
||||||
CountryListPage.routeName,
|
CountryListPage.routeName,
|
||||||
PlaceListPage.routeName,
|
|
||||||
TagListPage.routeName,
|
TagListPage.routeName,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -116,6 +115,11 @@ class SettingsDefaults {
|
||||||
|
|
||||||
static const tagEditorCurrentFilterSectionExpanded = true;
|
static const tagEditorCurrentFilterSectionExpanded = true;
|
||||||
|
|
||||||
|
// converter
|
||||||
|
|
||||||
|
static const convertMimeType = MimeTypes.jpeg;
|
||||||
|
static const convertWriteMetadata = true;
|
||||||
|
|
||||||
// rendering
|
// rendering
|
||||||
static const imageBackground = EntryBackground.white;
|
static const imageBackground = EntryBackground.white;
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,11 @@ class Settings extends ChangeNotifier {
|
||||||
static const tagEditorCurrentFilterSectionExpandedKey = 'tag_editor_current_filter_section_expanded';
|
static const tagEditorCurrentFilterSectionExpandedKey = 'tag_editor_current_filter_section_expanded';
|
||||||
static const tagEditorExpandedSectionKey = 'tag_editor_expanded_section';
|
static const tagEditorExpandedSectionKey = 'tag_editor_expanded_section';
|
||||||
|
|
||||||
|
// converter
|
||||||
|
|
||||||
|
static const convertMimeTypeKey = 'convert_mime_type';
|
||||||
|
static const convertWriteMetadataKey = 'convert_write_metadata';
|
||||||
|
|
||||||
// map
|
// map
|
||||||
static const mapStyleKey = 'info_map_style';
|
static const mapStyleKey = 'info_map_style';
|
||||||
static const mapDefaultCenterKey = 'map_default_center';
|
static const mapDefaultCenterKey = 'map_default_center';
|
||||||
|
@ -724,6 +729,16 @@ class Settings extends ChangeNotifier {
|
||||||
|
|
||||||
set tagEditorExpandedSection(String? newValue) => _set(tagEditorExpandedSectionKey, newValue);
|
set tagEditorExpandedSection(String? newValue) => _set(tagEditorExpandedSectionKey, newValue);
|
||||||
|
|
||||||
|
// converter
|
||||||
|
|
||||||
|
String get convertMimeType => getString(convertMimeTypeKey) ?? SettingsDefaults.convertMimeType;
|
||||||
|
|
||||||
|
set convertMimeType(String newValue) => _set(convertMimeTypeKey, newValue);
|
||||||
|
|
||||||
|
bool get convertWriteMetadata => getBool(convertWriteMetadataKey) ?? SettingsDefaults.convertWriteMetadata;
|
||||||
|
|
||||||
|
set convertWriteMetadata(bool newValue) => _set(convertWriteMetadataKey, newValue);
|
||||||
|
|
||||||
// map
|
// map
|
||||||
|
|
||||||
EntryMapStyle? get mapStyle {
|
EntryMapStyle? get mapStyle {
|
||||||
|
@ -1069,6 +1084,7 @@ class Settings extends ChangeNotifier {
|
||||||
case videoGestureVerticalDragBrightnessVolumeKey:
|
case videoGestureVerticalDragBrightnessVolumeKey:
|
||||||
case subtitleShowOutlineKey:
|
case subtitleShowOutlineKey:
|
||||||
case tagEditorCurrentFilterSectionExpandedKey:
|
case tagEditorCurrentFilterSectionExpandedKey:
|
||||||
|
case convertWriteMetadataKey:
|
||||||
case saveSearchHistoryKey:
|
case saveSearchHistoryKey:
|
||||||
case showPinchGestureAlternativesKey:
|
case showPinchGestureAlternativesKey:
|
||||||
case filePickerShowHiddenFilesKey:
|
case filePickerShowHiddenFilesKey:
|
||||||
|
@ -1106,6 +1122,7 @@ class Settings extends ChangeNotifier {
|
||||||
case subtitleTextAlignmentKey:
|
case subtitleTextAlignmentKey:
|
||||||
case subtitleTextPositionKey:
|
case subtitleTextPositionKey:
|
||||||
case tagEditorExpandedSectionKey:
|
case tagEditorExpandedSectionKey:
|
||||||
|
case convertMimeTypeKey:
|
||||||
case mapStyleKey:
|
case mapStyleKey:
|
||||||
case mapDefaultCenterKey:
|
case mapDefaultCenterKey:
|
||||||
case coordinateFormatKey:
|
case coordinateFormatKey:
|
||||||
|
|
|
@ -127,6 +127,7 @@ class PlatformMediaEditService implements MediaEditService {
|
||||||
'lengthUnit': options.lengthUnit.name,
|
'lengthUnit': options.lengthUnit.name,
|
||||||
'width': options.width,
|
'width': options.width,
|
||||||
'height': options.height,
|
'height': options.height,
|
||||||
|
'writeMetadata': options.writeMetadata,
|
||||||
'destinationPath': destinationAlbum,
|
'destinationPath': destinationAlbum,
|
||||||
'nameConflictStrategy': nameConflictStrategy.toPlatform(),
|
'nameConflictStrategy': nameConflictStrategy.toPlatform(),
|
||||||
})
|
})
|
||||||
|
@ -187,14 +188,16 @@ class PlatformMediaEditService implements MediaEditService {
|
||||||
@immutable
|
@immutable
|
||||||
class EntryConvertOptions extends Equatable {
|
class EntryConvertOptions extends Equatable {
|
||||||
final String mimeType;
|
final String mimeType;
|
||||||
|
final bool writeMetadata;
|
||||||
final LengthUnit lengthUnit;
|
final LengthUnit lengthUnit;
|
||||||
final int width, height;
|
final int width, height;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [mimeType, lengthUnit, width, height];
|
List<Object?> get props => [mimeType, writeMetadata, lengthUnit, width, height];
|
||||||
|
|
||||||
const EntryConvertOptions({
|
const EntryConvertOptions({
|
||||||
required this.mimeType,
|
required this.mimeType,
|
||||||
|
required this.writeMetadata,
|
||||||
required this.lengthUnit,
|
required this.lengthUnit,
|
||||||
required this.width,
|
required this.width,
|
||||||
required this.height,
|
required this.height,
|
||||||
|
|
14
lib/widgets/common/fx/transitions.dart
Normal file
14
lib/widgets/common/fx/transitions.dart
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class AvesTransitions {
|
||||||
|
static Widget formTransitionBuilder(Widget child, Animation<double> animation) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: animation,
|
||||||
|
child: SizeTransition(
|
||||||
|
sizeFactor: animation,
|
||||||
|
axisAlignment: -1,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,17 @@
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/metadata/enums/enums.dart';
|
import 'package:aves/model/metadata/enums/enums.dart';
|
||||||
import 'package:aves/model/metadata/enums/length_unit.dart';
|
import 'package:aves/model/metadata/enums/length_unit.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';
|
||||||
|
import 'package:aves/theme/durations.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/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:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'aves_dialog.dart';
|
import 'aves_dialog.dart';
|
||||||
|
|
||||||
|
@ -28,8 +32,8 @@ class ConvertEntryDialog extends StatefulWidget {
|
||||||
class _ConvertEntryDialogState extends State<ConvertEntryDialog> {
|
class _ConvertEntryDialogState extends State<ConvertEntryDialog> {
|
||||||
final TextEditingController _widthController = TextEditingController(), _heightController = TextEditingController();
|
final TextEditingController _widthController = TextEditingController(), _heightController = TextEditingController();
|
||||||
final ValueNotifier<bool> _isValidNotifier = ValueNotifier(false);
|
final ValueNotifier<bool> _isValidNotifier = ValueNotifier(false);
|
||||||
late String _mimeType;
|
late ValueNotifier<String> _mimeTypeNotifier;
|
||||||
late bool _sameSized;
|
late bool _writeMetadata, _sameSized;
|
||||||
late List<LengthUnit> _lengthUnitOptions;
|
late List<LengthUnit> _lengthUnitOptions;
|
||||||
late LengthUnit _lengthUnit;
|
late LengthUnit _lengthUnit;
|
||||||
|
|
||||||
|
@ -45,7 +49,8 @@ class _ConvertEntryDialogState extends State<ConvertEntryDialog> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_mimeType = MimeTypes.jpeg;
|
_mimeTypeNotifier = ValueNotifier(settings.convertMimeType);
|
||||||
|
_writeMetadata = settings.convertWriteMetadata;
|
||||||
_sameSized = entries.map((entry) => entry.displaySize).toSet().length == 1;
|
_sameSized = entries.map((entry) => entry.displaySize).toSet().length == 1;
|
||||||
_lengthUnitOptions = [
|
_lengthUnitOptions = [
|
||||||
if (_sameSized) LengthUnit.px,
|
if (_sameSized) LengthUnit.px,
|
||||||
|
@ -103,10 +108,10 @@ class _ConvertEntryDialogState extends State<ConvertEntryDialog> {
|
||||||
TextDropdownButton<String>(
|
TextDropdownButton<String>(
|
||||||
values: imageExportFormats,
|
values: imageExportFormats,
|
||||||
valueText: MimeUtils.displayType,
|
valueText: MimeUtils.displayType,
|
||||||
value: _mimeType,
|
value: _mimeTypeNotifier.value,
|
||||||
onChanged: (selected) {
|
onChanged: (selected) {
|
||||||
if (selected != null) {
|
if (selected != null) {
|
||||||
setState(() => _mimeType = selected);
|
setState(() => _mimeTypeNotifier.value = selected);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -195,7 +200,32 @@ class _ConvertEntryDialogState extends State<ConvertEntryDialog> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
ValueListenableBuilder<String>(
|
||||||
|
valueListenable: _mimeTypeNotifier,
|
||||||
|
builder: (context, mimeType, child) {
|
||||||
|
Widget child;
|
||||||
|
if (MimeTypes.canEditExif(mimeType) || MimeTypes.canEditIptc(mimeType) || MimeTypes.canEditXmp(mimeType)) {
|
||||||
|
child = SwitchListTile(
|
||||||
|
value: _writeMetadata,
|
||||||
|
onChanged: (v) => setState(() => _writeMetadata = v),
|
||||||
|
title: Text(context.l10n.exportEntryDialogWriteMetadata),
|
||||||
|
contentPadding: const EdgeInsetsDirectional.only(
|
||||||
|
start: AvesDialog.defaultHorizontalContentPadding,
|
||||||
|
end: AvesDialog.defaultHorizontalContentPadding - 8,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
child = const SizedBox(height: 16);
|
||||||
|
}
|
||||||
|
return AnimatedSwitcher(
|
||||||
|
duration: context.read<DurationsData>().formTransition,
|
||||||
|
switchInCurve: Curves.easeInOutCubic,
|
||||||
|
switchOutCurve: Curves.easeInOutCubic,
|
||||||
|
transitionBuilder: AvesTransitions.formTransitionBuilder,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
const CancelButton(),
|
const CancelButton(),
|
||||||
|
@ -209,12 +239,19 @@ class _ConvertEntryDialogState extends State<ConvertEntryDialog> {
|
||||||
final height = int.tryParse(_heightController.text);
|
final height = int.tryParse(_heightController.text);
|
||||||
final options = (width != null && height != null)
|
final options = (width != null && height != null)
|
||||||
? EntryConvertOptions(
|
? EntryConvertOptions(
|
||||||
mimeType: _mimeType,
|
mimeType: _mimeTypeNotifier.value,
|
||||||
|
writeMetadata: _writeMetadata,
|
||||||
lengthUnit: _lengthUnit,
|
lengthUnit: _lengthUnit,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
if (options != null) {
|
||||||
|
settings.convertMimeType = options.mimeType;
|
||||||
|
settings.convertWriteMetadata = options.writeMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
Navigator.maybeOf(context)?.pop(options);
|
Navigator.maybeOf(context)?.pop(options);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import 'package:aves/utils/time_utils.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';
|
||||||
|
import 'package:aves/widgets/common/fx/transitions.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/widgets/dialogs/aves_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||||
import 'package:aves/widgets/dialogs/item_picker.dart';
|
import 'package:aves/widgets/dialogs/item_picker.dart';
|
||||||
|
@ -111,7 +112,7 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
|
||||||
duration: context.read<DurationsData>().formTransition,
|
duration: context.read<DurationsData>().formTransition,
|
||||||
switchInCurve: Curves.easeInOutCubic,
|
switchInCurve: Curves.easeInOutCubic,
|
||||||
switchOutCurve: Curves.easeInOutCubic,
|
switchOutCurve: Curves.easeInOutCubic,
|
||||||
transitionBuilder: _formTransitionBuilder,
|
transitionBuilder: AvesTransitions.formTransitionBuilder,
|
||||||
child: Column(
|
child: Column(
|
||||||
key: ValueKey(_action),
|
key: ValueKey(_action),
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
@ -143,15 +144,6 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _formTransitionBuilder(Widget child, Animation<double> animation) => FadeTransition(
|
|
||||||
opacity: animation,
|
|
||||||
child: SizeTransition(
|
|
||||||
sizeFactor: animation,
|
|
||||||
axisAlignment: -1,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSetCustomContent(BuildContext context) {
|
Widget _buildSetCustomContent(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final locale = l10n.localeName;
|
final locale = l10n.localeName;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'package:aves/theme/themes.dart';
|
||||||
import 'package:aves/utils/constants.dart';
|
import 'package:aves/utils/constants.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/providers/media_query_data_provider.dart';
|
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||||
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||||
import 'package:aves/widgets/dialogs/item_picker.dart';
|
import 'package:aves/widgets/dialogs/item_picker.dart';
|
||||||
|
@ -114,7 +115,7 @@ class _EditEntryLocationDialogState extends State<EditEntryLocationDialog> {
|
||||||
duration: context.read<DurationsData>().formTransition,
|
duration: context.read<DurationsData>().formTransition,
|
||||||
switchInCurve: Curves.easeInOutCubic,
|
switchInCurve: Curves.easeInOutCubic,
|
||||||
switchOutCurve: Curves.easeInOutCubic,
|
switchOutCurve: Curves.easeInOutCubic,
|
||||||
transitionBuilder: _formTransitionBuilder,
|
transitionBuilder: AvesTransitions.formTransitionBuilder,
|
||||||
child: Column(
|
child: Column(
|
||||||
key: ValueKey(_action),
|
key: ValueKey(_action),
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
@ -145,15 +146,6 @@ class _EditEntryLocationDialogState extends State<EditEntryLocationDialog> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _formTransitionBuilder(Widget child, Animation<double> animation) => FadeTransition(
|
|
||||||
opacity: animation,
|
|
||||||
child: SizeTransition(
|
|
||||||
sizeFactor: animation,
|
|
||||||
axisAlignment: -1,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildChooseOnMapContent(BuildContext context) {
|
Widget _buildChooseOnMapContent(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves/theme/icons.dart';
|
||||||
import 'package:aves/theme/themes.dart';
|
import 'package:aves/theme/themes.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/identity/aves_caption.dart';
|
import 'package:aves/widgets/common/identity/aves_caption.dart';
|
||||||
import 'package:aves/widgets/common/identity/highlight_title.dart';
|
import 'package:aves/widgets/common/identity/highlight_title.dart';
|
||||||
import 'package:aves/widgets/common/tile_extent_controller.dart';
|
import 'package:aves/widgets/common/tile_extent_controller.dart';
|
||||||
|
@ -98,14 +99,7 @@ class _TileViewDialogState<S, G, L> extends State<TileViewDialog<S, G, L>> with
|
||||||
duration: context.read<DurationsData>().formTransition,
|
duration: context.read<DurationsData>().formTransition,
|
||||||
switchInCurve: Curves.easeInOutCubic,
|
switchInCurve: Curves.easeInOutCubic,
|
||||||
switchOutCurve: Curves.easeInOutCubic,
|
switchOutCurve: Curves.easeInOutCubic,
|
||||||
transitionBuilder: (child, animation) => FadeTransition(
|
transitionBuilder: AvesTransitions.formTransitionBuilder,
|
||||||
opacity: animation,
|
|
||||||
child: SizeTransition(
|
|
||||||
sizeFactor: animation,
|
|
||||||
axisAlignment: -1,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: _buildSection(
|
child: _buildSection(
|
||||||
show: canGroup,
|
show: canGroup,
|
||||||
icon: AIcons.group,
|
icon: AIcons.group,
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/filters/date.dart';
|
import 'package:aves/model/filters/date.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.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/identity/aves_filter_chip.dart';
|
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
||||||
import 'package:aves/widgets/stats/date/axis.dart';
|
import 'package:aves/widgets/stats/date/axis.dart';
|
||||||
import 'package:charts_flutter/flutter.dart' as charts;
|
import 'package:charts_flutter/flutter.dart' as charts;
|
||||||
|
@ -343,14 +344,7 @@ class _HistogramState extends State<Histogram> with AutomaticKeepAliveClientMixi
|
||||||
duration: context.read<DurationsData>().formTransition,
|
duration: context.read<DurationsData>().formTransition,
|
||||||
switchInCurve: Curves.easeInOutCubic,
|
switchInCurve: Curves.easeInOutCubic,
|
||||||
switchOutCurve: Curves.easeInOutCubic,
|
switchOutCurve: Curves.easeInOutCubic,
|
||||||
transitionBuilder: (child, animation) => FadeTransition(
|
transitionBuilder: AvesTransitions.formTransitionBuilder,
|
||||||
opacity: animation,
|
|
||||||
child: SizeTransition(
|
|
||||||
sizeFactor: animation,
|
|
||||||
axisAlignment: -1,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -215,6 +215,7 @@
|
||||||
"exportEntryDialogFormat",
|
"exportEntryDialogFormat",
|
||||||
"exportEntryDialogWidth",
|
"exportEntryDialogWidth",
|
||||||
"exportEntryDialogHeight",
|
"exportEntryDialogHeight",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"renameEntryDialogLabel",
|
"renameEntryDialogLabel",
|
||||||
"editEntryDialogCopyFromItem",
|
"editEntryDialogCopyFromItem",
|
||||||
"editEntryDialogTargetFieldsHeader",
|
"editEntryDialogTargetFieldsHeader",
|
||||||
|
@ -766,6 +767,7 @@
|
||||||
"exportEntryDialogFormat",
|
"exportEntryDialogFormat",
|
||||||
"exportEntryDialogWidth",
|
"exportEntryDialogWidth",
|
||||||
"exportEntryDialogHeight",
|
"exportEntryDialogHeight",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"renameEntryDialogLabel",
|
"renameEntryDialogLabel",
|
||||||
"editEntryDialogCopyFromItem",
|
"editEntryDialogCopyFromItem",
|
||||||
"editEntryDialogTargetFieldsHeader",
|
"editEntryDialogTargetFieldsHeader",
|
||||||
|
@ -1182,6 +1184,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
|
@ -1211,6 +1214,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
|
@ -1220,11 +1224,16 @@
|
||||||
|
|
||||||
"el": [
|
"el": [
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty"
|
"placeEmpty"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"es": [
|
||||||
|
"exportEntryDialogWriteMetadata"
|
||||||
|
],
|
||||||
|
|
||||||
"eu": [
|
"eu": [
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
"chipActionLock",
|
"chipActionLock",
|
||||||
|
@ -1247,6 +1256,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
|
@ -1356,6 +1366,7 @@
|
||||||
"renameProcessorName",
|
"renameProcessorName",
|
||||||
"deleteSingleAlbumConfirmationDialogMessage",
|
"deleteSingleAlbumConfirmationDialogMessage",
|
||||||
"deleteMultiAlbumConfirmationDialogMessage",
|
"deleteMultiAlbumConfirmationDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"renameEntryDialogLabel",
|
"renameEntryDialogLabel",
|
||||||
"editEntryDialogCopyFromItem",
|
"editEntryDialogCopyFromItem",
|
||||||
"editEntryDialogTargetFieldsHeader",
|
"editEntryDialogTargetFieldsHeader",
|
||||||
|
@ -1721,6 +1732,10 @@
|
||||||
"filePickerUseThisFolder"
|
"filePickerUseThisFolder"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"fr": [
|
||||||
|
"exportEntryDialogWriteMetadata"
|
||||||
|
],
|
||||||
|
|
||||||
"gl": [
|
"gl": [
|
||||||
"columnCount",
|
"columnCount",
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
|
@ -1826,6 +1841,7 @@
|
||||||
"exportEntryDialogFormat",
|
"exportEntryDialogFormat",
|
||||||
"exportEntryDialogWidth",
|
"exportEntryDialogWidth",
|
||||||
"exportEntryDialogHeight",
|
"exportEntryDialogHeight",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"renameEntryDialogLabel",
|
"renameEntryDialogLabel",
|
||||||
"editEntryDialogCopyFromItem",
|
"editEntryDialogCopyFromItem",
|
||||||
"editEntryDialogTargetFieldsHeader",
|
"editEntryDialogTargetFieldsHeader",
|
||||||
|
@ -2454,6 +2470,7 @@
|
||||||
"exportEntryDialogFormat",
|
"exportEntryDialogFormat",
|
||||||
"exportEntryDialogWidth",
|
"exportEntryDialogWidth",
|
||||||
"exportEntryDialogHeight",
|
"exportEntryDialogHeight",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"renameEntryDialogLabel",
|
"renameEntryDialogLabel",
|
||||||
"editEntryDialogCopyFromItem",
|
"editEntryDialogCopyFromItem",
|
||||||
"editEntryDialogTargetFieldsHeader",
|
"editEntryDialogTargetFieldsHeader",
|
||||||
|
@ -2850,13 +2867,15 @@
|
||||||
|
|
||||||
"id": [
|
"id": [
|
||||||
"lengthUnitPixel",
|
"lengthUnitPixel",
|
||||||
"lengthUnitPercent"
|
"lengthUnitPercent",
|
||||||
|
"exportEntryDialogWriteMetadata"
|
||||||
],
|
],
|
||||||
|
|
||||||
"it": [
|
"it": [
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
"lengthUnitPixel",
|
"lengthUnitPixel",
|
||||||
"lengthUnitPercent",
|
"lengthUnitPercent",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty"
|
"placeEmpty"
|
||||||
|
@ -2894,6 +2913,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"tooManyItemsErrorDialogMessage",
|
"tooManyItemsErrorDialogMessage",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
|
@ -2908,6 +2928,10 @@
|
||||||
"settingsWidgetDisplayedItem"
|
"settingsWidgetDisplayedItem"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"ko": [
|
||||||
|
"exportEntryDialogWriteMetadata"
|
||||||
|
],
|
||||||
|
|
||||||
"lt": [
|
"lt": [
|
||||||
"columnCount",
|
"columnCount",
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
|
@ -2934,6 +2958,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"tooManyItemsErrorDialogMessage",
|
"tooManyItemsErrorDialogMessage",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
|
@ -2969,6 +2994,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
|
@ -3013,6 +3039,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"tooManyItemsErrorDialogMessage",
|
"tooManyItemsErrorDialogMessage",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
|
@ -3064,6 +3091,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"editEntryDialogTargetFieldsHeader",
|
"editEntryDialogTargetFieldsHeader",
|
||||||
"editEntryDateDialogSetCustom",
|
"editEntryDateDialogSetCustom",
|
||||||
"editEntryLocationDialogTitle",
|
"editEntryLocationDialogTitle",
|
||||||
|
@ -3347,6 +3375,7 @@
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
"lengthUnitPixel",
|
"lengthUnitPixel",
|
||||||
"lengthUnitPercent",
|
"lengthUnitPercent",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty"
|
"placeEmpty"
|
||||||
|
@ -3375,6 +3404,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"tooManyItemsErrorDialogMessage",
|
"tooManyItemsErrorDialogMessage",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
|
@ -3388,6 +3418,7 @@
|
||||||
"chipActionGoToPlacePage",
|
"chipActionGoToPlacePage",
|
||||||
"lengthUnitPixel",
|
"lengthUnitPixel",
|
||||||
"lengthUnitPercent",
|
"lengthUnitPercent",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty"
|
"placeEmpty"
|
||||||
|
@ -3417,6 +3448,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"tooManyItemsErrorDialogMessage",
|
"tooManyItemsErrorDialogMessage",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
|
@ -3465,6 +3497,7 @@
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
"deleteSingleAlbumConfirmationDialogMessage",
|
"deleteSingleAlbumConfirmationDialogMessage",
|
||||||
"deleteMultiAlbumConfirmationDialogMessage",
|
"deleteMultiAlbumConfirmationDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"editEntryLocationDialogLatitude",
|
"editEntryLocationDialogLatitude",
|
||||||
"editEntryLocationDialogLongitude",
|
"editEntryLocationDialogLongitude",
|
||||||
"locationPickerUseThisLocationButton",
|
"locationPickerUseThisLocationButton",
|
||||||
|
@ -3873,6 +3906,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"editEntryDateDialogExtractFromTitle",
|
"editEntryDateDialogExtractFromTitle",
|
||||||
"editEntryDateDialogShift",
|
"editEntryDateDialogShift",
|
||||||
"removeEntryMetadataDialogTitle",
|
"removeEntryMetadataDialogTitle",
|
||||||
|
@ -4217,6 +4251,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
"placeEmpty",
|
"placeEmpty",
|
||||||
|
@ -4226,7 +4261,8 @@
|
||||||
|
|
||||||
"uk": [
|
"uk": [
|
||||||
"lengthUnitPixel",
|
"lengthUnitPixel",
|
||||||
"lengthUnitPercent"
|
"lengthUnitPercent",
|
||||||
|
"exportEntryDialogWriteMetadata"
|
||||||
],
|
],
|
||||||
|
|
||||||
"zh": [
|
"zh": [
|
||||||
|
@ -4253,6 +4289,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"tooManyItemsErrorDialogMessage",
|
"tooManyItemsErrorDialogMessage",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
|
@ -4291,6 +4328,7 @@
|
||||||
"authenticateToConfigureVault",
|
"authenticateToConfigureVault",
|
||||||
"authenticateToUnlockVault",
|
"authenticateToUnlockVault",
|
||||||
"vaultBinUsageDialogMessage",
|
"vaultBinUsageDialogMessage",
|
||||||
|
"exportEntryDialogWriteMetadata",
|
||||||
"tooManyItemsErrorDialogMessage",
|
"tooManyItemsErrorDialogMessage",
|
||||||
"drawerPlacePage",
|
"drawerPlacePage",
|
||||||
"placePageTitle",
|
"placePageTitle",
|
||||||
|
|
Loading…
Reference in a new issue