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())