From 91bdd8e6b97f4c1be9326d56d115194258eca698 Mon Sep 17 00:00:00 2001 From: koalasat Date: Sat, 9 Aug 2025 13:42:05 +0200 Subject: [PATCH 1/5] Update Android architecture versioning strategy --- .github/workflows/android-build.yml | 4 +- android/app/build.gradle.kts | 23 +++++++++- .../main/java/com/robosats/WebAppInterface.kt | 44 +++++++------------ 3 files changed, 41 insertions(+), 30 deletions(-) diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index 320be209..434f99fc 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -17,10 +17,10 @@ on: required: true push: branches: [ "main" ] - paths: [ "mobile", "frontend" ] + paths: [ "android", "frontend" ] pull_request: branches: [ "main" ] - paths: [ "mobile", "frontend" ] + paths: [ "android", "frontend" ] jobs: build-android: diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 647e650d..792a9dda 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,5 +1,7 @@ import com.android.build.api.dsl.Packaging +val baseVersionCode = 81 + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) @@ -13,7 +15,7 @@ android { applicationId = "com.robosats" minSdk = 26 targetSdk = 36 - versionCode = 15 + versionCode = baseVersionCode versionName = "0.8.1-alpha" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -60,11 +62,30 @@ android { } } + packaging { jniLibs.useLegacyPackaging = true } } +// Configure unique version codes for ABI splits to prevent downgrade issues +androidComponents { + onVariants { variant -> + val abiCodes = mapOf( + "armeabi-v7a" to 1, + "arm64-v8a" to 2, + "x86" to 3, + "x86_64" to 4 + ) + + variant.outputs.forEach { output -> + val abiName = output.filters.find { it.filterType.name == "ABI" }?.identifier + val abiVersionCode = abiCodes[abiName] ?: 9 // Universal APK gets 9 + output.versionCode.set(baseVersionCode * 1000 + abiVersionCode) + } + } +} + dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) diff --git a/android/app/src/main/java/com/robosats/WebAppInterface.kt b/android/app/src/main/java/com/robosats/WebAppInterface.kt index 23017bae..08d52b63 100644 --- a/android/app/src/main/java/com/robosats/WebAppInterface.kt +++ b/android/app/src/main/java/com/robosats/WebAppInterface.kt @@ -41,8 +41,7 @@ class WebAppInterface(private val context: MainActivity, private val webView: We // Security patterns for input validation private val UUID_PATTERN = Pattern.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", Pattern.CASE_INSENSITIVE) - private val SAFE_STRING_PATTERN = Pattern.compile("^[a-zA-Z0-9\s_\-.,:;!?()\[\]{}\"]*$") - + private val SAFE_STRING_PATTERN = Pattern.compile("^[a-zA-Z0-9\\s_\\-.,:;!?()\\[\\]{}\\"]*$") // Maximum length for input strings private val MAX_INPUT_LENGTH = 1000 @@ -108,17 +107,31 @@ class WebAppInterface(private val context: MainActivity, private val webView: We @JavascriptInterface fun copyToClipboard(message: String) { + // Validate input + if (!isValidInput(message, 10000)) { // Allow longer text for clipboard + Log.e(TAG, "Invalid input for copyToClipboard") + Toast.makeText(context, "Invalid content for clipboard", Toast.LENGTH_SHORT).show() + return + } + try { + // Limit clipboard content size for security + val truncatedMessage = if (message.length > 10000) { + message.substring(0, 10000) + "... (content truncated for security)" + } else { + message + } + // Copy to clipboard val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager - val clip = android.content.ClipData.newPlainText("RoboSats Data", message) + val clip = android.content.ClipData.newPlainText("RoboSats Data", truncatedMessage) clipboard.setPrimaryClip(clip) // Show a toast notification Toast.makeText(context, "Copied to clipboard", Toast.LENGTH_SHORT).show() // Log the action (don't log the content for privacy) - Log.d(TAG, "Text copied to clipboard") + Log.d(TAG, "Text copied to clipboard (${truncatedMessage.length} chars)") } catch (e: Exception) { Log.e(TAG, "Error copying to clipboard", e) Toast.makeText(context, "Failed to copy to clipboard", Toast.LENGTH_SHORT).show() @@ -383,29 +396,6 @@ class WebAppInterface(private val context: MainActivity, private val webView: We resolvePromise(uuid, key) } - @JavascriptInterface - fun restart() { - try { - Log.d(TAG, "Restarting app...") - - val intent = context.packageManager.getLaunchIntentForPackage(context.packageName) - intent?.let { - it.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - it.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) - - context.startActivity(it) - context.finish() - } ?: run { - Log.e(TAG, "Could not get launch intent for app restart") - Toast.makeText(context, "Failed to restart app", Toast.LENGTH_SHORT).show() - } - } catch (e: Exception) { - Log.e(TAG, "Error restarting app", e) - Toast.makeText(context, "Failed to restart app", Toast.LENGTH_SHORT).show() - } - } - private fun onWsMessage(path: String?, message: String?) { val encodedMessage = encodeForJavaScript(message) safeEvaluateJavascript("javascript:window.AndroidRobosats.onWSMessage('$path', '$encodedMessage')") From 58e4d3141283fbcad34c4a40b1aa15314a5dad79 Mon Sep 17 00:00:00 2001 From: koalasat Date: Sat, 9 Aug 2025 13:44:13 +0200 Subject: [PATCH 2/5] Revert unwanted changes --- .../main/java/com/robosats/WebAppInterface.kt | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/android/app/src/main/java/com/robosats/WebAppInterface.kt b/android/app/src/main/java/com/robosats/WebAppInterface.kt index 08d52b63..2ac657a4 100644 --- a/android/app/src/main/java/com/robosats/WebAppInterface.kt +++ b/android/app/src/main/java/com/robosats/WebAppInterface.kt @@ -41,7 +41,8 @@ class WebAppInterface(private val context: MainActivity, private val webView: We // Security patterns for input validation private val UUID_PATTERN = Pattern.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", Pattern.CASE_INSENSITIVE) - private val SAFE_STRING_PATTERN = Pattern.compile("^[a-zA-Z0-9\\s_\\-.,:;!?()\\[\\]{}\\"]*$") + private val SAFE_STRING_PATTERN = Pattern.compile("^[a-zA-Z0-9\\s_\\-.,:;!?()\\[\\]{}]*$") + // Maximum length for input strings private val MAX_INPUT_LENGTH = 1000 @@ -107,31 +108,18 @@ class WebAppInterface(private val context: MainActivity, private val webView: We @JavascriptInterface fun copyToClipboard(message: String) { - // Validate input - if (!isValidInput(message, 10000)) { // Allow longer text for clipboard - Log.e(TAG, "Invalid input for copyToClipboard") - Toast.makeText(context, "Invalid content for clipboard", Toast.LENGTH_SHORT).show() - return - } - try { - // Limit clipboard content size for security - val truncatedMessage = if (message.length > 10000) { - message.substring(0, 10000) + "... (content truncated for security)" - } else { - message - } // Copy to clipboard val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager - val clip = android.content.ClipData.newPlainText("RoboSats Data", truncatedMessage) + val clip = android.content.ClipData.newPlainText("RoboSats Data", message) clipboard.setPrimaryClip(clip) // Show a toast notification Toast.makeText(context, "Copied to clipboard", Toast.LENGTH_SHORT).show() // Log the action (don't log the content for privacy) - Log.d(TAG, "Text copied to clipboard (${truncatedMessage.length} chars)") + Log.d(TAG, "Text copied to clipboard (${message.length} chars)") } catch (e: Exception) { Log.e(TAG, "Error copying to clipboard", e) Toast.makeText(context, "Failed to copy to clipboard", Toast.LENGTH_SHORT).show() From 541508c0b68ae271d92646edf5b4bb05208ddcf1 Mon Sep 17 00:00:00 2001 From: koalasat Date: Sat, 9 Aug 2025 13:45:16 +0200 Subject: [PATCH 3/5] Revert unwanted changes --- .../main/java/com/robosats/WebAppInterface.kt | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/com/robosats/WebAppInterface.kt b/android/app/src/main/java/com/robosats/WebAppInterface.kt index 2ac657a4..2e8d59a1 100644 --- a/android/app/src/main/java/com/robosats/WebAppInterface.kt +++ b/android/app/src/main/java/com/robosats/WebAppInterface.kt @@ -109,7 +109,6 @@ class WebAppInterface(private val context: MainActivity, private val webView: We @JavascriptInterface fun copyToClipboard(message: String) { try { - // Copy to clipboard val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager val clip = android.content.ClipData.newPlainText("RoboSats Data", message) @@ -119,7 +118,7 @@ class WebAppInterface(private val context: MainActivity, private val webView: We Toast.makeText(context, "Copied to clipboard", Toast.LENGTH_SHORT).show() // Log the action (don't log the content for privacy) - Log.d(TAG, "Text copied to clipboard (${message.length} chars)") + Log.d(TAG, "Text copied to clipboard") } catch (e: Exception) { Log.e(TAG, "Error copying to clipboard", e) Toast.makeText(context, "Failed to copy to clipboard", Toast.LENGTH_SHORT).show() @@ -384,6 +383,29 @@ class WebAppInterface(private val context: MainActivity, private val webView: We resolvePromise(uuid, key) } + @JavascriptInterface + fun restart() { + try { + Log.d(TAG, "Restarting app...") + + val intent = context.packageManager.getLaunchIntentForPackage(context.packageName) + intent?.let { + it.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + it.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + + context.startActivity(it) + context.finish() + } ?: run { + Log.e(TAG, "Could not get launch intent for app restart") + Toast.makeText(context, "Failed to restart app", Toast.LENGTH_SHORT).show() + } + } catch (e: Exception) { + Log.e(TAG, "Error restarting app", e) + Toast.makeText(context, "Failed to restart app", Toast.LENGTH_SHORT).show() + } + } + private fun onWsMessage(path: String?, message: String?) { val encodedMessage = encodeForJavaScript(message) safeEvaluateJavascript("javascript:window.AndroidRobosats.onWSMessage('$path', '$encodedMessage')") From 2a96dec939e1f8dbc96f9939397d1ff54995ffea Mon Sep 17 00:00:00 2001 From: koalasat Date: Sat, 9 Aug 2025 16:40:29 +0200 Subject: [PATCH 4/5] Change Universal version --- android/app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 792a9dda..18cd5249 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -80,7 +80,7 @@ androidComponents { variant.outputs.forEach { output -> val abiName = output.filters.find { it.filterType.name == "ABI" }?.identifier - val abiVersionCode = abiCodes[abiName] ?: 9 // Universal APK gets 9 + val abiVersionCode = abiCodes[abiName] ?: 0 // Universal APK gets 0 output.versionCode.set(baseVersionCode * 1000 + abiVersionCode) } } From bbe7fbb3d25bde856aa6a680d992b6a94083ced1 Mon Sep 17 00:00:00 2001 From: koalasat Date: Sat, 9 Aug 2025 16:43:19 +0200 Subject: [PATCH 5/5] Remove jump --- android/app/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 18cd5249..702827dc 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -62,7 +62,6 @@ android { } } - packaging { jniLibs.useLegacyPackaging = true }