@arfuhad/react-native-smart-camera 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 (95) hide show
  1. package/ARCHITECTURE.md +341 -0
  2. package/README.md +154 -0
  3. package/android/build.gradle +89 -0
  4. package/android/src/main/AndroidManifest.xml +2 -0
  5. package/android/src/main/java/expo/modules/smartcamera/ImageLoader.kt +106 -0
  6. package/android/src/main/java/expo/modules/smartcamera/MLKitFaceDetector.kt +273 -0
  7. package/android/src/main/java/expo/modules/smartcamera/SmartCameraModule.kt +205 -0
  8. package/android/src/main/java/expo/modules/smartcamera/SmartCameraView.kt +153 -0
  9. package/android/src/main/java/expo/modules/smartcamera/WebRTCFrameBridge.kt +184 -0
  10. package/app.plugin.js +17 -0
  11. package/build/SmartCamera.d.ts +17 -0
  12. package/build/SmartCamera.d.ts.map +1 -0
  13. package/build/SmartCamera.js +270 -0
  14. package/build/SmartCamera.js.map +1 -0
  15. package/build/SmartCameraModule.d.ts +112 -0
  16. package/build/SmartCameraModule.d.ts.map +1 -0
  17. package/build/SmartCameraModule.js +121 -0
  18. package/build/SmartCameraModule.js.map +1 -0
  19. package/build/SmartCameraView.d.ts +8 -0
  20. package/build/SmartCameraView.d.ts.map +1 -0
  21. package/build/SmartCameraView.js +7 -0
  22. package/build/SmartCameraView.js.map +1 -0
  23. package/build/detection/blinkProcessor.d.ts +23 -0
  24. package/build/detection/blinkProcessor.d.ts.map +1 -0
  25. package/build/detection/blinkProcessor.js +90 -0
  26. package/build/detection/blinkProcessor.js.map +1 -0
  27. package/build/detection/faceDetector.d.ts +16 -0
  28. package/build/detection/faceDetector.d.ts.map +1 -0
  29. package/build/detection/faceDetector.js +46 -0
  30. package/build/detection/faceDetector.js.map +1 -0
  31. package/build/detection/index.d.ts +4 -0
  32. package/build/detection/index.d.ts.map +1 -0
  33. package/build/detection/index.js +4 -0
  34. package/build/detection/index.js.map +1 -0
  35. package/build/detection/staticImageDetector.d.ts +25 -0
  36. package/build/detection/staticImageDetector.d.ts.map +1 -0
  37. package/build/detection/staticImageDetector.js +48 -0
  38. package/build/detection/staticImageDetector.js.map +1 -0
  39. package/build/hooks/index.d.ts +5 -0
  40. package/build/hooks/index.d.ts.map +1 -0
  41. package/build/hooks/index.js +5 -0
  42. package/build/hooks/index.js.map +1 -0
  43. package/build/hooks/useBlinkDetection.d.ts +39 -0
  44. package/build/hooks/useBlinkDetection.d.ts.map +1 -0
  45. package/build/hooks/useBlinkDetection.js +67 -0
  46. package/build/hooks/useBlinkDetection.js.map +1 -0
  47. package/build/hooks/useFaceDetection.d.ts +46 -0
  48. package/build/hooks/useFaceDetection.d.ts.map +1 -0
  49. package/build/hooks/useFaceDetection.js +80 -0
  50. package/build/hooks/useFaceDetection.js.map +1 -0
  51. package/build/hooks/useSmartCamera.d.ts +31 -0
  52. package/build/hooks/useSmartCamera.d.ts.map +1 -0
  53. package/build/hooks/useSmartCamera.js +75 -0
  54. package/build/hooks/useSmartCamera.js.map +1 -0
  55. package/build/hooks/useSmartCameraWebRTC.d.ts +58 -0
  56. package/build/hooks/useSmartCameraWebRTC.d.ts.map +1 -0
  57. package/build/hooks/useSmartCameraWebRTC.js +160 -0
  58. package/build/hooks/useSmartCameraWebRTC.js.map +1 -0
  59. package/build/index.d.ts +14 -0
  60. package/build/index.d.ts.map +1 -0
  61. package/build/index.js +20 -0
  62. package/build/index.js.map +1 -0
  63. package/build/types.d.ts +478 -0
  64. package/build/types.d.ts.map +1 -0
  65. package/build/types.js +2 -0
  66. package/build/types.js.map +1 -0
  67. package/build/utils/index.d.ts +98 -0
  68. package/build/utils/index.d.ts.map +1 -0
  69. package/build/utils/index.js +276 -0
  70. package/build/utils/index.js.map +1 -0
  71. package/build/webrtc/WebRTCBridge.d.ts +55 -0
  72. package/build/webrtc/WebRTCBridge.d.ts.map +1 -0
  73. package/build/webrtc/WebRTCBridge.js +113 -0
  74. package/build/webrtc/WebRTCBridge.js.map +1 -0
  75. package/build/webrtc/index.d.ts +3 -0
  76. package/build/webrtc/index.d.ts.map +1 -0
  77. package/build/webrtc/index.js +2 -0
  78. package/build/webrtc/index.js.map +1 -0
  79. package/build/webrtc/types.d.ts +64 -0
  80. package/build/webrtc/types.d.ts.map +1 -0
  81. package/build/webrtc/types.js +5 -0
  82. package/build/webrtc/types.js.map +1 -0
  83. package/expo-module.config.json +9 -0
  84. package/ios/MLKitFaceDetector.swift +310 -0
  85. package/ios/SmartCamera.podspec +33 -0
  86. package/ios/SmartCameraModule.swift +225 -0
  87. package/ios/SmartCameraView.swift +146 -0
  88. package/ios/WebRTCFrameBridge.swift +150 -0
  89. package/package.json +91 -0
  90. package/plugin/build/index.d.ts +28 -0
  91. package/plugin/build/index.js +33 -0
  92. package/plugin/build/withSmartCameraAndroid.d.ts +9 -0
  93. package/plugin/build/withSmartCameraAndroid.js +108 -0
  94. package/plugin/build/withSmartCameraIOS.d.ts +11 -0
  95. package/plugin/build/withSmartCameraIOS.js +92 -0
@@ -0,0 +1,341 @@
1
+ # react-native-smart-camera
2
+
3
+ Expo-compatible native module for camera preview, face detection, blink detection, and WebRTC streaming.
4
+
5
+ ## Overview
6
+
7
+ Build a production-ready Expo-compatible native module that provides:
8
+
9
+ - Camera preview via VisionCamera
10
+ - Real-time face and blink detection using Google ML Kit
11
+ - Static image face detection
12
+ - Native WebRTC bridge for video calling and streaming
13
+ - Expo config plugin for zero-config setup
14
+
15
+ ## Expo Compatibility
16
+
17
+ | Expo Workflow | Supported | Why |
18
+ |---------------|-----------|-----|
19
+ | Expo Managed | ❌ No | WebRTC + VisionCamera need native code |
20
+ | Expo Dev Client | ✅ Yes | Allows custom native modules |
21
+ | Expo Bare | ✅ Yes | Full native control |
22
+
23
+ **Requirements:**
24
+ - Expo SDK ≥ 49
25
+ - Expo Dev Client
26
+ - EAS Build
27
+
28
+ ## Architecture
29
+
30
+ ```
31
+ Expo App
32
+ ├── SmartCamera (JS Component)
33
+ ├── VisionCamera (native)
34
+ ├── Frame Processor (worklet)
35
+ ├── Native WebRTC Bridge
36
+ │ ├── iOS (Swift)
37
+ │ └── Android (Kotlin)
38
+ └── react-native-webrtc
39
+ ```
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ expo install react-native-smart-camera
45
+ expo prebuild
46
+ expo run:ios
47
+ expo run:android
48
+ ```
49
+
50
+ Or with EAS:
51
+
52
+ ```bash
53
+ eas build --profile development
54
+ ```
55
+
56
+ ## Usage
57
+
58
+ ```tsx
59
+ import { SmartCamera } from 'react-native-smart-camera';
60
+
61
+ <SmartCamera
62
+ camera="front"
63
+ faceDetection={{
64
+ enabled: true,
65
+ performanceMode: 'fast',
66
+ landmarkMode: 'all',
67
+ classificationMode: 'all',
68
+ }}
69
+ blinkDetection
70
+ onBlinkDetected={handleBlink}
71
+ onFaceDetected={handleFaces}
72
+ webrtc={{
73
+ enabled: true,
74
+ peerConnection,
75
+ mode: 'call',
76
+ }}
77
+ />
78
+ ```
79
+
80
+ ---
81
+
82
+ ## API Reference
83
+
84
+ ### SmartCamera Component Props
85
+
86
+ ```typescript
87
+ interface SmartCameraProps {
88
+ // Camera settings
89
+ camera?: 'front' | 'back';
90
+ fps?: number;
91
+ style?: ViewStyle;
92
+
93
+ // Face Detection Options
94
+ faceDetection?: FaceDetectionConfig;
95
+
96
+ // Blink Detection
97
+ blinkDetection?: boolean;
98
+ onBlinkDetected?: (event: BlinkEvent) => void;
99
+
100
+ // Face Detection Callback
101
+ onFaceDetected?: (faces: Face[]) => void;
102
+
103
+ // WebRTC Configuration
104
+ webrtc?: WebRTCConfig;
105
+ }
106
+ ```
107
+
108
+ ---
109
+
110
+ ## Face Detection Options
111
+
112
+ ### Common Options (Frame Processor and Static Images)
113
+
114
+ | Option | Description | Default | Options |
115
+ |--------|-------------|---------|---------|
116
+ | `performanceMode` | Favor speed or accuracy when detecting faces | `'fast'` | `'fast'`, `'accurate'` |
117
+ | `landmarkMode` | Whether to identify facial landmarks: eyes, ears, nose, cheeks, mouth | `'none'` | `'none'`, `'all'` |
118
+ | `contourMode` | Whether to detect contours of facial features. Contours are detected for only the most prominent face in an image | `'none'` | `'none'`, `'all'` |
119
+ | `classificationMode` | Whether to classify faces into categories such as 'smiling' and 'eyes open' | `'none'` | `'none'`, `'all'` |
120
+ | `minFaceSize` | Sets the smallest desired face size, expressed as the ratio of the width of the head to width of the image | `0.15` | `number` |
121
+ | `trackingEnabled` | Whether to assign faces an ID to track faces across images. Note: When contour detection is enabled, only one face is detected, so face tracking doesn't produce useful results. Don't enable both contour detection and face tracking | `false` | `boolean` |
122
+
123
+ ### Frame Processor Options
124
+
125
+ | Option | Description | Default | Options |
126
+ |--------|-------------|---------|---------|
127
+ | `cameraFacing` | Current active camera | `'front'` | `'front'`, `'back'` |
128
+ | `autoMode` | Should handle auto scale (face bounds, contour and landmarks) and rotation on native side? If disabled, all detection results will be relative to frame coordinates, not to screen/preview. You shouldn't use this option if you want to draw on screen using Skia Frame Processor | `false` | `boolean` |
129
+ | `windowWidth` | Required if you want to use `autoMode`. You must handle your own logic to get screen sizes, with or without statusbar size, etc. | `1.0` | `number` |
130
+ | `windowHeight` | Required if you want to use `autoMode`. You must handle your own logic to get screen sizes, with or without statusbar size, etc. | `1.0` | `number` |
131
+
132
+ ### Static Images Options
133
+
134
+ | Option | Description | Default | Options |
135
+ |--------|-------------|---------|---------|
136
+ | `image` | Image source for static face detection | - | `number`, `string`, `{ uri: string }` |
137
+
138
+ ---
139
+
140
+ ## Types
141
+
142
+ ```typescript
143
+ // Face Detection Options
144
+ interface FaceDetectionOptions {
145
+ performanceMode?: 'fast' | 'accurate';
146
+ landmarkMode?: 'none' | 'all';
147
+ contourMode?: 'none' | 'all';
148
+ classificationMode?: 'none' | 'all';
149
+ minFaceSize?: number;
150
+ trackingEnabled?: boolean;
151
+ }
152
+
153
+ // Frame Processor Options
154
+ interface FrameProcessorOptions extends FaceDetectionOptions {
155
+ cameraFacing?: 'front' | 'back';
156
+ autoMode?: boolean;
157
+ windowWidth?: number;
158
+ windowHeight?: number;
159
+ }
160
+
161
+ // Static Image Options
162
+ interface StaticImageOptions extends FaceDetectionOptions {
163
+ image: number | string | { uri: string };
164
+ }
165
+
166
+ // Face Detection Result
167
+ interface Face {
168
+ bounds: { x: number; y: number; width: number; height: number };
169
+ landmarks?: FaceLandmarks;
170
+ contours?: FaceContours;
171
+ smilingProbability?: number;
172
+ leftEyeOpenProbability?: number;
173
+ rightEyeOpenProbability?: number;
174
+ trackingId?: number;
175
+ }
176
+
177
+ // Blink Event
178
+ interface BlinkEvent {
179
+ timestamp: number;
180
+ leftEyeOpen: number; // 0.0 - 1.0
181
+ rightEyeOpen: number; // 0.0 - 1.0
182
+ isBlink: boolean;
183
+ faceId?: number;
184
+ }
185
+
186
+ // WebRTC Configuration
187
+ interface WebRTCConfig {
188
+ enabled: boolean;
189
+ peerConnection?: RTCPeerConnection;
190
+ mode?: 'call' | 'stream';
191
+ videoConstraints?: {
192
+ width?: number;
193
+ height?: number;
194
+ frameRate?: number;
195
+ };
196
+ }
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Frame Processor Functions
202
+
203
+ ```typescript
204
+ import { detectFaces, detectBlink } from 'react-native-smart-camera';
205
+
206
+ // Detect faces in a frame
207
+ const faces = detectFaces(frame, {
208
+ performanceMode: 'fast',
209
+ landmarkMode: 'all',
210
+ });
211
+
212
+ // Detect blink in a frame
213
+ const blinkEvent = detectBlink(frame, {
214
+ classificationMode: 'all',
215
+ });
216
+ ```
217
+
218
+ ---
219
+
220
+ ## Static Image Detection
221
+
222
+ ```typescript
223
+ import { detectFacesInImage } from 'react-native-smart-camera';
224
+
225
+ const faces = await detectFacesInImage({
226
+ image: require('./photo.jpg'),
227
+ performanceMode: 'accurate',
228
+ landmarkMode: 'all',
229
+ contourMode: 'all',
230
+ });
231
+ ```
232
+
233
+ ---
234
+
235
+ ## WebRTC Hooks
236
+
237
+ ```typescript
238
+ import { useSmartCameraWebRTC } from 'react-native-smart-camera';
239
+
240
+ const { videoTrack, startStreaming, stopStreaming, switchCamera } = useSmartCameraWebRTC({
241
+ enabled: true,
242
+ peerConnection,
243
+ mode: 'call',
244
+ });
245
+ ```
246
+
247
+ ---
248
+
249
+ ## Expo Config Plugin
250
+
251
+ Add to your `app.json`:
252
+
253
+ ```json
254
+ {
255
+ "plugins": ["react-native-smart-camera"]
256
+ }
257
+ ```
258
+
259
+ The plugin automatically:
260
+ - Adds camera permissions
261
+ - Adds microphone permissions
262
+ - Adds required iOS frameworks
263
+ - Configures Android Proguard rules
264
+
265
+ ---
266
+
267
+ ## Project Structure
268
+
269
+ ```
270
+ react-native-smart-camera/
271
+ ├── src/
272
+ │ ├── index.ts
273
+ │ ├── SmartCamera.tsx
274
+ │ ├── types.ts
275
+ │ ├── hooks/
276
+ │ │ ├── useSmartCamera.ts
277
+ │ │ ├── useBlinkDetection.ts
278
+ │ │ ├── useFaceDetection.ts
279
+ │ │ └── useSmartCameraWebRTC.ts
280
+ │ ├── detection/
281
+ │ │ ├── blinkProcessor.ts
282
+ │ │ ├── faceDetector.ts
283
+ │ │ └── staticImageDetector.ts
284
+ │ └── webrtc/
285
+ │ ├── WebRTCBridge.ts
286
+ │ └── types.ts
287
+ ├── android/
288
+ │ ├── src/main/java/expo/modules/smartcamera/
289
+ │ │ ├── SmartCameraModule.kt
290
+ │ │ ├── SmartCameraView.kt
291
+ │ │ ├── MLKitBridge.kt
292
+ │ │ └── WebRTCFrameBridge.kt
293
+ │ └── build.gradle
294
+ ├── ios/
295
+ │ ├── SmartCameraModule.swift
296
+ │ ├── SmartCameraView.swift
297
+ │ ├── MLKitBridge.swift
298
+ │ ├── WebRTCFrameBridge.swift
299
+ │ └── SmartCamera.podspec
300
+ ├── plugin/
301
+ │ ├── index.ts
302
+ │ ├── withSmartCameraIOS.ts
303
+ │ └── withSmartCameraAndroid.ts
304
+ ├── example/
305
+ │ ├── App.tsx
306
+ │ ├── app.json
307
+ │ └── package.json
308
+ ├── app.plugin.js
309
+ ├── package.json
310
+ ├── tsconfig.json
311
+ └── README.md
312
+ ```
313
+
314
+ ---
315
+
316
+ ## Limitations
317
+
318
+ - ❌ Not supported in Expo Go
319
+ - ❌ Requires Dev Client or Bare Workflow
320
+ - ❌ Native build required (via EAS or local)
321
+
322
+ ---
323
+
324
+ ## Peer Dependencies
325
+
326
+ ```json
327
+ {
328
+ "peerDependencies": {
329
+ "react-native-vision-camera": ">=3.0.0",
330
+ "react-native-webrtc": ">=118.0.0",
331
+ "react-native-worklets-core": ">=1.0.0"
332
+ }
333
+ }
334
+ ```
335
+
336
+ ---
337
+
338
+ ## License
339
+
340
+ MIT
341
+
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # react-native-smart-camera
2
+
3
+ Expo-compatible native module for camera preview, face detection, blink detection, and WebRTC streaming.
4
+
5
+ ## Features
6
+
7
+ - 📷 Camera preview via VisionCamera
8
+ - 👁️ Real-time face detection with Google ML Kit
9
+ - 😉 Blink detection with configurable sensitivity
10
+ - 📡 Native WebRTC bridge for video calling and streaming
11
+ - 🔌 Expo config plugin for zero-config setup
12
+ - 📱 Static image face detection
13
+
14
+ ## Expo Compatibility
15
+
16
+ | Expo Workflow | Supported | Notes |
17
+ | --------------- | --------- | ---------------------------------- |
18
+ | Expo Managed | ❌ No | Requires native code |
19
+ | Expo Dev Client | ✅ Yes | Recommended for development |
20
+ | Expo Bare | ✅ Yes | Full native control |
21
+
22
+ **Requirements:**
23
+ - Expo SDK ≥ 49
24
+ - Expo Dev Client or Bare Workflow
25
+ - EAS Build or local native builds
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ # Install the package
31
+ npx expo install react-native-smart-camera
32
+
33
+ # Install peer dependencies
34
+ npx expo install react-native-vision-camera react-native-webrtc react-native-worklets-core
35
+
36
+ # Rebuild native code
37
+ npx expo prebuild
38
+ npx expo run:ios
39
+ # or
40
+ npx expo run:android
41
+ ```
42
+
43
+ ## Expo Config Plugin
44
+
45
+ Add to your `app.json` or `app.config.js`:
46
+
47
+ ```json
48
+ {
49
+ "plugins": [
50
+ [
51
+ "react-native-smart-camera",
52
+ {
53
+ "cameraPermissionText": "Allow camera access for face detection",
54
+ "microphonePermissionText": "Allow microphone for audio streaming"
55
+ }
56
+ ]
57
+ ]
58
+ }
59
+ ```
60
+
61
+ ## Usage
62
+
63
+ ### Basic Camera Preview
64
+
65
+ ```tsx
66
+ import { SmartCamera } from 'react-native-smart-camera';
67
+
68
+ export default function App() {
69
+ return (
70
+ <SmartCamera
71
+ camera="front"
72
+ style={{ flex: 1 }}
73
+ />
74
+ );
75
+ }
76
+ ```
77
+
78
+ ### Face Detection
79
+
80
+ ```tsx
81
+ import { SmartCamera, Face } from 'react-native-smart-camera';
82
+
83
+ export default function App() {
84
+ const handleFaces = (faces: Face[]) => {
85
+ console.log(`Detected ${faces.length} faces`);
86
+ };
87
+
88
+ return (
89
+ <SmartCamera
90
+ camera="front"
91
+ faceDetection={{
92
+ enabled: true,
93
+ performanceMode: 'fast',
94
+ landmarkMode: 'all',
95
+ classificationMode: 'all',
96
+ }}
97
+ onFaceDetected={handleFaces}
98
+ style={{ flex: 1 }}
99
+ />
100
+ );
101
+ }
102
+ ```
103
+
104
+ ### Blink Detection
105
+
106
+ ```tsx
107
+ import { SmartCamera, BlinkEvent } from 'react-native-smart-camera';
108
+
109
+ export default function App() {
110
+ const handleBlink = (event: BlinkEvent) => {
111
+ console.log('Blink detected!', event);
112
+ };
113
+
114
+ return (
115
+ <SmartCamera
116
+ camera="front"
117
+ blinkDetection
118
+ onBlinkDetected={handleBlink}
119
+ style={{ flex: 1 }}
120
+ />
121
+ );
122
+ }
123
+ ```
124
+
125
+ ### Static Image Detection
126
+
127
+ ```tsx
128
+ import { detectFacesInImage } from 'react-native-smart-camera';
129
+
130
+ const faces = await detectFacesInImage({
131
+ image: require('./photo.jpg'),
132
+ performanceMode: 'accurate',
133
+ landmarkMode: 'all',
134
+ });
135
+ ```
136
+
137
+ ## Face Detection Options
138
+
139
+ | Option | Description | Default | Options |
140
+ | ------------------ | ----------------------------------------------- | --------- | --------------------- |
141
+ | `performanceMode` | Speed vs accuracy trade-off | `'fast'` | `'fast'`, `'accurate'`|
142
+ | `landmarkMode` | Detect facial landmarks | `'none'` | `'none'`, `'all'` |
143
+ | `contourMode` | Detect facial contours | `'none'` | `'none'`, `'all'` |
144
+ | `classificationMode` | Classify smiling/eyes open | `'none'` | `'none'`, `'all'` |
145
+ | `minFaceSize` | Minimum face size ratio | `0.15` | `number` |
146
+ | `trackingEnabled` | Track faces across frames | `false` | `boolean` |
147
+
148
+ ## API Reference
149
+
150
+ See [ARCHITECTURE.md](./ARCHITECTURE.md) for full API documentation.
151
+
152
+ ## License
153
+
154
+ MIT
@@ -0,0 +1,89 @@
1
+ apply plugin: 'com.android.library'
2
+ apply plugin: 'kotlin-android'
3
+ apply plugin: 'maven-publish'
4
+
5
+ group = 'expo.modules.smartcamera'
6
+ version = '0.1.0'
7
+
8
+ buildscript {
9
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
10
+ if (expoModulesCorePlugin.exists()) {
11
+ apply from: expoModulesCorePlugin
12
+ applyKotlinExpoModulesCorePlugin()
13
+ }
14
+
15
+ // Simple fallback that allows building without expo-modules-core for lint
16
+ repositories {
17
+ mavenCentral()
18
+ google()
19
+ }
20
+
21
+ dependencies {
22
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
23
+ }
24
+ }
25
+
26
+ afterEvaluate {
27
+ publishing {
28
+ publications {
29
+ release(MavenPublication) {
30
+ from components.release
31
+ }
32
+ }
33
+ repositories {
34
+ maven {
35
+ url = mavenLocal().url
36
+ }
37
+ }
38
+ }
39
+ }
40
+
41
+ android {
42
+ namespace "expo.modules.smartcamera"
43
+ compileSdkVersion safeExtGet("compileSdkVersion", 34)
44
+
45
+ defaultConfig {
46
+ minSdkVersion safeExtGet("minSdkVersion", 23)
47
+ targetSdkVersion safeExtGet("targetSdkVersion", 34)
48
+ }
49
+
50
+ publishing {
51
+ singleVariant("release") {
52
+ withSourcesJar()
53
+ }
54
+ }
55
+
56
+ lintOptions {
57
+ abortOnError false
58
+ }
59
+
60
+ compileOptions {
61
+ sourceCompatibility JavaVersion.VERSION_17
62
+ targetCompatibility JavaVersion.VERSION_17
63
+ }
64
+
65
+ kotlinOptions {
66
+ jvmTarget = JavaVersion.VERSION_17.majorVersion
67
+ }
68
+ }
69
+
70
+ repositories {
71
+ mavenCentral()
72
+ google()
73
+ }
74
+
75
+ dependencies {
76
+ implementation project(':expo-modules-core')
77
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.22"
78
+
79
+ // Kotlin Coroutines
80
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"
81
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
82
+
83
+ // Google ML Kit Face Detection
84
+ implementation 'com.google.mlkit:face-detection:16.1.6'
85
+ }
86
+
87
+ def safeExtGet(prop, fallback) {
88
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
89
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,106 @@
1
+ package expo.modules.smartcamera
2
+
3
+ import android.graphics.Bitmap
4
+ import android.graphics.BitmapFactory
5
+ import android.net.Uri
6
+ import android.util.Base64
7
+ import android.util.Log
8
+ import kotlinx.coroutines.Dispatchers
9
+ import kotlinx.coroutines.withContext
10
+ import java.io.File
11
+ import java.net.URL
12
+
13
+ /**
14
+ * Utility class for loading images from various sources
15
+ */
16
+ class ImageLoader {
17
+ companion object {
18
+ private const val TAG = "ImageLoader"
19
+ }
20
+
21
+ /**
22
+ * Load an image from various sources (suspending function for async loading)
23
+ *
24
+ * @param source Image source - can be String URI, Map with "uri" key, or resource ID
25
+ * @return Bitmap
26
+ * @throws IllegalArgumentException if the source is invalid
27
+ */
28
+ suspend fun loadImage(source: Any): Bitmap = withContext(Dispatchers.IO) {
29
+ when (source) {
30
+ is String -> loadFromString(source)
31
+ is Map<*, *> -> {
32
+ val uri = source["uri"] as? String
33
+ val base64 = source["base64"] as? String
34
+
35
+ when {
36
+ uri != null -> loadFromString(uri)
37
+ base64 != null -> loadFromBase64(base64)
38
+ else -> throw IllegalArgumentException("Invalid image source map - must contain 'uri' or 'base64'")
39
+ }
40
+ }
41
+ is Number -> throw IllegalArgumentException("Loading from resource ID not supported. Use a URI instead.")
42
+ else -> throw IllegalArgumentException("Unsupported image source type: ${source::class.java.simpleName}")
43
+ }
44
+ }
45
+
46
+ private fun loadFromString(source: String): Bitmap {
47
+ return when {
48
+ source.startsWith("data:image") -> {
49
+ // Base64 data URI
50
+ val base64Data = source.substringAfter("base64,")
51
+ loadFromBase64(base64Data)
52
+ }
53
+ source.startsWith("http://") || source.startsWith("https://") -> {
54
+ loadFromUrl(source)
55
+ }
56
+ source.startsWith("file://") -> {
57
+ loadFromFile(source.removePrefix("file://"))
58
+ }
59
+ else -> {
60
+ // Try as file path
61
+ loadFromFile(source)
62
+ }
63
+ }
64
+ }
65
+
66
+ private fun loadFromBase64(base64: String): Bitmap {
67
+ return try {
68
+ val decodedBytes = Base64.decode(base64, Base64.DEFAULT)
69
+ BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size)
70
+ ?: throw IllegalStateException("Failed to decode base64 image data")
71
+ } catch (e: Exception) {
72
+ Log.e(TAG, "Failed to decode base64 image", e)
73
+ throw IllegalArgumentException("Failed to decode base64 image: ${e.message}", e)
74
+ }
75
+ }
76
+
77
+ private fun loadFromUrl(url: String): Bitmap {
78
+ return try {
79
+ val connection = URL(url).openConnection()
80
+ connection.connectTimeout = 10000
81
+ connection.readTimeout = 30000
82
+ connection.doInput = true
83
+ connection.connect()
84
+ val inputStream = connection.getInputStream()
85
+ BitmapFactory.decodeStream(inputStream)
86
+ ?: throw IllegalStateException("Failed to decode image from URL")
87
+ } catch (e: Exception) {
88
+ Log.e(TAG, "Failed to load image from URL: $url", e)
89
+ throw IllegalArgumentException("Failed to load image from URL: ${e.message}", e)
90
+ }
91
+ }
92
+
93
+ private fun loadFromFile(path: String): Bitmap {
94
+ return try {
95
+ val file = File(path)
96
+ if (!file.exists()) {
97
+ throw IllegalArgumentException("File not found: $path")
98
+ }
99
+ BitmapFactory.decodeFile(path)
100
+ ?: throw IllegalStateException("Failed to decode image from file")
101
+ } catch (e: Exception) {
102
+ Log.e(TAG, "Failed to load image from file: $path", e)
103
+ throw IllegalArgumentException("Failed to load image from file: ${e.message}", e)
104
+ }
105
+ }
106
+ }