guard against large tiff
This commit is contained in:
parent
8c5a600151
commit
0d946b5a43
8 changed files with 142 additions and 78 deletions
|
@ -52,6 +52,7 @@ import deckers.thibault.aves.utils.LogUtils
|
|||
import deckers.thibault.aves.utils.MimeTypes
|
||||
import deckers.thibault.aves.utils.MimeTypes.isImage
|
||||
import deckers.thibault.aves.utils.MimeTypes.isMultimedia
|
||||
import deckers.thibault.aves.utils.MimeTypes.isSupportedByExifInterface
|
||||
import deckers.thibault.aves.utils.MimeTypes.isSupportedByMetadataExtractor
|
||||
import deckers.thibault.aves.utils.MimeTypes.isVideo
|
||||
import deckers.thibault.aves.utils.MimeTypes.tiffExtensionPattern
|
||||
|
@ -86,6 +87,7 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
private fun getAllMetadata(call: MethodCall, result: MethodChannel.Result) {
|
||||
val mimeType = call.argument<String>("mimeType")
|
||||
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
|
||||
val sizeBytes = call.argument<Number>("sizeBytes")?.toLong()
|
||||
if (mimeType == null || uri == null) {
|
||||
result.error("getAllMetadata-args", "failed because of missing arguments", null)
|
||||
return
|
||||
|
@ -95,10 +97,10 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
var foundExif = false
|
||||
var foundXmp = false
|
||||
|
||||
if (isSupportedByMetadataExtractor(mimeType)) {
|
||||
if (isSupportedByMetadataExtractor(mimeType, sizeBytes)) {
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
val metadata = ImageMetadataReader.readMetadata(input)
|
||||
val metadata = ImageMetadataReader.readMetadata(input, sizeBytes ?: -1)
|
||||
foundExif = metadata.containsDirectoryOfType(ExifDirectoryBase::class.java)
|
||||
foundXmp = metadata.containsDirectoryOfType(XmpDirectory::class.java)
|
||||
|
||||
|
@ -138,7 +140,7 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (!foundExif) {
|
||||
if (!foundExif && isSupportedByExifInterface(mimeType, sizeBytes)) {
|
||||
// fallback to read EXIF via ExifInterface
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
|
@ -192,12 +194,13 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
val mimeType = call.argument<String>("mimeType")
|
||||
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
|
||||
val path = call.argument<String>("path")
|
||||
val sizeBytes = call.argument<Number>("sizeBytes")?.toLong()
|
||||
if (mimeType == null || uri == null) {
|
||||
result.error("getCatalogMetadata-args", "failed because of missing arguments", null)
|
||||
return
|
||||
}
|
||||
|
||||
val metadataMap = HashMap(getCatalogMetadataByMetadataExtractor(uri, mimeType, path))
|
||||
val metadataMap = HashMap(getCatalogMetadataByMetadataExtractor(uri, mimeType, path, sizeBytes))
|
||||
if (isVideo(mimeType)) {
|
||||
metadataMap.putAll(getVideoCatalogMetadataByMediaMetadataRetriever(uri))
|
||||
}
|
||||
|
@ -213,15 +216,15 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
// set `KEY_XMP_TITLE_DESCRIPTION` from these fields (by precedence):
|
||||
// - XMP / dc:title
|
||||
// - XMP / dc:description
|
||||
private fun getCatalogMetadataByMetadataExtractor(uri: Uri, mimeType: String, path: String?): Map<String, Any> {
|
||||
private fun getCatalogMetadataByMetadataExtractor(uri: Uri, mimeType: String, path: String?, sizeBytes: Long?): Map<String, Any> {
|
||||
val metadataMap = HashMap<String, Any>()
|
||||
|
||||
var foundExif = false
|
||||
|
||||
if (isSupportedByMetadataExtractor(mimeType)) {
|
||||
if (isSupportedByMetadataExtractor(mimeType, sizeBytes)) {
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
val metadata = ImageMetadataReader.readMetadata(input)
|
||||
val metadata = ImageMetadataReader.readMetadata(input, sizeBytes ?: -1)
|
||||
foundExif = metadata.containsDirectoryOfType(ExifDirectoryBase::class.java)
|
||||
|
||||
// File type
|
||||
|
@ -311,7 +314,7 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (!foundExif) {
|
||||
if (!foundExif && isSupportedByExifInterface(mimeType, sizeBytes)) {
|
||||
// fallback to read EXIF via ExifInterface
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
|
@ -371,6 +374,7 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
private fun getOverlayMetadata(call: MethodCall, result: MethodChannel.Result) {
|
||||
val mimeType = call.argument<String>("mimeType")
|
||||
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
|
||||
val sizeBytes = call.argument<Number>("sizeBytes")?.toLong()
|
||||
if (mimeType == null || uri == null) {
|
||||
result.error("getOverlayMetadata-args", "failed because of missing arguments", null)
|
||||
return
|
||||
|
@ -396,10 +400,10 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
var foundExif = false
|
||||
if (isSupportedByMetadataExtractor(mimeType)) {
|
||||
if (isSupportedByMetadataExtractor(mimeType, sizeBytes)) {
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
val metadata = ImageMetadataReader.readMetadata(input)
|
||||
val metadata = ImageMetadataReader.readMetadata(input, sizeBytes ?: -1)
|
||||
for (dir in metadata.getDirectoriesOfType(ExifSubIFDDirectory::class.java)) {
|
||||
foundExif = true
|
||||
dir.getSafeRational(ExifSubIFDDirectory.TAG_FNUMBER) { metadataMap[KEY_APERTURE] = it.numerator.toDouble() / it.denominator }
|
||||
|
@ -415,7 +419,7 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (!foundExif) {
|
||||
if (!foundExif && isSupportedByExifInterface(mimeType, sizeBytes)) {
|
||||
// fallback to read EXIF via ExifInterface
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
|
@ -488,26 +492,31 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
private fun getExifInterfaceMetadata(call: MethodCall, result: MethodChannel.Result) {
|
||||
val mimeType = call.argument<String>("mimeType")
|
||||
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
|
||||
if (uri == null) {
|
||||
val sizeBytes = call.argument<Number>("sizeBytes")?.toLong()
|
||||
if (mimeType == null || uri == null) {
|
||||
result.error("getExifInterfaceMetadata-args", "failed because of missing arguments", null)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
val exif = ExifInterface(input)
|
||||
val metadataMap = HashMap<String, String?>()
|
||||
for (tag in ExifInterfaceHelper.allTags.keys.filter { exif.hasAttribute(it) }) {
|
||||
metadataMap[tag] = exif.getAttribute(tag)
|
||||
val metadataMap = HashMap<String, String?>()
|
||||
if (isSupportedByExifInterface(mimeType, sizeBytes, strict = false)) {
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
val exif = ExifInterface(input)
|
||||
for (tag in ExifInterfaceHelper.allTags.keys.filter { exif.hasAttribute(it) }) {
|
||||
metadataMap[tag] = exif.getAttribute(tag)
|
||||
}
|
||||
}
|
||||
result.success(metadataMap)
|
||||
} ?: result.error("getExifInterfaceMetadata-noinput", "failed to get exif for uri=$uri", null)
|
||||
} catch (e: Exception) {
|
||||
// ExifInterface initialization can fail with a RuntimeException
|
||||
// caused by an internal MediaMetadataRetriever failure
|
||||
result.error("getExifInterfaceMetadata-failure", "failed to get exif for uri=$uri", e.message)
|
||||
} catch (e: Exception) {
|
||||
// ExifInterface initialization can fail with a RuntimeException
|
||||
// caused by an internal MediaMetadataRetriever failure
|
||||
result.error("getExifInterfaceMetadata-failure", "failed to get exif for uri=$uri", e.message)
|
||||
return
|
||||
}
|
||||
}
|
||||
result.success(metadataMap)
|
||||
}
|
||||
|
||||
private fun getMediaMetadataRetrieverMetadata(call: MethodCall, result: MethodChannel.Result) {
|
||||
|
@ -563,46 +572,45 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
private fun getMetadataExtractorSummary(call: MethodCall, result: MethodChannel.Result) {
|
||||
val mimeType = call.argument<String>("mimeType")
|
||||
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
|
||||
if (uri == null) {
|
||||
val sizeBytes = call.argument<Number>("sizeBytes")?.toLong()
|
||||
if (mimeType == null || uri == null) {
|
||||
result.error("getMetadataExtractorSummary-args", "failed because of missing arguments", null)
|
||||
return
|
||||
}
|
||||
|
||||
val metadataMap = HashMap<String, String>()
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
val metadata = ImageMetadataReader.readMetadata(input)
|
||||
metadataMap["mimeType"] = metadata.getDirectoriesOfType(FileTypeDirectory::class.java).joinToString { dir ->
|
||||
if (dir.containsTag(FileTypeDirectory.TAG_DETECTED_FILE_MIME_TYPE)) {
|
||||
dir.getString(FileTypeDirectory.TAG_DETECTED_FILE_MIME_TYPE)
|
||||
} else ""
|
||||
}
|
||||
metadataMap["typeName"] = metadata.getDirectoriesOfType(FileTypeDirectory::class.java).joinToString { dir ->
|
||||
if (dir.containsTag(FileTypeDirectory.TAG_DETECTED_FILE_TYPE_NAME)) {
|
||||
dir.getString(FileTypeDirectory.TAG_DETECTED_FILE_TYPE_NAME)
|
||||
} else ""
|
||||
}
|
||||
for (dir in metadata.directories) {
|
||||
val dirName = dir.name ?: ""
|
||||
var index = 0
|
||||
while (metadataMap.containsKey("$dirName ($index)")) index++
|
||||
var value = "${dir.tagCount} tags"
|
||||
dir.parent?.let { value += ", parent: ${it.name}" }
|
||||
metadataMap["$dirName ($index)"] = value
|
||||
if (isSupportedByMetadataExtractor(mimeType, sizeBytes)) {
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
val metadata = ImageMetadataReader.readMetadata(input, sizeBytes ?: -1)
|
||||
metadataMap["mimeType"] = metadata.getDirectoriesOfType(FileTypeDirectory::class.java).joinToString { dir ->
|
||||
if (dir.containsTag(FileTypeDirectory.TAG_DETECTED_FILE_MIME_TYPE)) {
|
||||
dir.getString(FileTypeDirectory.TAG_DETECTED_FILE_MIME_TYPE)
|
||||
} else ""
|
||||
}
|
||||
metadataMap["typeName"] = metadata.getDirectoriesOfType(FileTypeDirectory::class.java).joinToString { dir ->
|
||||
if (dir.containsTag(FileTypeDirectory.TAG_DETECTED_FILE_TYPE_NAME)) {
|
||||
dir.getString(FileTypeDirectory.TAG_DETECTED_FILE_TYPE_NAME)
|
||||
} else ""
|
||||
}
|
||||
for (dir in metadata.directories) {
|
||||
val dirName = dir.name ?: ""
|
||||
var index = 0
|
||||
while (metadataMap.containsKey("$dirName ($index)")) index++
|
||||
var value = "${dir.tagCount} tags"
|
||||
dir.parent?.let { value += ", parent: ${it.name}" }
|
||||
metadataMap["$dirName ($index)"] = value
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to get metadata by metadata-extractor for uri=$uri", e)
|
||||
} catch (e: NoClassDefFoundError) {
|
||||
Log.w(LOG_TAG, "failed to get metadata by metadata-extractor for uri=$uri", e)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to get metadata by metadata-extractor for uri=$uri", e)
|
||||
} catch (e: NoClassDefFoundError) {
|
||||
Log.w(LOG_TAG, "failed to get metadata by metadata-extractor for uri=$uri", e)
|
||||
}
|
||||
|
||||
if (metadataMap.isNotEmpty()) {
|
||||
result.success(metadataMap)
|
||||
} else {
|
||||
result.error("getMetadataExtractorSummary-failure", "failed to get metadata for uri=$uri", null)
|
||||
}
|
||||
result.success(metadataMap)
|
||||
}
|
||||
|
||||
private fun getEmbeddedPictures(call: MethodCall, result: MethodChannel.Result) {
|
||||
|
@ -628,26 +636,30 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
private fun getExifThumbnails(call: MethodCall, result: MethodChannel.Result) {
|
||||
val mimeType = call.argument<String>("mimeType")
|
||||
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
|
||||
if (uri == null) {
|
||||
val sizeBytes = call.argument<Number>("sizeBytes")?.toLong()
|
||||
if (mimeType == null || uri == null) {
|
||||
result.error("getExifThumbnails-args", "failed because of missing arguments", null)
|
||||
return
|
||||
}
|
||||
|
||||
val thumbnails = ArrayList<ByteArray>()
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
val exif = ExifInterface(input)
|
||||
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
||||
exif.thumbnailBitmap?.let { bitmap ->
|
||||
TransformationUtils.rotateImageExif(BitmapUtils.getBitmapPool(context), bitmap, orientation)?.let {
|
||||
thumbnails.add(it.getBytes(canHaveAlpha = false, recycle = false))
|
||||
if (isSupportedByExifInterface(mimeType, sizeBytes)) {
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
val exif = ExifInterface(input)
|
||||
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
||||
exif.thumbnailBitmap?.let { bitmap ->
|
||||
TransformationUtils.rotateImageExif(BitmapUtils.getBitmapPool(context), bitmap, orientation)?.let {
|
||||
thumbnails.add(it.getBytes(canHaveAlpha = false, recycle = false))
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// ExifInterface initialization can fail with a RuntimeException
|
||||
// caused by an internal MediaMetadataRetriever failure
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// ExifInterface initialization can fail with a RuntimeException
|
||||
// caused by an internal MediaMetadataRetriever failure
|
||||
}
|
||||
result.success(thumbnails)
|
||||
}
|
||||
|
@ -655,16 +667,17 @@ class MetadataHandler(private val context: Context) : MethodCallHandler {
|
|||
private fun getXmpThumbnails(call: MethodCall, result: MethodChannel.Result) {
|
||||
val mimeType = call.argument<String>("mimeType")
|
||||
val uri = call.argument<String>("uri")?.let { Uri.parse(it) }
|
||||
val sizeBytes = call.argument<Number>("sizeBytes")?.toLong()
|
||||
if (mimeType == null || uri == null) {
|
||||
result.error("getXmpThumbnails-args", "failed because of missing arguments", null)
|
||||
return
|
||||
}
|
||||
|
||||
val thumbnails = ArrayList<ByteArray>()
|
||||
if (isSupportedByMetadataExtractor(mimeType)) {
|
||||
if (isSupportedByMetadataExtractor(mimeType, sizeBytes)) {
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
val metadata = ImageMetadataReader.readMetadata(input)
|
||||
val metadata = ImageMetadataReader.readMetadata(input, sizeBytes ?: -1)
|
||||
for (dir in metadata.getDirectoriesOfType(XmpDirectory::class.java)) {
|
||||
val xmpMeta = dir.xmpMeta
|
||||
try {
|
||||
|
|
|
@ -27,6 +27,7 @@ import deckers.thibault.aves.metadata.MetadataExtractorHelper.getSafeLong
|
|||
import deckers.thibault.aves.model.provider.FieldMap
|
||||
import deckers.thibault.aves.utils.MimeTypes
|
||||
import deckers.thibault.aves.utils.StorageUtils
|
||||
import org.beyka.tiffbitmapfactory.TiffBitmapFactory
|
||||
import java.io.IOException
|
||||
|
||||
class SourceImageEntry {
|
||||
|
@ -129,7 +130,10 @@ class SourceImageEntry {
|
|||
fillByExifInterface(context)
|
||||
}
|
||||
if (!isSized) {
|
||||
fillByBitmapDecode(context)
|
||||
when (sourceMimeType) {
|
||||
MimeTypes.TIFF -> fillByTiffDecode(context)
|
||||
else -> fillByBitmapDecode(context)
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
@ -155,11 +159,13 @@ class SourceImageEntry {
|
|||
// finds: width, height, orientation, date, duration
|
||||
private fun fillByMetadataExtractor(context: Context) {
|
||||
// skip raw images because `metadata-extractor` reports the decoded dimensions instead of the raw dimensions
|
||||
if (!MimeTypes.isSupportedByMetadataExtractor(sourceMimeType) || MimeTypes.isRaw(sourceMimeType)) return
|
||||
if (!MimeTypes.isSupportedByMetadataExtractor(sourceMimeType, sizeBytes)
|
||||
|| MimeTypes.isRaw(sourceMimeType)
|
||||
) return
|
||||
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
val metadata = ImageMetadataReader.readMetadata(input)
|
||||
val metadata = ImageMetadataReader.readMetadata(input, sizeBytes ?: -1)
|
||||
|
||||
// do not switch on specific mime types, as the reported mime type could be wrong
|
||||
// (e.g. PNG registered as JPG)
|
||||
|
@ -207,7 +213,7 @@ class SourceImageEntry {
|
|||
|
||||
// finds: width, height, orientation, date
|
||||
private fun fillByExifInterface(context: Context) {
|
||||
if (!ExifInterface.isSupportedMimeType(sourceMimeType)) return;
|
||||
if (!MimeTypes.isSupportedByExifInterface(sourceMimeType, sizeBytes)) return;
|
||||
|
||||
try {
|
||||
StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||
|
@ -240,6 +246,22 @@ class SourceImageEntry {
|
|||
}
|
||||
}
|
||||
|
||||
private fun fillByTiffDecode(context: Context) {
|
||||
try {
|
||||
context.contentResolver.openFileDescriptor(uri, "r")?.use { descriptor ->
|
||||
val options = TiffBitmapFactory.Options().apply {
|
||||
inJustDecodeBounds = true
|
||||
}
|
||||
TiffBitmapFactory.decodeFileDescriptor(descriptor.fd, options)
|
||||
width = options.outWidth
|
||||
height = options.outHeight
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
// convenience method
|
||||
private fun toLong(o: Any?): Long? = when (o) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package deckers.thibault.aves.utils
|
||||
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
|
||||
object MimeTypes {
|
||||
private const val IMAGE = "image"
|
||||
|
||||
|
@ -65,12 +67,25 @@ object MimeTypes {
|
|||
else -> false
|
||||
}
|
||||
|
||||
// as of metadata-extractor v2.14.0
|
||||
fun isSupportedByMetadataExtractor(mimeType: String) = when (mimeType) {
|
||||
// opening large TIFF files yields an OOM (both with `metadata-extractor` v2.15.0 and `ExifInterface` v1.3.1),
|
||||
// so we define an arbitrary threshold to avoid a crash on launch.
|
||||
// It is not clear whether it is because of the file itself or its metadata.
|
||||
private const val tiffSizeBytesMax = 128 * (1 shl 20) // MB
|
||||
|
||||
// as of `metadata-extractor` v2.14.0
|
||||
fun isSupportedByMetadataExtractor(mimeType: String, sizeBytes: Long?) = when (mimeType) {
|
||||
WBMP, MP2T, WEBM -> false
|
||||
TIFF -> sizeBytes != null && sizeBytes < tiffSizeBytesMax
|
||||
else -> true
|
||||
}
|
||||
|
||||
// as of `ExifInterface` v1.3.1, `isSupportedMimeType` reports
|
||||
// no support for TIFF images, but it can actually open them (maybe other formats too)
|
||||
fun isSupportedByExifInterface(mimeType: String, sizeBytes: Long?, strict: Boolean = true) = when (mimeType) {
|
||||
TIFF -> sizeBytes != null && sizeBytes < tiffSizeBytesMax
|
||||
else -> ExifInterface.isSupportedMimeType(mimeType) || !strict
|
||||
}
|
||||
|
||||
// Glide automatically applies EXIF orientation when decoding images of known formats
|
||||
// but we need to rotate the decoded bitmap for the other formats
|
||||
// maybe related to ExifInterface version used by Glide:
|
||||
|
|
|
@ -173,7 +173,7 @@ class ImageEntry {
|
|||
bool get isSvg => mimeType == MimeTypes.svg;
|
||||
|
||||
// guess whether this is a photo, according to file type (used as a hint to e.g. display megapixels)
|
||||
bool get isPhoto => [MimeTypes.heic, MimeTypes.heif, MimeTypes.jpeg].contains(mimeType) || isRaw;
|
||||
bool get isPhoto => [MimeTypes.heic, MimeTypes.heif, MimeTypes.jpeg, MimeTypes.tiff].contains(mimeType) || isRaw;
|
||||
|
||||
// Android's `BitmapRegionDecoder` documentation states that "only the JPEG and PNG formats are supported"
|
||||
// but in practice (tested on API 25, 27, 29), it successfully decodes the formats listed below,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/painting.dart';
|
||||
|
||||
class BrandColors {
|
||||
static const Color adobeAfterEffects = Color(0xFF9A9AFF);
|
||||
static const Color adobeIllustrator = Color(0xFFFF9B00);
|
||||
static const Color adobePhotoshop = Color(0xFF2DAAFF);
|
||||
static const Color android = Color(0xFF3DDC84);
|
||||
|
@ -9,6 +10,8 @@ class BrandColors {
|
|||
static Color get(String text) {
|
||||
if (text != null) {
|
||||
switch (text.toLowerCase()) {
|
||||
case 'after effects':
|
||||
return adobeAfterEffects;
|
||||
case 'illustrator':
|
||||
return adobeIllustrator;
|
||||
case 'photoshop':
|
||||
|
|
|
@ -7,6 +7,7 @@ class XMP {
|
|||
'adsml-at': 'AdsML',
|
||||
'aux': 'Exif Aux',
|
||||
'Camera': 'Camera',
|
||||
'creatorAtom': 'After Effects',
|
||||
'crs': 'Camera Raw Settings',
|
||||
'dc': 'Dublin Core',
|
||||
'drone-dji': 'DJI Drone',
|
||||
|
|
|
@ -17,6 +17,7 @@ class MetadataService {
|
|||
final result = await platform.invokeMethod('getAllMetadata', <String, dynamic>{
|
||||
'mimeType': entry.mimeType,
|
||||
'uri': entry.uri,
|
||||
'sizeBytes': entry.sizeBytes,
|
||||
});
|
||||
return result as Map;
|
||||
} on PlatformException catch (e) {
|
||||
|
@ -44,6 +45,7 @@ class MetadataService {
|
|||
'mimeType': entry.mimeType,
|
||||
'uri': entry.uri,
|
||||
'path': entry.path,
|
||||
'sizeBytes': entry.sizeBytes,
|
||||
}) as Map;
|
||||
result['contentId'] = entry.contentId;
|
||||
return CatalogMetadata.fromMap(result);
|
||||
|
@ -69,6 +71,7 @@ class MetadataService {
|
|||
final result = await platform.invokeMethod('getOverlayMetadata', <String, dynamic>{
|
||||
'mimeType': entry.mimeType,
|
||||
'uri': entry.uri,
|
||||
'sizeBytes': entry.sizeBytes,
|
||||
}) as Map;
|
||||
return OverlayMetadata.fromMap(result);
|
||||
} on PlatformException catch (e) {
|
||||
|
@ -108,7 +111,9 @@ class MetadataService {
|
|||
try {
|
||||
// return map with all data available from the `ExifInterface` library
|
||||
final result = await platform.invokeMethod('getExifInterfaceMetadata', <String, dynamic>{
|
||||
'mimeType': entry.mimeType,
|
||||
'uri': entry.uri,
|
||||
'sizeBytes': entry.sizeBytes,
|
||||
}) as Map;
|
||||
return result;
|
||||
} on PlatformException catch (e) {
|
||||
|
@ -134,7 +139,9 @@ class MetadataService {
|
|||
try {
|
||||
// return map with the mime type and tag count for each directory found by `metadata-extractor`
|
||||
final result = await platform.invokeMethod('getMetadataExtractorSummary', <String, dynamic>{
|
||||
'mimeType': entry.mimeType,
|
||||
'uri': entry.uri,
|
||||
'sizeBytes': entry.sizeBytes,
|
||||
}) as Map;
|
||||
return result;
|
||||
} on PlatformException catch (e) {
|
||||
|
@ -155,10 +162,12 @@ class MetadataService {
|
|||
return [];
|
||||
}
|
||||
|
||||
static Future<List<Uint8List>> getExifThumbnails(String uri) async {
|
||||
static Future<List<Uint8List>> getExifThumbnails(ImageEntry entry) async {
|
||||
try {
|
||||
final result = await platform.invokeMethod('getExifThumbnails', <String, dynamic>{
|
||||
'uri': uri,
|
||||
'mimeType': entry.mimeType,
|
||||
'uri': entry.uri,
|
||||
'sizeBytes': entry.sizeBytes,
|
||||
});
|
||||
return (result as List).cast<Uint8List>();
|
||||
} on PlatformException catch (e) {
|
||||
|
@ -172,6 +181,7 @@ class MetadataService {
|
|||
final result = await platform.invokeMethod('getXmpThumbnails', <String, dynamic>{
|
||||
'mimeType': entry.mimeType,
|
||||
'uri': entry.uri,
|
||||
'sizeBytes': entry.sizeBytes,
|
||||
});
|
||||
return (result as List).cast<Uint8List>();
|
||||
} on PlatformException catch (e) {
|
||||
|
|
|
@ -36,7 +36,7 @@ class _MetadataThumbnailsState extends State<MetadataThumbnails> {
|
|||
_loader = MetadataService.getEmbeddedPictures(uri);
|
||||
break;
|
||||
case MetadataThumbnailSource.exif:
|
||||
_loader = MetadataService.getExifThumbnails(uri);
|
||||
_loader = MetadataService.getExifThumbnails(entry);
|
||||
break;
|
||||
case MetadataThumbnailSource.xmp:
|
||||
_loader = MetadataService.getXmpThumbnails(entry);
|
||||
|
|
Loading…
Reference in a new issue