Moves The WIFI/Mobile data connection watcher to the compose.
Moves Relay Connection Manager to the Compose Adds a 30 second delay to approve events on Amber
This commit is contained in:
@@ -293,6 +293,7 @@ class ServiceManager(
|
|||||||
start: Boolean = true,
|
start: Boolean = true,
|
||||||
pause: Boolean = true,
|
pause: Boolean = true,
|
||||||
) {
|
) {
|
||||||
|
Log.d("ManageRelayServices", "Force Restart $account $start $pause")
|
||||||
if (pause) {
|
if (pause) {
|
||||||
pause()
|
pause()
|
||||||
}
|
}
|
||||||
@@ -306,25 +307,25 @@ class ServiceManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun restartIfDifferentAccount(account: Account) {
|
fun setAccountAndRestart(account: Account) {
|
||||||
if (this.account != account) {
|
forceRestart(account, true, true)
|
||||||
forceRestart(account, true, true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun forceRestart() {
|
fun forceRestart() {
|
||||||
forceRestart(null, true, true)
|
forceRestart(null, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun justStart() {
|
fun justStartIfItHasAccount() {
|
||||||
forceRestart(null, true, false)
|
if (account != null) {
|
||||||
|
forceRestart(null, true, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pauseForGood() {
|
fun pauseForGood() {
|
||||||
forceRestart(null, false, true)
|
forceRestart(null, false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pauseForGoodAndClearAccount() {
|
fun pauseAndLogOff() {
|
||||||
account = null
|
account = null
|
||||||
forceRestart(null, false, true)
|
forceRestart(null, false, true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,13 +20,26 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui
|
package com.vitorpamplona.amethyst.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.Network
|
||||||
|
import android.net.NetworkCapabilities
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
|
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
|
||||||
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
|
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.google.accompanist.adaptive.calculateDisplayFeatures
|
import com.google.accompanist.adaptive.calculateDisplayFeatures
|
||||||
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -44,9 +57,68 @@ fun prepareSharedViewModel(act: MainActivity): SharedPreferencesViewModel {
|
|||||||
sharedPreferencesViewModel.updateDisplaySettings(windowSizeClass, displayFeatures)
|
sharedPreferencesViewModel.updateDisplaySettings(windowSizeClass, displayFeatures)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(act.isOnMobileDataState) {
|
ManageConnectivity(sharedPreferencesViewModel)
|
||||||
sharedPreferencesViewModel.updateConnectivityStatusState(act.isOnMobileDataState)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sharedPreferencesViewModel
|
return sharedPreferencesViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ManageConnectivity(sharedPreferencesViewModel: SharedPreferencesViewModel) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
var job = remember<Job?> { null }
|
||||||
|
|
||||||
|
val networkCallback =
|
||||||
|
remember {
|
||||||
|
object : ConnectivityManager.NetworkCallback() {
|
||||||
|
override fun onAvailable(network: Network) {
|
||||||
|
super.onAvailable(network)
|
||||||
|
sharedPreferencesViewModel.updateNetworkState(network.networkHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network capabilities have changed for the network
|
||||||
|
override fun onCapabilitiesChanged(
|
||||||
|
network: Network,
|
||||||
|
networkCapabilities: NetworkCapabilities,
|
||||||
|
) {
|
||||||
|
super.onCapabilitiesChanged(network, networkCapabilities)
|
||||||
|
sharedPreferencesViewModel.updateNetworkState(network.networkHandle)
|
||||||
|
sharedPreferencesViewModel.updateConnectivityStatusState(networkCapabilities)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LifecycleResumeEffect(sharedPreferencesViewModel) {
|
||||||
|
job?.cancel()
|
||||||
|
job =
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
Log.d("ManageConnectivity", "Register network listener from Resume")
|
||||||
|
val connectivityManager = context.getConnectivityManager()
|
||||||
|
connectivityManager.registerDefaultNetworkCallback(networkCallback)
|
||||||
|
connectivityManager.activeNetwork?.let { network ->
|
||||||
|
sharedPreferencesViewModel.updateNetworkState(network.networkHandle)
|
||||||
|
connectivityManager.getNetworkCapabilities(network)?.let {
|
||||||
|
sharedPreferencesViewModel.updateConnectivityStatusState(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPauseOrDispose {
|
||||||
|
job?.cancel()
|
||||||
|
job =
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
delay(30000) // 30 seconds
|
||||||
|
Log.d("ManageConnectivity", "Unregister network listener from Pause")
|
||||||
|
context.getConnectivityManager().unregisterNetworkCallback(networkCallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun SharedPreferencesViewModel.updateConnectivityStatusState(networkCapabilities: NetworkCapabilities) {
|
||||||
|
val metered = !networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
|
||||||
|
val mobileData = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
|
||||||
|
updateConnectivityStatusState(metered || mobileData)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.getConnectivityManager() = getSystemService(ConnectivityManager::class.java) as ConnectivityManager
|
||||||
|
|||||||
@@ -20,9 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui
|
package com.vitorpamplona.amethyst.ui
|
||||||
|
|
||||||
import android.net.ConnectivityManager
|
|
||||||
import android.net.Network
|
|
||||||
import android.net.NetworkCapabilities
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -33,11 +30,9 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.vitorpamplona.amethyst.Amethyst
|
|
||||||
import com.vitorpamplona.amethyst.debugState
|
import com.vitorpamplona.amethyst.debugState
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService
|
import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService
|
||||||
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
|
||||||
import com.vitorpamplona.amethyst.service.playback.composable.DEFAULT_MUTED_SETTING
|
import com.vitorpamplona.amethyst.service.playback.composable.DEFAULT_MUTED_SETTING
|
||||||
import com.vitorpamplona.amethyst.service.playback.pip.BackgroundMedia
|
import com.vitorpamplona.amethyst.service.playback.pip.BackgroundMedia
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||||
@@ -65,12 +60,9 @@ import kotlinx.coroutines.GlobalScope
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.Timer
|
|
||||||
import kotlin.concurrent.schedule
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
val isOnMobileDataState = mutableStateOf(false)
|
val isOnMobileDataState = mutableStateOf(false)
|
||||||
private val isOnWifiDataState = mutableStateOf(false)
|
|
||||||
|
|
||||||
private var shouldPauseService = true
|
private var shouldPauseService = true
|
||||||
|
|
||||||
@@ -96,10 +88,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun prepareToLaunchSigner() {
|
|
||||||
shouldPauseService = false
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
@@ -113,22 +101,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// starts muted every time
|
// starts muted every time
|
||||||
DEFAULT_MUTED_SETTING.value = true
|
DEFAULT_MUTED_SETTING.value = true
|
||||||
|
|
||||||
// Keep connection alive if it's calling the signer app
|
|
||||||
Log.d("shouldPauseService", "shouldPauseService onResume: $shouldPauseService")
|
|
||||||
if (shouldPauseService) {
|
|
||||||
GlobalScope.launch(Dispatchers.IO) { Amethyst.instance.serviceManager.justStart() }
|
|
||||||
}
|
|
||||||
|
|
||||||
val connectivityManager =
|
|
||||||
(getSystemService(ConnectivityManager::class.java) as ConnectivityManager)
|
|
||||||
connectivityManager.registerDefaultNetworkCallback(networkCallback)
|
|
||||||
connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)?.let {
|
|
||||||
updateNetworkCapabilities(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// resets state until next External Signer Call
|
|
||||||
Timer().schedule(350) { shouldPauseService = true }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
@@ -137,20 +109,11 @@ class MainActivity : AppCompatActivity() {
|
|||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
LanguageTranslatorService.clear()
|
LanguageTranslatorService.clear()
|
||||||
}
|
}
|
||||||
Amethyst.instance.serviceManager.cleanObservers()
|
|
||||||
|
|
||||||
// if (BuildConfig.DEBUG) {
|
// if (BuildConfig.DEBUG) {
|
||||||
GlobalScope.launch(Dispatchers.IO) { debugState(this@MainActivity) }
|
GlobalScope.launch(Dispatchers.IO) { debugState(this@MainActivity) }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Log.d("shouldPauseService", "shouldPauseService onPause: $shouldPauseService")
|
|
||||||
if (shouldPauseService) {
|
|
||||||
GlobalScope.launch(Dispatchers.IO) { Amethyst.instance.serviceManager.pauseForGood() }
|
|
||||||
}
|
|
||||||
|
|
||||||
(getSystemService(ConnectivityManager::class.java) as ConnectivityManager)
|
|
||||||
.unregisterNetworkCallback(networkCallback)
|
|
||||||
|
|
||||||
super.onPause()
|
super.onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,72 +141,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateNetworkCapabilities(networkCapabilities: NetworkCapabilities): Boolean {
|
|
||||||
val unmetered = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
|
|
||||||
|
|
||||||
val isOnMobileData = !unmetered || networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
|
|
||||||
val isOnWifi = unmetered && networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
|
|
||||||
|
|
||||||
var changedNetwork = false
|
|
||||||
|
|
||||||
if (isOnMobileDataState.value != isOnMobileData) {
|
|
||||||
isOnMobileDataState.value = isOnMobileData
|
|
||||||
|
|
||||||
changedNetwork = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOnWifiDataState.value != isOnWifi) {
|
|
||||||
isOnWifiDataState.value = isOnWifi
|
|
||||||
|
|
||||||
changedNetwork = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changedNetwork) {
|
|
||||||
if (isOnMobileData) {
|
|
||||||
HttpClientManager.setDefaultTimeout(HttpClientManager.DEFAULT_TIMEOUT_ON_MOBILE)
|
|
||||||
} else {
|
|
||||||
HttpClientManager.setDefaultTimeout(HttpClientManager.DEFAULT_TIMEOUT_ON_WIFI)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changedNetwork
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
|
||||||
private val networkCallback =
|
|
||||||
object : ConnectivityManager.NetworkCallback() {
|
|
||||||
var lastNetwork: Network? = null
|
|
||||||
|
|
||||||
override fun onAvailable(network: Network) {
|
|
||||||
super.onAvailable(network)
|
|
||||||
|
|
||||||
Log.d("ServiceManager NetworkCallback", "onAvailable: $shouldPauseService")
|
|
||||||
if (shouldPauseService && lastNetwork != null && lastNetwork != network) {
|
|
||||||
GlobalScope.launch(Dispatchers.IO) { Amethyst.instance.serviceManager.forceRestart() }
|
|
||||||
}
|
|
||||||
|
|
||||||
lastNetwork = network
|
|
||||||
}
|
|
||||||
|
|
||||||
// Network capabilities have changed for the network
|
|
||||||
override fun onCapabilitiesChanged(
|
|
||||||
network: Network,
|
|
||||||
networkCapabilities: NetworkCapabilities,
|
|
||||||
) {
|
|
||||||
super.onCapabilitiesChanged(network, networkCapabilities)
|
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
|
||||||
Log.d(
|
|
||||||
"ServiceManager NetworkCallback",
|
|
||||||
"onCapabilitiesChanged: ${network.networkHandle} hasMobileData ${isOnMobileDataState.value} hasWifi ${isOnWifiDataState.value}",
|
|
||||||
)
|
|
||||||
if (updateNetworkCapabilities(networkCapabilities) && shouldPauseService) {
|
|
||||||
Amethyst.instance.serviceManager.forceRestart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun uriToRoute(uri: String?): String? =
|
fun uriToRoute(uri: String?): String? =
|
||||||
|
|||||||
+1
-1
@@ -96,7 +96,7 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
private suspend fun requestLoginUI() {
|
private suspend fun requestLoginUI() {
|
||||||
_accountContent.update { AccountState.LoggedOff }
|
_accountContent.update { AccountState.LoggedOff }
|
||||||
|
|
||||||
viewModelScope.launch(Dispatchers.IO) { Amethyst.instance.serviceManager.pauseForGoodAndClearAccount() }
|
viewModelScope.launch(Dispatchers.IO) { Amethyst.instance.serviceManager.pauseAndLogOff() }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun loginAndStartUI(
|
suspend fun loginAndStartUI(
|
||||||
|
|||||||
+18
-9
@@ -20,11 +20,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.screen
|
package com.vitorpamplona.amethyst.ui.screen
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.compose.material3.windowsizeclass.WindowSizeClass
|
import androidx.compose.material3.windowsizeclass.WindowSizeClass
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -59,7 +59,8 @@ class SettingsState {
|
|||||||
var featureSet by mutableStateOf(FeatureSetType.SIMPLIFIED)
|
var featureSet by mutableStateOf(FeatureSetType.SIMPLIFIED)
|
||||||
var gallerySet by mutableStateOf(ProfileGalleryType.CLASSIC)
|
var gallerySet by mutableStateOf(ProfileGalleryType.CLASSIC)
|
||||||
|
|
||||||
var isOnMobileData: State<Boolean> = mutableStateOf(false)
|
var isOnMobileOrMeteredConnection by mutableStateOf(false)
|
||||||
|
var currentNetworkId by mutableStateOf(0L)
|
||||||
|
|
||||||
var windowSizeClass = mutableStateOf<WindowSizeClass?>(null)
|
var windowSizeClass = mutableStateOf<WindowSizeClass?>(null)
|
||||||
var displayFeatures = mutableStateOf<List<DisplayFeature>>(emptyList())
|
var displayFeatures = mutableStateOf<List<DisplayFeature>>(emptyList())
|
||||||
@@ -67,7 +68,7 @@ class SettingsState {
|
|||||||
val showProfilePictures =
|
val showProfilePictures =
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
when (automaticallyShowProfilePictures) {
|
when (automaticallyShowProfilePictures) {
|
||||||
ConnectivityType.WIFI_ONLY -> !isOnMobileData.value
|
ConnectivityType.WIFI_ONLY -> !isOnMobileOrMeteredConnection
|
||||||
ConnectivityType.NEVER -> false
|
ConnectivityType.NEVER -> false
|
||||||
ConnectivityType.ALWAYS -> true
|
ConnectivityType.ALWAYS -> true
|
||||||
}
|
}
|
||||||
@@ -84,7 +85,7 @@ class SettingsState {
|
|||||||
val showUrlPreview =
|
val showUrlPreview =
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
when (automaticallyShowUrlPreview) {
|
when (automaticallyShowUrlPreview) {
|
||||||
ConnectivityType.WIFI_ONLY -> !isOnMobileData.value
|
ConnectivityType.WIFI_ONLY -> !isOnMobileOrMeteredConnection
|
||||||
ConnectivityType.NEVER -> false
|
ConnectivityType.NEVER -> false
|
||||||
ConnectivityType.ALWAYS -> true
|
ConnectivityType.ALWAYS -> true
|
||||||
}
|
}
|
||||||
@@ -93,7 +94,7 @@ class SettingsState {
|
|||||||
val startVideoPlayback =
|
val startVideoPlayback =
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
when (automaticallyStartPlayback) {
|
when (automaticallyStartPlayback) {
|
||||||
ConnectivityType.WIFI_ONLY -> !isOnMobileData.value
|
ConnectivityType.WIFI_ONLY -> !isOnMobileOrMeteredConnection
|
||||||
ConnectivityType.NEVER -> false
|
ConnectivityType.NEVER -> false
|
||||||
ConnectivityType.ALWAYS -> true
|
ConnectivityType.ALWAYS -> true
|
||||||
}
|
}
|
||||||
@@ -102,7 +103,7 @@ class SettingsState {
|
|||||||
val showImages =
|
val showImages =
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
when (automaticallyShowImages) {
|
when (automaticallyShowImages) {
|
||||||
ConnectivityType.WIFI_ONLY -> !isOnMobileData.value
|
ConnectivityType.WIFI_ONLY -> !isOnMobileOrMeteredConnection
|
||||||
ConnectivityType.NEVER -> false
|
ConnectivityType.NEVER -> false
|
||||||
ConnectivityType.ALWAYS -> true
|
ConnectivityType.ALWAYS -> true
|
||||||
}
|
}
|
||||||
@@ -223,9 +224,17 @@ class SharedPreferencesViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateConnectivityStatusState(isOnMobileDataState: State<Boolean>) {
|
fun updateConnectivityStatusState(isOnMobileDataState: Boolean) {
|
||||||
if (sharedPrefs.isOnMobileData != isOnMobileDataState) {
|
if (sharedPrefs.isOnMobileOrMeteredConnection != isOnMobileDataState) {
|
||||||
sharedPrefs.isOnMobileData = isOnMobileDataState
|
Log.d("Connectivity", "updateConnectivityStatusState ${sharedPrefs.currentNetworkId}: ${sharedPrefs.isOnMobileOrMeteredConnection} -> $isOnMobileDataState")
|
||||||
|
sharedPrefs.isOnMobileOrMeteredConnection = isOnMobileDataState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateNetworkState(networkId: Long) {
|
||||||
|
if (sharedPrefs.currentNetworkId != networkId) {
|
||||||
|
Log.d("Connectivity", "updateNetworkState ${sharedPrefs.currentNetworkId} -> $networkId")
|
||||||
|
sharedPrefs.currentNetworkId = networkId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-7
@@ -1250,26 +1250,34 @@ class AccountViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTorSettings(newTorSettings: TorSettings) {
|
fun setTorSettings(newTorSettings: TorSettings) =
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
// Only restart relay connections if port or type changes
|
// Only restart relay connections if port or type changes
|
||||||
if (account.settings.setTorSettings(newTorSettings)) {
|
if (account.settings.setTorSettings(newTorSettings)) {
|
||||||
Amethyst.instance.serviceManager.forceRestart()
|
Amethyst.instance.serviceManager.forceRestart()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun restartServices() {
|
fun forceRestartServices() =
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
Amethyst.instance.serviceManager.restartIfDifferentAccount(account)
|
Amethyst.instance.serviceManager.setAccountAndRestart(account)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun changeProxyPort(port: Int) {
|
fun justStart() =
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
Amethyst.instance.serviceManager.justStartIfItHasAccount()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun justPause() =
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
Amethyst.instance.serviceManager.cleanObservers()
|
||||||
|
Amethyst.instance.serviceManager.pauseForGood()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeProxyPort(port: Int) =
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
Amethyst.instance.serviceManager.forceRestart()
|
Amethyst.instance.serviceManager.forceRestart()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class Factory(
|
class Factory(
|
||||||
val accountSettings: AccountSettings,
|
val accountSettings: AccountSettings,
|
||||||
|
|||||||
+53
-52
@@ -33,6 +33,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleEventObserver
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
|
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
@@ -52,6 +53,8 @@ import com.vitorpamplona.amethyst.ui.tor.TorStatus
|
|||||||
import com.vitorpamplona.amethyst.ui.tor.TorType
|
import com.vitorpamplona.amethyst.ui.tor.TorType
|
||||||
import com.vitorpamplona.quartz.nip55AndroidSigner.NostrSignerExternal
|
import com.vitorpamplona.quartz.nip55AndroidSigner.NostrSignerExternal
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -75,9 +78,7 @@ fun LoggedInPage(
|
|||||||
|
|
||||||
accountViewModel.firstRoute = route
|
accountViewModel.firstRoute = route
|
||||||
|
|
||||||
LaunchedEffect(key1 = accountViewModel) {
|
ManageRelayServices(accountViewModel, sharedPreferencesViewModel)
|
||||||
accountViewModel.restartServices()
|
|
||||||
}
|
|
||||||
|
|
||||||
ManageTorInstance(accountViewModel)
|
ManageTorInstance(accountViewModel)
|
||||||
|
|
||||||
@@ -92,40 +93,59 @@ fun LoggedInPage(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ManageRelayServices(
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
sharedPreferencesViewModel: SharedPreferencesViewModel,
|
||||||
|
) {
|
||||||
|
var job = remember<Job?> { null }
|
||||||
|
|
||||||
|
LaunchedEffect(
|
||||||
|
sharedPreferencesViewModel.sharedPrefs.currentNetworkId,
|
||||||
|
sharedPreferencesViewModel.sharedPrefs.isOnMobileOrMeteredConnection,
|
||||||
|
) {
|
||||||
|
Log.d("ManageRelayServices", "Change Network Id/State ${sharedPreferencesViewModel.sharedPrefs.currentNetworkId}, forcing restart")
|
||||||
|
if (sharedPreferencesViewModel.sharedPrefs.isOnMobileOrMeteredConnection) {
|
||||||
|
HttpClientManager.setDefaultTimeout(HttpClientManager.DEFAULT_TIMEOUT_ON_MOBILE)
|
||||||
|
} else {
|
||||||
|
HttpClientManager.setDefaultTimeout(HttpClientManager.DEFAULT_TIMEOUT_ON_WIFI)
|
||||||
|
}
|
||||||
|
accountViewModel.forceRestartServices()
|
||||||
|
}
|
||||||
|
|
||||||
|
LifecycleResumeEffect(sharedPreferencesViewModel, accountViewModel) {
|
||||||
|
job?.cancel()
|
||||||
|
Log.d("ManageRelayServices", "Starting Relay Services")
|
||||||
|
job = accountViewModel.justStart()
|
||||||
|
|
||||||
|
onPauseOrDispose {
|
||||||
|
job?.cancel()
|
||||||
|
job =
|
||||||
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
|
delay(30000) // 30 seconds
|
||||||
|
Log.d("ManageRelayServices", "Pausing Relay Services")
|
||||||
|
accountViewModel.justPause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NotificationRegistration(accountViewModel: AccountViewModel) {
|
fun NotificationRegistration(accountViewModel: AccountViewModel) {
|
||||||
val lifeCycleOwner = LocalLifecycleOwner.current
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
var job = remember<Job?> { null }
|
var job = remember<Job?> { null }
|
||||||
|
|
||||||
LaunchedEffect(accountViewModel) {
|
LifecycleResumeEffect(key1 = accountViewModel) {
|
||||||
|
Log.d("RegisterAccounts", "Registering for push notifications")
|
||||||
job?.cancel()
|
job?.cancel()
|
||||||
job =
|
job =
|
||||||
launch {
|
scope.launch {
|
||||||
val okHttpClient = HttpClientManager.getHttpClient(accountViewModel.account.shouldUseTorForTrustedRelays())
|
val okHttpClient = HttpClientManager.getHttpClient(accountViewModel.account.shouldUseTorForTrustedRelays())
|
||||||
PushNotificationUtils.checkAndInit(LocalPreferences.allSavedAccounts(), okHttpClient)
|
PushNotificationUtils.checkAndInit(LocalPreferences.allSavedAccounts(), okHttpClient)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
DisposableEffect(key1 = accountViewModel) {
|
onPauseOrDispose {
|
||||||
val observer =
|
job.cancel()
|
||||||
LifecycleEventObserver { _, event ->
|
|
||||||
when (event) {
|
|
||||||
Lifecycle.Event.ON_RESUME -> {
|
|
||||||
job?.cancel()
|
|
||||||
job =
|
|
||||||
scope.launch {
|
|
||||||
val okHttpClient = HttpClientManager.getHttpClient(accountViewModel.account.shouldUseTorForTrustedRelays())
|
|
||||||
PushNotificationUtils.checkAndInit(LocalPreferences.allSavedAccounts(), okHttpClient)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lifeCycleOwner.lifecycle.addObserver(observer)
|
|
||||||
onDispose {
|
|
||||||
lifeCycleOwner.lifecycle.removeObserver(observer)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,39 +163,21 @@ fun ManageTorInstance(accountViewModel: AccountViewModel) {
|
|||||||
@Composable
|
@Composable
|
||||||
fun ManageTorInstanceInner(accountViewModel: AccountViewModel) {
|
fun ManageTorInstanceInner(accountViewModel: AccountViewModel) {
|
||||||
val context = LocalContext.current.applicationContext
|
val context = LocalContext.current.applicationContext
|
||||||
val lifeCycleOwner = LocalLifecycleOwner.current
|
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
var job = remember<Job?> { null }
|
var job = remember<Job?> { null }
|
||||||
|
|
||||||
DisposableEffect(key1 = accountViewModel) {
|
LifecycleResumeEffect(key1 = accountViewModel) {
|
||||||
job?.cancel()
|
job?.cancel()
|
||||||
job = null
|
job = null
|
||||||
TorManager.startTorIfNotAlreadyOn(context)
|
TorManager.startTorIfNotAlreadyOn(context)
|
||||||
|
|
||||||
val observer =
|
onPauseOrDispose {
|
||||||
LifecycleEventObserver { _, event ->
|
job =
|
||||||
when (event) {
|
scope.launch {
|
||||||
Lifecycle.Event.ON_RESUME -> {
|
delay(30000) // 5 seconds
|
||||||
job?.cancel()
|
TorManager.stopTor(context)
|
||||||
job = null
|
|
||||||
TorManager.startTorIfNotAlreadyOn(context)
|
|
||||||
}
|
|
||||||
Lifecycle.Event.ON_PAUSE -> {
|
|
||||||
job =
|
|
||||||
scope.launch {
|
|
||||||
delay(5000) // 5 seconds
|
|
||||||
TorManager.stopTor(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
lifeCycleOwner.lifecycle.addObserver(observer)
|
|
||||||
onDispose {
|
|
||||||
lifeCycleOwner.lifecycle.removeObserver(observer)
|
|
||||||
TorManager.stopTor(context)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,6 +188,7 @@ fun WatchConnection(accountViewModel: AccountViewModel) {
|
|||||||
|
|
||||||
LaunchedEffect(key1 = status, key2 = accountViewModel) {
|
LaunchedEffect(key1 = status, key2 = accountViewModel) {
|
||||||
if (status is TorStatus.Active) {
|
if (status is TorStatus.Active) {
|
||||||
|
Log.d("ServiceManager", "Tor is Active, force restart connections")
|
||||||
accountViewModel.changeProxyPort((status as TorStatus.Active).port)
|
accountViewModel.changeProxyPort((status as TorStatus.Active).port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,7 +227,6 @@ private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) {
|
|||||||
accountViewModel.account.signer.launcher.registerLauncher(
|
accountViewModel.account.signer.launcher.registerLauncher(
|
||||||
launcher = {
|
launcher = {
|
||||||
try {
|
try {
|
||||||
activity.prepareToLaunchSigner()
|
|
||||||
launcher.launch(it)
|
launcher.launch(it)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
@@ -244,7 +246,6 @@ private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) {
|
|||||||
accountViewModel.account.signer.launcher.registerLauncher(
|
accountViewModel.account.signer.launcher.registerLauncher(
|
||||||
launcher = {
|
launcher = {
|
||||||
try {
|
try {
|
||||||
activity.prepareToLaunchSigner()
|
|
||||||
launcher.launch(it)
|
launcher.launch(it)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
|
|||||||
@@ -644,7 +644,6 @@ private fun PrepareExternalSignerReceiver(onLogin: (pubkey: String, packageName:
|
|||||||
externalSignerLauncher.registerLauncher(
|
externalSignerLauncher.registerLauncher(
|
||||||
launcher = {
|
launcher = {
|
||||||
try {
|
try {
|
||||||
activity.prepareToLaunchSigner()
|
|
||||||
launcher.launch(it)
|
launcher.launch(it)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
|
|||||||
Reference in New Issue
Block a user