expo-paste-input 0.1.9 → 0.1.10

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 (52) hide show
  1. package/android/build/.transforms/04cfbef6d6438014a0f56f30321e9943/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/expo/modules/pasteinput/ExpoPasteInputView$createActionModeCallback$1.dex +0 -0
  2. package/android/build/.transforms/04cfbef6d6438014a0f56f30321e9943/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/expo/modules/pasteinput/ExpoPasteInputView.dex +0 -0
  3. package/android/build/.transforms/61052c0b101cc72f1be04cd5637e14e3/transformed/classes/classes_dex/classes.dex +0 -0
  4. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  5. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/expo/modules/pasteinput/ExpoPasteInputView$createActionModeCallback$1.class +0 -0
  6. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/expo/modules/pasteinput/ExpoPasteInputView.class +0 -0
  7. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  8. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  9. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  10. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  11. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.values.at +0 -0
  12. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  13. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
  14. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  15. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  16. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  17. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len +0 -0
  18. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
  19. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  20. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  21. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  22. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  23. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  24. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  25. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  26. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  27. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.values.at +0 -0
  28. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/counters.tab +1 -1
  29. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
  30. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.values.at +0 -0
  31. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
  32. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream +0 -0
  33. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.keystream.len +0 -0
  34. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.len +0 -0
  35. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
  36. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab_i +0 -0
  37. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  38. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  39. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  40. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.len +0 -0
  41. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  42. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  43. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  44. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  45. package/android/build/tmp/kotlin-classes/debug/expo/modules/pasteinput/ExpoPasteInputView$createActionModeCallback$1.class +0 -0
  46. package/android/build/tmp/kotlin-classes/debug/expo/modules/pasteinput/ExpoPasteInputView.class +0 -0
  47. package/android/src/main/java/expo/modules/pasteinput/ExpoPasteInputView.kt +46 -30
  48. package/ios/ExpoPasteInputView.swift +6 -39
  49. package/package.json +1 -1
  50. package/android/build/.transforms/04cfbef6d6438014a0f56f30321e9943/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/expo/modules/pasteinput/ExpoPasteInputView$enhanceOnTextContextMenuItem$callback$1.dex +0 -0
  51. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/expo/modules/pasteinput/ExpoPasteInputView$enhanceOnTextContextMenuItem$callback$1.class +0 -0
  52. package/android/build/tmp/kotlin-classes/debug/expo/modules/pasteinput/ExpoPasteInputView$enhanceOnTextContextMenuItem$callback$1.class +0 -0
@@ -184,52 +184,56 @@ class ExpoPasteInputView(context: Context, appContext: AppContext) : ExpoView(co
184
184
  originalInsertionActionModeCallback = editText.customInsertionActionModeCallback
185
185
  }
186
186
 
187
- // Set up a custom ActionMode.Callback to intercept paste from context menu
188
- // This is the most reliable way to intercept paste before the toast appears
189
- val callback = object : ActionMode.Callback {
187
+ // Set up custom callbacks to intercept paste from both selection and insertion menus.
188
+ customSelectionActionModeCallback = createActionModeCallback(
189
+ editText = editText,
190
+ originalCallback = originalSelectionActionModeCallback
191
+ )
192
+ editText.customSelectionActionModeCallback = customSelectionActionModeCallback
193
+
194
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
195
+ customInsertionActionModeCallback = createActionModeCallback(
196
+ editText = editText,
197
+ originalCallback = originalInsertionActionModeCallback
198
+ )
199
+ editText.customInsertionActionModeCallback = customInsertionActionModeCallback
200
+ }
201
+
202
+ } catch (e: Exception) {
203
+ // If ActionMode.Callback approach fails, we'll rely on OnReceiveContentListener
204
+ // which should still work but may show the toast briefly
205
+ }
206
+ }
207
+
208
+ private fun createActionModeCallback(
209
+ editText: EditText,
210
+ originalCallback: ActionMode.Callback?
211
+ ): ActionMode.Callback {
212
+ return object : ActionMode.Callback {
190
213
  override fun onCreateActionMode(mode: ActionMode?, menu: android.view.Menu?): Boolean {
191
214
  // Delegate to original callback if it exists
192
- return getOriginalActionModeCallback()?.onCreateActionMode(mode, menu) ?: true
215
+ return originalCallback?.onCreateActionMode(mode, menu) ?: true
193
216
  }
194
217
 
195
218
  override fun onPrepareActionMode(mode: ActionMode?, menu: android.view.Menu?): Boolean {
196
219
  // Delegate to original callback if it exists
197
- return getOriginalActionModeCallback()?.onPrepareActionMode(mode, menu) ?: false
220
+ return originalCallback?.onPrepareActionMode(mode, menu) ?: false
198
221
  }
199
222
 
200
223
  override fun onActionItemClicked(mode: ActionMode?, item: android.view.MenuItem?): Boolean {
201
224
  if (item?.itemId == android.R.id.paste) {
202
- return handlePasteFromActionMode(editText, mode, item, getOriginalActionModeCallback())
225
+ return handlePasteFromActionMode(editText, mode, item, originalCallback)
203
226
  }
204
227
 
205
228
  // For other actions, delegate to original callback or return false
206
- return getOriginalActionModeCallback()?.onActionItemClicked(mode, item) ?: false
229
+ return originalCallback?.onActionItemClicked(mode, item) ?: false
207
230
  }
208
231
 
209
232
  override fun onDestroyActionMode(mode: ActionMode?) {
210
233
  // Delegate to original callback if it exists
211
- getOriginalActionModeCallback()?.onDestroyActionMode(mode)
234
+ originalCallback?.onDestroyActionMode(mode)
212
235
  }
213
236
  }
214
-
215
- customSelectionActionModeCallback = callback
216
- editText.customSelectionActionModeCallback = customSelectionActionModeCallback
217
-
218
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
219
- customInsertionActionModeCallback = callback
220
- editText.customInsertionActionModeCallback = customInsertionActionModeCallback
221
- }
222
-
223
- } catch (e: Exception) {
224
- // If ActionMode.Callback approach fails, we'll rely on OnReceiveContentListener
225
- // which should still work but may show the toast briefly
226
- }
227
- }
228
-
229
- private fun getOriginalActionModeCallback(): ActionMode.Callback? {
230
- // Cursor/insertion menu and selection menu can use different callbacks.
231
- // Prefer insertion callback first for insertion-mode paste.
232
- return originalInsertionActionModeCallback ?: originalSelectionActionModeCallback
233
237
  }
234
238
 
235
239
  private fun handlePasteFromActionMode(
@@ -298,6 +302,8 @@ class ExpoPasteInputView(context: Context, appContext: AppContext) : ExpoView(co
298
302
  } else {
299
303
  imageUris.add(uri)
300
304
  }
305
+ } else if (isLikelyGifUri(uri)) {
306
+ gifUris.add(uri)
301
307
  } else if (isLikelyImageUri(uri)) {
302
308
  imageUris.add(uri)
303
309
  }
@@ -323,12 +329,16 @@ class ExpoPasteInputView(context: Context, appContext: AppContext) : ExpoView(co
323
329
  return fileName.endsWith(".png") ||
324
330
  fileName.endsWith(".jpg") ||
325
331
  fileName.endsWith(".jpeg") ||
326
- fileName.endsWith(".gif") ||
327
332
  fileName.endsWith(".webp") ||
328
333
  fileName.endsWith(".heic") ||
329
334
  fileName.endsWith(".heif")
330
335
  }
331
336
 
337
+ private fun isLikelyGifUri(uri: Uri): Boolean {
338
+ val fileName = uri.lastPathSegment?.lowercase() ?: return false
339
+ return fileName.endsWith(".gif")
340
+ }
341
+
332
342
  private fun markSuppressOnReceiveContent() {
333
343
  suppressOnReceiveContentUntilMs = SystemClock.elapsedRealtime() + 500L
334
344
  }
@@ -404,12 +414,18 @@ class ExpoPasteInputView(context: Context, appContext: AppContext) : ExpoView(co
404
414
 
405
415
  // Save to cache directory
406
416
  val cacheDir = context.cacheDir
417
+ val usePng = bitmap.hasAlpha()
418
+ val fileExtension = if (usePng) "png" else "jpg"
407
419
  // Use UUID-like approach for better uniqueness: timestamp + counter
408
- val fileName = "${System.currentTimeMillis()}_${filePaths.size}.jpg"
420
+ val fileName = "${System.currentTimeMillis()}_${filePaths.size}.$fileExtension"
409
421
  val file = File(cacheDir, fileName)
410
422
 
411
423
  FileOutputStream(file).use { outputStream ->
412
- bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream)
424
+ if (usePng) {
425
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
426
+ } else {
427
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream)
428
+ }
413
429
  outputStream.flush()
414
430
  }
415
431
 
@@ -151,10 +151,12 @@ class ExpoPasteInputView: ExpoView {
151
151
  let wrapper = weakWrapper.value {
152
152
  // Only process if this is our wrapped text input
153
153
  if action == #selector(UIResponderStandardEditActions.paste(_:)) {
154
- let pasteboard = UIPasteboard.general
155
- if pasteboard.hasStrings || wrapper.hasSupportedImageContent(in: pasteboard) {
156
- return true
157
- }
154
+ // IMPORTANT:
155
+ // Do not read UIPasteboard content here. iOS may show the
156
+ // "App would like to paste" privacy prompt on menu-open checks.
157
+ // We allow paste action visibility and read the pasteboard only
158
+ // when the user explicitly taps Paste in `paste(_:)`.
159
+ return true
158
160
  }
159
161
  }
160
162
 
@@ -607,41 +609,6 @@ class ExpoPasteInputView: ExpoView {
607
609
  stopMonitoring()
608
610
  }
609
611
 
610
- private func hasSupportedImageContent(in pasteboard: UIPasteboard) -> Bool {
611
- if pasteboard.hasImages {
612
- return true
613
- }
614
-
615
- if hasPasteboardData(forAnyTypeIn: gifTypes.union(webpTypes), pasteboard: pasteboard) {
616
- return true
617
- }
618
-
619
- for item in pasteboard.items {
620
- for (key, value) in item {
621
- let lowercasedKey = key.lowercased()
622
- let isImageLikeType = lowercasedKey.contains("image") ||
623
- lowercasedKey.contains("png") ||
624
- lowercasedKey.contains("jpeg") ||
625
- lowercasedKey.contains("jpg") ||
626
- lowercasedKey.contains("tiff") ||
627
- lowercasedKey.contains("heic") ||
628
- lowercasedKey.contains("heif") ||
629
- lowercasedKey.contains("gif") ||
630
- lowercasedKey.contains("webp")
631
-
632
- if isImageLikeType && (value is Data || value is UIImage) {
633
- return true
634
- }
635
-
636
- if value is UIImage {
637
- return true
638
- }
639
- }
640
- }
641
-
642
- return false
643
- }
644
-
645
612
  private func hasPasteboardData(forAnyTypeIn types: Set<String>, pasteboard: UIPasteboard) -> Bool {
646
613
  for type in types {
647
614
  if let data = pasteboard.data(forPasteboardType: type), !data.isEmpty {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-paste-input",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "A wrapper around React Native TextInput to paste images and GIFs from the clipboard (iOS, Android, Web)",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",