diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 24d60fac8d..dca939f558 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -45,6 +45,7 @@ import org.session.libsession.snode.SnodeModule; import org.session.libsession.utilities.Address; import org.session.libsession.utilities.ConfigFactoryUpdateListener; import org.session.libsession.utilities.Device; +import org.session.libsession.utilities.Environment; import org.session.libsession.utilities.ProfilePictureUtilities; import org.session.libsession.utilities.SSKEnvironment; import org.session.libsession.utilities.TextSecurePreferences; @@ -237,7 +238,8 @@ public class ApplicationContext extends Application implements DefaultLifecycleO messageNotifier = new OptimizedMessageNotifier(new DefaultMessageNotifier()); broadcaster = new Broadcaster(this); LokiAPIDatabase apiDB = getDatabaseComponent().lokiAPIDatabase(); - SnodeModule.Companion.configure(apiDB, broadcaster); + boolean useTestNet = textSecurePreferences.getEnvironment() == Environment.TEST_NET; + SnodeModule.Companion.configure(apiDB, broadcaster, useTestNet); initializeExpiringMessageManager(); initializeTypingStatusRepository(); initializeTypingStatusSender(); @@ -507,7 +509,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO // Method to clear the local data - returns true on success otherwise false /** - * Clear all local profile data and message history then restart the app after a brief delay. + * Clear all local profile data and message history. * @return true on success, false otherwise. */ @SuppressLint("ApplySharedPref") @@ -519,6 +521,16 @@ public class ApplicationContext extends Application implements DefaultLifecycleO return false; } configFactory.keyPairChanged(); + return true; + } + + /** + * Clear all local profile data and message history then restart the app after a brief delay. + * @return true on success, false otherwise. + */ + @SuppressLint("ApplySharedPref") + public boolean clearAllDataAndRestart() { + clearAllData(); Util.runOnMain(() -> new Handler().postDelayed(ApplicationContext.this::restartApplication, 200)); return true; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenu.kt b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenu.kt index 9892d6e77d..2625c951d3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenu.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenu.kt @@ -1,33 +1,37 @@ package org.thoughtcrime.securesms.debugmenu import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import network.loki.messenger.BuildConfig +import network.loki.messenger.R +import org.thoughtcrime.securesms.debugmenu.DebugMenuViewModel.Commands.ChangeEnvironment +import org.thoughtcrime.securesms.debugmenu.DebugMenuViewModel.Commands.HideEnvironmentWarningDialog +import org.thoughtcrime.securesms.debugmenu.DebugMenuViewModel.Commands.ShowEnvironmentWarningDialog +import org.thoughtcrime.securesms.ui.AlertDialog import org.thoughtcrime.securesms.ui.Cell -import org.thoughtcrime.securesms.ui.components.AppBarCloseIcon -import org.thoughtcrime.securesms.ui.components.AppBarText +import org.thoughtcrime.securesms.ui.DialogButtonModel +import org.thoughtcrime.securesms.ui.GetString +import org.thoughtcrime.securesms.ui.LoadingDialog import org.thoughtcrime.securesms.ui.components.BackAppBar import org.thoughtcrime.securesms.ui.components.DropDown -import org.thoughtcrime.securesms.ui.components.appBarColors import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalType @@ -42,37 +46,84 @@ fun DebugMenu( modifier: Modifier = Modifier, onClose: () -> Unit ){ - Column( - modifier = modifier - .fillMaxSize() - .background(color = LocalColors.current.background) - ) { - // App bar - BackAppBar(title = "Debug Menu", onBack = onClose) + val snackbarHostState = remember { SnackbarHostState() } + + Scaffold( + modifier = modifier.fillMaxSize(), + snackbarHost = { + SnackbarHost(hostState = snackbarHostState) + } + ) { contentPadding -> + // display a snackbar when required + LaunchedEffect(uiState.snackMessage) { + if(!uiState.snackMessage.isNullOrEmpty()){ + snackbarHostState.showSnackbar(uiState.snackMessage) + } + } + + // Alert dialogs + if (uiState.showEnvironmentWarningDialog) { + AlertDialog( + onDismissRequest = { sendCommand(HideEnvironmentWarningDialog) }, + title = "Are you sure you want to switch environments?", + text = "Changing this setting will result in all conversations and Snode data being cleared...", + showCloseButton = false, // display the 'x' button + buttons = listOf( + DialogButtonModel( + text = GetString(R.string.cancel), + contentDescription = GetString(R.string.cancel), + onClick = { sendCommand(HideEnvironmentWarningDialog) } + ), + DialogButtonModel( + text = GetString(R.string.ok), + contentDescription = GetString(R.string.ok), + onClick = { sendCommand(ChangeEnvironment) } + ) + ) + ) + } + + if (uiState.showEnvironmentLoadingDialog) { + LoadingDialog(title = "Changing Environment...") + } Column( modifier = Modifier - .padding(horizontal = LocalDimensions.current.spacing) - .verticalScroll(rememberScrollState()) + .padding(contentPadding) + .fillMaxSize() + .background(color = LocalColors.current.background) ) { - // Info pane - DebugCell("App Info"){ - Text( - text = "Version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE} - ${BuildConfig.GIT_HASH.take(6)})", - style = LocalType.current.base - ) - } + // App bar + BackAppBar(title = "Debug Menu", onBack = onClose) - // Environment - DebugCell("Environment"){ - DropDown( - modifier = Modifier.fillMaxWidth(0.6f), - selectedText = uiState.currentEnvironment, - values = uiState.environments, - onValueSelected = { - sendCommand(DebugMenuViewModel.Commands.ChangeEnvironment(it)) - } - ) + Column( + modifier = Modifier + .padding(horizontal = LocalDimensions.current.spacing) + .verticalScroll(rememberScrollState()) + ) { + // Info pane + DebugCell("App Info") { + Text( + text = "Version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE} - ${ + BuildConfig.GIT_HASH.take( + 6 + ) + })", + style = LocalType.current.base + ) + } + + // Environment + DebugCell("Environment") { + DropDown( + modifier = Modifier.fillMaxWidth(0.6f), + selectedText = uiState.currentEnvironment, + values = uiState.environments, + onValueSelected = { + sendCommand(ShowEnvironmentWarningDialog(it)) + } + ) + } } } } @@ -109,7 +160,10 @@ fun PreviewDebugMenu(){ DebugMenu( uiState = DebugMenuViewModel.UIState( currentEnvironment = "Development", - environments = listOf("Development", "Production") + environments = listOf("Development", "Production"), + snackMessage = null, + showEnvironmentWarningDialog = false, + showEnvironmentLoadingDialog = false ), sendCommand = {}, onClose = {} diff --git a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt index 49353b3158..750b3e20c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt @@ -1,12 +1,17 @@ package org.thoughtcrime.securesms.debugmenu import android.app.Application +import android.widget.Toast import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import network.loki.messenger.R import org.session.libsession.messaging.open_groups.OpenGroupApi import org.session.libsession.snode.SnodeAPI import org.session.libsession.utilities.TextSecurePreferences @@ -14,6 +19,7 @@ import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.ApplicationContext import org.session.libsession.utilities.Environment import org.thoughtcrime.securesms.dependencies.DatabaseComponent +import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import javax.inject.Inject @HiltViewModel @@ -26,59 +32,84 @@ class DebugMenuViewModel @Inject constructor( private val _uiState = MutableStateFlow( UIState( currentEnvironment = textSecurePreferences.getEnvironment().label, - environments = Environment.entries.map { it.label } + environments = Environment.entries.map { it.label }, + snackMessage = null, + showEnvironmentWarningDialog = false, + showEnvironmentLoadingDialog = false ) ) val uiState: StateFlow get() = _uiState + private var temporaryEnv: Environment? = null + fun onCommand(command: Commands) { when (command) { - is Commands.ChangeEnvironment -> changeEnvironment(command.environment) + is Commands.ChangeEnvironment -> changeEnvironment() + + is Commands.HideEnvironmentWarningDialog -> _uiState.value = + _uiState.value.copy(showEnvironmentWarningDialog = false) + + is Commands.ShowEnvironmentWarningDialog -> + showEnvironmentWarningDialog(command.environment) } } - private fun changeEnvironment(environment: String) { + private fun showEnvironmentWarningDialog(environment: String) { if(environment == _uiState.value.currentEnvironment) return val env = Environment.entries.firstOrNull { it.label == environment } ?: return + temporaryEnv = env + + _uiState.value = _uiState.value.copy(showEnvironmentWarningDialog = true) + } + + private fun changeEnvironment() { + val env = temporaryEnv ?: return + // show a loading state + _uiState.value = _uiState.value.copy( + showEnvironmentWarningDialog = false, + showEnvironmentLoadingDialog = true + ) // clear remote and local data, then restart the app viewModelScope.launch { - val deletionResultMap: Map? = try { - val openGroups = - DatabaseComponent.get(application).lokiThreadDatabase().getAllOpenGroups() - openGroups.map { it.value.server }.toSet().forEach { server -> - OpenGroupApi.deleteAllInboxMessages(server).get() - } - SnodeAPI.deleteAllMessages().get() + try { + ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(application).get() } catch (e: Exception) { - Log.e( - TAG, "Failed to delete network message from debug menu", e - ) - null + // we can ignore fails here as we might be switching environments before the user gets a public key } - - // If the network data deletion was successful proceed to delete the local data as well. - if (deletionResultMap?.values?.all { it } == true) { - // save the environment - textSecurePreferences.setEnvironment(env) - - // clear local data and restart - ApplicationContext.getInstance(application).clearAllData() - } else { // the remote deletion failed, show an error - + ApplicationContext.getInstance(application).clearAllData().let { success -> + if(success){ + // save the environment + textSecurePreferences.setEnvironment(env) + delay(500) + ApplicationContext.getInstance(application).restartApplication() + } else { + _uiState.value = _uiState.value.copy( + showEnvironmentWarningDialog = false, + showEnvironmentLoadingDialog = false + ) + Log.e(TAG, "Failed to force sync when deleting data") + _uiState.value = _uiState.value.copy(snackMessage = "Sorry, something went wrong...") + return@launch + } } } } data class UIState( val currentEnvironment: String, - val environments: List + val environments: List, + val snackMessage: String?, + val showEnvironmentWarningDialog: Boolean, + val showEnvironmentLoadingDialog: Boolean ) sealed class Commands { - data class ChangeEnvironment(val environment: String) : Commands() + object ChangeEnvironment : Commands() + data class ShowEnvironmentWarningDialog(val environment: String) : Commands() + object HideEnvironmentWarningDialog : Commands() } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotifications.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotifications.kt index e56b55aaab..c2f32e553c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotifications.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotifications.kt @@ -45,7 +45,7 @@ internal fun MessageNotificationsScreen( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { - CircularProgressIndicator(LocalColors.current.primary) + CircularProgressIndicator(color = LocalColors.current.primary) } return diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt index a39f270bf2..f1f5bee89f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsViewModel.kt @@ -72,7 +72,7 @@ internal class MessageNotificationsViewModel( _uiStates.update { it.copy(clearData = true) } viewModelScope.launch(Dispatchers.IO) { - ApplicationContext.getInstance(application).clearAllData() + ApplicationContext.getInstance(application).clearAllDataAndRestart() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt index 17d97dec7b..80c8da8a3f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/ClearAllDataDialog.kt @@ -125,7 +125,7 @@ class ClearAllDataDialog : DialogFragment() { } return } - ApplicationContext.getInstance(context).clearAllData().let { success -> + ApplicationContext.getInstance(context).clearAllDataAndRestart().let { success -> withContext(Main) { if (success) { dismiss() @@ -162,7 +162,7 @@ class ClearAllDataDialog : DialogFragment() { } else if (deletionResultMap.values.all { it }) { // ..otherwise if the network data deletion was successful proceed to delete the local data as well. - ApplicationContext.getInstance(context).clearAllData() + ApplicationContext.getInstance(context).clearAllDataAndRestart() withContext(Main) { dismiss() } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt index bfa85937aa..1a77201bc9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt @@ -177,7 +177,8 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { btnGroupNameDisplay.text = getDisplayName() publicKeyTextView.text = hexEncodedPublicKey val gitCommitFirstSixChars = BuildConfig.GIT_HASH.take(6) - versionTextView.text = String.format(getString(R.string.version_s), "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE} - $gitCommitFirstSixChars)") + val environment: String = if(BuildConfig.BUILD_TYPE == "release") "" else " - ${prefs.getEnvironment().label}" + versionTextView.text = String.format(getString(R.string.version_s), "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE} - $gitCommitFirstSixChars) $environment") } binding.composeView.setThemedContent { diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt index 5894e1072a..552e24cc58 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/AlertDialog.kt @@ -3,13 +3,16 @@ package org.thoughtcrime.securesms.ui import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -29,6 +32,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import network.loki.messenger.R +import org.thoughtcrime.securesms.ui.components.CircularProgressIndicator import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalType @@ -48,6 +52,7 @@ class DialogButtonModel( @Composable fun AlertDialog( onDismissRequest: () -> Unit, + modifier: Modifier = Modifier, title: String? = null, text: String? = null, buttons: List? = null, @@ -55,18 +60,10 @@ fun AlertDialog( content: @Composable () -> Unit = {} ) { BasicAlertDialog( + modifier = modifier, onDismissRequest = onDismissRequest, content = { - Box( - modifier = Modifier.background( - color = LocalColors.current.backgroundSecondary, - shape = MaterialTheme.shapes.small) - .border( - width = 1.dp, - color = LocalColors.current.borders, - shape = MaterialTheme.shapes.small) - - ) { + DialogBg { // only show the 'x' button is required if (showCloseButton) { IconButton( @@ -133,7 +130,7 @@ fun AlertDialog( @Composable fun DialogButton( text: String, - modifier: Modifier, + modifier: Modifier = Modifier, color: Color = Color.Unspecified, onClick: () -> Unit ) { @@ -154,6 +151,62 @@ fun DialogButton( } } +@Composable +fun DialogBg( + content: @Composable BoxScope.() -> Unit +){ + Box( + modifier = Modifier + .background( + color = LocalColors.current.backgroundSecondary, + shape = MaterialTheme.shapes.small + ) + .border( + width = 1.dp, + color = LocalColors.current.borders, + shape = MaterialTheme.shapes.small + ) + + ) { + content() + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun LoadingDialog( + modifier: Modifier = Modifier, + title: String? = null, +){ + BasicAlertDialog( + modifier = modifier, + onDismissRequest = {}, + content = { + DialogBg { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(LocalDimensions.current.spacing) + ) { + CircularProgressIndicator( + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + + Spacer(modifier = Modifier.height(LocalDimensions.current.spacing)) + + title?.let { + Text( + it, + modifier = Modifier.align(Alignment.CenterHorizontally), + style = LocalType.current.large + ) + } + } + } + } + ) +} + @Preview @Composable fun PreviewSimpleDialog() { @@ -200,3 +253,13 @@ fun PreviewXCloseDialog() { ) } } + +@Preview +@Composable +fun PreviewLoadingDialog() { + PreviewTheme { + LoadingDialog( + title = stringResource(R.string.warning) + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/components/CircularProgressIndicator.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/CircularProgressIndicator.kt index bbad82d29e..f555c82426 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/components/CircularProgressIndicator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/CircularProgressIndicator.kt @@ -8,18 +8,24 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp @Composable -fun CircularProgressIndicator(color: Color = LocalContentColor.current) { +fun CircularProgressIndicator( + modifier: Modifier = Modifier, + color: Color = LocalContentColor.current +) { androidx.compose.material3.CircularProgressIndicator( - modifier = Modifier.size(40.dp), + modifier = modifier.size(40.dp), color = color, strokeWidth = 2.dp ) } @Composable -fun SmallCircularProgressIndicator(color: Color = LocalContentColor.current) { +fun SmallCircularProgressIndicator( + modifier: Modifier = Modifier, + color: Color = LocalContentColor.current +) { androidx.compose.material3.CircularProgressIndicator( - modifier = Modifier.size(20.dp), + modifier = modifier.size(20.dp), color = color, strokeWidth = 2.dp ) diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index c3335c5a99..414394dff7 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -2,7 +2,6 @@ package org.session.libsession.snode -import android.os.Build import com.goterl.lazysodium.exceptions.SodiumException import com.goterl.lazysodium.interfaces.GenericHash import com.goterl.lazysodium.interfaces.PwHash @@ -76,9 +75,7 @@ object SnodeAPI { // Use port 4433 to enforce pinned certificates private val seedNodePort = 4443 - private const val useTestnet = false - - private val seedNodePool = if (useTestnet) setOf( + private val seedNodePool = if (SnodeModule.shared.useTestNet) setOf( "http://public.loki.foundation:38157" ) else setOf( "https://seed1.getsession.org:$seedNodePort", diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeModule.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeModule.kt index 2deb30998e..a048cc124f 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeModule.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeModule.kt @@ -3,16 +3,18 @@ package org.session.libsession.snode import org.session.libsignal.database.LokiAPIDatabaseProtocol import org.session.libsignal.utilities.Broadcaster -class SnodeModule(val storage: LokiAPIDatabaseProtocol, val broadcaster: Broadcaster) { +class SnodeModule( + val storage: LokiAPIDatabaseProtocol, val broadcaster: Broadcaster, val useTestNet: Boolean +) { companion object { lateinit var shared: SnodeModule val isInitialized: Boolean get() = Companion::shared.isInitialized - fun configure(storage: LokiAPIDatabaseProtocol, broadcaster: Broadcaster) { + fun configure(storage: LokiAPIDatabaseProtocol, broadcaster: Broadcaster, useTestNet: Boolean) { if (isInitialized) { return } - shared = SnodeModule(storage, broadcaster) + shared = SnodeModule(storage, broadcaster, useTestNet) } } } \ No newline at end of file