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
@@ -1,14 +1,11 @@
package com.vitorpamplona.amethyst package com.vitorpamplona.amethyst
import android.content.Context
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import coil.Coil import coil.Coil
import coil.decode.GifDecoder import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder import coil.decode.ImageDecoderDecoder
import coil.decode.SvgDecoder import coil.decode.SvgDecoder
import coil.disk.DiskCache
import coil.util.DebugLogger
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.ExternalSignerUtils import com.vitorpamplona.amethyst.service.ExternalSignerUtils
@@ -36,9 +33,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch 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 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 isStarted: Boolean = false // to not open amber in a loop trying to use auth relays and registering for notifications
private var account: Account? = null private var account: Account? = null
@@ -69,7 +65,7 @@ object ServiceManager {
add(GifDecoder.Factory()) add(GifDecoder.Factory())
} }
add(SvgDecoder.Factory()) add(SvgDecoder.Factory())
}.logger(DebugLogger()) } // .logger(DebugLogger())
.okHttpClient { HttpClient.getHttpClient() } .okHttpClient { HttpClient.getHttpClient() }
.respectCacheHeaders(false) .respectCacheHeaders(false)
.build() .build()
@@ -54,6 +54,9 @@ import java.nio.charset.StandardCharsets
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private val isOnMobileDataState = mutableStateOf(false) private val isOnMobileDataState = mutableStateOf(false)
// Service Manager is only active when the activity is active.
private val serviceManager = ServiceManager()
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class) @OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
@RequiresApi(Build.VERSION_CODES.R) @RequiresApi(Build.VERSION_CODES.R)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -88,7 +91,7 @@ class MainActivity : AppCompatActivity() {
accountStateViewModel.tryLoginExistingAccountAsync() accountStateViewModel.tryLoginExistingAccountAsync()
} }
AccountScreen(accountStateViewModel, sharedPreferencesViewModel) AccountScreen(accountStateViewModel, sharedPreferencesViewModel, serviceManager)
} }
} }
} }
@@ -102,9 +105,9 @@ class MainActivity : AppCompatActivity() {
DefaultMutedSetting.value = true DefaultMutedSetting.value = true
// Only starts after login // Only starts after login
if (ServiceManager.shouldPauseService) { if (serviceManager.shouldPauseService) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
ServiceManager.justStart() serviceManager.justStart()
} }
} }
@@ -117,7 +120,7 @@ class MainActivity : AppCompatActivity() {
override fun onPause() { override fun onPause() {
LanguageTranslatorService.clear() LanguageTranslatorService.clear()
ServiceManager.cleanObservers() serviceManager.cleanObservers()
// if (BuildConfig.DEBUG) { // if (BuildConfig.DEBUG) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
@@ -125,9 +128,9 @@ class MainActivity : AppCompatActivity() {
} }
// } // }
if (ServiceManager.shouldPauseService) { if (serviceManager.shouldPauseService) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
ServiceManager.pauseForGood() serviceManager.pauseForGood()
} }
} }
@@ -153,7 +156,7 @@ class MainActivity : AppCompatActivity() {
super.onTrimMemory(level) super.onTrimMemory(level)
println("Trim Memory $level") println("Trim Memory $level")
GlobalScope.launch(Dispatchers.Default) { GlobalScope.launch(Dispatchers.Default) {
ServiceManager.trimMemory() serviceManager.trimMemory()
} }
} }
@@ -163,7 +166,7 @@ class MainActivity : AppCompatActivity() {
super.onAvailable(network) super.onAvailable(network)
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
ServiceManager.forceRestartIfItShould() serviceManager.forceRestartIfItShould()
} }
} }
@@ -182,7 +185,7 @@ class MainActivity : AppCompatActivity() {
if (isOnMobileDataState.value != isOnMobileData) { if (isOnMobileDataState.value != isOnMobileData) {
isOnMobileDataState.value = isOnMobileData isOnMobileDataState.value = isOnMobileData
ServiceManager.forceRestartIfItShould() serviceManager.forceRestartIfItShould()
} }
} }
} }
@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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.LocalViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.MainScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.MainScreen
@@ -27,44 +29,55 @@ import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginPage
@Composable @Composable
fun AccountScreen( fun AccountScreen(
accountStateViewModel: AccountStateViewModel, accountStateViewModel: AccountStateViewModel,
sharedPreferencesViewModel: SharedPreferencesViewModel sharedPreferencesViewModel: SharedPreferencesViewModel,
serviceManager: ServiceManager
) { ) {
val accountState by accountStateViewModel.accountContent.collectAsStateWithLifecycle() val accountState by accountStateViewModel.accountContent.collectAsStateWithLifecycle()
Column() { Crossfade(
Crossfade( targetState = accountState,
targetState = accountState, animationSpec = tween(durationMillis = 100),
animationSpec = tween(durationMillis = 100), label = "AccountState"
label = "AccountState" ) { state ->
) { state -> when (state) {
when (state) { is AccountState.Loading -> {
is AccountState.Loading -> { LoadingAccounts()
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( CompositionLocalProvider(
LocalViewModelStoreOwner provides state.currentViewModelStore LocalViewModelStoreOwner provides state.currentViewModelStore
) { ) {
LoggedInPage( LoggedInPage(
state.account, state.account,
accountStateViewModel, accountStateViewModel,
sharedPreferencesViewModel sharedPreferencesViewModel
) )
}
} }
is AccountState.LoggedInViewOnly -> { }
CompositionLocalProvider( is AccountState.LoggedInViewOnly -> {
LocalViewModelStoreOwner provides state.currentViewModelStore LaunchedEffect(key1 = accountState) {
) { serviceManager.restartIfDifferentAccount(state.account)
LoggedInPage( }
state.account,
accountStateViewModel, CompositionLocalProvider(
sharedPreferencesViewModel LocalViewModelStoreOwner provides state.currentViewModelStore
) ) {
} LoggedInPage(
state.account,
accountStateViewModel,
sharedPreferencesViewModel
)
} }
} }
} }
@@ -78,7 +91,7 @@ fun LoggedInPage(
sharedPreferencesViewModel: SharedPreferencesViewModel sharedPreferencesViewModel: SharedPreferencesViewModel
) { ) {
val accountViewModel: AccountViewModel = viewModel( val accountViewModel: AccountViewModel = viewModel(
key = "AccountStateViewModel", key = "AccountViewModel",
factory = AccountViewModel.Factory( factory = AccountViewModel.Factory(
account, account,
sharedPreferencesViewModel.sharedPrefs sharedPreferencesViewModel.sharedPrefs
@@ -6,7 +6,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.AccountInfo import com.vitorpamplona.amethyst.AccountInfo
import com.vitorpamplona.amethyst.LocalPreferences import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.HttpClient import com.vitorpamplona.amethyst.service.HttpClient
import com.vitorpamplona.quartz.crypto.KeyPair import com.vitorpamplona.quartz.crypto.KeyPair
@@ -78,16 +77,12 @@ class AccountStateViewModel() : ViewModel() {
startUI(account) startUI(account)
} }
@OptIn(DelicateCoroutinesApi::class)
suspend fun startUI(account: Account) = withContext(Dispatchers.Main) { suspend fun startUI(account: Account) = withContext(Dispatchers.Main) {
if (account.keyPair.privKey != null) { if (account.keyPair.privKey != null) {
_accountContent.update { AccountState.LoggedIn(account) } _accountContent.update { AccountState.LoggedIn(account) }
} else { } else {
_accountContent.update { AccountState.LoggedInViewOnly(account) } _accountContent.update { AccountState.LoggedInViewOnly(account) }
} }
GlobalScope.launch(Dispatchers.IO) {
ServiceManager.restartIfDifferentAccount(account)
}
account.saveable.observeForever(saveListener) account.saveable.observeForever(saveListener)
} }