Exiv2 ffi package

This commit is contained in:
Ming Ming 2024-12-16 01:40:24 +08:00
parent 809d81db9d
commit f1704cc37d
29 changed files with 934 additions and 0 deletions

7
np_exiv2/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock

View file

@ -0,0 +1 @@
include: package:np_lints/np.yaml

7
np_exiv2/ffigen.yaml Normal file
View file

@ -0,0 +1,7 @@
output: "lib/src/generated_bindings.g.dart"
name: "NpExiv2C"
headers:
entry-points:
- "headers/np_exiv2_c.h"
preamble: |
// ignore_for_file: unused_element, unused_field

View file

@ -0,0 +1,91 @@
#pragma once
#pragma GCC visibility push(default)
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
exiv2_type_id_unsigned_byte = 0,
exiv2_type_id_ascii_string,
exiv2_type_id_unsigned_short,
exiv2_type_id_unsigned_long,
exiv2_type_id_unsigned_rational,
exiv2_type_id_signed_byte,
exiv2_type_id_undefined,
exiv2_type_id_signed_short,
exiv2_type_id_signed_long,
exiv2_type_id_signed_rational,
exiv2_type_id_tiff_float,
exiv2_type_id_tiff_double,
exiv2_type_id_tiff_ifd,
exiv2_type_id_unsigned_long_long,
exiv2_type_id_signed_long_long,
exiv2_type_id_tiff_ifd8,
exiv2_type_id_string,
// int32_t[3] (year, month, day)
exiv2_type_id_date,
// int32_t[5] (hour, minute, second, tz_hour, tz_minute)
exiv2_type_id_time,
exiv2_type_id_comment,
exiv2_type_id_directory,
exiv2_type_id_xmp_text,
exiv2_type_id_xmp_alt,
exiv2_type_id_xmp_bag,
exiv2_type_id_xmp_seq,
exiv2_type_id_lang_alt,
exiv2_type_id_invalid_type_id,
} Exiv2TypeId;
/**
* A key value pair
*/
typedef struct {
const char *tag_key;
Exiv2TypeId type_id;
const uint8_t *data;
// size of data in bytes
size_t size;
// number of elements in data if it's an array, 1 otherwise
size_t count;
} Exiv2Metadatum;
typedef struct {
uint32_t width;
uint32_t height;
const Exiv2Metadatum *iptc_data;
size_t iptc_count;
const Exiv2Metadatum *exif_data;
size_t exif_count;
const Exiv2Metadatum *xmp_data;
size_t xmp_count;
} Exiv2ReadResult;
/**
* Extract metadata from a file pointed to by @a path
*
* @return A handle to retrieve actual results, 0 if failed
*/
const Exiv2ReadResult *exiv2_read_file(const char *path);
/**
* Extract metadata from a buffer
*
* @return A handle to retrieve actual results, 0 if failed
*/
const Exiv2ReadResult *exiv2_read_buffer(const uint8_t *buffer,
const size_t size);
/**
* Release the resources of a Exiv2ReadResult object returned by
* @a exiv2_read_file
*/
void exiv2_result_free(const Exiv2ReadResult *that);
#ifdef __cplusplus
}
#endif
#pragma GCC visibility pop

View file

@ -0,0 +1,3 @@
library np_exiv2;
export 'src/api.dart';

296
np_exiv2/lib/src/api.dart Normal file
View file

@ -0,0 +1,296 @@
import 'dart:convert';
import 'dart:ffi';
import 'dart:typed_data';
import 'package:euc/jis.dart';
import 'package:ffi/ffi.dart' as ffi;
import 'package:logging/logging.dart';
import 'package:np_common/object_util.dart';
import 'package:np_exiv2/src/generated_bindings.g.dart';
import 'package:quiver/iterables.dart';
enum TypeId {
unsignedByte,
asciiString,
unsignedShort,
unsignedLong,
unsignedRational,
signedByte,
undefined,
signedShort,
signedLong,
signedRational,
tiffFloat,
tiffDouble,
tiffIfd,
unsignedLongLong,
signedLongLong,
tiffIfd8,
string,
date,
time,
comment,
directory,
xmpText,
xmpAlt,
xmpBag,
xmpSeq,
langAlt,
invalidTypeId,
;
factory TypeId.fromNative(Exiv2TypeId src) {
return TypeId.values[src.index];
}
}
class Date {
const Date(this.year, this.month, this.day);
final int year;
final int month;
final int day;
}
class Time {
const Time(this.hour, this.minute, this.second, this.tzHour, this.tzMinute);
final int hour;
final int minute;
final int second;
final int tzHour;
final int tzMinute;
}
class Rational {
const Rational(this.numerator, this.denominator);
double toDouble() => numerator / denominator;
@override
String toString() => "$numerator/$denominator";
final int numerator;
final int denominator;
}
class Value {
const Value({
required this.typeId,
required Uint8List data,
required int count,
}) : _data = data,
_count = count;
T as<T>() => asTyped() as T;
Object asTyped() {
try {
switch (typeId) {
case TypeId.unsignedByte:
return _listOrValue(_data);
case TypeId.asciiString:
// this is supposed to be ascii but vendors are putting utf-8 strings
return utf8.decode(_data);
case TypeId.unsignedShort:
return _listOrValue(_data.buffer.asUint16List());
case TypeId.unsignedLong:
case TypeId.tiffIfd:
return _listOrValue(_data.buffer.asUint32List());
case TypeId.unsignedRational:
return _listOrValue(partition(_data.buffer.asUint32List(), 2)
.map((e) => Rational(e[0], e[1]))
.toList());
case TypeId.signedByte:
return _listOrValue(_data.buffer.asInt8List());
case TypeId.signedShort:
return _listOrValue(_data.buffer.asInt16List());
case TypeId.signedLong:
return _listOrValue(_data.buffer.asInt32List());
case TypeId.signedRational:
return _listOrValue(partition(_data.buffer.asInt32List(), 2)
.map((e) => Rational(e[0], e[1]))
.toList());
case TypeId.tiffFloat:
return _listOrValue(_data.buffer.asFloat32List());
case TypeId.tiffDouble:
return _listOrValue(_data.buffer.asFloat64List());
case TypeId.unsignedLongLong:
case TypeId.tiffIfd8:
return _listOrValue(_data.buffer.asUint64List());
case TypeId.signedLongLong:
return _listOrValue(_data.buffer.asInt64List());
case TypeId.string:
return utf8.decode(_data);
case TypeId.date:
return _data.buffer.asInt32List().let((e) => Date(e[0], e[1], e[2]));
case TypeId.time:
return _data.buffer
.asInt32List()
.let((e) => Time(e[0], e[1], e[2], e[3], e[4]));
case TypeId.comment:
return _convertCommentValue();
case TypeId.undefined:
case TypeId.directory:
case TypeId.invalidTypeId:
return _data.buffer.asUint8List();
case TypeId.xmpText:
case TypeId.xmpAlt:
case TypeId.xmpBag:
case TypeId.xmpSeq:
case TypeId.langAlt:
throw UnsupportedError("XMP not supported");
}
} catch (e, stackTrace) {
_log.severe("[asTyped] Failed to convert data to type: $typeId, $_data",
e, stackTrace);
rethrow;
}
}
String toDebugString() {
return "Value{"
"typeId: $typeId, "
"size: ${_data.length}, "
"count: $_count, "
"}";
}
Object _listOrValue(List<Object> values) {
return _count == 1 ? values.first : values;
}
String _convertCommentValue() {
// the first 8 chars is the charset, valid values are [ASCII, JIS,
// UNICODE]
return _data.buffer.asUint8List().let((e) {
String? charset;
Uint8List data = e;
if (e.length >= 8) {
charset = ascii.decode(e.sublist(0, 8));
data = e.sublist(8);
}
if (charset == "ASCII") {
return ascii.decode(data, allowInvalid: true);
} else if (charset == "JIS") {
return ShiftJIS().decode(data);
} else if (charset == "UNICODE") {
// UTF16
return String.fromCharCodes(data.buffer.asUint16List());
} else {
// unknown, treat as utf8
return utf8.decode(data);
}
});
}
final TypeId typeId;
final Uint8List _data;
final int _count;
static final _log = Logger("np_exiv2.api.Value");
}
class Metadatum {
const Metadatum({
required this.tagKey,
required this.value,
});
factory Metadatum.fromNative(Exiv2Metadatum src) {
return Metadatum(
tagKey: src.tag_key.cast<ffi.Utf8>().toDartString(),
value: Value(
typeId: TypeId.fromNative(src.type_id),
count: src.count,
data: Uint8List.fromList(src.data.asTypedList(src.size)),
),
);
}
final String tagKey;
final Value value;
}
class ReadResult {
const ReadResult(this.width, this.height, this.iptcData, this.exifData);
factory ReadResult.fromNative(Exiv2ReadResult src) {
_log.fine(
"[fromNative] w: ${src.width}, h: ${src.height}, iptcCount: ${src.iptc_count}, exifCount: ${src.exif_count}");
final iptcData = <Metadatum>[];
for (var i = 0; i < src.iptc_count; ++i) {
iptcData.add(Metadatum.fromNative(src.iptc_data[i]));
}
final exifData = <Metadatum>[];
for (var i = 0; i < src.exif_count; ++i) {
exifData.add(Metadatum.fromNative(src.exif_data[i]));
}
return ReadResult(src.width, src.height, iptcData, exifData);
}
final int width;
final int height;
final List<Metadatum> iptcData;
final List<Metadatum> exifData;
static final _log = Logger("np_exiv2.api.ReadResult");
}
ReadResult readFile(String path) {
final lib = _ensureLib();
final stopwatch = Stopwatch()..start();
final pathC = path.toNativeUtf8();
try {
_log.fine("[readFile] Reading $path");
final result = lib.exiv2_read_file(pathC.cast());
if (result == nullptr) {
_log.severe("[readFile] Result is null for file: $path");
throw StateError("Failed to read file");
}
try {
return ReadResult.fromNative(result[0]);
} finally {
lib.exiv2_result_free(result);
}
} finally {
ffi.malloc.free(pathC);
_log.fine("[readFile] Done in ${stopwatch.elapsedMilliseconds}ms");
}
}
ReadResult readBuffer(Uint8List buffer) {
final lib = _ensureLib();
final stopwatch = Stopwatch()..start();
Pointer<Uint8>? cbuffer;
try {
_log.fine("[readBuffer] Allocating buffer with size: ${buffer.length}");
cbuffer = ffi.malloc.allocate<Uint8>(buffer.length);
final cbufferView = cbuffer.asTypedList(buffer.length);
cbufferView.setAll(0, buffer);
_log.fine("[readBuffer] Reading buffer");
final result = lib.exiv2_read_buffer(cbuffer, buffer.length);
if (result == nullptr) {
_log.severe("[readBuffer] Result is null for buffer");
throw StateError("Failed to read buffer");
}
try {
return ReadResult.fromNative(result[0]);
} finally {
lib.exiv2_result_free(result);
}
} finally {
if (cbuffer != null) {
ffi.malloc.free(cbuffer);
_log.fine("[readBuffer] Done in ${stopwatch.elapsedMilliseconds}ms");
}
}
}
NpExiv2C _ensureLib() {
_lib ??= NpExiv2C(DynamicLibrary.open("libnp_exiv2_c.so"));
return _lib!;
}
NpExiv2C? _lib;
final _log = Logger("np_exiv2.api");

View file

@ -0,0 +1,329 @@
// ignore_for_file: unused_element, unused_field
// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
// ignore_for_file: type=lint
import 'dart:ffi' as ffi;
class NpExiv2C {
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
NpExiv2C(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup;
/// The symbols are looked up with [lookup].
NpExiv2C.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;
/// Extract metadata from a file pointed to by @a path
///
/// @return A handle to retrieve actual results, 0 if failed
ffi.Pointer<Exiv2ReadResult> exiv2_read_file(
ffi.Pointer<ffi.Char> path,
) {
return _exiv2_read_file(
path,
);
}
late final _exiv2_read_filePtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<Exiv2ReadResult> Function(
ffi.Pointer<ffi.Char>)>>('exiv2_read_file');
late final _exiv2_read_file = _exiv2_read_filePtr.asFunction<
ffi.Pointer<Exiv2ReadResult> Function(ffi.Pointer<ffi.Char>)>();
/// Extract metadata from a buffer
///
/// @return A handle to retrieve actual results, 0 if failed
ffi.Pointer<Exiv2ReadResult> exiv2_read_buffer(
ffi.Pointer<ffi.Uint8> buffer,
int size,
) {
return _exiv2_read_buffer(
buffer,
size,
);
}
late final _exiv2_read_bufferPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<Exiv2ReadResult> Function(
ffi.Pointer<ffi.Uint8>, ffi.Size)>>('exiv2_read_buffer');
late final _exiv2_read_buffer = _exiv2_read_bufferPtr.asFunction<
ffi.Pointer<Exiv2ReadResult> Function(ffi.Pointer<ffi.Uint8>, int)>();
/// Release the resources of a Exiv2ReadResult object returned by
/// @a exiv2_read_file
void exiv2_result_free(
ffi.Pointer<Exiv2ReadResult> that,
) {
return _exiv2_result_free(
that,
);
}
late final _exiv2_result_freePtr = _lookup<
ffi.NativeFunction<ffi.Void Function(ffi.Pointer<Exiv2ReadResult>)>>(
'exiv2_result_free');
late final _exiv2_result_free = _exiv2_result_freePtr
.asFunction<void Function(ffi.Pointer<Exiv2ReadResult>)>();
}
final class __mbstate_t extends ffi.Union {
@ffi.Array.multi([128])
external ffi.Array<ffi.Char> __mbstate8;
@ffi.LongLong()
external int _mbstateL;
}
enum Exiv2TypeId {
exiv2_type_id_unsigned_byte(0),
exiv2_type_id_ascii_string(1),
exiv2_type_id_unsigned_short(2),
exiv2_type_id_unsigned_long(3),
exiv2_type_id_unsigned_rational(4),
exiv2_type_id_signed_byte(5),
exiv2_type_id_undefined(6),
exiv2_type_id_signed_short(7),
exiv2_type_id_signed_long(8),
exiv2_type_id_signed_rational(9),
exiv2_type_id_tiff_float(10),
exiv2_type_id_tiff_double(11),
exiv2_type_id_tiff_ifd(12),
exiv2_type_id_unsigned_long_long(13),
exiv2_type_id_signed_long_long(14),
exiv2_type_id_tiff_ifd8(15),
exiv2_type_id_string(16),
exiv2_type_id_date(17),
exiv2_type_id_time(18),
exiv2_type_id_comment(19),
exiv2_type_id_directory(20),
exiv2_type_id_xmp_text(21),
exiv2_type_id_xmp_alt(22),
exiv2_type_id_xmp_bag(23),
exiv2_type_id_xmp_seq(24),
exiv2_type_id_lang_alt(25),
exiv2_type_id_invalid_type_id(26);
final int value;
const Exiv2TypeId(this.value);
static Exiv2TypeId fromValue(int value) => switch (value) {
0 => exiv2_type_id_unsigned_byte,
1 => exiv2_type_id_ascii_string,
2 => exiv2_type_id_unsigned_short,
3 => exiv2_type_id_unsigned_long,
4 => exiv2_type_id_unsigned_rational,
5 => exiv2_type_id_signed_byte,
6 => exiv2_type_id_undefined,
7 => exiv2_type_id_signed_short,
8 => exiv2_type_id_signed_long,
9 => exiv2_type_id_signed_rational,
10 => exiv2_type_id_tiff_float,
11 => exiv2_type_id_tiff_double,
12 => exiv2_type_id_tiff_ifd,
13 => exiv2_type_id_unsigned_long_long,
14 => exiv2_type_id_signed_long_long,
15 => exiv2_type_id_tiff_ifd8,
16 => exiv2_type_id_string,
17 => exiv2_type_id_date,
18 => exiv2_type_id_time,
19 => exiv2_type_id_comment,
20 => exiv2_type_id_directory,
21 => exiv2_type_id_xmp_text,
22 => exiv2_type_id_xmp_alt,
23 => exiv2_type_id_xmp_bag,
24 => exiv2_type_id_xmp_seq,
25 => exiv2_type_id_lang_alt,
26 => exiv2_type_id_invalid_type_id,
_ => throw ArgumentError("Unknown value for Exiv2TypeId: $value"),
};
}
/// A key value pair
final class Exiv2Metadatum extends ffi.Struct {
external ffi.Pointer<ffi.Char> tag_key;
@ffi.UnsignedInt()
external int type_idAsInt;
Exiv2TypeId get type_id => Exiv2TypeId.fromValue(type_idAsInt);
external ffi.Pointer<ffi.Uint8> data;
@ffi.Size()
external int size;
@ffi.Size()
external int count;
}
final class Exiv2ReadResult extends ffi.Struct {
@ffi.Uint32()
external int width;
@ffi.Uint32()
external int height;
external ffi.Pointer<Exiv2Metadatum> iptc_data;
@ffi.Size()
external int iptc_count;
external ffi.Pointer<Exiv2Metadatum> exif_data;
@ffi.Size()
external int exif_count;
external ffi.Pointer<Exiv2Metadatum> xmp_data;
@ffi.Size()
external int xmp_count;
}
const int NULL = 0;
const int __has_safe_buffers = 1;
const int __DARWIN_ONLY_64_BIT_INO_T = 0;
const int __DARWIN_ONLY_UNIX_CONFORMANCE = 0;
const int __DARWIN_ONLY_VERS_1050 = 0;
const int __DARWIN_UNIX03 = 0;
const int __DARWIN_64_BIT_INO_T = 0;
const int __DARWIN_VERS_1050 = 0;
const int __DARWIN_NON_CANCELABLE = 0;
const String __DARWIN_SUF_EXTSN = '\$DARWIN_EXTSN';
const int __DARWIN_C_ANSI = 4096;
const int __DARWIN_C_FULL = 900000;
const int __DARWIN_C_LEVEL = 900000;
const int __STDC_WANT_LIB_EXT1__ = 1;
const int __DARWIN_NO_LONG_LONG = 0;
const int __has_ptrcheck = 0;
const int USER_ADDR_NULL = 0;
const int __WORDSIZE = 64;
const int INT8_MAX = 127;
const int INT16_MAX = 32767;
const int INT32_MAX = 2147483647;
const int INT64_MAX = 9223372036854775807;
const int INT8_MIN = -128;
const int INT16_MIN = -32768;
const int INT32_MIN = -2147483648;
const int INT64_MIN = -9223372036854775808;
const int UINT8_MAX = 255;
const int UINT16_MAX = 65535;
const int UINT32_MAX = 4294967295;
const int UINT64_MAX = -1;
const int INT_LEAST8_MIN = -128;
const int INT_LEAST16_MIN = -32768;
const int INT_LEAST32_MIN = -2147483648;
const int INT_LEAST64_MIN = -9223372036854775808;
const int INT_LEAST8_MAX = 127;
const int INT_LEAST16_MAX = 32767;
const int INT_LEAST32_MAX = 2147483647;
const int INT_LEAST64_MAX = 9223372036854775807;
const int UINT_LEAST8_MAX = 255;
const int UINT_LEAST16_MAX = 65535;
const int UINT_LEAST32_MAX = 4294967295;
const int UINT_LEAST64_MAX = -1;
const int INT_FAST8_MIN = -128;
const int INT_FAST16_MIN = -32768;
const int INT_FAST32_MIN = -2147483648;
const int INT_FAST64_MIN = -9223372036854775808;
const int INT_FAST8_MAX = 127;
const int INT_FAST16_MAX = 32767;
const int INT_FAST32_MAX = 2147483647;
const int INT_FAST64_MAX = 9223372036854775807;
const int UINT_FAST8_MAX = 255;
const int UINT_FAST16_MAX = 65535;
const int UINT_FAST32_MAX = 4294967295;
const int UINT_FAST64_MAX = -1;
const int INTPTR_MAX = 9223372036854775807;
const int INTPTR_MIN = -9223372036854775808;
const int UINTPTR_MAX = -1;
const int INTMAX_MAX = 9223372036854775807;
const int UINTMAX_MAX = -1;
const int INTMAX_MIN = -9223372036854775808;
const int PTRDIFF_MIN = -9223372036854775808;
const int PTRDIFF_MAX = 9223372036854775807;
const int SIZE_MAX = -1;
const int RSIZE_MAX = 9223372036854775807;
const int WCHAR_MAX = 2147483647;
const int WCHAR_MIN = -2147483648;
const int WINT_MIN = -2147483648;
const int WINT_MAX = 2147483647;
const int SIG_ATOMIC_MIN = -2147483648;
const int SIG_ATOMIC_MAX = 2147483647;

23
np_exiv2/pubspec.yaml Normal file
View file

@ -0,0 +1,23 @@
name: np_exiv2
description: A starting point for Dart libraries or applications.
version: 1.0.0
# repository: https://github.com/my_org/my_repo
publish_to: none
environment:
sdk: ">=3.3.0 <4.0.0"
# Add regular dependencies here.
dependencies:
euc: ^1.0.6+8
ffi: ^2.1.3
logging: ^1.2.0
np_common:
path: ../np_common
path: ^1.8.3
quiver: ^3.2.1
dev_dependencies:
ffigen: ^14.0.1
np_lints:
path: ../np_lints

29
np_exiv2_lib/.gitignore vendored Normal file
View file

@ -0,0 +1,29 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
build/

30
np_exiv2_lib/.metadata Normal file
View file

@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "54e66469a933b60ddf175f858f82eaeb97e48c8d"
channel: "stable"
project_type: plugin
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
- platform: android
create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

9
np_exiv2_lib/android/.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.cxx

View file

@ -0,0 +1,23 @@
package io.flutter.plugins;
import io.flutter.plugin.common.PluginRegistry;
/**
* Generated file. Do not edit.
*/
public final class GeneratedPluginRegistrant {
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {
return;
}
}
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
final String key = GeneratedPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {
return true;
}
registry.registrarFor(key);
return false;
}
}

View file

@ -0,0 +1,42 @@
group 'com.nkming.nc_photos.np_exiv2_lib'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.8.20'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
namespace 'com.nkming.nc_photos.np_exiv2_lib'
compileSdk 34
defaultConfig {
minSdk 23
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}

View file

@ -0,0 +1 @@
rootProject.name = 'np_exiv2_lib'

View file

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,21 @@
package com.nkming.nc_photos.np_exiv2_lib
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
/** NpExiv2LibPlugin */
class NpExiv2LibPlugin: FlutterPlugin, MethodCallHandler {
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
}
override fun onMethodCall(call: MethodCall, result: Result) {
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
}
}

20
np_exiv2_lib/pubspec.yaml Normal file
View file

@ -0,0 +1,20 @@
name: np_exiv2_lib
description: "A new Flutter plugin project."
version: 0.0.1
homepage:
publish_to: none
environment:
sdk: ">=3.3.0 <4.0.0"
flutter: ">=3.19.0"
dependencies:
flutter:
sdk: flutter
flutter:
plugin:
platforms:
android:
package: com.nkming.nc_photos.np_exiv2_lib
pluginClass: NpExiv2LibPlugin