@tbvjaos510/react-native-paste-input 0.9.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/.circleci/config.yml +130 -0
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/android/.project +17 -0
- package/android/.settings/org.eclipse.buildship.core.prefs +13 -0
- package/android/build.gradle +94 -0
- package/android/generated/java/com/facebook/react/viewmanagers/PasteTextInputManagerDelegate.java +236 -0
- package/android/generated/java/com/facebook/react/viewmanagers/PasteTextInputManagerInterface.java +84 -0
- package/android/generated/jni/CMakeLists.txt +36 -0
- package/android/generated/jni/PasteTextInputSpecs-generated.cpp +27 -0
- package/android/generated/jni/PasteTextInputSpecs.h +28 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ComponentDescriptors.cpp +22 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ComponentDescriptors.h +166 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/EventEmitters.cpp +183 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/EventEmitters.h +148 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/PasteTextInputSpecsJSI-generated.cpp +17 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/PasteTextInputSpecsJSI.h +19 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/Props.cpp +640 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/Props.h +144 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ShadowNodes.cpp +247 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/ShadowNodes.h +95 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/States.cpp +14 -0
- package/android/generated/jni/react/renderer/components/PasteTextInputSpecs/States.h +19 -0
- package/android/gradle.properties +6 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/IPasteInputListener.kt +8 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputActionCallback.kt +72 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputEditText.kt +60 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputFileFromUrl.kt +50 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteInputListener.kt +84 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputManager.kt +72 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputPackage.kt +14 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/PasteTextInputPasteEvent.java +41 -0
- package/android/src/main/java/com/mattermost/pasteinputtext/RealPathUtil.kt +174 -0
- package/ios/NSData+MimeType.h +20 -0
- package/ios/NSData+MimeType.m +59 -0
- package/ios/PasteInput-Bridging-Header.h +1 -0
- package/ios/PasteInput.xcodeproj/project.pbxproj +319 -0
- package/ios/PasteInputTextView.h +20 -0
- package/ios/PasteInputTextView.m +68 -0
- package/ios/PasteInputView.h +18 -0
- package/ios/PasteInputView.m +96 -0
- package/ios/PasteTextInput.h +13 -0
- package/ios/PasteTextInput.mm +726 -0
- package/ios/PasteTextInputManager.mm +171 -0
- package/ios/Swime/MimeType.h +70 -0
- package/ios/Swime/MimeType.m +701 -0
- package/ios/Swime/Swime.h +14 -0
- package/ios/Swime/Swime.m +28 -0
- package/ios/Swime/SwimeProxy.h +18 -0
- package/ios/Swime/SwimeProxy.m +66 -0
- package/ios/Swime/SwimeUtils.h +12 -0
- package/ios/Swime/SwimeUtils.m +23 -0
- package/ios/UIImage+ImageEffects.h +112 -0
- package/ios/UIImage+ImageEffects.m +310 -0
- package/ios/UIImage+vImageScaling.h +16 -0
- package/ios/UIImage+vImageScaling.m +48 -0
- package/ios/UIPasteboard+GetImageInfo.h +19 -0
- package/ios/UIPasteboard+GetImageInfo.m +98 -0
- package/ios/generated/PasteTextInputSpecs/ComponentDescriptors.cpp +22 -0
- package/ios/generated/PasteTextInputSpecs/ComponentDescriptors.h +42 -0
- package/ios/generated/PasteTextInputSpecs/EventEmitters.cpp +41 -0
- package/ios/generated/PasteTextInputSpecs/EventEmitters.h +37 -0
- package/ios/generated/PasteTextInputSpecs/PasteTextInputSpecs-generated.mm +16 -0
- package/ios/generated/PasteTextInputSpecs/PasteTextInputSpecs.h +38 -0
- package/ios/generated/PasteTextInputSpecs/Props.cpp +142 -0
- package/ios/generated/PasteTextInputSpecs/Props.h +81 -0
- package/ios/generated/PasteTextInputSpecs/RCTComponentViewHelpers.h +106 -0
- package/ios/generated/PasteTextInputSpecs/ShadowNodes.cpp +127 -0
- package/ios/generated/PasteTextInputSpecs/ShadowNodes.h +85 -0
- package/ios/generated/PasteTextInputSpecs/States.cpp +16 -0
- package/ios/generated/PasteTextInputSpecs/States.h +53 -0
- package/ios/generated/PasteTextInputSpecsJSI-generated.cpp +17 -0
- package/ios/generated/PasteTextInputSpecsJSI.h +19 -0
- package/lib/commonjs/PasteTextInput.js +446 -0
- package/lib/commonjs/PasteTextInput.js.map +1 -0
- package/lib/commonjs/PasteTextInputNativeComponent.ts +277 -0
- package/lib/commonjs/index.js +25 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/module.d.js +2 -0
- package/lib/commonjs/module.d.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/types.js +6 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/PasteTextInput.js +440 -0
- package/lib/module/PasteTextInput.js.map +1 -0
- package/lib/module/PasteTextInputNativeComponent.ts +277 -0
- package/lib/module/index.js +9 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/module.d.js +2 -0
- package/lib/module/module.d.js.map +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/src/PasteTextInput.d.ts +5 -0
- package/lib/typescript/src/PasteTextInput.d.ts.map +1 -0
- package/lib/typescript/src/PasteTextInputNativeComponent.d.ts +168 -0
- package/lib/typescript/src/PasteTextInputNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +56 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +140 -0
- package/react-native-paste-input.podspec +20 -0
- package/react-native.config.js +13 -0
- package/src/PasteTextInput.tsx +615 -0
- package/src/PasteTextInputNativeComponent.ts +277 -0
- package/src/index.ts +13 -0
- package/src/module.d.ts +4 -0
- package/src/types.ts +71 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
package com.mattermost.pasteinputtext
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableArray
|
|
5
|
+
import com.facebook.react.bridge.WritableMap
|
|
6
|
+
import com.facebook.react.uimanager.events.EventDispatcher
|
|
7
|
+
import com.facebook.react.views.textinput.ReactEditText
|
|
8
|
+
import java.io.IOException
|
|
9
|
+
import java.net.URL
|
|
10
|
+
|
|
11
|
+
class PasteInputFileFromUrl(target: ReactEditText, uri: String, surfaceId: Int, eventDispatcher: EventDispatcher?): Runnable {
|
|
12
|
+
private val mTarget = target
|
|
13
|
+
private val mUri = uri
|
|
14
|
+
private val mEventDispatcher = eventDispatcher
|
|
15
|
+
private val mSurfaceId = surfaceId
|
|
16
|
+
|
|
17
|
+
override fun run() {
|
|
18
|
+
var files: WritableArray? = null
|
|
19
|
+
var error: WritableMap? = null
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
val url = URL(mUri)
|
|
23
|
+
val conn = url.openConnection()
|
|
24
|
+
val mimeType = conn.getHeaderField("Content-Type")
|
|
25
|
+
val fileSize = java.lang.Long.parseLong(conn.getHeaderField("Content-Length"))
|
|
26
|
+
val contentDisposition = conn.getHeaderField("Content-Disposition")
|
|
27
|
+
val startIndex = contentDisposition.indexOf("filename=\"") + 10
|
|
28
|
+
val endIndex = contentDisposition.length - 1
|
|
29
|
+
val fileName = contentDisposition.substring(startIndex, endIndex)
|
|
30
|
+
val file = Arguments.createMap()
|
|
31
|
+
|
|
32
|
+
file.putString("type", mimeType)
|
|
33
|
+
file.putDouble("fileSize", fileSize.toDouble())
|
|
34
|
+
file.putString("fileName", fileName)
|
|
35
|
+
file.putString("uri", mUri)
|
|
36
|
+
|
|
37
|
+
files = Arguments.createArray()
|
|
38
|
+
files.pushMap(file)
|
|
39
|
+
} catch (e: IOException) {
|
|
40
|
+
error = Arguments.createMap()
|
|
41
|
+
error.putString("message", e.localizedMessage)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
val event = Arguments.createMap()
|
|
45
|
+
event.putArray("data", files)
|
|
46
|
+
event.putMap("error", error)
|
|
47
|
+
|
|
48
|
+
mEventDispatcher?.dispatchEvent(PasteTextInputPasteEvent(mSurfaceId, mTarget.id, event))
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package com.mattermost.pasteinputtext
|
|
2
|
+
|
|
3
|
+
import android.content.ClipboardManager
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import android.util.Patterns
|
|
7
|
+
import android.webkit.MimeTypeMap
|
|
8
|
+
import android.webkit.URLUtil
|
|
9
|
+
import com.facebook.react.bridge.Arguments
|
|
10
|
+
import com.facebook.react.bridge.ReactContext
|
|
11
|
+
import com.facebook.react.bridge.WritableArray
|
|
12
|
+
import com.facebook.react.bridge.WritableMap
|
|
13
|
+
import com.facebook.react.uimanager.events.EventDispatcher
|
|
14
|
+
import java.io.FileNotFoundException
|
|
15
|
+
|
|
16
|
+
class PasteInputListener(editText: PasteInputEditText, surfaceId: Int) : IPasteInputListener {
|
|
17
|
+
private val mEditText = editText
|
|
18
|
+
private val mSurfaceId = surfaceId
|
|
19
|
+
|
|
20
|
+
override fun onPaste(itemUri: Uri, eventDispatcher: EventDispatcher?) {
|
|
21
|
+
val reactContext = mEditText.context as ReactContext
|
|
22
|
+
reactContext.contentResolver.getType(itemUri) ?: return
|
|
23
|
+
|
|
24
|
+
var uriString: String = itemUri.toString()
|
|
25
|
+
val mimeType: String
|
|
26
|
+
val fileSize: Long
|
|
27
|
+
var files: WritableArray? = null
|
|
28
|
+
var error: WritableMap? = null
|
|
29
|
+
|
|
30
|
+
// Special handle for Google docs
|
|
31
|
+
if (uriString == "content://com.google.android.apps.docs.editors.kix.editors.clipboard") {
|
|
32
|
+
val clipboardManager = reactContext.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
33
|
+
val clipData = clipboardManager.primaryClip ?: return
|
|
34
|
+
val item = clipData.getItemAt(0) ?: return
|
|
35
|
+
val htmlText = item.htmlText
|
|
36
|
+
|
|
37
|
+
// Find uri from html
|
|
38
|
+
val matcher = Patterns.WEB_URL.matcher(htmlText)
|
|
39
|
+
if (matcher.find()) {
|
|
40
|
+
uriString = htmlText.substring(matcher.start(1), matcher.end())
|
|
41
|
+
}
|
|
42
|
+
} else if (uriString.startsWith("http")) {
|
|
43
|
+
val pastImageFromUrlThread = Thread(PasteInputFileFromUrl(
|
|
44
|
+
mEditText,
|
|
45
|
+
uriString,
|
|
46
|
+
mSurfaceId,
|
|
47
|
+
eventDispatcher
|
|
48
|
+
))
|
|
49
|
+
pastImageFromUrlThread.start()
|
|
50
|
+
return
|
|
51
|
+
} else {
|
|
52
|
+
uriString = RealPathUtil.getRealPathFromURI(reactContext, itemUri) ?: return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
val extension: String = MimeTypeMap.getFileExtensionFromUrl(uriString) ?: return
|
|
56
|
+
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) ?: return
|
|
57
|
+
val fileName: String = URLUtil.guessFileName(uriString, null, mimeType)
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
val contentResolver = reactContext.contentResolver
|
|
61
|
+
val assetFileDescriptor = contentResolver.openAssetFileDescriptor(itemUri, "r") ?: return
|
|
62
|
+
val file = Arguments.createMap()
|
|
63
|
+
|
|
64
|
+
files = Arguments.createArray()
|
|
65
|
+
fileSize = assetFileDescriptor.length
|
|
66
|
+
file.putString("type", mimeType)
|
|
67
|
+
file.putDouble("fileSize", fileSize.toDouble())
|
|
68
|
+
file.putString("fileName", fileName)
|
|
69
|
+
file.putString("uri", "file://$uriString")
|
|
70
|
+
|
|
71
|
+
files.pushMap(file)
|
|
72
|
+
assetFileDescriptor.close()
|
|
73
|
+
} catch (e: FileNotFoundException) {
|
|
74
|
+
error = Arguments.createMap()
|
|
75
|
+
error.putString("message", e.localizedMessage)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
val event = Arguments.createMap()
|
|
79
|
+
event.putArray("data", files)
|
|
80
|
+
event.putMap("error", error)
|
|
81
|
+
|
|
82
|
+
eventDispatcher?.dispatchEvent(PasteTextInputPasteEvent(mSurfaceId, mEditText.id, event))
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
package com.mattermost.pasteinputtext
|
|
2
|
+
|
|
3
|
+
import android.text.InputType
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import com.facebook.react.bridge.ReactContext
|
|
6
|
+
import com.facebook.react.common.MapBuilder
|
|
7
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
8
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
9
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
10
|
+
import com.facebook.react.uimanager.annotations.ReactProp
|
|
11
|
+
import com.facebook.react.uimanager.events.EventDispatcher
|
|
12
|
+
import com.facebook.react.views.textinput.ReactEditText
|
|
13
|
+
import com.facebook.react.views.textinput.ReactTextInputManager
|
|
14
|
+
|
|
15
|
+
@ReactModule(name = "PasteTextInput")
|
|
16
|
+
class PasteTextInputManager(context: ReactApplicationContext) : ReactTextInputManager() {
|
|
17
|
+
private var disableCopyPaste: Boolean = false
|
|
18
|
+
private val mContext = context
|
|
19
|
+
|
|
20
|
+
override fun getName(): String = NAME
|
|
21
|
+
|
|
22
|
+
@ReactProp(name = "disableCopyPaste", defaultBoolean = false)
|
|
23
|
+
fun setDisableCopyPaste(editText: PasteInputEditText, disabled: Boolean) {
|
|
24
|
+
disableCopyPaste = disabled
|
|
25
|
+
val eventDispatcher = getEventDispatcher(mContext, editText)
|
|
26
|
+
editText.customInsertionActionModeCallback = PasteInputActionCallback(editText, disabled, eventDispatcher)
|
|
27
|
+
editText.customSelectionActionModeCallback = PasteInputActionCallback(editText, disabled, eventDispatcher)
|
|
28
|
+
editText.setDisableCopyPaste(disabled)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private fun getEventDispatcher(reactContext: ReactContext, editText: ReactEditText): EventDispatcher? {
|
|
32
|
+
return UIManagerHelper.getEventDispatcherForReactTag(reactContext, editText.id)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
override fun createViewInstance(context: ThemedReactContext): PasteInputEditText {
|
|
36
|
+
val editText = PasteInputEditText(context)
|
|
37
|
+
val inputType = editText.inputType
|
|
38
|
+
|
|
39
|
+
editText.inputType = inputType and (InputType.TYPE_TEXT_FLAG_MULTI_LINE.inv())
|
|
40
|
+
editText.returnKeyType = "done"
|
|
41
|
+
val eventDispatcher = getEventDispatcher(mContext, editText)
|
|
42
|
+
editText.customInsertionActionModeCallback = PasteInputActionCallback(editText, disableCopyPaste, eventDispatcher)
|
|
43
|
+
editText.customSelectionActionModeCallback = PasteInputActionCallback(editText, disableCopyPaste, eventDispatcher)
|
|
44
|
+
|
|
45
|
+
return editText
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
override fun addEventEmitters(reactContext: ThemedReactContext, editText: ReactEditText) {
|
|
49
|
+
super.addEventEmitters(reactContext, editText)
|
|
50
|
+
|
|
51
|
+
val pasteInputEditText = editText as PasteInputEditText
|
|
52
|
+
val eventDispatcher = getEventDispatcher(reactContext, editText)
|
|
53
|
+
pasteInputEditText.setOnPasteListener(PasteInputListener(pasteInputEditText, reactContext.surfaceId), eventDispatcher)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
override fun getExportedCustomBubblingEventTypeConstants(): MutableMap<String, Any> {
|
|
57
|
+
val map = super.getExportedCustomBubblingEventTypeConstants()?.toMutableMap()
|
|
58
|
+
?: return mutableMapOf()
|
|
59
|
+
|
|
60
|
+
map["onPaste"] = MapBuilder.of(
|
|
61
|
+
"phasedRegistrationNames",
|
|
62
|
+
MapBuilder.of("bubbled", "onPaste")
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return map
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
companion object {
|
|
69
|
+
const val NAME = "PasteTextInput"
|
|
70
|
+
const val CACHE_DIR_NAME = "mmPasteInput"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
package com.mattermost.pasteinputtext
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.uimanager.ViewManager
|
|
7
|
+
|
|
8
|
+
class PasteTextInputPackage : ReactPackage {
|
|
9
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
|
|
10
|
+
listOf(PasteTextInputManager(reactContext))
|
|
11
|
+
|
|
12
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
|
|
13
|
+
emptyList()
|
|
14
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
package com.mattermost.pasteinputtext;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
|
|
6
|
+
import com.facebook.react.bridge.Arguments;
|
|
7
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
8
|
+
import com.facebook.react.bridge.WritableMap;
|
|
9
|
+
import com.facebook.react.uimanager.events.Event;
|
|
10
|
+
|
|
11
|
+
public class PasteTextInputPasteEvent extends Event<PasteTextInputPasteEvent> {
|
|
12
|
+
private static final String EVENT_NAME = "onPaste";
|
|
13
|
+
private final ReadableMap mEventData;
|
|
14
|
+
|
|
15
|
+
@Deprecated
|
|
16
|
+
public PasteTextInputPasteEvent(int viewId) {
|
|
17
|
+
this(-1, viewId, null);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public PasteTextInputPasteEvent(int surfaceId, int viewId, ReadableMap eventData) {
|
|
21
|
+
super(surfaceId, viewId);
|
|
22
|
+
mEventData = eventData;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public boolean canCoalesce() {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@NonNull
|
|
30
|
+
public String getEventName() {
|
|
31
|
+
return EVENT_NAME;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Nullable
|
|
35
|
+
protected WritableMap getEventData() {
|
|
36
|
+
WritableMap eventData = Arguments.createMap();
|
|
37
|
+
eventData.putInt("target", this.getViewTag());
|
|
38
|
+
eventData.merge(mEventData);
|
|
39
|
+
return eventData;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
package com.mattermost.pasteinputtext
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.database.Cursor
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import android.provider.DocumentsContract
|
|
7
|
+
import android.provider.MediaStore
|
|
8
|
+
import android.provider.OpenableColumns
|
|
9
|
+
import android.util.Log
|
|
10
|
+
import android.text.TextUtils
|
|
11
|
+
import java.io.*
|
|
12
|
+
|
|
13
|
+
object RealPathUtil {
|
|
14
|
+
init {
|
|
15
|
+
deleteTempFiles(File(PasteTextInputManager.CACHE_DIR_NAME))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
fun getRealPathFromURI(context: Context, uri: Uri): String? {
|
|
19
|
+
// DocumentProvider
|
|
20
|
+
if (DocumentsContract.isDocumentUri(context, uri)) {
|
|
21
|
+
// ExternalStorageProvider
|
|
22
|
+
if (isExternalStorageDocument(uri)) {
|
|
23
|
+
val docId = DocumentsContract.getDocumentId(uri)
|
|
24
|
+
val split = docId.split((":").toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
|
25
|
+
val type = split[0]
|
|
26
|
+
if ("primary".equals(type, ignoreCase = true)) {
|
|
27
|
+
return context.getExternalFilesDir(split[1])?.absolutePath
|
|
28
|
+
}
|
|
29
|
+
} else if (isDownloadsDocument(uri)) {
|
|
30
|
+
// DownloadsProvider
|
|
31
|
+
val id = DocumentsContract.getDocumentId(uri)
|
|
32
|
+
if (!TextUtils.isEmpty(id)) {
|
|
33
|
+
if (id.startsWith("raw:")) {
|
|
34
|
+
return id.replaceFirst(("raw:").toRegex(), "")
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return getPathFromSavingTempFile(context, uri)
|
|
38
|
+
} catch (e: NumberFormatException) {
|
|
39
|
+
Log.e("ReactNative", "DownloadsProvider unexpected uri $uri")
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} else if (isMediaDocument(uri)) {
|
|
44
|
+
// MediaProvider
|
|
45
|
+
val docId = DocumentsContract.getDocumentId(uri)
|
|
46
|
+
val split = docId.split((":").toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
|
47
|
+
val type = split[0]
|
|
48
|
+
var contentUri: Uri? = null
|
|
49
|
+
when (type) {
|
|
50
|
+
"image" -> {
|
|
51
|
+
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
|
52
|
+
}
|
|
53
|
+
"video" -> {
|
|
54
|
+
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
|
55
|
+
}
|
|
56
|
+
"audio" -> {
|
|
57
|
+
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
val selectionArgs = arrayOf(split[1])
|
|
61
|
+
return contentUri?.let { getDataColumn(context, it, selectionArgs) }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if ("content".equals(uri.scheme, ignoreCase = true)) {
|
|
65
|
+
// MediaStore (and general)
|
|
66
|
+
if (isGooglePhotosUri(uri)) {
|
|
67
|
+
return uri.lastPathSegment
|
|
68
|
+
}
|
|
69
|
+
// Try save to tmp file, and return tmp file path
|
|
70
|
+
return getPathFromSavingTempFile(context, uri)
|
|
71
|
+
} else if ("file".equals(uri.scheme, ignoreCase = true)) {
|
|
72
|
+
return uri.path
|
|
73
|
+
}
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private fun getPathFromSavingTempFile(context: Context, uri: Uri): String? {
|
|
78
|
+
val tmpFile: File
|
|
79
|
+
var fileName: String? = null
|
|
80
|
+
// Try and get the filename from the Uri
|
|
81
|
+
try {
|
|
82
|
+
val returnCursor = context.contentResolver.query(uri, null, null, null, null)
|
|
83
|
+
val nameIndex = returnCursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
|
84
|
+
returnCursor?.moveToFirst()
|
|
85
|
+
fileName = sanitizeFilename(nameIndex?.let { returnCursor.getString(it) })
|
|
86
|
+
returnCursor?.close()
|
|
87
|
+
} catch (e: Exception) {
|
|
88
|
+
// just continue to get the filename with the last segment of the path
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
if (fileName == null) {
|
|
92
|
+
fileName = sanitizeFilename(uri.lastPathSegment.toString().trim())
|
|
93
|
+
}
|
|
94
|
+
val cacheDir = File(context.cacheDir, PasteTextInputManager.CACHE_DIR_NAME)
|
|
95
|
+
if (!cacheDir.exists()) {
|
|
96
|
+
cacheDir.mkdirs()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
tmpFile = fileName?.let { File(cacheDir, it) }!!
|
|
100
|
+
tmpFile.createNewFile()
|
|
101
|
+
val pfd = context.contentResolver.openFileDescriptor(uri, "r")
|
|
102
|
+
val src = FileInputStream(pfd?.fileDescriptor).channel
|
|
103
|
+
val dst = FileOutputStream(tmpFile).channel
|
|
104
|
+
dst.transferFrom(src, 0, src.size())
|
|
105
|
+
src.close()
|
|
106
|
+
dst.close()
|
|
107
|
+
pfd?.close()
|
|
108
|
+
} catch (ex: IOException) {
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
return tmpFile.absolutePath
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private fun sanitizeFilename(filename: String?): String? {
|
|
115
|
+
if (filename == null) {
|
|
116
|
+
return null
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
val f = File(filename)
|
|
120
|
+
return f.name
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private fun getDataColumn(context: Context, uri: Uri,
|
|
124
|
+
selectionArgs: Array<String>): String? {
|
|
125
|
+
var cursor: Cursor? = null
|
|
126
|
+
val column = "_data"
|
|
127
|
+
val selection = "_id=?"
|
|
128
|
+
val projection = arrayOf(column)
|
|
129
|
+
try {
|
|
130
|
+
cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
|
|
131
|
+
if (cursor != null && cursor.moveToFirst()) {
|
|
132
|
+
val index = cursor.getColumnIndexOrThrow(column)
|
|
133
|
+
return cursor.getString(index)
|
|
134
|
+
}
|
|
135
|
+
} finally {
|
|
136
|
+
cursor?.close()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return null
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private fun isExternalStorageDocument(uri: Uri): Boolean {
|
|
143
|
+
return "com.android.externalstorage.documents" == uri.authority
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private fun isDownloadsDocument(uri: Uri): Boolean {
|
|
147
|
+
return "com.android.providers.downloads.documents" == uri.authority
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private fun isMediaDocument(uri: Uri): Boolean {
|
|
151
|
+
return "com.android.providers.media.documents" == uri.authority
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private fun isGooglePhotosUri(uri: Uri): Boolean {
|
|
155
|
+
return "com.google.android.apps.photos.content" == uri.authority
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private fun deleteTempFiles(dir: File) {
|
|
159
|
+
try {
|
|
160
|
+
if (dir.isDirectory) {
|
|
161
|
+
deleteRecursive(dir)
|
|
162
|
+
}
|
|
163
|
+
} catch (e: Exception) {
|
|
164
|
+
// do nothing
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private fun deleteRecursive(fileOrDirectory: File) {
|
|
169
|
+
if (fileOrDirectory.isDirectory)
|
|
170
|
+
for (child in fileOrDirectory.listFiles()!!)
|
|
171
|
+
deleteRecursive(child)
|
|
172
|
+
fileOrDirectory.delete()
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//
|
|
2
|
+
// NSData+MimeType.h
|
|
3
|
+
// Mattermost
|
|
4
|
+
//
|
|
5
|
+
// Created by Tek Min Ewe on 04/08/2019.
|
|
6
|
+
// Copyright © 2019 Facebook. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#import <Foundation/Foundation.h>
|
|
10
|
+
|
|
11
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
12
|
+
|
|
13
|
+
@interface NSData (MimeType)
|
|
14
|
+
|
|
15
|
+
-(NSString *)mimeType;
|
|
16
|
+
-(NSString *)extension;
|
|
17
|
+
|
|
18
|
+
@end
|
|
19
|
+
|
|
20
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//
|
|
2
|
+
// NSData+MimeType.m
|
|
3
|
+
// Mattermost
|
|
4
|
+
//
|
|
5
|
+
// Created by Tek Min Ewe on 04/08/2019.
|
|
6
|
+
// Copyright © 2019 Facebook. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#import "NSData+MimeType.h"
|
|
10
|
+
|
|
11
|
+
@implementation NSData (MimeType)
|
|
12
|
+
|
|
13
|
+
-(NSString *)mimeType {
|
|
14
|
+
uint8_t c;
|
|
15
|
+
[self getBytes:&c length:1];
|
|
16
|
+
|
|
17
|
+
switch (c) {
|
|
18
|
+
case 0xFF:
|
|
19
|
+
return @"image/jpeg";
|
|
20
|
+
break;
|
|
21
|
+
case 0x89:
|
|
22
|
+
return @"image/png";
|
|
23
|
+
break;
|
|
24
|
+
case 0x47:
|
|
25
|
+
return @"image/gif";
|
|
26
|
+
break;
|
|
27
|
+
case 0x49:
|
|
28
|
+
case 0x4D:
|
|
29
|
+
return @"image/tiff";
|
|
30
|
+
break;
|
|
31
|
+
default:
|
|
32
|
+
return @"";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
-(NSString *)extension {
|
|
37
|
+
uint8_t c;
|
|
38
|
+
[self getBytes:&c length:1];
|
|
39
|
+
|
|
40
|
+
switch (c) {
|
|
41
|
+
case 0xFF:
|
|
42
|
+
return @"jpg";
|
|
43
|
+
break;
|
|
44
|
+
case 0x89:
|
|
45
|
+
return @"png";
|
|
46
|
+
break;
|
|
47
|
+
case 0x47:
|
|
48
|
+
return @"gif";
|
|
49
|
+
break;
|
|
50
|
+
case 0x49:
|
|
51
|
+
case 0x4D:
|
|
52
|
+
return @"tiff";
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
return @"";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|