mirror of
https://github.com/RoboSats/robosats.git
synced 2025-08-05 18:00:00 +00:00
Working
This commit is contained in:
@ -8,6 +8,7 @@ declare global {
|
|||||||
|
|
||||||
interface AndroidAppRobosats {
|
interface AndroidAppRobosats {
|
||||||
generateRoboname: (uuid: string, initialString: string) => void;
|
generateRoboname: (uuid: string, initialString: string) => void;
|
||||||
|
generateRobohash: (uuid: string, initialString: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AndroidRobosats {
|
class AndroidRobosats {
|
||||||
|
@ -32,15 +32,20 @@ class RoboidentitiesAndroidClient implements RoboidentitiesClient {
|
|||||||
if (this.robohashes[key]) {
|
if (this.robohashes[key]) {
|
||||||
return this.robohashes[key];
|
return this.robohashes[key];
|
||||||
} else {
|
} else {
|
||||||
const response = await window.NativeRobosats?.postMessage({
|
try {
|
||||||
category: 'roboidentities',
|
const result = await new Promise<string>((resolve, reject) => {
|
||||||
type: 'robohash',
|
const uuid: string = uuidv4();
|
||||||
detail: key,
|
window.AndroidAppRobosats?.generateRobohash(uuid, initialString);
|
||||||
});
|
window.AndroidRobosats?.storePromise(uuid, resolve, reject);
|
||||||
const result: string = response ? Object.values(response)[0] : '';
|
});
|
||||||
const image: string = `data:image/png;base64,${result}`;
|
|
||||||
this.robohashes[key] = image;
|
const image: string = `data:image/png;base64,${result}`;
|
||||||
return image;
|
this.robohashes[key] = image;
|
||||||
|
return image;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating robohash:', error);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,11 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.koalasat.robosats"
|
namespace = "com.robosats"
|
||||||
compileSdk = 36
|
compileSdk = 36
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.koalasat.robosats"
|
applicationId = "com.robosats"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.koalasat.robosats
|
package com.robosats
|
||||||
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
|
|||||||
fun useAppContext() {
|
fun useAppContext() {
|
||||||
// Context of the app under test.
|
// Context of the app under test.
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
assertEquals("com.koalasat.robosats", appContext.packageName)
|
assertEquals("com.robosats", appContext.packageName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
android:theme="@style/Theme.Robosats">
|
android:theme="@style/Theme.Robosats">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name="com.robosats.MainActivity"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
package com.koalasat.robosats
|
package com.robosats
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.webkit.*
|
import android.webkit.*
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.koalasat.robosats.WebAppInterface
|
import com.robosats.R
|
||||||
import com.robosats.tor.TorKmp
|
import com.robosats.tor.TorKmp
|
||||||
import com.robosats.tor.TorKmpManager
|
import com.robosats.tor.TorKmpManager
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.net.HttpURLConnection
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
|
import java.net.Proxy
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var webView: WebView
|
private lateinit var webView: WebView
|
||||||
@ -50,10 +56,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Show a toast notification about the critical error
|
// Show a toast notification about the critical error
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
android.widget.Toast.makeText(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
"Critical error: Tor initialization failed. App cannot proceed securely.",
|
"Critical error: Tor initialization failed. App cannot proceed securely.",
|
||||||
android.widget.Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,10 +72,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
try {
|
try {
|
||||||
// Display connecting message
|
// Display connecting message
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
android.widget.Toast.makeText(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
"Connecting to Tor network...",
|
"Connecting to Tor network...",
|
||||||
android.widget.Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,10 +90,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
// Update status on UI thread every few retries
|
// Update status on UI thread every few retries
|
||||||
if (retries % 3 == 0) {
|
if (retries % 3 == 0) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
android.widget.Toast.makeText(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
"Still connecting to Tor (attempt $retries/$maxRetries)...",
|
"Still connecting to Tor (attempt $retries/$maxRetries)...",
|
||||||
android.widget.Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,10 +105,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Show success message
|
// Show success message
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
android.widget.Toast.makeText(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
"Tor connected successfully",
|
"Tor connected successfully",
|
||||||
android.widget.Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
|
|
||||||
// Now that Tor is connected, set up the WebView
|
// Now that Tor is connected, set up the WebView
|
||||||
@ -113,10 +119,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
Log.e("TorInitialization", "Failed to connect to Tor after $maxRetries retries")
|
Log.e("TorInitialization", "Failed to connect to Tor after $maxRetries retries")
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
android.widget.Toast.makeText(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
"Failed to connect to Tor after multiple attempts. App cannot proceed securely.",
|
"Failed to connect to Tor after multiple attempts. App cannot proceed securely.",
|
||||||
android.widget.Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,10 +130,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
Log.e("TorInitialization", "Error during Tor connection: ${e.message}", e)
|
Log.e("TorInitialization", "Error during Tor connection: ${e.message}", e)
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
android.widget.Toast.makeText(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
"Error connecting to Tor: ${e.message}",
|
"Error connecting to Tor: ${e.message}",
|
||||||
android.widget.Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,10 +196,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Show message that we're setting up secure browsing
|
// Show message that we're setting up secure browsing
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
android.widget.Toast.makeText(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
"Setting up secure Tor browsing...",
|
"Setting up secure Tor browsing...",
|
||||||
android.widget.Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,10 +231,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Success - now configure WebViewClient and load URL on UI thread
|
// Success - now configure WebViewClient and load URL on UI thread
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
android.widget.Toast.makeText(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
"Secure connection established",
|
"Secure connection established",
|
||||||
android.widget.Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
|
|
||||||
// Create a custom WebViewClient that forces all traffic through Tor
|
// Create a custom WebViewClient that forces all traffic through Tor
|
||||||
@ -249,14 +255,14 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// For .onion domains, we must use SOCKS proxy type
|
// For .onion domains, we must use SOCKS proxy type
|
||||||
val proxyType = if (isOnionDomain)
|
val proxyType = if (isOnionDomain)
|
||||||
java.net.Proxy.Type.SOCKS
|
Proxy.Type.SOCKS
|
||||||
else
|
else
|
||||||
java.net.Proxy.Type.HTTP
|
Proxy.Type.HTTP
|
||||||
|
|
||||||
// Create a proxy instance for Tor with the appropriate type
|
// Create a proxy instance for Tor with the appropriate type
|
||||||
val torProxy = java.net.Proxy(
|
val torProxy = Proxy(
|
||||||
proxyType,
|
proxyType,
|
||||||
java.net.InetSocketAddress(proxyHost, proxyPort)
|
InetSocketAddress(proxyHost, proxyPort)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isOnionDomain) {
|
if (isOnionDomain) {
|
||||||
@ -264,14 +270,14 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create connection with proxy already configured
|
// Create connection with proxy already configured
|
||||||
val url = java.net.URL(urlString)
|
val url = URL(urlString)
|
||||||
val connection = url.openConnection(torProxy)
|
val connection = url.openConnection(torProxy)
|
||||||
|
|
||||||
// Configure basic connection properties
|
// Configure basic connection properties
|
||||||
connection.connectTimeout = 60000 // Longer timeout for onion domains
|
connection.connectTimeout = 60000 // Longer timeout for onion domains
|
||||||
connection.readTimeout = 60000
|
connection.readTimeout = 60000
|
||||||
|
|
||||||
if (connection is java.net.HttpURLConnection) {
|
if (connection is HttpURLConnection) {
|
||||||
// Ensure no connection reuse to prevent proxy leaks
|
// Ensure no connection reuse to prevent proxy leaks
|
||||||
connection.setRequestProperty("Connection", "close")
|
connection.setRequestProperty("Connection", "close")
|
||||||
|
|
||||||
@ -305,7 +311,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Get the correct input stream based on response code
|
// Get the correct input stream based on response code
|
||||||
val inputStream = if (responseCode >= 400) {
|
val inputStream = if (responseCode >= 400) {
|
||||||
connection.errorStream ?: java.io.ByteArrayInputStream(byteArrayOf())
|
connection.errorStream ?: ByteArrayInputStream(byteArrayOf())
|
||||||
} else {
|
} else {
|
||||||
connection.inputStream
|
connection.inputStream
|
||||||
}
|
}
|
||||||
@ -371,7 +377,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
super.onReceivedError(view, request, error)
|
super.onReceivedError(view, request, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPageStarted(view: WebView, url: String, favicon: android.graphics.Bitmap?) {
|
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
|
||||||
// Verify Tor is connected when page starts loading
|
// Verify Tor is connected when page starts loading
|
||||||
if (!torKmp.isConnected()) {
|
if (!torKmp.isConnected()) {
|
||||||
Log.e("SecurityError", "Tor disconnected as page started loading")
|
Log.e("SecurityError", "Tor disconnected as page started loading")
|
||||||
@ -409,10 +415,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
// Show error and exit - DO NOT LOAD WEBVIEW
|
// Show error and exit - DO NOT LOAD WEBVIEW
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
// Show toast with error
|
// Show toast with error
|
||||||
android.widget.Toast.makeText(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
"SECURITY ERROR: Cannot set up secure browsing: ${e.message}",
|
"SECURITY ERROR: Cannot set up secure browsing: ${e.message}",
|
||||||
android.widget.Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -478,7 +484,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Try to set global proxy through ConnectivityManager
|
// Try to set global proxy through ConnectivityManager
|
||||||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE)
|
val connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE)
|
||||||
val setDefaultProxyMethod = connectivityManager.javaClass.getDeclaredMethod("setDefaultProxy", proxyClass)
|
val setDefaultProxyMethod = connectivityManager.javaClass.getDeclaredMethod("setDefaultProxy", proxyClass)
|
||||||
setDefaultProxyMethod.isAccessible = true
|
setDefaultProxyMethod.isAccessible = true
|
||||||
setDefaultProxyMethod.invoke(connectivityManager, proxyInfo)
|
setDefaultProxyMethod.invoke(connectivityManager, proxyInfo)
|
@ -1,4 +1,4 @@
|
|||||||
package com.koalasat.robosats
|
package com.robosats
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package com.koalasat.robosats
|
package com.robosats
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@ -26,13 +26,11 @@ class WebAppInterface(private val context: Context, private val webView: WebView
|
|||||||
fun generateRoboname(uuid: String, message: String) {
|
fun generateRoboname(uuid: String, message: String) {
|
||||||
try {
|
try {
|
||||||
val roboname = roboIdentities.generateRoboname(message)
|
val roboname = roboIdentities.generateRoboname(message)
|
||||||
Log.d(TAG, "Generated roboname: $roboname for message: $message")
|
|
||||||
|
|
||||||
webView.post {
|
webView.post {
|
||||||
webView.evaluateJavascript("javascript:window.AndroidRobosats.onResolvePromise('${uuid}', '${roboname}')", null)
|
webView.evaluateJavascript("javascript:window.AndroidRobosats.onResolvePromise('${uuid}', '${roboname}')", null)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Error in generateRoboname: ${e.message}", e)
|
Log.e(TAG, "Error in generateRoboname", e)
|
||||||
|
|
||||||
// Handle error gracefully by returning a fallback value
|
// Handle error gracefully by returning a fallback value
|
||||||
webView.post {
|
webView.post {
|
||||||
@ -43,4 +41,24 @@ class WebAppInterface(private val context: Context, private val webView: WebView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
fun generateRobohash(uuid: String, message: String) {
|
||||||
|
try {
|
||||||
|
val roboname = roboIdentities.generateRobohash(message)
|
||||||
|
webView.post {
|
||||||
|
webView.evaluateJavascript("javascript:window.AndroidRobosats.onResolvePromise('${uuid}', '${roboname}')", null)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error in generateRobohash", e)
|
||||||
|
|
||||||
|
// Handle error gracefully by returning a fallback value
|
||||||
|
webView.post {
|
||||||
|
webView.evaluateJavascript(
|
||||||
|
"javascript:window.AndroidRobosats.onRejectPromise('${uuid}', 'Error generating robot hash')",
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
tools:context=".MainActivity">
|
tools:context="com.robosats.MainActivity">
|
||||||
|
|
||||||
<WebView
|
<WebView
|
||||||
android:id="@+id/webView"
|
android:id="@+id/webView"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.koalasat.robosats
|
package com.robosats
|
||||||
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user