@developer_tribe/react-native-comnyx 0.13.13 → 0.15.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 (132) hide show
  1. package/android/generated/RCTAppDependencyProvider.h +25 -0
  2. package/android/generated/RCTAppDependencyProvider.mm +55 -0
  3. package/android/generated/RCTModulesConformingToProtocolsProvider.h +18 -0
  4. package/android/generated/RCTModulesConformingToProtocolsProvider.mm +33 -0
  5. package/android/generated/RCTThirdPartyComponentsProvider.h +16 -0
  6. package/android/generated/RCTThirdPartyComponentsProvider.mm +23 -0
  7. package/android/generated/ReactAppDependencyProvider.podspec +34 -0
  8. package/android/generated/jni/CMakeLists.txt +36 -0
  9. package/android/generated/jni/RNComnyxSpec-generated.cpp +22 -0
  10. package/android/generated/jni/RNComnyxSpec.h +24 -0
  11. package/android/generated/jni/react/renderer/components/RNComnyxSpec/RNComnyxSpecJSI-generated.cpp +17 -0
  12. package/android/generated/jni/react/renderer/components/RNComnyxSpec/RNComnyxSpecJSI.h +19 -0
  13. package/android/src/main/AndroidManifest.xml +15 -0
  14. package/android/src/main/AndroidManifestNew.xml +4 -0
  15. package/android/src/main/java/com/comnyx/ComnyxMediaPickerModule.kt +347 -0
  16. package/android/src/main/java/com/comnyx/ComnyxPackage.kt +1 -1
  17. package/android/src/main/java/com/comnyx/VideoPlayerActivity.kt +91 -0
  18. package/ios/ComnyxMediaPicker.m +29 -0
  19. package/ios/ComnyxMediaPicker.swift +436 -0
  20. package/lib/commonjs/NativeComnyxMediaPicker.js +83 -0
  21. package/lib/commonjs/NativeComnyxMediaPicker.js.map +1 -0
  22. package/lib/commonjs/api/index.js +19 -0
  23. package/lib/commonjs/api/index.js.map +1 -1
  24. package/lib/commonjs/api/media.js +76 -0
  25. package/lib/commonjs/api/media.js.map +1 -0
  26. package/lib/commonjs/assets/attachment-01.png +0 -0
  27. package/lib/commonjs/assets/gallery.png +0 -0
  28. package/lib/commonjs/assets/video-play.png +0 -0
  29. package/lib/commonjs/assets/x-circle.png +0 -0
  30. package/lib/commonjs/components/ChatList.js +48 -22
  31. package/lib/commonjs/components/ChatList.js.map +1 -1
  32. package/lib/commonjs/components/LinkifyText.js +5 -1
  33. package/lib/commonjs/components/LinkifyText.js.map +1 -1
  34. package/lib/commonjs/components/MediaMessageItem.js +333 -0
  35. package/lib/commonjs/components/MediaMessageItem.js.map +1 -0
  36. package/lib/commonjs/components/MediaPickerButton.js +244 -0
  37. package/lib/commonjs/components/MediaPickerButton.js.map +1 -0
  38. package/lib/commonjs/components/MediaViewerModal.js +164 -0
  39. package/lib/commonjs/components/MediaViewerModal.js.map +1 -0
  40. package/lib/commonjs/components/MessageInput.js +344 -73
  41. package/lib/commonjs/components/MessageInput.js.map +1 -1
  42. package/lib/commonjs/components/MessageItem.js +17 -8
  43. package/lib/commonjs/components/MessageItem.js.map +1 -1
  44. package/lib/commonjs/constants/translations.js +174 -29
  45. package/lib/commonjs/constants/translations.js.map +1 -1
  46. package/lib/commonjs/data/fake/media.js +105 -0
  47. package/lib/commonjs/data/fake/media.js.map +1 -0
  48. package/lib/commonjs/types/MediaTypes.js +2 -0
  49. package/lib/commonjs/types/MediaTypes.js.map +1 -0
  50. package/lib/commonjs/version.js +1 -1
  51. package/lib/commonjs/version.js.map +1 -1
  52. package/lib/module/NativeComnyxMediaPicker.js +73 -0
  53. package/lib/module/NativeComnyxMediaPicker.js.map +1 -0
  54. package/lib/module/api/index.js +1 -0
  55. package/lib/module/api/index.js.map +1 -1
  56. package/lib/module/api/media.js +70 -0
  57. package/lib/module/api/media.js.map +1 -0
  58. package/lib/module/assets/attachment-01.png +0 -0
  59. package/lib/module/assets/gallery.png +0 -0
  60. package/lib/module/assets/video-play.png +0 -0
  61. package/lib/module/assets/x-circle.png +0 -0
  62. package/lib/module/components/ChatList.js +48 -22
  63. package/lib/module/components/ChatList.js.map +1 -1
  64. package/lib/module/components/LinkifyText.js +5 -1
  65. package/lib/module/components/LinkifyText.js.map +1 -1
  66. package/lib/module/components/MediaMessageItem.js +330 -0
  67. package/lib/module/components/MediaMessageItem.js.map +1 -0
  68. package/lib/module/components/MediaPickerButton.js +240 -0
  69. package/lib/module/components/MediaPickerButton.js.map +1 -0
  70. package/lib/module/components/MediaViewerModal.js +160 -0
  71. package/lib/module/components/MediaViewerModal.js.map +1 -0
  72. package/lib/module/components/MessageInput.js +347 -75
  73. package/lib/module/components/MessageInput.js.map +1 -1
  74. package/lib/module/components/MessageItem.js +17 -8
  75. package/lib/module/components/MessageItem.js.map +1 -1
  76. package/lib/module/constants/translations.js +174 -29
  77. package/lib/module/constants/translations.js.map +1 -1
  78. package/lib/module/data/fake/media.js +99 -0
  79. package/lib/module/data/fake/media.js.map +1 -0
  80. package/lib/module/types/MediaTypes.js +2 -0
  81. package/lib/module/types/MediaTypes.js.map +1 -0
  82. package/lib/module/version.js +1 -1
  83. package/lib/module/version.js.map +1 -1
  84. package/lib/typescript/src/NativeComnyxMediaPicker.d.ts +9 -0
  85. package/lib/typescript/src/NativeComnyxMediaPicker.d.ts.map +1 -0
  86. package/lib/typescript/src/api/index.d.ts +1 -0
  87. package/lib/typescript/src/api/index.d.ts.map +1 -1
  88. package/lib/typescript/src/api/media.d.ts +7 -0
  89. package/lib/typescript/src/api/media.d.ts.map +1 -0
  90. package/lib/typescript/src/components/ChatList.d.ts.map +1 -1
  91. package/lib/typescript/src/components/LinkifyText.d.ts.map +1 -1
  92. package/lib/typescript/src/components/MediaMessageItem.d.ts +6 -0
  93. package/lib/typescript/src/components/MediaMessageItem.d.ts.map +1 -0
  94. package/lib/typescript/src/components/MediaPickerButton.d.ts +5 -0
  95. package/lib/typescript/src/components/MediaPickerButton.d.ts.map +1 -0
  96. package/lib/typescript/src/components/MediaViewerModal.d.ts +8 -0
  97. package/lib/typescript/src/components/MediaViewerModal.d.ts.map +1 -0
  98. package/lib/typescript/src/components/MessageInput.d.ts.map +1 -1
  99. package/lib/typescript/src/components/MessageItem.d.ts.map +1 -1
  100. package/lib/typescript/src/constants/translations.d.ts.map +1 -1
  101. package/lib/typescript/src/data/fake/media.d.ts +6 -0
  102. package/lib/typescript/src/data/fake/media.d.ts.map +1 -0
  103. package/lib/typescript/src/types/Conversation.d.ts +19 -0
  104. package/lib/typescript/src/types/Conversation.d.ts.map +1 -1
  105. package/lib/typescript/src/types/LocalizationKeys.d.ts +5 -0
  106. package/lib/typescript/src/types/LocalizationKeys.d.ts.map +1 -1
  107. package/lib/typescript/src/types/MediaTypes.d.ts +26 -0
  108. package/lib/typescript/src/types/MediaTypes.d.ts.map +1 -0
  109. package/lib/typescript/src/version.d.ts +1 -1
  110. package/lib/typescript/src/version.d.ts.map +1 -1
  111. package/package.json +1 -1
  112. package/src/NativeComnyxMediaPicker.ts +83 -0
  113. package/src/api/index.ts +1 -0
  114. package/src/api/media.ts +116 -0
  115. package/src/assets/attachment-01.png +0 -0
  116. package/src/assets/gallery.png +0 -0
  117. package/src/assets/video-play.png +0 -0
  118. package/src/assets/x-circle.png +0 -0
  119. package/src/components/ChatList.tsx +81 -24
  120. package/src/components/CustomerForm.tsx +1 -1
  121. package/src/components/LinkifyText.tsx +3 -2
  122. package/src/components/MediaMessageItem.tsx +390 -0
  123. package/src/components/MediaPickerButton.tsx +269 -0
  124. package/src/components/MediaViewerModal.tsx +168 -0
  125. package/src/components/MessageInput.tsx +396 -84
  126. package/src/components/MessageItem.tsx +19 -5
  127. package/src/constants/translations.ts +145 -0
  128. package/src/data/fake/media.ts +110 -0
  129. package/src/types/Conversation.ts +20 -0
  130. package/src/types/LocalizationKeys.ts +5 -0
  131. package/src/types/MediaTypes.ts +27 -0
  132. package/src/version.ts +1 -1
@@ -0,0 +1,347 @@
1
+ package com.comnyx
2
+
3
+ import android.app.Activity
4
+ import android.content.Intent
5
+ import android.graphics.Bitmap
6
+ import android.graphics.BitmapFactory
7
+ import android.net.Uri
8
+ import android.provider.OpenableColumns
9
+ import android.webkit.MimeTypeMap
10
+ import com.facebook.react.bridge.*
11
+ import java.io.File
12
+ import java.io.FileOutputStream
13
+ import java.io.InputStream
14
+ import android.media.MediaMetadataRetriever
15
+
16
+ class ComnyxMediaPickerModule(reactContext: ReactApplicationContext) :
17
+ ReactContextBaseJavaModule(reactContext), ActivityEventListener {
18
+
19
+ companion object {
20
+ const val NAME = "ComnyxMediaPicker"
21
+ private const val REQUEST_MEDIA = 2003
22
+ private const val REQUEST_IMAGE = 2004
23
+ private const val REQUEST_VIDEO = 2005
24
+ private const val COMPRESS_THRESHOLD = 500 * 1024 // 500KB
25
+ private const val COMPRESS_QUALITY = 80
26
+ }
27
+
28
+ private var promise: Promise? = null
29
+
30
+ private fun rejectPendingPromise() {
31
+ promise?.reject("CANCELLED", "A new picker request was made before the previous one completed")
32
+ promise = null
33
+ }
34
+
35
+ init {
36
+ reactContext.addActivityEventListener(this)
37
+ }
38
+
39
+ override fun getName(): String = NAME
40
+
41
+
42
+
43
+ // MARK: - Pick Media (both images and videos)
44
+
45
+ @ReactMethod
46
+ fun pickMedia(promise: Promise) {
47
+ rejectPendingPromise()
48
+ this.promise = promise
49
+ val activity = reactApplicationContext.currentActivity
50
+ if (activity == null) {
51
+ promise.reject("NO_ACTIVITY", "Activity is not available")
52
+ return
53
+ }
54
+ val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
55
+ type = "*/*"
56
+ putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*"))
57
+ putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
58
+ addCategory(Intent.CATEGORY_OPENABLE)
59
+ }
60
+ activity.startActivityForResult(intent, REQUEST_MEDIA)
61
+ }
62
+
63
+ // MARK: - Pick Image Only
64
+
65
+ @ReactMethod
66
+ fun pickImage(promise: Promise) {
67
+ rejectPendingPromise()
68
+ this.promise = promise
69
+ val activity = reactApplicationContext.currentActivity
70
+ if (activity == null) {
71
+ promise.reject("NO_ACTIVITY", "Activity is not available")
72
+ return
73
+ }
74
+ val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
75
+ type = "image/*"
76
+ putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
77
+ addCategory(Intent.CATEGORY_OPENABLE)
78
+ }
79
+ activity.startActivityForResult(intent, REQUEST_IMAGE)
80
+ }
81
+
82
+ // MARK: - Pick Video Only
83
+
84
+ @ReactMethod
85
+ fun pickVideo(promise: Promise) {
86
+ rejectPendingPromise()
87
+ this.promise = promise
88
+ val activity = reactApplicationContext.currentActivity
89
+ if (activity == null) {
90
+ promise.reject("NO_ACTIVITY", "Activity is not available")
91
+ return
92
+ }
93
+ val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
94
+ type = "video/*"
95
+ putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
96
+ addCategory(Intent.CATEGORY_OPENABLE)
97
+ }
98
+ activity.startActivityForResult(intent, REQUEST_VIDEO)
99
+ }
100
+
101
+ // MARK: - Generate Thumbnail from Video URL
102
+
103
+ @ReactMethod
104
+ fun generateThumbnail(videoUrl: String, promise: Promise) {
105
+ android.util.Log.d("ComnyxMedia", "generateThumbnail called with: $videoUrl")
106
+ Thread {
107
+ try {
108
+ val retriever = MediaMetadataRetriever()
109
+ if (videoUrl.startsWith("http://") || videoUrl.startsWith("https://")) {
110
+ android.util.Log.d("ComnyxMedia", "Setting remote data source")
111
+ retriever.setDataSource(videoUrl, HashMap<String, String>())
112
+ } else {
113
+ android.util.Log.d("ComnyxMedia", "Setting local data source")
114
+ retriever.setDataSource(reactApplicationContext, Uri.parse(videoUrl))
115
+ }
116
+ val bitmap = retriever.getFrameAtTime(0, MediaMetadataRetriever.OPTION_CLOSEST_SYNC)
117
+ retriever.release()
118
+
119
+ if (bitmap != null) {
120
+ android.util.Log.d("ComnyxMedia", "Thumbnail bitmap generated: ${bitmap.width}x${bitmap.height}")
121
+ val thumbFile = File(reactApplicationContext.cacheDir, "thumb_${System.currentTimeMillis()}.jpg")
122
+ FileOutputStream(thumbFile).use { fos ->
123
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos)
124
+ }
125
+ bitmap.recycle()
126
+ promise.resolve("file://" + thumbFile.absolutePath)
127
+ } else {
128
+ android.util.Log.d("ComnyxMedia", "Thumbnail bitmap is null")
129
+ promise.resolve(null)
130
+ }
131
+ } catch (e: Exception) {
132
+ android.util.Log.e("ComnyxMedia", "generateThumbnail error: ${e.message}", e)
133
+ promise.resolve(null)
134
+ }
135
+ }.start()
136
+ }
137
+
138
+ // MARK: - Open Video
139
+
140
+ @ReactMethod
141
+ fun openVideo(uri: String, promise: Promise) {
142
+ try {
143
+ val activity = reactApplicationContext.currentActivity
144
+ if (activity == null) {
145
+ promise.reject("NO_ACTIVITY", "Activity is not available")
146
+ return
147
+ }
148
+
149
+ if (uri.startsWith("http://") || uri.startsWith("https://")) {
150
+ // Remote URL — use system player for faster streaming
151
+ val intent = Intent(Intent.ACTION_VIEW).apply {
152
+ setDataAndType(Uri.parse(uri), "video/*")
153
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
154
+ }
155
+ activity.startActivity(intent)
156
+ promise.resolve(null)
157
+ } else {
158
+ // Local file — use custom VideoPlayerActivity
159
+ val intent = Intent(activity, VideoPlayerActivity::class.java).apply {
160
+ putExtra("video_uri", uri)
161
+ }
162
+ activity.startActivity(intent)
163
+ promise.resolve(null)
164
+ }
165
+ } catch (e: Exception) {
166
+ promise.reject("OPEN_ERROR", e.message, e)
167
+ }
168
+ }
169
+
170
+ // MARK: - Activity Result
171
+
172
+ override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
173
+ if (requestCode != REQUEST_MEDIA && requestCode != REQUEST_IMAGE && requestCode != REQUEST_VIDEO) return
174
+
175
+ if (resultCode != Activity.RESULT_OK || data == null) {
176
+ promise?.resolve(null)
177
+ promise = null
178
+ return
179
+ }
180
+
181
+ // Collect all selected URIs
182
+ val uris = mutableListOf<Uri>()
183
+ val clipData = data.clipData
184
+ if (clipData != null) {
185
+ for (i in 0 until clipData.itemCount) {
186
+ uris.add(clipData.getItemAt(i).uri)
187
+ }
188
+ } else if (data.data != null) {
189
+ uris.add(data.data!!)
190
+ }
191
+
192
+ if (uris.isEmpty()) {
193
+ promise?.resolve(null)
194
+ promise = null
195
+ return
196
+ }
197
+
198
+ // Capture and clear promise to prevent double-resolve races
199
+ val currentPromise = promise
200
+ promise = null
201
+
202
+ // Move all heavy I/O to a background thread to prevent ANR
203
+ Thread {
204
+ try {
205
+ val context = reactApplicationContext
206
+ val contentResolver = context.contentResolver
207
+ val resultsArray = Arguments.createArray()
208
+
209
+ for (uri in uris) {
210
+ val mimeTypeStr = contentResolver.getType(uri) ?: ""
211
+ val isImage = mimeTypeStr.startsWith("image")
212
+ val mimeType = contentResolver.getType(uri) ?: if (isImage) "image/jpeg" else "video/mp4"
213
+
214
+ // Get file name
215
+ var fileName = "file_${System.currentTimeMillis()}"
216
+ val cursor = contentResolver.query(uri, null, null, null, null)
217
+ cursor?.use {
218
+ if (it.moveToFirst()) {
219
+ val nameIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
220
+ if (nameIndex >= 0) {
221
+ fileName = it.getString(nameIndex) ?: fileName
222
+ }
223
+ }
224
+ }
225
+
226
+ // Copy to temp file
227
+ val tempFile = File(context.cacheDir, "${System.currentTimeMillis()}_$fileName")
228
+ contentResolver.openInputStream(uri)?.use { inputStream ->
229
+ FileOutputStream(tempFile).use { outputStream ->
230
+ inputStream.copyTo(outputStream)
231
+ }
232
+ }
233
+
234
+ var finalFile = tempFile
235
+ var finalMimeType = mimeType
236
+ var finalFileSize = tempFile.length()
237
+
238
+ // Compress image if >500KB
239
+ if (isImage && finalFileSize > COMPRESS_THRESHOLD) {
240
+ val compressed = compressImage(tempFile)
241
+ if (compressed != null) {
242
+ tempFile.delete()
243
+ finalFile = compressed
244
+ finalMimeType = "image/jpeg"
245
+ finalFileSize = compressed.length()
246
+ }
247
+ }
248
+
249
+ val result = Arguments.createMap().apply {
250
+ putString("uri", Uri.fromFile(finalFile).toString())
251
+ putString("type", if (isImage) "image" else "video")
252
+ putString("fileName", finalFile.name)
253
+ putDouble("fileSize", finalFileSize.toDouble())
254
+ putString("mimeType", finalMimeType)
255
+ if (!isImage) {
256
+ val thumbnailUri = generateVideoThumbnail(finalFile)
257
+ if (thumbnailUri != null) {
258
+ putString("thumbnailUri", thumbnailUri)
259
+ }
260
+ }
261
+ }
262
+ resultsArray.pushMap(result)
263
+ }
264
+
265
+ currentPromise?.resolve(resultsArray)
266
+ } catch (e: Exception) {
267
+ currentPromise?.reject("PICK_ERROR", e.message, e)
268
+ }
269
+ }.start()
270
+ }
271
+
272
+ override fun onNewIntent(intent: Intent) {
273
+ // Not needed
274
+ }
275
+
276
+ // MARK: - Image Compression
277
+
278
+ private fun compressImage(file: File): File? {
279
+ return try {
280
+ val bitmap = BitmapFactory.decodeFile(file.absolutePath) ?: return null
281
+ val compressedFile = File(reactApplicationContext.cacheDir, "compressed_${System.currentTimeMillis()}.jpg")
282
+ FileOutputStream(compressedFile).use { outputStream ->
283
+ bitmap.compress(Bitmap.CompressFormat.JPEG, COMPRESS_QUALITY, outputStream)
284
+ }
285
+ bitmap.recycle()
286
+ compressedFile
287
+ } catch (e: Exception) {
288
+ null
289
+ }
290
+ }
291
+
292
+ private fun generateVideoThumbnail(videoFile: File): String? {
293
+ return try {
294
+ val retriever = MediaMetadataRetriever()
295
+ retriever.setDataSource(videoFile.absolutePath)
296
+ val bitmap = retriever.getFrameAtTime(0, MediaMetadataRetriever.OPTION_CLOSEST_SYNC)
297
+ retriever.release()
298
+ if (bitmap != null) {
299
+ val thumbFile = File(reactApplicationContext.cacheDir, "thumb_${System.currentTimeMillis()}.jpg")
300
+ FileOutputStream(thumbFile).use { outputStream ->
301
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 70, outputStream)
302
+ }
303
+ bitmap.recycle()
304
+ Uri.fromFile(thumbFile).toString()
305
+ } else null
306
+ } catch (e: Exception) {
307
+ null
308
+ }
309
+ }
310
+
311
+ @ReactMethod
312
+ fun deleteTempFile(uri: String, promise: Promise) {
313
+ Thread {
314
+ try {
315
+ val path = uri.removePrefix("file://")
316
+ val file = File(path)
317
+ if (file.exists()) {
318
+ file.delete()
319
+ }
320
+ promise.resolve(null)
321
+ } catch (e: Exception) {
322
+ promise.resolve(null)
323
+ }
324
+ }.start()
325
+ }
326
+
327
+ @ReactMethod
328
+ fun cleanupTempFiles(promise: Promise) {
329
+ Thread {
330
+ try {
331
+ val cacheDir = reactApplicationContext.cacheDir
332
+ val files = cacheDir.listFiles() ?: emptyArray()
333
+ for (file in files) {
334
+ val name = file.name.lowercase()
335
+ if (name.startsWith("thumb_") || name.startsWith("compressed_") ||
336
+ (name.contains("_") && (name.endsWith(".jpg") || name.endsWith(".jpeg") ||
337
+ name.endsWith(".png") || name.endsWith(".mp4") || name.endsWith(".mov")))) {
338
+ file.delete()
339
+ }
340
+ }
341
+ promise.resolve(null)
342
+ } catch (e: Exception) {
343
+ promise.resolve(null)
344
+ }
345
+ }.start()
346
+ }
347
+ }
@@ -7,7 +7,7 @@ import com.facebook.react.uimanager.ViewManager
7
7
 
8
8
  class ComnyxPackage : ReactPackage {
9
9
  override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
- return listOf(ComnyxModule(reactContext))
10
+ return listOf(ComnyxModule(reactContext), ComnyxMediaPickerModule(reactContext))
11
11
  }
12
12
 
13
13
  override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
@@ -0,0 +1,91 @@
1
+ package com.comnyx
2
+
3
+ import android.net.Uri
4
+ import android.os.Bundle
5
+ import android.view.Gravity
6
+ import android.view.Window
7
+ import android.view.WindowManager
8
+ import android.widget.FrameLayout
9
+ import android.widget.ImageButton
10
+ import android.widget.ProgressBar
11
+ import android.widget.VideoView
12
+ import android.app.Activity as AndroidActivity
13
+ import android.widget.MediaController
14
+
15
+ class VideoPlayerActivity : AndroidActivity() {
16
+ override fun onCreate(savedInstanceState: Bundle?) {
17
+ super.onCreate(savedInstanceState)
18
+
19
+ // Full screen
20
+ requestWindowFeature(Window.FEATURE_NO_TITLE)
21
+ window.setFlags(
22
+ WindowManager.LayoutParams.FLAG_FULLSCREEN,
23
+ WindowManager.LayoutParams.FLAG_FULLSCREEN
24
+ )
25
+
26
+ val container = FrameLayout(this)
27
+ container.setBackgroundColor(android.graphics.Color.BLACK)
28
+
29
+ val videoView = VideoView(this)
30
+ val videoParams = FrameLayout.LayoutParams(
31
+ FrameLayout.LayoutParams.MATCH_PARENT,
32
+ FrameLayout.LayoutParams.MATCH_PARENT,
33
+ Gravity.CENTER
34
+ )
35
+ container.addView(videoView, videoParams)
36
+
37
+ // Loading spinner
38
+ val progressBar = ProgressBar(this)
39
+ val progressParams = FrameLayout.LayoutParams(
40
+ FrameLayout.LayoutParams.WRAP_CONTENT,
41
+ FrameLayout.LayoutParams.WRAP_CONTENT,
42
+ Gravity.CENTER
43
+ )
44
+ container.addView(progressBar, progressParams)
45
+
46
+ // Close button (X) top-right
47
+ val closeButton = ImageButton(this)
48
+ closeButton.setBackgroundColor(android.graphics.Color.TRANSPARENT)
49
+ closeButton.setImageResource(android.R.drawable.ic_menu_close_clear_cancel)
50
+ closeButton.setColorFilter(android.graphics.Color.WHITE)
51
+ closeButton.setPadding(24, 24, 24, 24)
52
+ val closeParams = FrameLayout.LayoutParams(
53
+ FrameLayout.LayoutParams.WRAP_CONTENT,
54
+ FrameLayout.LayoutParams.WRAP_CONTENT,
55
+ Gravity.TOP or Gravity.END
56
+ )
57
+ closeParams.topMargin = 60
58
+ closeParams.rightMargin = 24
59
+ container.addView(closeButton, closeParams)
60
+
61
+ closeButton.setOnClickListener {
62
+ finish()
63
+ }
64
+
65
+ setContentView(container)
66
+
67
+ val videoUri = intent.getStringExtra("video_uri") ?: run {
68
+ finish()
69
+ return
70
+ }
71
+
72
+ // MediaController for play/pause/seek controls
73
+ val mediaController = MediaController(this)
74
+ mediaController.setAnchorView(videoView)
75
+ videoView.setMediaController(mediaController)
76
+
77
+ videoView.setVideoURI(Uri.parse(videoUri))
78
+ videoView.setOnPreparedListener { mp ->
79
+ progressBar.visibility = android.view.View.GONE
80
+ mp.isLooping = false
81
+ videoView.start()
82
+ }
83
+ videoView.setOnCompletionListener {
84
+ finish()
85
+ }
86
+ videoView.setOnErrorListener { _, _, _ ->
87
+ finish()
88
+ true
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,29 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface RCT_EXTERN_MODULE(ComnyxMediaPicker, NSObject)
4
+
5
+ RCT_EXTERN_METHOD(pickMedia:(RCTPromiseResolveBlock)resolve
6
+ reject:(RCTPromiseRejectBlock)reject)
7
+
8
+ RCT_EXTERN_METHOD(pickImage:(RCTPromiseResolveBlock)resolve
9
+ reject:(RCTPromiseRejectBlock)reject)
10
+
11
+ RCT_EXTERN_METHOD(pickVideo:(RCTPromiseResolveBlock)resolve
12
+ reject:(RCTPromiseRejectBlock)reject)
13
+
14
+ RCT_EXTERN_METHOD(generateThumbnail:(NSString *)videoUrl
15
+ resolve:(RCTPromiseResolveBlock)resolve
16
+ reject:(RCTPromiseRejectBlock)reject)
17
+
18
+ RCT_EXTERN_METHOD(openVideo:(NSString *)uri
19
+ resolve:(RCTPromiseResolveBlock)resolve
20
+ reject:(RCTPromiseRejectBlock)reject)
21
+
22
+ RCT_EXTERN_METHOD(deleteTempFile:(NSString *)uri
23
+ resolve:(RCTPromiseResolveBlock)resolve
24
+ reject:(RCTPromiseRejectBlock)reject)
25
+
26
+ RCT_EXTERN_METHOD(cleanupTempFiles:(RCTPromiseResolveBlock)resolve
27
+ reject:(RCTPromiseRejectBlock)reject)
28
+
29
+ @end