Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
aeonBTC authored Dec 21, 2024
1 parent 4f6da25 commit 6c38389
Show file tree
Hide file tree
Showing 14 changed files with 471 additions and 212 deletions.
5 changes: 3 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ android {
applicationId = "com.example.mempal"
minSdk = 24
targetSdk = 34
versionCode = 9
versionName = "1.4.1"
versionCode = 10
versionName = "1.4.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled = true
}
Expand Down Expand Up @@ -101,6 +101,7 @@ dependencies {

implementation(libs.tor.android)
implementation(libs.jtorctl)
implementation(libs.androidx.localbroadcastmanager)

// WorkManager
implementation("androidx.work:work-runtime-ktx:2.9.0")
Expand Down
51 changes: 43 additions & 8 deletions app/src/main/java/com/example/mempal/api/NetworkClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import java.lang.ref.WeakReference
import java.util.concurrent.TimeUnit

object NetworkClient {
private const val TIMEOUT_SECONDS = 25L
private const val TEST_TIMEOUT_SECONDS = 15L
private const val ONION_TEST_TIMEOUT_SECONDS = 45L
private const val TIMEOUT_SECONDS = 30L
private const val TEST_TIMEOUT_SECONDS = 20L
private const val ONION_TEST_TIMEOUT_SECONDS = 60L
private var retrofit: Retrofit? = null
private var contextRef: WeakReference<Context>? = null
private val _isInitialized = MutableStateFlow(false)
Expand All @@ -31,15 +31,33 @@ object NetworkClient {
private var connectivityManager: ConnectivityManager? = null
private val _isNetworkAvailable = MutableStateFlow(false)
val isNetworkAvailable: StateFlow<Boolean> = _isNetworkAvailable
private var lastInitAttempt = 0L
private val minInitRetryDelay = 2000L

private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
coroutineScope?.launch {
_isNetworkAvailable.value = true
// Always try to reinitialize when network becomes available
setupRetrofit(TorManager.getInstance().torStatus.value == TorStatus.CONNECTED)
_isInitialized.value = true

// Add delay before reinitializing to prevent rapid retries
val now = System.currentTimeMillis()
if (now - lastInitAttempt < minInitRetryDelay) {
delay(minInitRetryDelay)
}
lastInitAttempt = now

// Check Tor status before initializing
val torManager = TorManager.getInstance()
if (torManager.isTorEnabled()) {
if (torManager.torStatus.value == TorStatus.CONNECTED) {
setupRetrofit(true)
_isInitialized.value = true
}
} else {
setupRetrofit(false)
_isInitialized.value = true
}
}
}

Expand Down Expand Up @@ -72,6 +90,7 @@ object NetworkClient {

// Check initial network state
_isNetworkAvailable.value = isNetworkCurrentlyAvailable()
lastInitAttempt = System.currentTimeMillis()

// Check if current API URL is an onion address and manage Tor accordingly
val settingsRepository = SettingsRepository.getInstance(context)
Expand All @@ -92,11 +111,17 @@ object NetworkClient {
torManager.torStatus.collect { status ->
println("Tor status changed: $status")
if (status == TorStatus.CONNECTED || status == TorStatus.DISCONNECTED) {
println("Setting up Retrofit with useProxy=${status == TorStatus.CONNECTED}")
// Add delay before reinitializing to prevent rapid retries
val now = System.currentTimeMillis()
if (now - lastInitAttempt < minInitRetryDelay) {
delay(minInitRetryDelay)
}
lastInitAttempt = now

if (isNetworkCurrentlyAvailable()) {
println("Setting up Retrofit with useProxy=${status == TorStatus.CONNECTED}")
setupRetrofit(status == TorStatus.CONNECTED)
_isInitialized.value = true
println("NetworkClient initialization complete")
} else {
_isInitialized.value = false
}
Expand Down Expand Up @@ -139,6 +164,16 @@ object NetworkClient {
.readTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
.writeTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addInterceptor { chain ->
var attempt = 0
var response = chain.proceed(chain.request())
while (!response.isSuccessful && attempt < 3) {
attempt++
response.close()
response = chain.proceed(chain.request())
}
response
}

if (useProxy && baseUrl.contains(".onion")) {
println("Setting up Tor proxy")
Expand Down
153 changes: 92 additions & 61 deletions app/src/main/java/com/example/mempal/api/WidgetNetworkClient.kt
Original file line number Diff line number Diff line change
@@ -1,62 +1,93 @@
package com.example.mempal.api

import android.content.Context
import com.example.mempal.repository.SettingsRepository
import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

object WidgetNetworkClient {
private const val TIMEOUT_SECONDS = 10L
private const val DEFAULT_API_URL = "https://mempool.space"

private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}

fun getMempoolApi(context: Context): MempoolApi {
val settingsRepository = SettingsRepository.getInstance(context)
val userApiUrl = settingsRepository.getApiUrl()

// If the user's custom server is a .onion address, use the default mempool.space
val baseUrl = if (userApiUrl.contains(".onion")) {
DEFAULT_API_URL
} else {
userApiUrl
}.let { url ->
if (!url.endsWith("/")) "$url/" else url
}

val clientBuilder = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
.readTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
.writeTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addInterceptor { chain ->
var attempt = 0
var response = chain.proceed(chain.request())
while (!response.isSuccessful && attempt < 2) {
attempt++
response.close()
response = chain.proceed(chain.request())
}
response
}

val gson = GsonBuilder()
.setLenient()
.create()

val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(clientBuilder.build())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()

return retrofit.create(MempoolApi::class.java)
}
package com.example.mempal.api

import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import com.example.mempal.repository.SettingsRepository
import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

object WidgetNetworkClient {
private const val TIMEOUT_SECONDS = 10L
private const val DEFAULT_API_URL = "https://mempool.space"
private var retrofit: Retrofit? = null
private var mempoolApi: MempoolApi? = null

private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}

fun getMempoolApi(context: Context): MempoolApi {
// Check if we have a valid API instance
mempoolApi?.let { api ->
if (!hasUrlChanged(context)) {
return api
}
}

// Create new API instance
return createMempoolApi(context).also {
mempoolApi = it
}
}

private fun hasUrlChanged(context: Context): Boolean {
val settingsRepository = SettingsRepository.getInstance(context)
val currentUrl = settingsRepository.getApiUrl()
return retrofit?.baseUrl()?.toString()?.contains(currentUrl) != true
}

private fun isNetworkAvailable(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = connectivityManager.activeNetwork ?: return false
val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}

private fun createMempoolApi(context: Context): MempoolApi {
val settingsRepository = SettingsRepository.getInstance(context)
val userApiUrl = settingsRepository.getApiUrl()

// If the user's custom server is a .onion address, use the default mempool.space
val baseUrl = if (userApiUrl.contains(".onion")) {
DEFAULT_API_URL
} else {
userApiUrl
}.let { url ->
if (!url.endsWith("/")) "$url/" else url
}

val clientBuilder = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
.readTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
.writeTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addInterceptor { chain ->
var attempt = 0
var response = chain.proceed(chain.request())
while (!response.isSuccessful && attempt < 2) {
attempt++
response.close()
response = chain.proceed(chain.request())
}
response
}

val gson = GsonBuilder()
.setLenient()
.create()

retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(clientBuilder.build())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()

return retrofit!!.create(MempoolApi::class.java)
}
}
Loading

0 comments on commit 6c38389

Please sign in to comment.