From cd487f0c074903cfbf08fe1dda6b54c9684f8a5f Mon Sep 17 00:00:00 2001 From: nain <126972030+nain-F49FF806@users.noreply.github.com> Date: Fri, 26 Jul 2024 18:54:24 +0200 Subject: [PATCH] Support sharing media attachment with paste Any file with mimetype application/*, audio/*, model/*, video/* is supported. Note: We do not yet do any size checking. But we do report attach file size --- android/app/build.gradle.kts | 2 +- android/app/src/main/AndroidManifest.xml | 15 ++ .../sharepaste/common/EncryptCompose.kt | 6 +- .../sharepaste/intents/ShareAttachActivity.kt | 160 ++++++++++++++++++ .../sharepaste/intents/ShareTextActivity.kt | 2 +- android/app/src/main/res/values/strings.xml | 2 + .../sharepaste/rsnative/PrivateBinRs.kt | 9 +- 7 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 android/app/src/main/java/alt/nainapps/sharepaste/intents/ShareAttachActivity.kt diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 19e0e23..11b7c67 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -13,7 +13,7 @@ android { applicationId = "alt.nainapps.sharepaste" minSdk = 26 targetSdk = 34 - versionCode = 1721991000 + versionCode = 1722012000 versionName = "2024.07.26" setProperty("archivesBaseName", "sharepaste.oo") diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 9b9addd..5108550 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -33,6 +33,21 @@ + + + + + + + + + + + ( + Intent.EXTRA_STREAM + ) as? Uri + + contentUri?.let { contentUri -> + try { + val mimeType = contentResolver.getType(contentUri).also { + Log.i(TAG, "Attach file type: $it") + } ?: "" + + /* + * Get the file's content URI from the incoming Intent, + * then query the server app to get the file's display name + * and size. + */ + contentResolver.query(contentUri, null, null, null, null)?.use { cursor -> + /* + * Get the column indexes of the data in the Cursor, + * move to the first row in the Cursor, get the data, + * and display it. + */ + val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) + val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) + cursor.moveToFirst() + attachName = cursor.getString(nameIndex) + attachSize = bytesToHumanReadableSize(cursor.getLong(sizeIndex)) + } + + contentResolver.openInputStream(contentUri)?.use { inputStream -> + // inputStream is guaranteed to be non-null here + // Process the input stream + Log.i(TAG, "Processing input stream...") + // Convert InputStream to Base64 String + inputStreamToBase64String(inputStream).let { + // Construct the Data URI + attach = "data:$mimeType;base64,$it" + } + } + } catch (e: FileNotFoundException) { + // Handle the case where the file was not found + Log.e(TAG, "File not found: ${e.message}") + } catch (e: IOException) { + // Handle general IO errors, including permission issues + Log.e(TAG, "IO Error: ${e.message}") + } + } + + setContent { + SharePasteO2Theme { + // A surface container using the 'background' color from the theme + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Spacer(modifier = Modifier.height(16.dp)) + Text( + textAlign = TextAlign.Center, + text = "Attachment: $attachName ($attachSize)" + ) + // Note: TODO(" We should maybe warn when too big file size above ") + EncryptAndShareUI( + text = text, + attach = attach, + attachName = attachName, + customPrivatebinHost = customPrivatebinHost + ) + } + } + } + } + } +} + +// https://stackoverflow.com/a/68822715 +fun bytesToHumanReadableSize(bytes: Long) = when { + bytes >= 1 shl 30 -> "%.1f GB".format(bytes.toDouble() / (1 shl 30)) + bytes >= 1 shl 20 -> "%.1f MB".format(bytes.toDouble() / (1 shl 20)) + bytes >= 1 shl 10 -> "%.0f kB".format(bytes.toDouble() / (1 shl 10)) + else -> "$bytes bytes" +} + +@OptIn(ExperimentalEncodingApi::class) +fun inputStreamToBase64String(inputStream: InputStream): String? = try { + Base64.encode(inputStream.readBytes()) +} catch (e: Exception) { + Log.e(TAG, "Encoding Base64 Error: ${e.message}") + null // Handle exception appropriately +} + +@Composable +private fun Greeting(name: String, modifier: Modifier = Modifier) { + Text( + text = "Hello $name!", + modifier = modifier + ) +} + +@Preview(showBackground = true) +@Composable +private fun GreetingPreview() { + SharePasteO2Theme { + Greeting("Android") + } +} diff --git a/android/app/src/main/java/alt/nainapps/sharepaste/intents/ShareTextActivity.kt b/android/app/src/main/java/alt/nainapps/sharepaste/intents/ShareTextActivity.kt index 7387b01..f23debf 100644 --- a/android/app/src/main/java/alt/nainapps/sharepaste/intents/ShareTextActivity.kt +++ b/android/app/src/main/java/alt/nainapps/sharepaste/intents/ShareTextActivity.kt @@ -101,7 +101,7 @@ class ShareTextActivity : ComponentActivity() { } @Composable -fun Greeting(name: String, modifier: Modifier = Modifier) { +private fun Greeting(name: String, modifier: Modifier = Modifier) { Text( text = "Hello $name!", modifier = modifier diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index e27a6ec..166b225 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -4,4 +4,6 @@ Encrypt&Share Settings https://privatebin.net + Attachment + Size \ No newline at end of file diff --git a/android/rsnative/src/main/java/alt/nainapps/sharepaste/rsnative/PrivateBinRs.kt b/android/rsnative/src/main/java/alt/nainapps/sharepaste/rsnative/PrivateBinRs.kt index 2ea1ce0..12ee954 100644 --- a/android/rsnative/src/main/java/alt/nainapps/sharepaste/rsnative/PrivateBinRs.kt +++ b/android/rsnative/src/main/java/alt/nainapps/sharepaste/rsnative/PrivateBinRs.kt @@ -23,8 +23,13 @@ class PrivateBinRs(private val defaultBaseUrl: String? = null) { burn = burn ?: false ) - fun send(text: String, opts: Opts = defaultOpts): PostPasteResponse { - val decryptedPaste = DecryptedPaste(text, null, null) + fun send( + text: String, + opts: Opts = defaultOpts, + attachment: String? = null, + attachmentName: String? = null + ): PostPasteResponse { + val decryptedPaste = DecryptedPaste(text, attachment, attachmentName) val api = Api(opts.url ?: defaultOpts.url!!, opts) val postPasteResponse = api.postPaste(decryptedPaste, opts.password ?: "", opts) // postPasteResponse.toUrl(api.base())