@codebymanan/react-native-photo-editor 0.1.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 (115) hide show
  1. package/LICENSE +20 -0
  2. package/PhotoEditor.podspec +23 -0
  3. package/README.md +200 -0
  4. package/android/build.gradle +65 -0
  5. package/android/src/main/AndroidManifest.xml +14 -0
  6. package/android/src/main/assets/beyond_wonderland.ttf +0 -0
  7. package/android/src/main/assets/emojione-android.ttf +0 -0
  8. package/android/src/main/assets/filters/auto_fix.png +0 -0
  9. package/android/src/main/assets/filters/b_n_w.png +0 -0
  10. package/android/src/main/assets/filters/brightness.png +0 -0
  11. package/android/src/main/assets/filters/contrast.png +0 -0
  12. package/android/src/main/assets/filters/cross_process.png +0 -0
  13. package/android/src/main/assets/filters/documentary.png +0 -0
  14. package/android/src/main/assets/filters/dual_tone.png +0 -0
  15. package/android/src/main/assets/filters/fill_light.png +0 -0
  16. package/android/src/main/assets/filters/fish_eye.png +0 -0
  17. package/android/src/main/assets/filters/flip_horizental.png +0 -0
  18. package/android/src/main/assets/filters/flip_vertical.png +0 -0
  19. package/android/src/main/assets/filters/grain.png +0 -0
  20. package/android/src/main/assets/filters/gray_scale.png +0 -0
  21. package/android/src/main/assets/filters/lomish.png +0 -0
  22. package/android/src/main/assets/filters/negative.png +0 -0
  23. package/android/src/main/assets/filters/original.jpg +0 -0
  24. package/android/src/main/assets/filters/posterize.png +0 -0
  25. package/android/src/main/assets/filters/rotate.png +0 -0
  26. package/android/src/main/assets/filters/saturate.png +0 -0
  27. package/android/src/main/assets/filters/sepia.png +0 -0
  28. package/android/src/main/assets/filters/sharpen.png +0 -0
  29. package/android/src/main/assets/filters/temprature.png +0 -0
  30. package/android/src/main/assets/filters/tint.png +0 -0
  31. package/android/src/main/assets/filters/vignette.png +0 -0
  32. package/android/src/main/java/com/photoeditor/PhotoEditorModule.kt +130 -0
  33. package/android/src/main/java/com/photoeditor/PhotoEditorPackage.kt +31 -0
  34. package/android/src/main/java/com/photoeditor/editor/ColorPickerAdapter.kt +100 -0
  35. package/android/src/main/java/com/photoeditor/editor/EditImageActivity.kt +407 -0
  36. package/android/src/main/java/com/photoeditor/editor/EditorLocale.kt +41 -0
  37. package/android/src/main/java/com/photoeditor/editor/EditorStrings.kt +46 -0
  38. package/android/src/main/java/com/photoeditor/editor/EmojiBSFragment.kt +121 -0
  39. package/android/src/main/java/com/photoeditor/editor/PropertiesBSFragment.kt +76 -0
  40. package/android/src/main/java/com/photoeditor/editor/ShapeBSFragment.kt +112 -0
  41. package/android/src/main/java/com/photoeditor/editor/StickerBSFragment.kt +130 -0
  42. package/android/src/main/java/com/photoeditor/editor/TextEditorDialogFragment.kt +133 -0
  43. package/android/src/main/java/com/photoeditor/editor/base/BaseActivity.kt +67 -0
  44. package/android/src/main/java/com/photoeditor/editor/base/BaseFragment.kt +10 -0
  45. package/android/src/main/java/com/photoeditor/editor/filters/FilterListener.kt +7 -0
  46. package/android/src/main/java/com/photoeditor/editor/filters/FilterViewAdapter.kt +105 -0
  47. package/android/src/main/java/com/photoeditor/editor/tools/EditingToolsAdapter.kt +70 -0
  48. package/android/src/main/java/com/photoeditor/editor/tools/ToolType.kt +10 -0
  49. package/android/src/main/res/drawable/pe_blank_image.jpg +0 -0
  50. package/android/src/main/res/drawable/pe_ic_brush.xml +9 -0
  51. package/android/src/main/res/drawable/pe_ic_close.xml +9 -0
  52. package/android/src/main/res/drawable/pe_ic_eraser.xml +4 -0
  53. package/android/src/main/res/drawable/pe_ic_insert_emoticon.xml +9 -0
  54. package/android/src/main/res/drawable/pe_ic_oval.xml +9 -0
  55. package/android/src/main/res/drawable/pe_ic_photo_filter.xml +5 -0
  56. package/android/src/main/res/drawable/pe_ic_rectangle.xml +9 -0
  57. package/android/src/main/res/drawable/pe_ic_redo.xml +10 -0
  58. package/android/src/main/res/drawable/pe_ic_redo_disabled.xml +10 -0
  59. package/android/src/main/res/drawable/pe_ic_save.xml +9 -0
  60. package/android/src/main/res/drawable/pe_ic_sticker.xml +9 -0
  61. package/android/src/main/res/drawable/pe_ic_text.xml +9 -0
  62. package/android/src/main/res/drawable/pe_ic_undo.xml +10 -0
  63. package/android/src/main/res/drawable/pe_ic_undo_disabled.xml +10 -0
  64. package/android/src/main/res/drawable/pe_original.jpg +0 -0
  65. package/android/src/main/res/drawable/pe_redo.xml +6 -0
  66. package/android/src/main/res/drawable/pe_rounded_border_text_view.xml +14 -0
  67. package/android/src/main/res/drawable/pe_undo.xml +6 -0
  68. package/android/src/main/res/font/pe_roboto_medium.xml +7 -0
  69. package/android/src/main/res/layout/pe_activity_edit_image.xml +111 -0
  70. package/android/src/main/res/layout/pe_add_text_dialog.xml +56 -0
  71. package/android/src/main/res/layout/pe_color_picker_item_list.xml +15 -0
  72. package/android/src/main/res/layout/pe_fragment_bottom_properties_dialog.xml +77 -0
  73. package/android/src/main/res/layout/pe_fragment_bottom_shapes_dialog.xml +135 -0
  74. package/android/src/main/res/layout/pe_fragment_bottom_sticker_emoji_dialog.xml +62 -0
  75. package/android/src/main/res/layout/pe_row_editing_tools.xml +34 -0
  76. package/android/src/main/res/layout/pe_row_emoji.xml +17 -0
  77. package/android/src/main/res/layout/pe_row_filter_view.xml +35 -0
  78. package/android/src/main/res/layout/pe_row_sticker.xml +23 -0
  79. package/android/src/main/res/values/colors.xml +22 -0
  80. package/android/src/main/res/values/dimens.xml +7 -0
  81. package/android/src/main/res/values/font_certs.xml +17 -0
  82. package/android/src/main/res/values/preloaded_fonts.xml +6 -0
  83. package/android/src/main/res/values/strings.xml +462 -0
  84. package/android/src/main/res/values/styles.xml +28 -0
  85. package/ios/PhotoEditor.h +5 -0
  86. package/ios/PhotoEditor.mm +58 -0
  87. package/ios/PhotoEditorManager.swift +164 -0
  88. package/lib/module/NativePhotoEditor.js +5 -0
  89. package/lib/module/NativePhotoEditor.js.map +1 -0
  90. package/lib/module/index.js +4 -0
  91. package/lib/module/index.js.map +1 -0
  92. package/lib/module/open.js +6 -0
  93. package/lib/module/open.js.map +1 -0
  94. package/lib/module/open.native.js +7 -0
  95. package/lib/module/open.native.js.map +1 -0
  96. package/lib/module/package.json +1 -0
  97. package/lib/module/types.js +2 -0
  98. package/lib/module/types.js.map +1 -0
  99. package/lib/typescript/package.json +1 -0
  100. package/lib/typescript/src/NativePhotoEditor.d.ts +14 -0
  101. package/lib/typescript/src/NativePhotoEditor.d.ts.map +1 -0
  102. package/lib/typescript/src/index.d.ts +3 -0
  103. package/lib/typescript/src/index.d.ts.map +1 -0
  104. package/lib/typescript/src/open.d.ts +3 -0
  105. package/lib/typescript/src/open.d.ts.map +1 -0
  106. package/lib/typescript/src/open.native.d.ts +3 -0
  107. package/lib/typescript/src/open.native.d.ts.map +1 -0
  108. package/lib/typescript/src/types.d.ts +39 -0
  109. package/lib/typescript/src/types.d.ts.map +1 -0
  110. package/package.json +128 -0
  111. package/src/NativePhotoEditor.ts +12 -0
  112. package/src/index.tsx +2 -0
  113. package/src/open.native.tsx +6 -0
  114. package/src/open.tsx +7 -0
  115. package/src/types.ts +36 -0
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abdul Manan
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,23 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "PhotoEditor"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/codebymanan/react-native-photo-editor.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
+ s.private_header_files = "ios/**/*.h"
18
+ s.swift_version = "5.0"
19
+
20
+ s.dependency "ZLImageEditor", "~> 3.0"
21
+
22
+ install_modules_dependencies(s)
23
+ end
package/README.md ADDED
@@ -0,0 +1,200 @@
1
+ # @codebymanan/react-native-photo-editor
2
+
3
+ A React Native photo editor plugin wrapping native editors for Android and iOS.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install @codebymanan/react-native-photo-editor
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```js
14
+ import { open } from '@codebymanan/react-native-photo-editor';
15
+
16
+ const editedPath = await open({
17
+ path: 'https://example.com/photo.jpg',
18
+ stickers: ['https://example.com/sticker.png'],
19
+ });
20
+ ```
21
+
22
+ `open()` resolves with the file path of the edited image, or rejects with
23
+ `E_CANCELLED` if the user backs out.
24
+
25
+ | Option | Type | Description |
26
+ | --- | --- | --- |
27
+ | `path` | `string` | **Required.** Image to edit. Accepts a local file path, a `file://` or `content://` URI, or a remote `http(s)` URL. |
28
+ | `stickers` | `string[]` | Sticker image URLs shown in the sticker picker. |
29
+ | `language` | `string` | BCP-47 tag (`'fr'`, `'pt-BR'`, `'zh-Hans'`) for the editor UI. Defaults to the device language. See [Localization](#localization). |
30
+ | `translations` | `Record<string, string>` | Per-key string overrides. See [Localization](#localization). |
31
+
32
+ ## Localization
33
+
34
+ By default the editor follows the device language on both platforms. Pass
35
+ `language` only when your app has its own language picker and the editor should
36
+ follow that instead:
37
+
38
+ ```js
39
+ await open({ path, language: 'fr' });
40
+ ```
41
+
42
+ ### iOS
43
+
44
+ The iOS editor already ships 18 languages and follows the device locale with no
45
+ setup. Nothing to configure.
46
+
47
+ `language` maps onto those 18: Chinese (Simplified and Traditional), English,
48
+ Japanese, French, German, Russian, Vietnamese, Korean, Malay, Italian,
49
+ Indonesian, Portuguese, Spanish, Turkish, Arabic, Ukrainian and Dutch. A tag
50
+ outside that set falls back to English rather than to the device language,
51
+ since the caller has explicitly asked not to use the device setting.
52
+
53
+ ### Android
54
+
55
+ The Android editor ships English only, but every user-facing string is a normal
56
+ Android string resource, so **your app can translate it without forking this
57
+ library**.
58
+
59
+ Add a `values-<language>` folder to your own app and redefine the keys you care
60
+ about. Android's resource merger gives your app priority over library
61
+ resources, so your values win:
62
+
63
+ ```xml
64
+ <!-- android/app/src/main/res/values-fr/strings.xml -->
65
+ <resources>
66
+ <string name="pe_label_brush">Pinceau</string>
67
+ <string name="pe_label_save">Enregistrer</string>
68
+ <string name="pe_msg_saving">Enregistrement…</string>
69
+ </resources>
70
+ ```
71
+
72
+ That's the whole mechanism. Three things worth knowing:
73
+
74
+ - **Override only what you need.** Keys you leave out fall back to this
75
+ library's English.
76
+ - **Any language works**, including ones this library has never heard of. The
77
+ key names are the only contract.
78
+ - **Match the qualifier.** Overriding a key in the default `values/` folder does
79
+ *not* win against a more specific match. Put French overrides in
80
+ `values-fr/`, German in `values-de/`, and so on.
81
+ - **`language` selects the folder, it does not create one.** Passing
82
+ `language: 'fr'` makes the editor resolve strings against `values-fr/`; if
83
+ your app has no `values-fr/`, the editor stays in English. The example app
84
+ ships one to demonstrate this.
85
+
86
+ ### Supplying strings from JS
87
+
88
+ `translations` overrides individual strings by key, for when your copy lives in
89
+ JS rather than in native resources, or when you need a language iOS does not
90
+ ship:
91
+
92
+ ```js
93
+ await open({
94
+ path,
95
+ language: 'sv',
96
+ translations: {
97
+ pe_label_save: 'Spara',
98
+ pe_label_cancel: 'Avbryt',
99
+ pe_msg_saving: 'Sparar…',
100
+ },
101
+ });
102
+ ```
103
+
104
+ Keys you leave out keep their built-in value, and on Android a `values-<lang>`
105
+ folder still applies underneath, so the two mechanisms stack: resources for the
106
+ bulk, `translations` for whatever you want to override per call.
107
+
108
+ **Android honours every key below. iOS honours nine of them** — the only strings
109
+ the underlying editor exposes — and ignores the rest. That asymmetry is the
110
+ reason to reach for a `values-<lang>` folder first on Android and treat
111
+ `translations` as the cross-platform layer on top.
112
+
113
+ #### String keys
114
+
115
+ | Key | English | iOS |
116
+ | --- | --- | --- |
117
+ | `pe_app_name` | Photo Editor | |
118
+ | `pe_label_brush` | Brush | |
119
+ | `pe_label_shape` | Shape | |
120
+ | `pe_label_oval` | Oval | |
121
+ | `pe_label_rectangle` | Rectangle | |
122
+ | `pe_label_line` | Line | |
123
+ | `pe_label_arrow` | Arrow | |
124
+ | `pe_label_emoji` | Emoji | |
125
+ | `pe_label_sticker` | Sticker | |
126
+ | `pe_label_eraser` | Eraser | |
127
+ | `pe_label_eraser_mode` | Eraser Mode | |
128
+ | `pe_label_text` | Text | |
129
+ | `pe_label_filter` | Filter | |
130
+ | `pe_label_adjust` | Adjust | |
131
+ | `pe_label_opacity` | Opacity | |
132
+ | `pe_label_undo` | Undo | ✓ |
133
+ | `pe_label_redo` | Redo | |
134
+ | `pe_label_close` | Close | | |
135
+ | `pe_label_done` | Done | ✓ |
136
+ | `pe_label_save` | Save | ✓ |
137
+ | `pe_label_cancel` | Cancel | ✓ |
138
+ | `pe_label_discard` | Discard | |
139
+ | `pe_msg_save_image` | Do you want to exit without saving the image? | |
140
+ | `pe_msg_saving` | Saving… | ✓ |
141
+ | `pe_msg_save_failed` | Failed to save image | |
142
+ | `pe_msg_drag_to_remove` | *(iOS only)* Drag here to remove | ✓ |
143
+ | `pe_filter_none` | None | |
144
+ | `pe_filter_auto_fix` | Auto Fix | |
145
+ | `pe_filter_brightness` | Brightness | ✓ |
146
+ | `pe_filter_contrast` | Contrast | ✓ |
147
+ | `pe_filter_documentary` | Documentary | |
148
+ | `pe_filter_duo_tone` | Duo Tone | |
149
+ | `pe_filter_fill_light` | Fill Light | |
150
+ | `pe_filter_fish_eye` | Fish Eye | |
151
+ | `pe_filter_grain` | Grain | |
152
+ | `pe_filter_gray_scale` | Grayscale | |
153
+ | `pe_filter_lomish` | Lomish | |
154
+ | `pe_filter_negative` | Negative | |
155
+ | `pe_filter_posterize` | Posterize | |
156
+ | `pe_filter_saturate` | Saturate | ✓ |
157
+ | `pe_filter_sepia` | Sepia | |
158
+ | `pe_filter_sharpen` | Sharpen | |
159
+ | `pe_filter_temperature` | Temperature | |
160
+ | `pe_filter_tint` | Tint | |
161
+ | `pe_filter_vignette` | Vignette | |
162
+ | `pe_filter_cross_process` | Cross Process | |
163
+ | `pe_filter_black_white` | Black & White | |
164
+ | `pe_filter_flip_horizontal` | Flip Horizontal | |
165
+ | `pe_filter_flip_vertical` | Flip Vertical | |
166
+ | `pe_filter_rotate` | Rotate | |
167
+
168
+ ### Right-to-left languages
169
+
170
+ **Android** mirrors the editor for an RTL tag such as `language: 'ar'`, but only
171
+ if your app has opted into RTL at all:
172
+
173
+ ```xml
174
+ <!-- android/app/src/main/AndroidManifest.xml -->
175
+ <application android:supportsRtl="true">
176
+ ```
177
+
178
+ Without that flag Android pins the whole app to left-to-right and the editor
179
+ cannot override it. The editor's own layouts use `start`/`end` throughout and
180
+ the undo and redo arrows are `autoMirrored`, so nothing else is needed.
181
+
182
+ **iOS** translates but does not mirror. `language` selects the editor's string
183
+ bundle; UIKit takes layout direction from the app's own localizations, and the
184
+ underlying editor never sets `semanticContentAttribute`. So the iOS editor lays
185
+ out right-to-left when your app itself is running in an RTL language, and not
186
+ because of this option.
187
+
188
+ ## Contributing
189
+
190
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
191
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
192
+ - [Code of conduct](CODE_OF_CONDUCT.md)
193
+
194
+ ## License
195
+
196
+ MIT
197
+
198
+ ---
199
+
200
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -0,0 +1,65 @@
1
+ buildscript {
2
+ ext.PhotoEditor = [
3
+ kotlinVersion: "2.0.21",
4
+ minSdkVersion: 24,
5
+ compileSdkVersion: 36
6
+ ]
7
+
8
+ ext.getExtOrDefault = { prop ->
9
+ if (rootProject.ext.has(prop)) {
10
+ return rootProject.ext.get(prop)
11
+ }
12
+
13
+ return PhotoEditor[prop]
14
+ }
15
+
16
+ repositories {
17
+ google()
18
+ mavenCentral()
19
+ }
20
+
21
+ dependencies {
22
+ classpath "com.android.tools.build:gradle:8.7.2"
23
+ // noinspection DifferentKotlinGradleVersion
24
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
25
+ }
26
+ }
27
+
28
+
29
+ apply plugin: "com.android.library"
30
+ apply plugin: "kotlin-android"
31
+
32
+ apply plugin: "com.facebook.react"
33
+
34
+ android {
35
+ namespace "com.photoeditor"
36
+
37
+ compileSdkVersion getExtOrDefault("compileSdkVersion")
38
+
39
+ defaultConfig {
40
+ minSdkVersion getExtOrDefault("minSdkVersion")
41
+ }
42
+
43
+ compileOptions {
44
+ sourceCompatibility JavaVersion.VERSION_17
45
+ targetCompatibility JavaVersion.VERSION_17
46
+ }
47
+ }
48
+
49
+ dependencies {
50
+ implementation "com.facebook.react:react-android"
51
+
52
+ // Editing engine
53
+ implementation "com.burhanrashid52:photoeditor:3.0.2"
54
+
55
+ // Editor screen UI
56
+ implementation "androidx.appcompat:appcompat:1.7.0"
57
+ implementation "androidx.constraintlayout:constraintlayout:2.1.4"
58
+ implementation "androidx.core:core-ktx:1.13.1"
59
+ implementation "androidx.fragment:fragment-ktx:1.8.1"
60
+ implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3"
61
+ implementation "androidx.cardview:cardview:1.0.0"
62
+ implementation "com.google.android.material:material:1.12.0"
63
+ implementation "com.github.bumptech.glide:glide:4.16.0"
64
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"
65
+ }
@@ -0,0 +1,14 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+
3
+ <!-- Needed to load remote images and stickers into the editor -->
4
+ <uses-permission android:name="android.permission.INTERNET" />
5
+
6
+ <application>
7
+ <activity
8
+ android:name="com.photoeditor.editor.EditImageActivity"
9
+ android:exported="false"
10
+ android:screenOrientation="portrait"
11
+ android:theme="@style/PhotoEditorTheme.NoActionBar" />
12
+ </application>
13
+
14
+ </manifest>
@@ -0,0 +1,130 @@
1
+ package com.photoeditor
2
+
3
+ import android.app.Activity
4
+ import android.content.Intent
5
+ import android.os.Bundle
6
+ import com.facebook.react.bridge.BaseActivityEventListener
7
+ import com.facebook.react.bridge.Promise
8
+ import com.facebook.react.bridge.ReactApplicationContext
9
+ import com.facebook.react.bridge.ReadableMap
10
+ import com.facebook.react.bridge.ReadableType
11
+ import com.photoeditor.editor.EditImageActivity
12
+ import com.photoeditor.editor.EditorLocale
13
+
14
+ class PhotoEditorModule(reactContext: ReactApplicationContext) :
15
+ NativePhotoEditorSpec(reactContext) {
16
+
17
+ private var pendingPromise: Promise? = null
18
+
19
+ private val activityEventListener = object : BaseActivityEventListener() {
20
+ override fun onActivityResult(
21
+ activity: Activity,
22
+ requestCode: Int,
23
+ resultCode: Int,
24
+ data: Intent?,
25
+ ) {
26
+ if (requestCode != EDITOR_REQUEST_CODE) return
27
+ val promise = pendingPromise ?: return
28
+ pendingPromise = null
29
+
30
+ when (resultCode) {
31
+ Activity.RESULT_OK -> {
32
+ val path = data?.getStringExtra(EditImageActivity.RESULT_EXTRA_PATH)
33
+ if (path.isNullOrBlank()) {
34
+ promise.reject(E_FAILED, "Editor returned no image path")
35
+ } else {
36
+ promise.resolve(path)
37
+ }
38
+ }
39
+
40
+ Activity.RESULT_CANCELED ->
41
+ promise.reject(E_CANCELLED, "User cancelled image editing")
42
+
43
+ EditImageActivity.RESULT_ERROR -> {
44
+ val message = data?.getStringExtra(EditImageActivity.RESULT_EXTRA_ERROR)
45
+ promise.reject(E_FAILED, message ?: "Photo editor failed")
46
+ }
47
+
48
+ else -> promise.reject(E_FAILED, "Photo editor failed")
49
+ }
50
+ }
51
+ }
52
+
53
+ init {
54
+ reactContext.addActivityEventListener(activityEventListener)
55
+ }
56
+
57
+ override fun open(options: ReadableMap, promise: Promise) {
58
+ val activity = currentActivity
59
+ if (activity == null) {
60
+ promise.reject(E_NO_ACTIVITY, "No activity attached to React context")
61
+ return
62
+ }
63
+ if (pendingPromise != null) {
64
+ promise.reject(E_IN_PROGRESS, "Another editor session is already open")
65
+ return
66
+ }
67
+
68
+ val path = options.getString("path")
69
+ if (path.isNullOrBlank()) {
70
+ promise.reject(E_INVALID_OPTIONS, "Missing required option: path")
71
+ return
72
+ }
73
+
74
+ val stickers = ArrayList<String>()
75
+ options.getArray("stickers")?.let { array ->
76
+ for (i in 0 until array.size()) {
77
+ array.getString(i)?.let(stickers::add)
78
+ }
79
+ }
80
+
81
+ pendingPromise = promise
82
+
83
+ // Assigned unconditionally so a call without a language clears the tag left
84
+ // behind by an earlier one. The activity reads it from attachBaseContext(),
85
+ // which runs before getIntent() is usable.
86
+ val language = options.getString("language")?.takeIf { it.isNotBlank() }
87
+ EditorLocale.pendingTag = language
88
+
89
+ val intent = Intent(activity, EditImageActivity::class.java)
90
+ .putExtra(EditImageActivity.EXTRA_IMAGE_PATH, path)
91
+ .putExtra(EditImageActivity.EXTRA_LANGUAGE, language)
92
+ .putExtra(EditImageActivity.EXTRA_TRANSLATIONS, translationsBundle(options))
93
+ .putStringArrayListExtra(EditImageActivity.EXTRA_STICKERS, stickers)
94
+ activity.startActivityForResult(intent, EDITOR_REQUEST_CODE)
95
+ }
96
+
97
+ /**
98
+ * Flattens the `translations` option into a Bundle. Non-string values are
99
+ * dropped rather than coerced, so a mistyped entry falls back to the built-in
100
+ * string instead of rendering something like "true".
101
+ */
102
+ private fun translationsBundle(options: ReadableMap): Bundle? {
103
+ val translations = options.getMap("translations") ?: return null
104
+ val bundle = Bundle()
105
+ val iterator = translations.keySetIterator()
106
+ while (iterator.hasNextKey()) {
107
+ val key = iterator.nextKey()
108
+ if (translations.getType(key) != ReadableType.String) continue
109
+ translations.getString(key)?.let { bundle.putString(key, it) }
110
+ }
111
+ return if (bundle.isEmpty) null else bundle
112
+ }
113
+
114
+ override fun invalidate() {
115
+ reactApplicationContext.removeActivityEventListener(activityEventListener)
116
+ super.invalidate()
117
+ }
118
+
119
+ companion object {
120
+ const val NAME = NativePhotoEditorSpec.NAME
121
+
122
+ private const val EDITOR_REQUEST_CODE = 4521
123
+
124
+ private const val E_NO_ACTIVITY = "E_NO_ACTIVITY"
125
+ private const val E_IN_PROGRESS = "E_IN_PROGRESS"
126
+ private const val E_INVALID_OPTIONS = "E_INVALID_OPTIONS"
127
+ private const val E_CANCELLED = "E_CANCELLED"
128
+ private const val E_FAILED = "E_FAILED"
129
+ }
130
+ }
@@ -0,0 +1,31 @@
1
+ package com.photoeditor
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+ import java.util.HashMap
9
+
10
+ class PhotoEditorPackage : BaseReactPackage() {
11
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
+ return if (name == PhotoEditorModule.NAME) {
13
+ PhotoEditorModule(reactContext)
14
+ } else {
15
+ null
16
+ }
17
+ }
18
+
19
+ override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
20
+ mapOf(
21
+ PhotoEditorModule.NAME to ReactModuleInfo(
22
+ name = PhotoEditorModule.NAME,
23
+ className = PhotoEditorModule.NAME,
24
+ canOverrideExistingModule = false,
25
+ needsEagerInit = false,
26
+ isCxxModule = false,
27
+ isTurboModule = true
28
+ )
29
+ )
30
+ }
31
+ }
@@ -0,0 +1,100 @@
1
+ package com.photoeditor.editor
2
+
3
+ import com.photoeditor.R
4
+
5
+ import android.content.Context
6
+ import android.view.LayoutInflater
7
+ import android.view.View
8
+ import android.view.ViewGroup
9
+ import androidx.recyclerview.widget.RecyclerView
10
+ import androidx.core.content.ContextCompat
11
+ import java.util.ArrayList
12
+
13
+ /**
14
+ * Created by Ahmed Adel on 5/8/17.
15
+ */
16
+ class ColorPickerAdapter internal constructor(
17
+ private var context: Context,
18
+ colorPickerColors: List<Int>
19
+ ) : RecyclerView.Adapter<ColorPickerAdapter.ViewHolder>() {
20
+ private var inflater: LayoutInflater
21
+ private val colorPickerColors: List<Int>
22
+ private lateinit var onColorPickerClickListener: OnColorPickerClickListener
23
+
24
+ internal constructor(context: Context) : this(context, getDefaultColors(context)) {
25
+ this.context = context
26
+ inflater = LayoutInflater.from(context)
27
+ }
28
+
29
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
30
+ val view = inflater.inflate(R.layout.pe_color_picker_item_list, parent, false)
31
+ return ViewHolder(view)
32
+ }
33
+
34
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
35
+ holder.colorPickerView.setBackgroundColor(colorPickerColors[position])
36
+ }
37
+
38
+ override fun getItemCount(): Int {
39
+ return colorPickerColors.size
40
+ }
41
+
42
+ fun setOnColorPickerClickListener(onColorPickerClickListener: OnColorPickerClickListener) {
43
+ this.onColorPickerClickListener = onColorPickerClickListener
44
+ }
45
+
46
+ inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
47
+ var colorPickerView: View = itemView.findViewById(R.id.color_picker_view)
48
+
49
+ init {
50
+ itemView.setOnClickListener {
51
+ onColorPickerClickListener.onColorPickerClickListener(
52
+ colorPickerColors[adapterPosition]
53
+ )
54
+ }
55
+ }
56
+ }
57
+
58
+ interface OnColorPickerClickListener {
59
+ fun onColorPickerClickListener(colorCode: Int)
60
+ }
61
+
62
+ companion object {
63
+ fun getDefaultColors(context: Context): List<Int> {
64
+ val colorPickerColors = ArrayList<Int>()
65
+ colorPickerColors.add(ContextCompat.getColor((context), R.color.pe_blue_color_picker))
66
+ colorPickerColors.add(ContextCompat.getColor((context), R.color.pe_brown_color_picker))
67
+ colorPickerColors.add(ContextCompat.getColor((context), R.color.pe_green_color_picker))
68
+ colorPickerColors.add(ContextCompat.getColor((context), R.color.pe_orange_color_picker))
69
+ colorPickerColors.add(ContextCompat.getColor((context), R.color.pe_red_color_picker))
70
+ colorPickerColors.add(ContextCompat.getColor((context), R.color.pe_black))
71
+ colorPickerColors.add(
72
+ ContextCompat.getColor(
73
+ (context),
74
+ R.color.pe_red_orange_color_picker
75
+ )
76
+ )
77
+ colorPickerColors.add(
78
+ ContextCompat.getColor(
79
+ (context),
80
+ R.color.pe_sky_blue_color_picker
81
+ )
82
+ )
83
+ colorPickerColors.add(ContextCompat.getColor((context), R.color.pe_violet_color_picker))
84
+ colorPickerColors.add(ContextCompat.getColor((context), R.color.pe_white))
85
+ colorPickerColors.add(ContextCompat.getColor((context), R.color.pe_yellow_color_picker))
86
+ colorPickerColors.add(
87
+ ContextCompat.getColor(
88
+ (context),
89
+ R.color.pe_yellow_green_color_picker
90
+ )
91
+ )
92
+ return colorPickerColors
93
+ }
94
+ }
95
+
96
+ init {
97
+ inflater = LayoutInflater.from(context)
98
+ this.colorPickerColors = colorPickerColors
99
+ }
100
+ }