@lodev09/react-native-exify 0.2.7 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Exify.podspec +3 -23
- package/LICENSE +1 -1
- package/README.md +45 -18
- package/android/build.gradle +26 -53
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/java/com/lodev09/exify/ExifyModule.kt +154 -33
- package/android/src/main/java/com/lodev09/exify/ExifyPackage.kt +28 -10
- package/android/src/main/java/com/lodev09/exify/ExifyTags.kt +137 -135
- package/android/src/main/java/com/lodev09/exify/ExifyUtils.kt +31 -8
- package/ios/Exify.h +9 -0
- package/ios/Exify.mm +384 -25
- package/lib/module/index.js +12 -22
- package/lib/module/index.js.map +1 -1
- package/lib/module/package.json +1 -0
- package/lib/module/specs/NativeExifyModule.js +5 -0
- package/lib/module/specs/NativeExifyModule.js.map +1 -0
- package/lib/module/types.js +1 -1
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/index.d.ts +7 -9
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/NativeExifyModule.d.ts +8 -0
- package/lib/typescript/src/specs/NativeExifyModule.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +2 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +82 -95
- package/src/index.ts +12 -31
- package/src/specs/NativeExifyModule.ts +8 -0
- package/src/types.ts +141 -140
- package/android/gradle.properties +0 -5
- package/android/src/main/AndroidManifestNew.xml +0 -2
- package/ios/Exify-Bridging-Header.h +0 -11
- package/ios/Exify.swift +0 -134
- package/ios/ExifyUtils.swift +0 -140
- package/lib/commonjs/index.js +0 -53
- package/lib/commonjs/index.js.map +0 -1
- package/lib/commonjs/types.js +0 -2
- package/lib/commonjs/types.js.map +0 -1
package/Exify.podspec
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
|
|
3
3
|
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
-
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
|
|
5
4
|
|
|
6
5
|
Pod::Spec.new do |s|
|
|
7
6
|
s.name = "Exify"
|
|
@@ -14,27 +13,8 @@ Pod::Spec.new do |s|
|
|
|
14
13
|
s.platforms = { :ios => min_ios_version_supported }
|
|
15
14
|
s.source = { :git => "https://github.com/lodev09/react-native-exify.git", :tag => "#{s.version}" }
|
|
16
15
|
|
|
17
|
-
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
|
|
17
|
+
s.private_header_files = "ios/**/*.h"
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
|
|
21
|
-
if respond_to?(:install_modules_dependencies, true)
|
|
22
|
-
install_modules_dependencies(s)
|
|
23
|
-
else
|
|
24
|
-
s.dependency "React-Core"
|
|
25
|
-
|
|
26
|
-
# Don't install the dependencies when we run `pod install` in the old architecture.
|
|
27
|
-
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
|
|
28
|
-
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
|
|
29
|
-
s.pod_target_xcconfig = {
|
|
30
|
-
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
|
|
31
|
-
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
|
|
32
|
-
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
|
|
33
|
-
}
|
|
34
|
-
s.dependency "React-Codegen"
|
|
35
|
-
s.dependency "RCT-Folly"
|
|
36
|
-
s.dependency "RCTRequired"
|
|
37
|
-
s.dependency "RCTTypeSafety"
|
|
38
|
-
end
|
|
39
|
-
end
|
|
19
|
+
install_modules_dependencies(s)
|
|
40
20
|
end
|
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026 lodev09
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
|
6
6
|
in the Software without restriction, including without limitation the rights
|
package/README.md
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
# React Native Exify
|
|
2
2
|
|
|
3
3
|
[](https://github.com/lodev09/react-native-exify/actions/workflows/ci.yml)
|
|
4
|
-

|
|
5
4
|

|
|
5
|
+
[](https://deepwiki.com/lodev09/react-native-exify)
|
|
6
6
|
|
|
7
|
-
A simple library to read and write image Exif metadata
|
|
7
|
+
A simple library to read and write image Exif metadata for your React Native Apps. 🏷️
|
|
8
|
+
|
|
9
|
+
<img alt="@lodev09/react-native-exify" src="preview.gif" width="400" />
|
|
8
10
|
|
|
9
11
|
## Features
|
|
10
|
-
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
|
|
13
|
+
* Read Exif data from an image
|
|
14
|
+
* Write Exif data into an image
|
|
15
|
+
* Tags are typed and standardized
|
|
16
|
+
* Works with Expo and bare React Native projects
|
|
17
|
+
* Supports New Architecture (Turbo Module)
|
|
14
18
|
|
|
15
19
|
## Installation
|
|
16
20
|
|
|
@@ -20,21 +24,31 @@ yarn add @lodev09/react-native-exify
|
|
|
20
24
|
|
|
21
25
|
## Usage
|
|
22
26
|
|
|
23
|
-
```
|
|
24
|
-
import
|
|
27
|
+
```tsx
|
|
28
|
+
import * as Exify from '@lodev09/react-native-exify';
|
|
25
29
|
```
|
|
26
30
|
|
|
27
|
-
###
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
### Reading Exif 🔍
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
30
34
|
const uri = 'file://path/to/image.jpg'
|
|
31
35
|
|
|
32
|
-
const tags = await
|
|
36
|
+
const tags = await Exify.read(uri)
|
|
33
37
|
console.log(tags)
|
|
34
38
|
```
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
> [!IMPORTANT]
|
|
41
|
+
> The `uri` must include a scheme (e.g. `file://`, `ph://`, `content://`). Bare file paths like `/var/mobile/.../image.jpg` are not supported and will throw an error.
|
|
42
|
+
|
|
43
|
+
> [!NOTE]
|
|
44
|
+
> On Android 10+, GPS data is redacted from `content://` URIs by default. The library automatically requests `ACCESS_MEDIA_LOCATION` at runtime to access unredacted location data. Your app must have media read access (`READ_MEDIA_IMAGES` or `READ_EXTERNAL_STORAGE`) granted first.
|
|
45
|
+
> If you're already using a library like [`expo-media-library`](https://docs.expo.dev/versions/latest/sdk/media-library/) that grants `ACCESS_MEDIA_LOCATION`, exify will use the existing grant.
|
|
46
|
+
|
|
47
|
+
### Writing Exif ✍️
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import type { ExifTags } from '@lodev09/react-native-exify';
|
|
51
|
+
|
|
38
52
|
const uri = 'file://path/to/image.jpg'
|
|
39
53
|
const newTags: ExifTags = {
|
|
40
54
|
GPSLatitude: 69.69,
|
|
@@ -42,17 +56,30 @@ const newTags: ExifTags = {
|
|
|
42
56
|
UserComment: 'Someone wrote GPS here!',
|
|
43
57
|
}
|
|
44
58
|
|
|
45
|
-
const result = await
|
|
59
|
+
const result = await Exify.write(uri, newTags)
|
|
46
60
|
console.log(result.tags)
|
|
47
61
|
```
|
|
48
62
|
|
|
49
63
|
> [!NOTE]
|
|
50
|
-
> On
|
|
51
|
-
> If you're getting the photo from a [camera](https://
|
|
64
|
+
> On iOS, writing exif into an Asset file will duplicate the image. iOS does not allow writing exif into an Asset file directly.
|
|
65
|
+
> If you're getting the photo from a [camera](https://docs.expo.dev/versions/latest/sdk/camera/), write it into the output file first before saving to the Asset library!
|
|
66
|
+
|
|
67
|
+
## Example
|
|
52
68
|
|
|
53
69
|
See [example](example) for more detailed usage.
|
|
54
70
|
|
|
71
|
+
### Built with True Sheet
|
|
72
|
+
|
|
73
|
+
The example app uses [@lodev09/react-native-true-sheet](https://github.com/lodev09/react-native-true-sheet) for the image picker UI. Check it out!
|
|
74
|
+
|
|
55
75
|
## Contributing
|
|
56
|
-
Contributions are welcome!
|
|
57
76
|
|
|
58
77
|
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
[MIT](LICENSE)
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
Made with ❤️ by [@lodev09](http://linkedin.com/in/lodev09/)
|
package/android/build.gradle
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
buildscript {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
ext.Exify = [
|
|
3
|
+
kotlinVersion: "2.0.21",
|
|
4
|
+
minSdkVersion: 24,
|
|
5
|
+
compileSdkVersion: 36,
|
|
6
|
+
targetSdkVersion: 36
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
ext.getExtOrDefault = { prop ->
|
|
10
|
+
if (rootProject.ext.has(prop)) {
|
|
11
|
+
return rootProject.ext.get(prop)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return Exify[prop]
|
|
15
|
+
}
|
|
4
16
|
|
|
5
17
|
repositories {
|
|
6
18
|
google()
|
|
@@ -8,57 +20,30 @@ buildscript {
|
|
|
8
20
|
}
|
|
9
21
|
|
|
10
22
|
dependencies {
|
|
11
|
-
classpath "com.android.tools.build:gradle:7.2
|
|
23
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
12
24
|
// noinspection DifferentKotlinGradleVersion
|
|
13
|
-
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$
|
|
25
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
14
26
|
}
|
|
15
27
|
}
|
|
16
28
|
|
|
17
|
-
def isNewArchitectureEnabled() {
|
|
18
|
-
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
19
|
-
}
|
|
20
29
|
|
|
21
30
|
apply plugin: "com.android.library"
|
|
22
31
|
apply plugin: "kotlin-android"
|
|
23
32
|
|
|
24
|
-
|
|
25
|
-
apply plugin: "com.facebook.react"
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
def getExtOrDefault(name) {
|
|
29
|
-
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["Exify_" + name]
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
def getExtOrIntegerDefault(name) {
|
|
33
|
-
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Exify_" + name]).toInteger()
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
def supportsNamespace() {
|
|
37
|
-
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
|
38
|
-
def major = parsed[0].toInteger()
|
|
39
|
-
def minor = parsed[1].toInteger()
|
|
40
|
-
|
|
41
|
-
// Namespace support was added in 7.3.0
|
|
42
|
-
return (major == 7 && minor >= 3) || major >= 8
|
|
43
|
-
}
|
|
33
|
+
apply plugin: "com.facebook.react"
|
|
44
34
|
|
|
45
35
|
android {
|
|
46
|
-
|
|
47
|
-
namespace "com.lodev09.exify"
|
|
48
|
-
|
|
49
|
-
sourceSets {
|
|
50
|
-
main {
|
|
51
|
-
manifest.srcFile "src/main/AndroidManifestNew.xml"
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
36
|
+
namespace "com.lodev09.exify"
|
|
55
37
|
|
|
56
|
-
compileSdkVersion
|
|
38
|
+
compileSdkVersion getExtOrDefault("compileSdkVersion")
|
|
57
39
|
|
|
58
40
|
defaultConfig {
|
|
59
|
-
minSdkVersion
|
|
60
|
-
targetSdkVersion
|
|
41
|
+
minSdkVersion getExtOrDefault("minSdkVersion")
|
|
42
|
+
targetSdkVersion getExtOrDefault("targetSdkVersion")
|
|
43
|
+
}
|
|
61
44
|
|
|
45
|
+
buildFeatures {
|
|
46
|
+
buildConfig true
|
|
62
47
|
}
|
|
63
48
|
|
|
64
49
|
buildTypes {
|
|
@@ -67,7 +52,7 @@ android {
|
|
|
67
52
|
}
|
|
68
53
|
}
|
|
69
54
|
|
|
70
|
-
|
|
55
|
+
lint {
|
|
71
56
|
disable "GradleCompatible"
|
|
72
57
|
}
|
|
73
58
|
|
|
@@ -77,19 +62,7 @@ android {
|
|
|
77
62
|
}
|
|
78
63
|
}
|
|
79
64
|
|
|
80
|
-
repositories {
|
|
81
|
-
mavenCentral()
|
|
82
|
-
google()
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
86
|
-
|
|
87
65
|
dependencies {
|
|
88
|
-
|
|
89
|
-
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
|
|
90
|
-
// noinspection GradleDynamicVersion
|
|
91
|
-
implementation "com.facebook.react:react-native:+"
|
|
92
|
-
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
66
|
+
implementation "com.facebook.react:react-android"
|
|
93
67
|
implementation "androidx.exifinterface:exifinterface:1.3.7"
|
|
94
68
|
}
|
|
95
|
-
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
-
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
|
|
3
3
|
</manifest>
|
|
@@ -1,50 +1,120 @@
|
|
|
1
1
|
package com.lodev09.exify
|
|
2
2
|
|
|
3
|
-
import android.
|
|
3
|
+
import android.Manifest
|
|
4
|
+
import android.content.pm.PackageManager
|
|
4
5
|
import android.net.Uri
|
|
6
|
+
import android.os.Build
|
|
7
|
+
import android.provider.MediaStore
|
|
8
|
+
import androidx.core.content.ContextCompat
|
|
5
9
|
import androidx.exifinterface.media.ExifInterface
|
|
6
|
-
import com.lodev09.exify.ExifyUtils.formatTags
|
|
7
10
|
import com.facebook.react.bridge.Arguments
|
|
8
11
|
import com.facebook.react.bridge.Promise
|
|
9
12
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
10
|
-
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
11
|
-
import com.facebook.react.bridge.ReactMethod
|
|
12
13
|
import com.facebook.react.bridge.ReadableMap
|
|
13
14
|
import com.facebook.react.bridge.ReadableType
|
|
15
|
+
import com.facebook.react.modules.core.PermissionAwareActivity
|
|
16
|
+
import com.facebook.react.modules.core.PermissionListener
|
|
17
|
+
import com.facebook.react.util.RNLog
|
|
18
|
+
import com.lodev09.exify.ExifyUtils.formatTags
|
|
14
19
|
import java.io.IOException
|
|
15
20
|
|
|
16
21
|
private const val ERROR_TAG = "E_EXIFY_ERROR"
|
|
22
|
+
private const val MEDIA_LOCATION_REQUEST_CODE = 4209
|
|
17
23
|
|
|
18
|
-
class ExifyModule(
|
|
19
|
-
|
|
24
|
+
class ExifyModule(
|
|
25
|
+
reactContext: ReactApplicationContext,
|
|
26
|
+
) : NativeExifyModuleSpec(reactContext) {
|
|
27
|
+
private val context = reactContext
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
override fun read(
|
|
30
|
+
uri: String,
|
|
31
|
+
promise: Promise,
|
|
32
|
+
) {
|
|
33
|
+
val photoUri = Uri.parse(uri)
|
|
34
|
+
val scheme = photoUri.scheme
|
|
22
35
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
36
|
+
if (scheme == null) {
|
|
37
|
+
RNLog.w(context, "Exify: URI must include a scheme (e.g. file://): $uri")
|
|
38
|
+
promise.reject(ERROR_TAG, "URI must include a scheme (e.g. file://): $uri")
|
|
39
|
+
return
|
|
40
|
+
}
|
|
26
41
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
42
|
+
// On Android Q+, request ACCESS_MEDIA_LOCATION for unredacted GPS data
|
|
43
|
+
if (scheme == "content" && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
|
|
44
|
+
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_MEDIA_LOCATION) !=
|
|
45
|
+
PackageManager.PERMISSION_GRANTED
|
|
46
|
+
) {
|
|
47
|
+
val activity = context.currentActivity as? PermissionAwareActivity
|
|
48
|
+
if (activity != null) {
|
|
49
|
+
activity.requestPermissions(
|
|
50
|
+
arrayOf(Manifest.permission.ACCESS_MEDIA_LOCATION),
|
|
51
|
+
MEDIA_LOCATION_REQUEST_CODE,
|
|
52
|
+
PermissionListener { requestCode, _, _ ->
|
|
53
|
+
if (requestCode == MEDIA_LOCATION_REQUEST_CODE) {
|
|
54
|
+
readExif(uri, photoUri, scheme, promise)
|
|
55
|
+
true
|
|
56
|
+
} else {
|
|
57
|
+
false
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
}
|
|
30
64
|
|
|
65
|
+
readExif(uri, photoUri, scheme, promise)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private fun readExif(
|
|
69
|
+
uri: String,
|
|
70
|
+
photoUri: Uri,
|
|
71
|
+
scheme: String,
|
|
72
|
+
promise: Promise,
|
|
73
|
+
) {
|
|
31
74
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
75
|
+
val inputStream =
|
|
76
|
+
if (scheme == "http" || scheme == "https") {
|
|
77
|
+
java.net.URL(uri).openStream()
|
|
78
|
+
} else if (scheme == "content" && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
79
|
+
try {
|
|
80
|
+
context.contentResolver.openInputStream(MediaStore.setRequireOriginal(photoUri))
|
|
81
|
+
} catch (e: SecurityException) {
|
|
82
|
+
context.contentResolver.openInputStream(photoUri)
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
context.contentResolver.openInputStream(photoUri)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (inputStream == null) {
|
|
89
|
+
RNLog.w(context, "Exify: Could not open URI: $uri")
|
|
90
|
+
promise.reject(ERROR_TAG, "Could not open URI: $uri")
|
|
91
|
+
return
|
|
92
|
+
}
|
|
35
93
|
|
|
94
|
+
inputStream.use {
|
|
95
|
+
val tags = formatTags(ExifInterface(it))
|
|
36
96
|
promise.resolve(tags)
|
|
37
97
|
}
|
|
38
98
|
} catch (e: Exception) {
|
|
39
|
-
|
|
40
|
-
e.
|
|
99
|
+
RNLog.w(context, "Exify: ${e.message}")
|
|
100
|
+
promise.reject(ERROR_TAG, e.message, e)
|
|
41
101
|
}
|
|
42
102
|
}
|
|
43
103
|
|
|
44
|
-
@ReactMethod
|
|
45
104
|
@Throws(IOException::class)
|
|
46
|
-
fun
|
|
105
|
+
override fun write(
|
|
106
|
+
uri: String,
|
|
107
|
+
tags: ReadableMap,
|
|
108
|
+
promise: Promise,
|
|
109
|
+
) {
|
|
47
110
|
val photoUri = Uri.parse(uri)
|
|
111
|
+
|
|
112
|
+
if (photoUri.scheme == null) {
|
|
113
|
+
RNLog.w(context, "Exify: URI must include a scheme (e.g. file://): $uri")
|
|
114
|
+
promise.reject(ERROR_TAG, "URI must include a scheme (e.g. file://): $uri")
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
48
118
|
val params = Arguments.createMap()
|
|
49
119
|
|
|
50
120
|
try {
|
|
@@ -57,15 +127,35 @@ class ExifyModule(reactContext: ReactApplicationContext) :
|
|
|
57
127
|
val type = tags.getType(tag)
|
|
58
128
|
|
|
59
129
|
when (type) {
|
|
60
|
-
ReadableType.Boolean ->
|
|
61
|
-
|
|
130
|
+
ReadableType.Boolean -> {
|
|
131
|
+
exif.setAttribute(tag, tags.getBoolean(tag).toString())
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
ReadableType.Number -> {
|
|
62
135
|
when (valType) {
|
|
63
|
-
"double" ->
|
|
136
|
+
"double" -> {
|
|
137
|
+
val value = tags.getDouble(tag)
|
|
138
|
+
if (tag.startsWith("GPS")) {
|
|
139
|
+
exif.setAttribute(tag, ExifyUtils.decimalToRational(value))
|
|
140
|
+
} else {
|
|
141
|
+
exif.setAttribute(tag, value.toBigDecimal().toPlainString())
|
|
142
|
+
}
|
|
143
|
+
}
|
|
64
144
|
else -> exif.setAttribute(tag, tags.getDouble(tag).toInt().toString())
|
|
65
145
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
ReadableType.String -> {
|
|
149
|
+
exif.setAttribute(tag, tags.getString(tag))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
ReadableType.Array -> {
|
|
153
|
+
exif.setAttribute(tag, tags.getArray(tag).toString())
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
else -> {
|
|
157
|
+
exif.setAttribute(tag, tags.getString(tag))
|
|
158
|
+
}
|
|
69
159
|
}
|
|
70
160
|
}
|
|
71
161
|
|
|
@@ -73,14 +163,46 @@ class ExifyModule(reactContext: ReactApplicationContext) :
|
|
|
73
163
|
tags.hasKey(ExifInterface.TAG_GPS_LATITUDE) &&
|
|
74
164
|
tags.hasKey(ExifInterface.TAG_GPS_LONGITUDE)
|
|
75
165
|
) {
|
|
76
|
-
|
|
77
|
-
tags.
|
|
78
|
-
|
|
79
|
-
|
|
166
|
+
val hasExplicitRef =
|
|
167
|
+
tags.hasKey(ExifInterface.TAG_GPS_LATITUDE_REF) ||
|
|
168
|
+
tags.hasKey(ExifInterface.TAG_GPS_LONGITUDE_REF)
|
|
169
|
+
|
|
170
|
+
if (hasExplicitRef) {
|
|
171
|
+
val lat = tags.getDouble(ExifInterface.TAG_GPS_LATITUDE)
|
|
172
|
+
val lng = tags.getDouble(ExifInterface.TAG_GPS_LONGITUDE)
|
|
173
|
+
val latRef =
|
|
174
|
+
if (tags.hasKey(ExifInterface.TAG_GPS_LATITUDE_REF)) {
|
|
175
|
+
tags.getString(ExifInterface.TAG_GPS_LATITUDE_REF)
|
|
176
|
+
} else {
|
|
177
|
+
if (lat >= 0) "N" else "S"
|
|
178
|
+
}
|
|
179
|
+
val lngRef =
|
|
180
|
+
if (tags.hasKey(ExifInterface.TAG_GPS_LONGITUDE_REF)) {
|
|
181
|
+
tags.getString(ExifInterface.TAG_GPS_LONGITUDE_REF)
|
|
182
|
+
} else {
|
|
183
|
+
if (lng >= 0) "E" else "W"
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, ExifyUtils.decimalToDms(Math.abs(lat)))
|
|
187
|
+
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, latRef)
|
|
188
|
+
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, ExifyUtils.decimalToDms(Math.abs(lng)))
|
|
189
|
+
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, lngRef)
|
|
190
|
+
} else {
|
|
191
|
+
exif.setLatLong(
|
|
192
|
+
tags.getDouble(ExifInterface.TAG_GPS_LATITUDE),
|
|
193
|
+
tags.getDouble(ExifInterface.TAG_GPS_LONGITUDE),
|
|
194
|
+
)
|
|
195
|
+
}
|
|
80
196
|
}
|
|
81
197
|
|
|
82
198
|
if (tags.hasKey(ExifInterface.TAG_GPS_ALTITUDE)) {
|
|
83
|
-
|
|
199
|
+
val alt = tags.getDouble(ExifInterface.TAG_GPS_ALTITUDE)
|
|
200
|
+
if (tags.hasKey(ExifInterface.TAG_GPS_ALTITUDE_REF)) {
|
|
201
|
+
exif.setAttribute(ExifInterface.TAG_GPS_ALTITUDE, ExifyUtils.decimalToRational(Math.abs(alt)))
|
|
202
|
+
exif.setAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF, tags.getInt(ExifInterface.TAG_GPS_ALTITUDE_REF).toString())
|
|
203
|
+
} else {
|
|
204
|
+
exif.setAltitude(alt)
|
|
205
|
+
}
|
|
84
206
|
}
|
|
85
207
|
|
|
86
208
|
params.putString("uri", uri)
|
|
@@ -89,7 +211,6 @@ class ExifyModule(reactContext: ReactApplicationContext) :
|
|
|
89
211
|
exif.saveAttributes()
|
|
90
212
|
promise.resolve(params)
|
|
91
213
|
}
|
|
92
|
-
|
|
93
214
|
} catch (e: Exception) {
|
|
94
215
|
promise.reject(ERROR_TAG, e.message, e)
|
|
95
216
|
e.printStackTrace()
|
|
@@ -97,6 +218,6 @@ class ExifyModule(reactContext: ReactApplicationContext) :
|
|
|
97
218
|
}
|
|
98
219
|
|
|
99
220
|
companion object {
|
|
100
|
-
const val NAME =
|
|
221
|
+
const val NAME = NativeExifyModuleSpec.NAME
|
|
101
222
|
}
|
|
102
223
|
}
|
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
package com.lodev09.exify
|
|
2
2
|
|
|
3
|
-
import com.facebook.react.
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
4
|
import com.facebook.react.bridge.NativeModule
|
|
5
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
-
import com.facebook.react.
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
+
import java.util.HashMap
|
|
7
9
|
|
|
10
|
+
class ExifyPackage : BaseReactPackage() {
|
|
11
|
+
override fun getModule(
|
|
12
|
+
name: String,
|
|
13
|
+
reactContext: ReactApplicationContext,
|
|
14
|
+
): NativeModule? =
|
|
15
|
+
if (name == ExifyModule.NAME) {
|
|
16
|
+
ExifyModule(reactContext)
|
|
17
|
+
} else {
|
|
18
|
+
null
|
|
19
|
+
}
|
|
8
20
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider =
|
|
22
|
+
ReactModuleInfoProvider {
|
|
23
|
+
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
|
|
24
|
+
moduleInfos[ExifyModule.NAME] =
|
|
25
|
+
ReactModuleInfo(
|
|
26
|
+
ExifyModule.NAME,
|
|
27
|
+
ExifyModule.NAME,
|
|
28
|
+
false, // canOverrideExistingModule
|
|
29
|
+
false, // needsEagerInit
|
|
30
|
+
false, // isCxxModule
|
|
31
|
+
true, // isTurboModule
|
|
32
|
+
)
|
|
33
|
+
moduleInfos
|
|
34
|
+
}
|
|
17
35
|
}
|