@lodev09/react-native-exify 0.2.7 → 1.0.0

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.
Files changed (37) hide show
  1. package/Exify.podspec +3 -23
  2. package/LICENSE +1 -1
  3. package/README.md +44 -18
  4. package/android/build.gradle +26 -53
  5. package/android/src/main/AndroidManifest.xml +2 -2
  6. package/android/src/main/java/com/lodev09/exify/ExifyModule.kt +146 -32
  7. package/android/src/main/java/com/lodev09/exify/ExifyPackage.kt +28 -10
  8. package/android/src/main/java/com/lodev09/exify/ExifyTags.kt +137 -135
  9. package/android/src/main/java/com/lodev09/exify/ExifyUtils.kt +31 -8
  10. package/ios/Exify.h +9 -0
  11. package/ios/Exify.mm +384 -25
  12. package/lib/module/index.js +12 -22
  13. package/lib/module/index.js.map +1 -1
  14. package/lib/module/package.json +1 -0
  15. package/lib/module/specs/NativeExifyModule.js +5 -0
  16. package/lib/module/specs/NativeExifyModule.js.map +1 -0
  17. package/lib/module/types.js +1 -1
  18. package/lib/typescript/package.json +1 -0
  19. package/lib/typescript/src/index.d.ts +7 -9
  20. package/lib/typescript/src/index.d.ts.map +1 -1
  21. package/lib/typescript/src/specs/NativeExifyModule.d.ts +8 -0
  22. package/lib/typescript/src/specs/NativeExifyModule.d.ts.map +1 -0
  23. package/lib/typescript/src/types.d.ts +2 -1
  24. package/lib/typescript/src/types.d.ts.map +1 -1
  25. package/package.json +82 -95
  26. package/src/index.ts +12 -31
  27. package/src/specs/NativeExifyModule.ts +8 -0
  28. package/src/types.ts +141 -140
  29. package/android/gradle.properties +0 -5
  30. package/android/src/main/AndroidManifestNew.xml +0 -2
  31. package/ios/Exify-Bridging-Header.h +0 -11
  32. package/ios/Exify.swift +0 -134
  33. package/ios/ExifyUtils.swift +0 -140
  34. package/lib/commonjs/index.js +0 -53
  35. package/lib/commonjs/index.js.map +0 -1
  36. package/lib/commonjs/types.js +0 -2
  37. 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
- # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
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) 2024 lodev09
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,19 @@
1
1
  # React Native Exify
2
2
 
3
3
  [![CI](https://github.com/lodev09/react-native-exify/actions/workflows/ci.yml/badge.svg)](https://github.com/lodev09/react-native-exify/actions/workflows/ci.yml)
4
- ![GitHub Release](https://img.shields.io/github/v/release/lodev09/react-native-exify)
5
4
  ![NPM Downloads](https://img.shields.io/npm/dw/%40lodev09%2Freact-native-exify)
6
5
 
7
- A simple library to read and write image Exif metadata in React Native. Inspired from [this thread](https://github.com/mrousavy/react-native-vision-camera/issues/780).
6
+ A simple library to read and write image Exif metadata for your React Native Apps. 🏷️
7
+
8
+ <img alt="@lodev09/react-native-exify" src="preview.gif" width="400" />
8
9
 
9
10
  ## Features
10
- * ✅ Read Exif data from an image
11
- * Write Exif data into an image
12
- * Tags are typed and standardized
13
- * Works with Expo and bare React Native projects
11
+
12
+ * Read Exif data from an image
13
+ * Write Exif data into an image
14
+ * Tags are typed and standardized
15
+ * Works with Expo and bare React Native projects
16
+ * Supports New Architecture (Turbo Module)
14
17
 
15
18
  ## Installation
16
19
 
@@ -20,21 +23,31 @@ yarn add @lodev09/react-native-exify
20
23
 
21
24
  ## Usage
22
25
 
23
- ```ts
24
- import { writeAsync, readAsync, ExifTags } from '@lodev09/react-native-exify';
26
+ ```tsx
27
+ import * as Exify from '@lodev09/react-native-exify';
25
28
  ```
26
29
 
27
- ### 🧐 Reading Exif
28
- ```ts
29
- // ...
30
+ ### Reading Exif 🔍
31
+
32
+ ```tsx
30
33
  const uri = 'file://path/to/image.jpg'
31
34
 
32
- const tags = await readAsync(uri)
35
+ const tags = await Exify.read(uri)
33
36
  console.log(tags)
34
37
  ```
35
38
 
36
- ### ✍️ Writing Exif
37
- ```ts
39
+ > [!IMPORTANT]
40
+ > 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.
41
+
42
+ > [!NOTE]
43
+ > 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.
44
+ > 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.
45
+
46
+ ### Writing Exif ✍️
47
+
48
+ ```tsx
49
+ import type { ExifTags } from '@lodev09/react-native-exify';
50
+
38
51
  const uri = 'file://path/to/image.jpg'
39
52
  const newTags: ExifTags = {
40
53
  GPSLatitude: 69.69,
@@ -42,17 +55,30 @@ const newTags: ExifTags = {
42
55
  UserComment: 'Someone wrote GPS here!',
43
56
  }
44
57
 
45
- const result = await writeAsync(uri, newTags)
58
+ const result = await Exify.write(uri, newTags)
46
59
  console.log(result.tags)
47
60
  ```
48
61
 
49
62
  > [!NOTE]
50
- > On IOS, writing exif into an Asset file will duplicate the image. IOS does not allow writing exif into an Asset file directly.
51
- > If you're getting the photo from a [camera](https://github.com/mrousavy/react-native-vision-camera/), write it into the output file first before saving to the Asset library!
63
+ > On iOS, writing exif into an Asset file will duplicate the image. iOS does not allow writing exif into an Asset file directly.
64
+ > 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!
65
+
66
+ ## Example
52
67
 
53
68
  See [example](example) for more detailed usage.
54
69
 
70
+ ### Built with True Sheet
71
+
72
+ 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!
73
+
55
74
  ## Contributing
56
- Contributions are welcome!
57
75
 
58
76
  See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
77
+
78
+ ## License
79
+
80
+ [MIT](LICENSE)
81
+
82
+ ---
83
+
84
+ Made with ❤️ by [@lodev09](http://linkedin.com/in/lodev09/)
@@ -1,6 +1,18 @@
1
1
  buildscript {
2
- // Buildscript is evaluated before everything else so we can't use getExtOrDefault
3
- def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["Exify_kotlinVersion"]
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.1"
23
+ classpath "com.android.tools.build:gradle:8.7.2"
12
24
  // noinspection DifferentKotlinGradleVersion
13
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
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
- if (isNewArchitectureEnabled()) {
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
- if (supportsNamespace()) {
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 getExtOrIntegerDefault("compileSdkVersion")
38
+ compileSdkVersion getExtOrDefault("compileSdkVersion")
57
39
 
58
40
  defaultConfig {
59
- minSdkVersion getExtOrIntegerDefault("minSdkVersion")
60
- targetSdkVersion getExtOrIntegerDefault("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
- lintOptions {
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
- // For < 0.71, this will be from the local maven repo
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
- package="com.lodev09.exify">
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.content.Context
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(reactContext: ReactApplicationContext) :
19
- ReactContextBaseJavaModule(reactContext) {
24
+ class ExifyModule(
25
+ reactContext: ReactApplicationContext,
26
+ ) : NativeExifyModuleSpec(reactContext) {
27
+ private val context = reactContext
20
28
 
21
- private val context: Context = reactContext
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
- override fun getName(): String {
24
- return NAME
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
- @ReactMethod
28
- fun readAsync(uri: String, promise: Promise) {
29
- val photoUri = Uri.parse(uri)
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
- context.contentResolver.openInputStream(photoUri)?.use {
33
- val tags = formatTags(ExifInterface(it))
34
- it.close()
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
- promise.resolve(null)
40
- e.printStackTrace()
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 writeAsync(uri: String, tags: ReadableMap, promise: Promise) {
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,28 @@ class ExifyModule(reactContext: ReactApplicationContext) :
57
127
  val type = tags.getType(tag)
58
128
 
59
129
  when (type) {
60
- ReadableType.Boolean -> exif.setAttribute(tag, tags.getBoolean(tag).toString())
61
- ReadableType.Number ->
130
+ ReadableType.Boolean -> {
131
+ exif.setAttribute(tag, tags.getBoolean(tag).toString())
132
+ }
133
+
134
+ ReadableType.Number -> {
62
135
  when (valType) {
63
136
  "double" -> exif.setAttribute(tag, tags.getDouble(tag).toBigDecimal().toPlainString())
64
137
  else -> exif.setAttribute(tag, tags.getDouble(tag).toInt().toString())
65
138
  }
66
- ReadableType.String -> exif.setAttribute(tag, tags.getString(tag))
67
- ReadableType.Array -> exif.setAttribute(tag, tags.getArray(tag).toString())
68
- else -> exif.setAttribute(tag, tags.getString(tag))
139
+ }
140
+
141
+ ReadableType.String -> {
142
+ exif.setAttribute(tag, tags.getString(tag))
143
+ }
144
+
145
+ ReadableType.Array -> {
146
+ exif.setAttribute(tag, tags.getArray(tag).toString())
147
+ }
148
+
149
+ else -> {
150
+ exif.setAttribute(tag, tags.getString(tag))
151
+ }
69
152
  }
70
153
  }
71
154
 
@@ -73,14 +156,46 @@ class ExifyModule(reactContext: ReactApplicationContext) :
73
156
  tags.hasKey(ExifInterface.TAG_GPS_LATITUDE) &&
74
157
  tags.hasKey(ExifInterface.TAG_GPS_LONGITUDE)
75
158
  ) {
76
- exif.setLatLong(
77
- tags.getDouble(ExifInterface.TAG_GPS_LATITUDE),
78
- tags.getDouble(ExifInterface.TAG_GPS_LONGITUDE)
79
- )
159
+ val hasExplicitRef =
160
+ tags.hasKey(ExifInterface.TAG_GPS_LATITUDE_REF) ||
161
+ tags.hasKey(ExifInterface.TAG_GPS_LONGITUDE_REF)
162
+
163
+ if (hasExplicitRef) {
164
+ val lat = tags.getDouble(ExifInterface.TAG_GPS_LATITUDE)
165
+ val lng = tags.getDouble(ExifInterface.TAG_GPS_LONGITUDE)
166
+ val latRef =
167
+ if (tags.hasKey(ExifInterface.TAG_GPS_LATITUDE_REF)) {
168
+ tags.getString(ExifInterface.TAG_GPS_LATITUDE_REF)
169
+ } else {
170
+ if (lat >= 0) "N" else "S"
171
+ }
172
+ val lngRef =
173
+ if (tags.hasKey(ExifInterface.TAG_GPS_LONGITUDE_REF)) {
174
+ tags.getString(ExifInterface.TAG_GPS_LONGITUDE_REF)
175
+ } else {
176
+ if (lng >= 0) "E" else "W"
177
+ }
178
+
179
+ exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, ExifyUtils.decimalToDms(Math.abs(lat)))
180
+ exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, latRef)
181
+ exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, ExifyUtils.decimalToDms(Math.abs(lng)))
182
+ exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, lngRef)
183
+ } else {
184
+ exif.setLatLong(
185
+ tags.getDouble(ExifInterface.TAG_GPS_LATITUDE),
186
+ tags.getDouble(ExifInterface.TAG_GPS_LONGITUDE),
187
+ )
188
+ }
80
189
  }
81
190
 
82
191
  if (tags.hasKey(ExifInterface.TAG_GPS_ALTITUDE)) {
83
- exif.setAltitude(tags.getDouble(ExifInterface.TAG_GPS_ALTITUDE))
192
+ val alt = tags.getDouble(ExifInterface.TAG_GPS_ALTITUDE)
193
+ if (tags.hasKey(ExifInterface.TAG_GPS_ALTITUDE_REF)) {
194
+ exif.setAttribute(ExifInterface.TAG_GPS_ALTITUDE, ExifyUtils.decimalToRational(Math.abs(alt)))
195
+ exif.setAttribute(ExifInterface.TAG_GPS_ALTITUDE_REF, tags.getInt(ExifInterface.TAG_GPS_ALTITUDE_REF).toString())
196
+ } else {
197
+ exif.setAltitude(alt)
198
+ }
84
199
  }
85
200
 
86
201
  params.putString("uri", uri)
@@ -89,7 +204,6 @@ class ExifyModule(reactContext: ReactApplicationContext) :
89
204
  exif.saveAttributes()
90
205
  promise.resolve(params)
91
206
  }
92
-
93
207
  } catch (e: Exception) {
94
208
  promise.reject(ERROR_TAG, e.message, e)
95
209
  e.printStackTrace()
@@ -97,6 +211,6 @@ class ExifyModule(reactContext: ReactApplicationContext) :
97
211
  }
98
212
 
99
213
  companion object {
100
- const val NAME = "Exify"
214
+ const val NAME = NativeExifyModuleSpec.NAME
101
215
  }
102
216
  }
@@ -1,17 +1,35 @@
1
1
  package com.lodev09.exify
2
2
 
3
- import com.facebook.react.ReactPackage
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.uimanager.ViewManager
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
- class ExifyPackage : ReactPackage {
10
- override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
11
- return listOf(ExifyModule(reactContext))
12
- }
13
-
14
- override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
15
- return emptyList()
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
  }