@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.
- package/LICENSE +20 -0
- package/PhotoEditor.podspec +23 -0
- package/README.md +200 -0
- package/android/build.gradle +65 -0
- package/android/src/main/AndroidManifest.xml +14 -0
- package/android/src/main/assets/beyond_wonderland.ttf +0 -0
- package/android/src/main/assets/emojione-android.ttf +0 -0
- package/android/src/main/assets/filters/auto_fix.png +0 -0
- package/android/src/main/assets/filters/b_n_w.png +0 -0
- package/android/src/main/assets/filters/brightness.png +0 -0
- package/android/src/main/assets/filters/contrast.png +0 -0
- package/android/src/main/assets/filters/cross_process.png +0 -0
- package/android/src/main/assets/filters/documentary.png +0 -0
- package/android/src/main/assets/filters/dual_tone.png +0 -0
- package/android/src/main/assets/filters/fill_light.png +0 -0
- package/android/src/main/assets/filters/fish_eye.png +0 -0
- package/android/src/main/assets/filters/flip_horizental.png +0 -0
- package/android/src/main/assets/filters/flip_vertical.png +0 -0
- package/android/src/main/assets/filters/grain.png +0 -0
- package/android/src/main/assets/filters/gray_scale.png +0 -0
- package/android/src/main/assets/filters/lomish.png +0 -0
- package/android/src/main/assets/filters/negative.png +0 -0
- package/android/src/main/assets/filters/original.jpg +0 -0
- package/android/src/main/assets/filters/posterize.png +0 -0
- package/android/src/main/assets/filters/rotate.png +0 -0
- package/android/src/main/assets/filters/saturate.png +0 -0
- package/android/src/main/assets/filters/sepia.png +0 -0
- package/android/src/main/assets/filters/sharpen.png +0 -0
- package/android/src/main/assets/filters/temprature.png +0 -0
- package/android/src/main/assets/filters/tint.png +0 -0
- package/android/src/main/assets/filters/vignette.png +0 -0
- package/android/src/main/java/com/photoeditor/PhotoEditorModule.kt +130 -0
- package/android/src/main/java/com/photoeditor/PhotoEditorPackage.kt +31 -0
- package/android/src/main/java/com/photoeditor/editor/ColorPickerAdapter.kt +100 -0
- package/android/src/main/java/com/photoeditor/editor/EditImageActivity.kt +407 -0
- package/android/src/main/java/com/photoeditor/editor/EditorLocale.kt +41 -0
- package/android/src/main/java/com/photoeditor/editor/EditorStrings.kt +46 -0
- package/android/src/main/java/com/photoeditor/editor/EmojiBSFragment.kt +121 -0
- package/android/src/main/java/com/photoeditor/editor/PropertiesBSFragment.kt +76 -0
- package/android/src/main/java/com/photoeditor/editor/ShapeBSFragment.kt +112 -0
- package/android/src/main/java/com/photoeditor/editor/StickerBSFragment.kt +130 -0
- package/android/src/main/java/com/photoeditor/editor/TextEditorDialogFragment.kt +133 -0
- package/android/src/main/java/com/photoeditor/editor/base/BaseActivity.kt +67 -0
- package/android/src/main/java/com/photoeditor/editor/base/BaseFragment.kt +10 -0
- package/android/src/main/java/com/photoeditor/editor/filters/FilterListener.kt +7 -0
- package/android/src/main/java/com/photoeditor/editor/filters/FilterViewAdapter.kt +105 -0
- package/android/src/main/java/com/photoeditor/editor/tools/EditingToolsAdapter.kt +70 -0
- package/android/src/main/java/com/photoeditor/editor/tools/ToolType.kt +10 -0
- package/android/src/main/res/drawable/pe_blank_image.jpg +0 -0
- package/android/src/main/res/drawable/pe_ic_brush.xml +9 -0
- package/android/src/main/res/drawable/pe_ic_close.xml +9 -0
- package/android/src/main/res/drawable/pe_ic_eraser.xml +4 -0
- package/android/src/main/res/drawable/pe_ic_insert_emoticon.xml +9 -0
- package/android/src/main/res/drawable/pe_ic_oval.xml +9 -0
- package/android/src/main/res/drawable/pe_ic_photo_filter.xml +5 -0
- package/android/src/main/res/drawable/pe_ic_rectangle.xml +9 -0
- package/android/src/main/res/drawable/pe_ic_redo.xml +10 -0
- package/android/src/main/res/drawable/pe_ic_redo_disabled.xml +10 -0
- package/android/src/main/res/drawable/pe_ic_save.xml +9 -0
- package/android/src/main/res/drawable/pe_ic_sticker.xml +9 -0
- package/android/src/main/res/drawable/pe_ic_text.xml +9 -0
- package/android/src/main/res/drawable/pe_ic_undo.xml +10 -0
- package/android/src/main/res/drawable/pe_ic_undo_disabled.xml +10 -0
- package/android/src/main/res/drawable/pe_original.jpg +0 -0
- package/android/src/main/res/drawable/pe_redo.xml +6 -0
- package/android/src/main/res/drawable/pe_rounded_border_text_view.xml +14 -0
- package/android/src/main/res/drawable/pe_undo.xml +6 -0
- package/android/src/main/res/font/pe_roboto_medium.xml +7 -0
- package/android/src/main/res/layout/pe_activity_edit_image.xml +111 -0
- package/android/src/main/res/layout/pe_add_text_dialog.xml +56 -0
- package/android/src/main/res/layout/pe_color_picker_item_list.xml +15 -0
- package/android/src/main/res/layout/pe_fragment_bottom_properties_dialog.xml +77 -0
- package/android/src/main/res/layout/pe_fragment_bottom_shapes_dialog.xml +135 -0
- package/android/src/main/res/layout/pe_fragment_bottom_sticker_emoji_dialog.xml +62 -0
- package/android/src/main/res/layout/pe_row_editing_tools.xml +34 -0
- package/android/src/main/res/layout/pe_row_emoji.xml +17 -0
- package/android/src/main/res/layout/pe_row_filter_view.xml +35 -0
- package/android/src/main/res/layout/pe_row_sticker.xml +23 -0
- package/android/src/main/res/values/colors.xml +22 -0
- package/android/src/main/res/values/dimens.xml +7 -0
- package/android/src/main/res/values/font_certs.xml +17 -0
- package/android/src/main/res/values/preloaded_fonts.xml +6 -0
- package/android/src/main/res/values/strings.xml +462 -0
- package/android/src/main/res/values/styles.xml +28 -0
- package/ios/PhotoEditor.h +5 -0
- package/ios/PhotoEditor.mm +58 -0
- package/ios/PhotoEditorManager.swift +164 -0
- package/lib/module/NativePhotoEditor.js +5 -0
- package/lib/module/NativePhotoEditor.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/open.js +6 -0
- package/lib/module/open.js.map +1 -0
- package/lib/module/open.native.js +7 -0
- package/lib/module/open.native.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativePhotoEditor.d.ts +14 -0
- package/lib/typescript/src/NativePhotoEditor.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/open.d.ts +3 -0
- package/lib/typescript/src/open.d.ts.map +1 -0
- package/lib/typescript/src/open.native.d.ts +3 -0
- package/lib/typescript/src/open.native.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +39 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +128 -0
- package/src/NativePhotoEditor.ts +12 -0
- package/src/index.tsx +2 -0
- package/src/open.native.tsx +6 -0
- package/src/open.tsx +7 -0
- 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>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
+
}
|