[Share Extension] Support on Android for sharing videos to app (#5466)
This commit is contained in:
+79
-21
@@ -12,6 +12,11 @@ import java.io.File
|
|||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
|
|
||||||
|
enum class AttachmentType {
|
||||||
|
IMAGE,
|
||||||
|
VIDEO,
|
||||||
|
}
|
||||||
|
|
||||||
class ExpoReceiveAndroidIntentsModule : Module() {
|
class ExpoReceiveAndroidIntentsModule : Module() {
|
||||||
override fun definition() =
|
override fun definition() =
|
||||||
ModuleDefinition {
|
ModuleDefinition {
|
||||||
@@ -23,17 +28,26 @@ class ExpoReceiveAndroidIntentsModule : Module() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleIntent(intent: Intent?) {
|
private fun handleIntent(intent: Intent?) {
|
||||||
if (appContext.currentActivity == null || intent == null) return
|
if (appContext.currentActivity == null) return
|
||||||
|
intent?.let {
|
||||||
if (intent.action == Intent.ACTION_SEND) {
|
if (it.action == Intent.ACTION_SEND && it.type == "text/plain") {
|
||||||
if (intent.type == "text/plain") {
|
handleTextIntent(it)
|
||||||
handleTextIntent(intent)
|
return
|
||||||
} else if (intent.type.toString().startsWith("image/")) {
|
|
||||||
handleImageIntent(intent)
|
|
||||||
}
|
}
|
||||||
} else if (intent.action == Intent.ACTION_SEND_MULTIPLE) {
|
|
||||||
if (intent.type.toString().startsWith("image/")) {
|
val type =
|
||||||
handleImagesIntent(intent)
|
if (it.type.toString().startsWith("image/")) {
|
||||||
|
AttachmentType.IMAGE
|
||||||
|
} else if (it.type.toString().startsWith("video/")) {
|
||||||
|
AttachmentType.VIDEO
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it.action == Intent.ACTION_SEND) {
|
||||||
|
handleAttachmentIntent(it, type)
|
||||||
|
} else if (it.action == Intent.ACTION_SEND_MULTIPLE) {
|
||||||
|
handleAttachmentsIntent(it, type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,26 +62,46 @@ class ExpoReceiveAndroidIntentsModule : Module() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleImageIntent(intent: Intent) {
|
private fun handleAttachmentIntent(
|
||||||
|
intent: Intent,
|
||||||
|
type: AttachmentType,
|
||||||
|
) {
|
||||||
val uri =
|
val uri =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||||
} else {
|
} else {
|
||||||
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
||||||
}
|
}
|
||||||
if (uri == null) return
|
|
||||||
|
|
||||||
handleImageIntents(listOf(uri))
|
uri?.let {
|
||||||
|
when (type) {
|
||||||
|
AttachmentType.IMAGE -> handleImageIntents(listOf(it))
|
||||||
|
AttachmentType.VIDEO -> handleVideoIntents(listOf(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleImagesIntent(intent: Intent) {
|
private fun handleAttachmentsIntent(
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
intent: Intent,
|
||||||
intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri::class.java)?.let {
|
type: AttachmentType,
|
||||||
handleImageIntents(it.filterIsInstance<Uri>().take(4))
|
) {
|
||||||
|
val uris =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
intent
|
||||||
|
.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
||||||
|
?.filterIsInstance<Uri>()
|
||||||
|
?.take(4)
|
||||||
|
} else {
|
||||||
|
intent
|
||||||
|
.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
||||||
|
?.filterIsInstance<Uri>()
|
||||||
|
?.take(4)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)?.let {
|
uris?.let {
|
||||||
handleImageIntents(it.filterIsInstance<Uri>().take(4))
|
when (type) {
|
||||||
|
AttachmentType.IMAGE -> handleImageIntents(it)
|
||||||
|
else -> return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,11 +127,33 @@ class ExpoReceiveAndroidIntentsModule : Module() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleVideoIntents(uris: List<Uri>) {
|
||||||
|
val uri = uris[0]
|
||||||
|
// If there is no extension for the file, substringAfterLast returns the original string - not
|
||||||
|
// null, so we check for that below
|
||||||
|
// It doesn't actually matter what the extension is, so defaulting to mp4 is fine, even if the
|
||||||
|
// video isn't actually an mp4
|
||||||
|
var extension = uri.path?.substringAfterLast(".")
|
||||||
|
if (extension == null || extension == uri.path) {
|
||||||
|
extension = "mp4"
|
||||||
|
}
|
||||||
|
val file = createFile(extension)
|
||||||
|
|
||||||
|
val out = FileOutputStream(file)
|
||||||
|
appContext.currentActivity?.contentResolver?.openInputStream(uri)?.use {
|
||||||
|
it.copyTo(out)
|
||||||
|
}
|
||||||
|
"bluesky://intent/compose?videoUri=${URLEncoder.encode(file.path, "UTF-8")}".toUri().let {
|
||||||
|
val newIntent = Intent(Intent.ACTION_VIEW, it)
|
||||||
|
appContext.currentActivity?.startActivity(newIntent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getImageInfo(uri: Uri): Map<String, Any> {
|
private fun getImageInfo(uri: Uri): Map<String, Any> {
|
||||||
val bitmap = MediaStore.Images.Media.getBitmap(appContext.currentActivity?.contentResolver, uri)
|
val bitmap = MediaStore.Images.Media.getBitmap(appContext.currentActivity?.contentResolver, uri)
|
||||||
// We have to save this so that we can access it later when uploading the image.
|
// We have to save this so that we can access it later when uploading the image.
|
||||||
// createTempFile will automatically place a unique string between "img" and "temp.jpeg"
|
// createTempFile will automatically place a unique string between "img" and "temp.jpeg"
|
||||||
val file = File.createTempFile("img", "temp.jpeg", appContext.currentActivity?.cacheDir)
|
val file = createFile("jpeg")
|
||||||
val out = FileOutputStream(file)
|
val out = FileOutputStream(file)
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out)
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out)
|
||||||
out.flush()
|
out.flush()
|
||||||
@@ -110,6 +166,8 @@ class ExpoReceiveAndroidIntentsModule : Module() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createFile(extension: String): File = File.createTempFile(extension, "temp.$extension", appContext.currentActivity?.cacheDir)
|
||||||
|
|
||||||
// We will pas the width and height to the app here, since getting measurements
|
// We will pas the width and height to the app here, since getting measurements
|
||||||
// on the RN side is a bit more involved, and we already have them here anyway.
|
// on the RN side is a bit more involved, and we already have them here anyway.
|
||||||
private fun buildUriData(info: Map<String, Any>): String {
|
private fun buildUriData(info: Map<String, Any>): String {
|
||||||
|
|||||||
@@ -27,6 +27,29 @@ const withIntentFilters = config => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
action: [
|
||||||
|
{
|
||||||
|
$: {
|
||||||
|
'android:name': 'android.intent.action.SEND',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
category: [
|
||||||
|
{
|
||||||
|
$: {
|
||||||
|
'android:name': 'android.intent.category.DEFAULT',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
$: {
|
||||||
|
'android:mimeType': 'video/*',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
action: [
|
action: [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user