Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 31 additions & 23 deletions android/app/src/main/java/com/robosats/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package com.robosats
import android.Manifest
import android.annotation.SuppressLint
import android.app.Application
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.util.Log
Expand All @@ -20,9 +23,6 @@ import android.webkit.WebSettings
import android.webkit.WebStorage
import android.webkit.WebView
import android.webkit.WebViewClient
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
Expand All @@ -31,6 +31,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.robosats.models.EncryptedStorage
import com.robosats.models.LanguageManager
import com.robosats.services.NotificationsService
import com.robosats.tor.TorKmp
import com.robosats.tor.TorKmpManager
Expand All @@ -50,8 +51,12 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Initialize EncryptedStorage
EncryptedStorage.init(this)

// Initialize language manager with system language
LanguageManager.init(this)

// Lock the screen orientation to portrait mode
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT

Expand All @@ -70,7 +75,7 @@ class MainActivity : AppCompatActivity() {
}

// Set initial status message
updateStatus("Initializing Tor connection...")
updateStatus(getString(R.string.init_tor))

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
ContextCompat.checkSelfPermission(
Expand All @@ -86,20 +91,22 @@ class MainActivity : AppCompatActivity() {
}

val intent = intent
intentData = ""
if (intent != null) {
val orderId = intent.getStringExtra("order_id")
if (orderId?.isNotEmpty() == true) {
intentData = orderId
}
}

// Initialize Tor and setup WebView only after Tor is properly connected
initializeTor()

val settingProxy = EncryptedStorage.getEncryptedStorage("settings_use_proxy")
if (settingProxy == "false") {
// Setup WebView to use Orbot if the user previously clicked
onUseOrbotButtonClicked()
} else {
// Initialize Tor and setup WebView only after Tor is properly connected
initializeTor()
}
}

Expand All @@ -126,7 +133,7 @@ class MainActivity : AppCompatActivity() {
// Show a message to the user
Toast.makeText(
this,
"Using Orbot. Make sure it's running!",
getString(R.string.using_orbot),
Toast.LENGTH_LONG
).show()

Expand Down Expand Up @@ -181,7 +188,7 @@ class MainActivity : AppCompatActivity() {

// Show error message on the loading screen
runOnUiThread {
updateStatus("Critical error: Tor initialization failed. App cannot proceed securely.")
updateStatus(getString(R.string.tor_init_error))
}
}
}
Expand All @@ -200,7 +207,7 @@ class MainActivity : AppCompatActivity() {
try {
// Display connecting message
runOnUiThread {
updateStatus("Connecting to Tor network...")
updateStatus(getString(R.string.connecting_tor))
}

// Wait for Tor to connect with retry mechanism
Expand All @@ -214,7 +221,7 @@ class MainActivity : AppCompatActivity() {
// Update status on UI thread every few retries
if (retries % 3 == 0) {
runOnUiThread {
updateStatus("Still connecting to Tor (attempt $retries/$maxRetries)...")
updateStatus(getString(R.string.still_connecting_tor))
}
}
}
Expand All @@ -225,7 +232,7 @@ class MainActivity : AppCompatActivity() {

// Show success message and proceed
runOnUiThread {
updateStatus("Tor connected successfully. Setting up secure browser...")
updateStatus(getString(R.string.connected_tor))

HttpClientManager.setDefaultProxy(getTorKmpObject().proxy)

Expand All @@ -237,14 +244,14 @@ class MainActivity : AppCompatActivity() {
Log.e("TorInitialization", "Failed to connect to Tor after $maxRetries retries")

runOnUiThread {
updateStatus("Failed to connect to Tor after multiple attempts. App cannot proceed securely.")
updateStatus(getString(R.string.fail_tor))
}
}
} catch (e: Exception) {
Log.e("TorInitialization", "Error during Tor connection: ${e.message}", e)

runOnUiThread {
updateStatus("Error connecting to Tor: ${e.message}")
updateStatus(getString(R.string.error_tor) + "${e.message}")
}
}
}
Expand Down Expand Up @@ -283,7 +290,7 @@ class MainActivity : AppCompatActivity() {

// Show message that we're setting up secure browsing
runOnUiThread {
updateStatus(if (useProxy) "Setting up secure Tor browsing..." else "Setting up Orbot browsing...")
updateStatus(if (useProxy) getString(R.string.setting_tor) else getString(R.string.setting_orbot))
}

// Configure proxy for WebView in a background thread to avoid NetworkOnMainThreadException
Expand All @@ -296,7 +303,7 @@ class MainActivity : AppCompatActivity() {

// Success - now configure WebViewClient and load URL on UI thread
runOnUiThread {
updateStatus("Secure connection established. Loading app...")
updateStatus(getString(R.string.loading_app))

// Set up WebViewClient that allows external links and deep links to be opened
webView.webViewClient = object : WebViewClient() {
Expand Down Expand Up @@ -369,11 +376,13 @@ class MainActivity : AppCompatActivity() {
val notifications = EncryptedStorage.getEncryptedStorage("settings_notifications")
if (notifications != "false") initializeNotifications()

webView.post {
try {
webView.evaluateJavascript("javascript:window.AndroidDataRobosats = { navigateToPage: '$intentData' }", null)
} catch (e: Exception) {
Log.e("NavigateToPage", "Error evaluating JavaScript: $e")
if (intentData != "") {
webView.post {
try {
webView.evaluateJavascript("javascript:window.AndroidDataRobosats = { navigateToPage: '$intentData' }", null)
} catch (e: Exception) {
Log.e("NavigateToPage", "Error evaluating JavaScript: $e")
}
}
}
}
Expand Down Expand Up @@ -441,9 +450,7 @@ class MainActivity : AppCompatActivity() {
cookieManager.setAcceptThirdPartyCookies(webView, false) // Block 3rd party cookies

// 10. Disable Service Workers (not needed for our local app)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ServiceWorkerController.getInstance().setServiceWorkerClient(null)
}
ServiceWorkerController.getInstance().setServiceWorkerClient(null)

// --- USABILITY SETTINGS ---

Expand All @@ -458,6 +465,7 @@ class MainActivity : AppCompatActivity() {
webSettings.textZoom = 100
}


/**
* Clear all WebView data when activity is destroyed
*/
Expand Down
103 changes: 103 additions & 0 deletions android/app/src/main/java/com/robosats/models/LanguageManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.robosats.models

import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import android.os.LocaleList
import android.util.Log
import com.robosats.MainActivity
import java.util.Locale

/**
* Manages the app's language settings
* Uses the system's default language if it's one of the supported languages,
* otherwise defaults to English
*/
object LanguageManager {
lateinit var resources: Resources
private val TAG = "LanguageManager"
private val SETTINGS_KEY = "settings_language"

// List of supported language codes based on the app's supported languages
private val SUPPORTED_LANGUAGES = setOf(
"en", // English
"es", // Spanish
"de", // German
"pl", // Polish
"fr", // French
"sw", // Swahili
"ru", // Russian
"ja", // Japanese
"it", // Italian
"pt", // Portuguese
"zh-si", // Simplified Chinese (special handling required)
"zh-tr", // Traditional Chinese (special handling required)
"sv", // Swedish
"cs", // Czech
"th", // Thai
"ca", // Catalan
"eu" // Basque
)

fun init(context: MainActivity) {
resources = context.resources
applySystemLanguage()
}

/**
* Apply the system's default language to the app if it's supported,
* otherwise use English as the default language
* This is called only once during app initialization
*/
fun applySystemLanguage() {
// Get system locale
val systemLocale =
Resources.getSystem().configuration.locales.get(0)

// Determine the locale to use
val localeToUse = getValidatedLocale(systemLocale)
Log.d(TAG, "System locale: ${systemLocale.language}, Using locale: ${localeToUse.language}")

val lang = systemLocale.language
if (EncryptedStorage.getEncryptedStorage(SETTINGS_KEY) == "" && SUPPORTED_LANGUAGES.contains(lang)) {
EncryptedStorage.setEncryptedStorage(SETTINGS_KEY, lang)
}

// Create configuration with the validated locale
val config = Configuration(resources.configuration)

val localeList = LocaleList(localeToUse)
LocaleList.setDefault(localeList)
config.setLocales(localeList)

// Update the configuration
@Suppress("DEPRECATION")
resources.updateConfiguration(config, resources.displayMetrics)
}

/**
* Validates if the system locale is supported by the app
* If not, returns the English locale as default
*/
private fun getValidatedLocale(systemLocale: Locale): Locale {
val languageCode = systemLocale.language

// Handle Chinese special cases
if (languageCode == "zh") {
val country = systemLocale.country
// Check if it's Simplified (China, Singapore) or Traditional (Taiwan, Hong Kong)
return when (country) {
"CN", "SG" -> Locale.SIMPLIFIED_CHINESE
"TW", "HK" -> Locale.TRADITIONAL_CHINESE
else -> Locale.ENGLISH // Default to English for other Chinese variants
}
}

// For other languages, check if they're in our supported list
return if (SUPPORTED_LANGUAGES.contains(languageCode.lowercase())) {
systemLocale
} else {
Locale.ENGLISH // Default to English if language not supported
}
}
}
11 changes: 10 additions & 1 deletion android/app/src/main/java/com/robosats/models/NostrClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,18 @@ import org.json.JSONObject
object NostrClient {
private var subscriptionNotificationId = "robosatsNotificationId"
private var authors = garagePubKeys()
private var initialized = false

fun init() {
RelayPool.register(Client)
if (!initialized) {
try {
RelayPool.register(Client)
initialized = true
} catch (e: Exception) {
Log.e("NostrClient", "Error initializing NostrClient: ${e.message}", e)
// Don't set initialized to true if there was an error
}
}
}

fun stop() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,20 @@ class NotificationsService : Service() {
}

override fun onCreate() {
val connectivityManager =
(getSystemService(ConnectivityManager::class.java) as ConnectivityManager)
connectivityManager.registerDefaultNetworkCallback(networkCallback)
NostrClient.init()
super.onCreate()
try {
val connectivityManager =
(getSystemService(ConnectivityManager::class.java) as ConnectivityManager)
connectivityManager.registerDefaultNetworkCallback(networkCallback)

// Initialize NostrClient safely
NostrClient.init()

super.onCreate()
} catch (e: Exception) {
Log.e("NotificationsService", "Error in onCreate", e)
// Call super.onCreate() even if there's an error to ensure proper service lifecycle
super.onCreate()
}
}

@RequiresPermission(Manifest.permission.POST_NOTIFICATIONS)
Expand Down
20 changes: 20 additions & 0 deletions android/app/src/main/res/values-ca/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<resources>
<string name="app_name">Robosats</string>
<string name="service">Servei en segon pla</string>
<string name="connection">Connexió</string>
<string name="configuration">Configuració</string>
<string name="robosats_is_running_in_background">Robosats s\'està executant en segon pla cercant notificacions</string>
<string name="notifications">Notificacions</string>
<string name="useOrbotButton">Utilitzar Orbot</string>
<string name="init_tor">Inicialitzant connexió Tor...</string>
<string name="using_orbot">Utilitzant Orbot. Assegureu-vos que està en execució!</string>
<string name="tor_init_error">Error crític: La inicialització de Tor ha fallat. L\'aplicació no pot continuar de manera segura.</string>
<string name="connecting_tor">Connectant a la xarxa Tor...</string>
<string name="still_connecting_tor">Encara connectant a Tor...</string>
<string name="connected_tor">Tor connectat amb èxit. Configurant navegador segur...</string>
<string name="fail_tor">No s\'ha pogut connectar a Tor després de diversos intents. L\'aplicació no pot continuar de manera segura.</string>
<string name="error_tor">"Error en connectar a Tor: "</string>
<string name="setting_tor">Configurant navegació segura amb Tor...</string>
<string name="setting_orbot">Configurant navegació amb Orbot...</string>
<string name="loading_app">Connexió segura establerta. Carregant aplicació...</string>
</resources>
20 changes: 20 additions & 0 deletions android/app/src/main/res/values-cs/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<resources>
<string name="app_name">Robosats</string>
<string name="service">Služba na pozadí</string>
<string name="connection">Připojení</string>
<string name="configuration">Konfigurace</string>
<string name="robosats_is_running_in_background">Robosats běží na pozadí a vyhledává oznámení</string>
<string name="notifications">Oznámení</string>
<string name="useOrbotButton">Použít Orbot</string>
<string name="init_tor">Inicializace připojení Tor...</string>
<string name="using_orbot">Používá se Orbot. Ujistěte se, že běží!</string>
<string name="tor_init_error">Kritická chyba: Inicializace Tor selhala. Aplikace nemůže bezpečně pokračovat.</string>
<string name="connecting_tor">Připojování k síti Tor...</string>
<string name="still_connecting_tor">Stále se připojuje k Tor...</string>
<string name="connected_tor">Tor úspěšně připojen. Nastavování bezpečného prohlížeče...</string>
<string name="fail_tor">Nepodařilo se připojit k Tor po několika pokusech. Aplikace nemůže bezpečně pokračovat.</string>
<string name="error_tor">"Chyba při připojování k Tor: "</string>
<string name="setting_tor">Nastavování bezpečného prohlížení přes Tor...</string>
<string name="setting_orbot">Nastavování prohlížení přes Orbot...</string>
<string name="loading_app">Bezpečné připojení navázáno. Načítání aplikace...</string>
</resources>
Loading