Moves service manager to an Activity scoped class.

This commit is contained in:
Vitor Pamplona 2023-11-09 15:25:59 -05:00
parent 408b6793c0
commit 5ad6a2c70b
4 changed files with 60 additions and 53 deletions

View File

@ -1,14 +1,11 @@
package com.vitorpamplona.amethyst
import android.content.Context
import android.os.Build
import android.util.Log
import coil.Coil
import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import coil.decode.SvgDecoder
import coil.disk.DiskCache
import coil.util.DebugLogger
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.ExternalSignerUtils
@ -36,9 +33,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File
object ServiceManager {
class ServiceManager {
var shouldPauseService: Boolean = true // to not open amber in a loop trying to use auth relays and registering for notifications
private var isStarted: Boolean = false // to not open amber in a loop trying to use auth relays and registering for notifications
private var account: Account? = null
@ -69,7 +65,7 @@ object ServiceManager {
add(GifDecoder.Factory())
}
add(SvgDecoder.Factory())
}.logger(DebugLogger())
} // .logger(DebugLogger())
.okHttpClient { HttpClient.getHttpClient() }
.respectCacheHeaders(false)
.build()

View File

@ -54,6 +54,9 @@ import java.nio.charset.StandardCharsets
class MainActivity : AppCompatActivity() {
private val isOnMobileDataState = mutableStateOf(false)
// Service Manager is only active when the activity is active.
private val serviceManager = ServiceManager()
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
@RequiresApi(Build.VERSION_CODES.R)
override fun onCreate(savedInstanceState: Bundle?) {
@ -88,7 +91,7 @@ class MainActivity : AppCompatActivity() {
accountStateViewModel.tryLoginExistingAccountAsync()
}
AccountScreen(accountStateViewModel, sharedPreferencesViewModel)
AccountScreen(accountStateViewModel, sharedPreferencesViewModel, serviceManager)
}
}
}
@ -102,9 +105,9 @@ class MainActivity : AppCompatActivity() {
DefaultMutedSetting.value = true
// Only starts after login
if (ServiceManager.shouldPauseService) {
if (serviceManager.shouldPauseService) {
GlobalScope.launch(Dispatchers.IO) {
ServiceManager.justStart()
serviceManager.justStart()
}
}
@ -117,7 +120,7 @@ class MainActivity : AppCompatActivity() {
override fun onPause() {
LanguageTranslatorService.clear()
ServiceManager.cleanObservers()
serviceManager.cleanObservers()
// if (BuildConfig.DEBUG) {
GlobalScope.launch(Dispatchers.IO) {
@ -125,9 +128,9 @@ class MainActivity : AppCompatActivity() {
}
// }
if (ServiceManager.shouldPauseService) {
if (serviceManager.shouldPauseService) {
GlobalScope.launch(Dispatchers.IO) {
ServiceManager.pauseForGood()
serviceManager.pauseForGood()
}
}
@ -153,7 +156,7 @@ class MainActivity : AppCompatActivity() {
super.onTrimMemory(level)
println("Trim Memory $level")
GlobalScope.launch(Dispatchers.Default) {
ServiceManager.trimMemory()
serviceManager.trimMemory()
}
}
@ -163,7 +166,7 @@ class MainActivity : AppCompatActivity() {
super.onAvailable(network)
GlobalScope.launch(Dispatchers.IO) {
ServiceManager.forceRestartIfItShould()
serviceManager.forceRestartIfItShould()
}
}
@ -182,7 +185,7 @@ class MainActivity : AppCompatActivity() {
if (isOnMobileDataState.value != isOnMobileData) {
isOnMobileDataState.value = isOnMobileData
ServiceManager.forceRestartIfItShould()
serviceManager.forceRestartIfItShould()
}
}
}

View File

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -19,6 +20,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.MainScreen
@ -27,44 +29,55 @@ import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginPage
@Composable
fun AccountScreen(
accountStateViewModel: AccountStateViewModel,
sharedPreferencesViewModel: SharedPreferencesViewModel
sharedPreferencesViewModel: SharedPreferencesViewModel,
serviceManager: ServiceManager
) {
val accountState by accountStateViewModel.accountContent.collectAsStateWithLifecycle()
Column() {
Crossfade(
targetState = accountState,
animationSpec = tween(durationMillis = 100),
label = "AccountState"
) { state ->
when (state) {
is AccountState.Loading -> {
LoadingAccounts()
Crossfade(
targetState = accountState,
animationSpec = tween(durationMillis = 100),
label = "AccountState"
) { state ->
when (state) {
is AccountState.Loading -> {
LoadingAccounts()
}
is AccountState.LoggedOff -> {
LaunchedEffect(key1 = accountState) {
serviceManager.pauseForGood()
}
is AccountState.LoggedOff -> {
LoginPage(accountStateViewModel, isFirstLogin = true)
LoginPage(accountStateViewModel, isFirstLogin = true)
}
is AccountState.LoggedIn -> {
LaunchedEffect(key1 = accountState) {
serviceManager.restartIfDifferentAccount(state.account)
}
is AccountState.LoggedIn -> {
CompositionLocalProvider(
LocalViewModelStoreOwner provides state.currentViewModelStore
) {
LoggedInPage(
state.account,
accountStateViewModel,
sharedPreferencesViewModel
)
}
CompositionLocalProvider(
LocalViewModelStoreOwner provides state.currentViewModelStore
) {
LoggedInPage(
state.account,
accountStateViewModel,
sharedPreferencesViewModel
)
}
is AccountState.LoggedInViewOnly -> {
CompositionLocalProvider(
LocalViewModelStoreOwner provides state.currentViewModelStore
) {
LoggedInPage(
state.account,
accountStateViewModel,
sharedPreferencesViewModel
)
}
}
is AccountState.LoggedInViewOnly -> {
LaunchedEffect(key1 = accountState) {
serviceManager.restartIfDifferentAccount(state.account)
}
CompositionLocalProvider(
LocalViewModelStoreOwner provides state.currentViewModelStore
) {
LoggedInPage(
state.account,
accountStateViewModel,
sharedPreferencesViewModel
)
}
}
}
@ -78,7 +91,7 @@ fun LoggedInPage(
sharedPreferencesViewModel: SharedPreferencesViewModel
) {
val accountViewModel: AccountViewModel = viewModel(
key = "AccountStateViewModel",
key = "AccountViewModel",
factory = AccountViewModel.Factory(
account,
sharedPreferencesViewModel.sharedPrefs

View File

@ -6,7 +6,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.AccountInfo
import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.HttpClient
import com.vitorpamplona.quartz.crypto.KeyPair
@ -78,16 +77,12 @@ class AccountStateViewModel() : ViewModel() {
startUI(account)
}
@OptIn(DelicateCoroutinesApi::class)
suspend fun startUI(account: Account) = withContext(Dispatchers.Main) {
if (account.keyPair.privKey != null) {
_accountContent.update { AccountState.LoggedIn(account) }
} else {
_accountContent.update { AccountState.LoggedInViewOnly(account) }
}
GlobalScope.launch(Dispatchers.IO) {
ServiceManager.restartIfDifferentAccount(account)
}
account.saveable.observeForever(saveListener)
}