diff --git a/app/build.gradle b/app/build.gradle
index df003aa855..94edee60ad 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,6 +241,7 @@ dependencies {
     implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
     implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
     implementation "androidx.lifecycle:lifecycle-process:$lifecycleVersion"
+    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycleVersion"
     implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
     implementation "androidx.paging:paging-runtime-ktx:$pagingVersion"
     implementation 'androidx.activity:activity-ktx:1.5.1'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 98f9aa4b5f..f8765ff7bd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -136,6 +136,10 @@
             android:name="org.thoughtcrime.securesms.preferences.SettingsActivity"
             android:screenOrientation="portrait"
             android:label="@string/activity_settings_title" />
+        <activity
+            android:name="org.thoughtcrime.securesms.debugmenu.DebugActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/Theme.Session.DayNight.NoActionBar" />
         <activity
             android:name="org.thoughtcrime.securesms.home.PathActivity"
             android:screenOrientation="portrait" />
diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java
index b23a6741cb..dca939f558 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java
@@ -27,6 +27,9 @@ import android.os.Handler;
 import android.os.HandlerThread;
 
 import androidx.annotation.NonNull;
+import androidx.core.content.pm.ShortcutInfoCompat;
+import androidx.core.content.pm.ShortcutManagerCompat;
+import androidx.core.graphics.drawable.IconCompat;
 import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.ProcessLifecycleOwner;
@@ -42,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;
@@ -62,6 +66,7 @@ import org.thoughtcrime.securesms.database.LokiAPIDatabase;
 import org.thoughtcrime.securesms.database.Storage;
 import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
 import org.thoughtcrime.securesms.database.model.EmojiSearchData;
+import org.thoughtcrime.securesms.debugmenu.DebugActivity;
 import org.thoughtcrime.securesms.dependencies.AppComponent;
 import org.thoughtcrime.securesms.dependencies.ConfigFactory;
 import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
@@ -107,6 +112,7 @@ import dagger.hilt.EntryPoints;
 import dagger.hilt.android.HiltAndroidApp;
 import kotlin.Unit;
 import network.loki.messenger.BuildConfig;
+import network.loki.messenger.R;
 import network.loki.messenger.libsession_util.ConfigBase;
 import network.loki.messenger.libsession_util.UserProfile;
 
@@ -232,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();
@@ -248,6 +255,22 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
 
         NetworkConstraint networkConstraint = new NetworkConstraint.Factory(this).create();
         HTTP.INSTANCE.setConnectedToNetwork(networkConstraint::isMet);
+
+        // add our shortcut debug menu if we are not in a release build
+        if (BuildConfig.BUILD_TYPE != "release") {
+            // add the config settings shortcut
+            Intent intent = new Intent(this, DebugActivity.class);
+            intent.setAction(Intent.ACTION_VIEW);
+
+            ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(this, "shortcut_debug_menu")
+                    .setShortLabel("Debug Menu")
+                    .setLongLabel("Debug Menu")
+                    .setIcon(IconCompat.createWithResource(this, R.drawable.ic_settings))
+                    .setIntent(intent)
+                    .build();
+
+            ShortcutManagerCompat.pushDynamicShortcut(this, shortcut);
+        }
     }
 
     @Override
@@ -486,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")
@@ -498,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/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt
index d3192abc05..69844e33b6 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt
@@ -2085,7 +2085,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
     override fun showMessageDetail(messages: Set<MessageRecord>) {
         Intent(this, MessageDetailActivity::class.java)
             .apply { putExtra(MESSAGE_TIMESTAMP, messages.first().timestamp) }
-            .let { handleMessageDetail.launch(it) }
+            .let {
+                handleMessageDetail.launch(it)
+                overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
+            }
 
         endActionMode()
     }
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt
index b6f9e148b5..065ae8e11d 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.conversation.v2
 
 import android.annotation.SuppressLint
 import android.content.Intent
+import android.net.Uri
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.MotionEvent.ACTION_UP
@@ -15,6 +16,7 @@ import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ExperimentalLayoutApi
 import androidx.compose.foundation.layout.FlowRow
 import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.aspectRatio
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -28,6 +30,7 @@ import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
@@ -35,6 +38,7 @@ import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.res.painterResource
@@ -58,23 +62,21 @@ import org.thoughtcrime.securesms.ui.Avatar
 import org.thoughtcrime.securesms.ui.CarouselNextButton
 import org.thoughtcrime.securesms.ui.CarouselPrevButton
 import org.thoughtcrime.securesms.ui.Cell
-import org.thoughtcrime.securesms.ui.CellNoMargin
-import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
 import org.thoughtcrime.securesms.ui.Divider
 import org.thoughtcrime.securesms.ui.GetString
 import org.thoughtcrime.securesms.ui.HorizontalPagerIndicator
 import org.thoughtcrime.securesms.ui.LargeItemButton
+import org.thoughtcrime.securesms.ui.TitledText
+import org.thoughtcrime.securesms.ui.setComposeContent
+import org.thoughtcrime.securesms.ui.theme.LocalColors
 import org.thoughtcrime.securesms.ui.theme.LocalDimensions
+import org.thoughtcrime.securesms.ui.theme.LocalType
 import org.thoughtcrime.securesms.ui.theme.PreviewTheme
 import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
-import org.thoughtcrime.securesms.ui.TitledText
 import org.thoughtcrime.securesms.ui.theme.ThemeColors
-import org.thoughtcrime.securesms.ui.theme.LocalColors
 import org.thoughtcrime.securesms.ui.theme.blackAlpha40
-import org.thoughtcrime.securesms.ui.theme.dangerButtonColors
-import org.thoughtcrime.securesms.ui.setComposeContent
-import org.thoughtcrime.securesms.ui.theme.LocalType
 import org.thoughtcrime.securesms.ui.theme.bold
+import org.thoughtcrime.securesms.ui.theme.dangerButtonColors
 import org.thoughtcrime.securesms.ui.theme.monospace
 import javax.inject.Inject
 
@@ -191,8 +193,11 @@ fun CellMetadata(
 ) {
     state.apply {
         if (listOfNotNull(sent, received, error, senderInfo).isEmpty()) return
-        CellWithPaddingAndMargin {
-            Column(verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)) {
+        Cell(modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing)) {
+            Column(
+                modifier = Modifier.padding(LocalDimensions.current.spacing),
+                verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)
+            ) {
                 TitledText(sent)
                 TitledText(received)
                 TitledErrorText(error)
@@ -215,7 +220,7 @@ fun CellButtons(
     onResend: (() -> Unit)? = null,
     onDelete: () -> Unit = {},
 ) {
-    Cell {
+    Cell(modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing)) {
         Column {
             onReply?.let {
                 LargeItemButton(
@@ -254,8 +259,11 @@ fun Carousel(attachments: List<Attachment>, onClick: (Int) -> Unit) {
         Row {
             CarouselPrevButton(pagerState)
             Box(modifier = Modifier.weight(1f)) {
-                CellCarousel(pagerState, attachments, onClick)
-                HorizontalPagerIndicator(pagerState)
+                CarouselPager(pagerState, attachments, onClick)
+                HorizontalPagerIndicator(
+                    pagerState = pagerState,
+                    modifier = Modifier.padding(bottom = LocalDimensions.current.xxsSpacing)
+                )
                 ExpandButton(
                     modifier = Modifier
                         .align(Alignment.BottomEnd)
@@ -273,12 +281,15 @@ fun Carousel(attachments: List<Attachment>, onClick: (Int) -> Unit) {
     ExperimentalGlideComposeApi::class
 )
 @Composable
-private fun CellCarousel(
+private fun CarouselPager(
     pagerState: PagerState,
     attachments: List<Attachment>,
     onClick: (Int) -> Unit
 ) {
-    CellNoMargin {
+    Cell(
+        modifier = Modifier
+            .clip(MaterialTheme.shapes.small)
+    ) {
         HorizontalPager(state = pagerState) { i ->
             GlideImage(
                 contentScale = ContentScale.Crop,
@@ -317,6 +328,33 @@ fun PreviewMessageDetails(
     PreviewTheme(colors) {
         MessageDetails(
             state = MessageDetailsState(
+                imageAttachments = listOf(
+                    Attachment(
+                        fileDetails = listOf(
+                            TitledText(R.string.message_details_header__file_id, "Screen Shot 2023-07-06 at 11.35.50 am.png")
+                        ),
+                        fileName = "Screen Shot 2023-07-06 at 11.35.50 am.png",
+                        uri = Uri.parse(""),
+                        hasImage = true
+                    ),
+                    Attachment(
+                        fileDetails = listOf(
+                            TitledText(R.string.message_details_header__file_id, "Screen Shot 2023-07-06 at 11.35.50 am.png")
+                        ),
+                        fileName = "Screen Shot 2023-07-06 at 11.35.50 am.png",
+                        uri = Uri.parse(""),
+                        hasImage = true
+                    ),
+                    Attachment(
+                        fileDetails = listOf(
+                            TitledText(R.string.message_details_header__file_id, "Screen Shot 2023-07-06 at 11.35.50 am.png")
+                        ),
+                        fileName = "Screen Shot 2023-07-06 at 11.35.50 am.png",
+                        uri = Uri.parse(""),
+                        hasImage = true
+                    )
+
+                ),
                 nonImageAttachmentFileDetails = listOf(
                     TitledText(R.string.message_details_header__file_id, "Screen Shot 2023-07-06 at 11.35.50 am.png"),
                     TitledText(R.string.message_details_header__file_type, "image/png"),
@@ -337,7 +375,7 @@ fun PreviewMessageDetails(
 fun FileDetails(fileDetails: List<TitledText>) {
     if (fileDetails.isEmpty()) return
 
-    Cell {
+    Cell(modifier = Modifier.padding(horizontal = LocalDimensions.current.spacing)) {
         FlowRow(
             modifier = Modifier.padding(horizontal = LocalDimensions.current.xsSpacing, vertical = LocalDimensions.current.spacing),
             verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugActivity.kt
new file mode 100644
index 0000000000..828b3c3a1e
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugActivity.kt
@@ -0,0 +1,21 @@
+package org.thoughtcrime.securesms.debugmenu
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import dagger.hilt.android.AndroidEntryPoint
+import org.thoughtcrime.securesms.ui.setComposeContent
+
+
+@AndroidEntryPoint
+class DebugActivity : AppCompatActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setComposeContent {
+            DebugMenuScreen(
+                onClose = { finish() }
+            )
+        }
+    }
+}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenu.kt b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenu.kt
new file mode 100644
index 0000000000..5eb378ef30
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenu.kt
@@ -0,0 +1,172 @@
+package org.thoughtcrime.securesms.debugmenu
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+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.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+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.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.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.theme.LocalColors
+import org.thoughtcrime.securesms.ui.theme.LocalDimensions
+import org.thoughtcrime.securesms.ui.theme.LocalType
+import org.thoughtcrime.securesms.ui.theme.PreviewTheme
+import org.thoughtcrime.securesms.ui.theme.bold
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun DebugMenu(
+    uiState: DebugMenuViewModel.UIState,
+    sendCommand: (DebugMenuViewModel.Commands) -> Unit,
+    modifier: Modifier = Modifier,
+    onClose: () -> Unit
+){
+    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, // don't 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(contentPadding)
+                .fillMaxSize()
+                .background(color = LocalColors.current.background)
+        ) {
+            // App bar
+            BackAppBar(title = "Debug Menu", onBack = onClose)
+
+            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))
+                        }
+                    )
+                }
+            }
+        }
+    }
+}
+
+@Composable
+fun ColumnScope.DebugCell(
+    title: String,
+    modifier: Modifier = Modifier,
+    content: @Composable () -> Unit
+) {
+    Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing))
+
+    Cell {
+        Column(
+            modifier = modifier.padding(LocalDimensions.current.spacing)
+        ) {
+            Text(
+                text = title,
+                style = LocalType.current.large.bold()
+            )
+
+            Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing))
+
+            content()
+        }
+    }
+}
+
+@Preview
+@Composable
+fun PreviewDebugMenu(){
+    PreviewTheme {
+        DebugMenu(
+            uiState = DebugMenuViewModel.UIState(
+                currentEnvironment = "Development",
+                environments = listOf("Development", "Production"),
+                snackMessage = null,
+                showEnvironmentWarningDialog = false,
+                showEnvironmentLoadingDialog = false
+            ),
+            sendCommand = {},
+            onClose = {}
+        )
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuScreen.kt
new file mode 100644
index 0000000000..6c0f22805a
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuScreen.kt
@@ -0,0 +1,23 @@
+package org.thoughtcrime.securesms.debugmenu
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.viewmodel.compose.viewModel
+
+@Composable
+fun DebugMenuScreen(
+    modifier: Modifier = Modifier,
+    debugMenuViewModel: DebugMenuViewModel = viewModel(),
+    onClose: () -> Unit
+) {
+    val uiState by debugMenuViewModel.uiState.collectAsState()
+
+    DebugMenu(
+        modifier = modifier,
+        uiState = uiState,
+        sendCommand = debugMenuViewModel::onCommand,
+        onClose = 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
new file mode 100644
index 0000000000..750b3e20c7
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt
@@ -0,0 +1,115 @@
+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
+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
+class DebugMenuViewModel @Inject constructor(
+    private val application: Application,
+    private val textSecurePreferences: TextSecurePreferences
+) : ViewModel() {
+    private val TAG = "DebugMenu"
+
+    private val _uiState = MutableStateFlow(
+        UIState(
+            currentEnvironment = textSecurePreferences.getEnvironment().label,
+            environments = Environment.entries.map { it.label },
+            snackMessage = null,
+            showEnvironmentWarningDialog = false,
+            showEnvironmentLoadingDialog = false
+        )
+    )
+    val uiState: StateFlow<UIState>
+        get() = _uiState
+
+    private var temporaryEnv: Environment? = null
+
+    fun onCommand(command: Commands) {
+        when (command) {
+            is Commands.ChangeEnvironment -> changeEnvironment()
+
+            is Commands.HideEnvironmentWarningDialog -> _uiState.value =
+                _uiState.value.copy(showEnvironmentWarningDialog = false)
+
+            is Commands.ShowEnvironmentWarningDialog ->
+                showEnvironmentWarningDialog(command.environment)
+        }
+    }
+
+    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 {
+            try {
+                ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(application).get()
+            } catch (e: Exception) {
+                // we can ignore fails here as we might be switching environments before the user gets a public key
+            }
+            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<String>,
+        val snackMessage: String?,
+        val showEnvironmentWarningDialog: Boolean,
+        val showEnvironmentLoadingDialog: Boolean
+    )
+
+    sealed class 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 e8b804f036..1a77201bc9 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt
@@ -68,6 +68,7 @@ import org.session.libsignal.utilities.Util.SECURE_RANDOM
 import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
 import org.thoughtcrime.securesms.avatar.AvatarSelection
 import org.thoughtcrime.securesms.components.ProfilePictureView
+import org.thoughtcrime.securesms.debugmenu.DebugActivity
 import org.thoughtcrime.securesms.dependencies.ConfigFactory
 import org.thoughtcrime.securesms.home.PathActivity
 import org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity
@@ -91,7 +92,6 @@ import org.thoughtcrime.securesms.util.BitmapUtil
 import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
 import org.thoughtcrime.securesms.util.NetworkUtils
 import org.thoughtcrime.securesms.util.push
-import org.thoughtcrime.securesms.util.show
 import java.io.File
 import javax.inject.Inject
 
@@ -162,6 +162,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
         super.onCreate(savedInstanceState, isReady)
         binding = ActivitySettingsBinding.inflate(layoutInflater)
         setContentView(binding.root)
+
+        // set the toolbar icon to a close icon
+        supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_baseline_close_24)
     }
 
     override fun onStart() {
@@ -174,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 {
@@ -182,6 +186,11 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
         }
     }
 
+    override fun finish() {
+        super.finish()
+        overridePendingTransition(R.anim.fade_scale_in, R.anim.slide_to_bottom)
+    }
+
     private fun getDisplayName(): String =
         TextSecurePreferences.getProfileName(this) ?: truncateIdForDisplay(hexEncodedPublicKey)
 
@@ -473,10 +482,12 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
 
     @Composable
     fun Buttons() {
-        Column {
+        Column(
+            modifier = Modifier
+                .padding(horizontal = LocalDimensions.current.spacing)
+        ) {
             Row(
                 modifier = Modifier
-                    .padding(horizontal = LocalDimensions.current.spacing)
                     .padding(top = LocalDimensions.current.xxsSpacing),
                 horizontalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing),
             ) {
@@ -498,27 +509,33 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
 
             Cell {
                 Column {
+                    // add the debug menu in non release builds
+                    if (BuildConfig.BUILD_TYPE != "release") {
+                        LargeItemButton(R.string.activity_settings_debug_button_title, R.drawable.ic_settings) { push<DebugActivity>() }
+                        Divider()
+                    }
+
                     Crossfade(if (hasPaths) R.drawable.ic_status else R.drawable.ic_path_yellow, label = "path") {
-                        LargeItemButtonWithDrawable(R.string.activity_path_title, it) { show<PathActivity>() }
+                        LargeItemButtonWithDrawable(R.string.activity_path_title, it) { push<PathActivity>() }
                     }
                     Divider()
-                    LargeItemButton(R.string.activity_settings_privacy_button_title, R.drawable.ic_privacy_icon) { show<PrivacySettingsActivity>() }
+                    LargeItemButton(R.string.activity_settings_privacy_button_title, R.drawable.ic_privacy_icon) { push<PrivacySettingsActivity>() }
                     Divider()
-                    LargeItemButton(R.string.activity_settings_notifications_button_title, R.drawable.ic_speaker, Modifier.contentDescription(R.string.AccessibilityId_notifications)) { show<NotificationSettingsActivity>() }
+                    LargeItemButton(R.string.activity_settings_notifications_button_title, R.drawable.ic_speaker, Modifier.contentDescription(R.string.AccessibilityId_notifications)) { push<NotificationSettingsActivity>() }
                     Divider()
-                    LargeItemButton(R.string.activity_settings_conversations_button_title, R.drawable.ic_conversations, Modifier.contentDescription(R.string.AccessibilityId_conversations)) { show<ChatSettingsActivity>() }
+                    LargeItemButton(R.string.activity_settings_conversations_button_title, R.drawable.ic_conversations, Modifier.contentDescription(R.string.AccessibilityId_conversations)) { push<ChatSettingsActivity>() }
                     Divider()
-                    LargeItemButton(R.string.activity_settings_message_requests_button_title, R.drawable.ic_message_requests, Modifier.contentDescription(R.string.AccessibilityId_message_requests)) { show<MessageRequestsActivity>() }
+                    LargeItemButton(R.string.activity_settings_message_requests_button_title, R.drawable.ic_message_requests, Modifier.contentDescription(R.string.AccessibilityId_message_requests)) { push<MessageRequestsActivity>() }
                     Divider()
-                    LargeItemButton(R.string.activity_settings_message_appearance_button_title, R.drawable.ic_appearance, Modifier.contentDescription(R.string.AccessibilityId_appearance)) { show<AppearanceSettingsActivity>() }
+                    LargeItemButton(R.string.activity_settings_message_appearance_button_title, R.drawable.ic_appearance, Modifier.contentDescription(R.string.AccessibilityId_appearance)) { push<AppearanceSettingsActivity>() }
                     Divider()
                     LargeItemButton(R.string.activity_settings_invite_button_title, R.drawable.ic_invite_friend, Modifier.contentDescription(R.string.AccessibilityId_invite_friend)) { sendInvitationToUseSession() }
                     Divider()
                     if (!prefs.getHidePassword()) {
-                        LargeItemButton(R.string.sessionRecoveryPassword, R.drawable.ic_shield_outline, Modifier.contentDescription(R.string.AccessibilityId_recovery_password_menu_item)) { show<RecoveryPasswordActivity>() }
+                        LargeItemButton(R.string.sessionRecoveryPassword, R.drawable.ic_shield_outline, Modifier.contentDescription(R.string.AccessibilityId_recovery_password_menu_item)) { push<RecoveryPasswordActivity>() }
                         Divider()
                     }
-                    LargeItemButton(R.string.activity_settings_help_button, R.drawable.ic_help, Modifier.contentDescription(R.string.AccessibilityId_help)) { show<HelpSettingsActivity>() }
+                    LargeItemButton(R.string.activity_settings_help_button, R.drawable.ic_help, Modifier.contentDescription(R.string.AccessibilityId_help)) { push<HelpSettingsActivity>() }
                     Divider()
                     LargeItemButton(R.string.activity_settings_clear_all_data_button_title, R.drawable.ic_delete, Modifier.contentDescription(R.string.AccessibilityId_clear_data), dangerButtonColors()) { ClearAllDataDialog().show(supportFragmentManager, "Clear All Data Dialog") }
                 }
diff --git a/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPassword.kt b/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPassword.kt
index 5f59943432..0b47708b5d 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPassword.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/recoverypassword/RecoveryPassword.kt
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.recoverypassword
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.height
@@ -25,19 +26,19 @@ import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.tooling.preview.PreviewParameter
 import androidx.compose.ui.unit.dp
 import network.loki.messenger.R
-import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
-import org.thoughtcrime.securesms.ui.theme.LocalDimensions
-import org.thoughtcrime.securesms.ui.theme.PreviewTheme
-import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
+import org.thoughtcrime.securesms.ui.Cell
 import org.thoughtcrime.securesms.ui.SessionShieldIcon
-import org.thoughtcrime.securesms.ui.theme.ThemeColors
-import org.thoughtcrime.securesms.ui.theme.LocalColors
 import org.thoughtcrime.securesms.ui.components.QrImage
 import org.thoughtcrime.securesms.ui.components.SlimOutlineButton
 import org.thoughtcrime.securesms.ui.components.SlimOutlineCopyButton
 import org.thoughtcrime.securesms.ui.components.border
 import org.thoughtcrime.securesms.ui.contentDescription
+import org.thoughtcrime.securesms.ui.theme.LocalColors
+import org.thoughtcrime.securesms.ui.theme.LocalDimensions
 import org.thoughtcrime.securesms.ui.theme.LocalType
+import org.thoughtcrime.securesms.ui.theme.PreviewTheme
+import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider
+import org.thoughtcrime.securesms.ui.theme.ThemeColors
 import org.thoughtcrime.securesms.ui.theme.monospace
 
 @Composable
@@ -53,6 +54,7 @@ internal fun RecoveryPasswordScreen(
             .contentDescription(R.string.AccessibilityId_recovery_password)
             .verticalScroll(rememberScrollState())
             .padding(bottom = LocalDimensions.current.smallSpacing)
+            .padding(horizontal = LocalDimensions.current.spacing)
     ) {
         RecoveryPasswordCell(mnemonic, seed, copyMnemonic)
         HideRecoveryPasswordCell(onHide)
@@ -69,8 +71,10 @@ private fun RecoveryPasswordCell(
         mutableStateOf(false)
     }
 
-    CellWithPaddingAndMargin {
-        Column {
+    Cell {
+        Column(
+            modifier = Modifier.padding(LocalDimensions.current.smallSpacing)
+        ) {
             Row {
                 Text(
                     stringResource(R.string.sessionRecoveryPassword),
@@ -148,8 +152,10 @@ private fun RecoveryPassword(mnemonic: String) {
 
 @Composable
 private fun HideRecoveryPasswordCell(onHide: () -> Unit = {}) {
-    CellWithPaddingAndMargin {
-        Row {
+    Cell {
+        Row(
+            modifier = Modifier.padding(LocalDimensions.current.smallSpacing)
+        ) {
             Column(
                 Modifier.weight(1f)
             ) {
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<DialogButtonModel>? = 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/Carousel.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt
index ea632d46e7..d94cfc929d 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Carousel.kt
@@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
@@ -42,14 +43,17 @@ import kotlin.math.sign
 
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
-fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) {
-    if (pagerState.pageCount >= 2) Box(
-        modifier = Modifier
-            .background(color = blackAlpha40, shape = pillShape)
-            .align(Alignment.BottomCenter)
-            .padding(LocalDimensions.current.xxsSpacing)
-    ) {
-        Box(modifier = Modifier.padding(LocalDimensions.current.xxsSpacing)) {
+fun BoxScope.HorizontalPagerIndicator(
+    pagerState: PagerState,
+    modifier: Modifier = Modifier
+) {
+    if (pagerState.pageCount >= 2){
+        Box(
+            modifier = modifier
+                .background(color = blackAlpha40, shape = pillShape)
+                .align(Alignment.BottomCenter)
+                .padding(LocalDimensions.current.xxsSpacing)
+        ) {
             ClickableHorizontalPagerIndicator(
                 pagerState = pagerState,
                 pageCount = pagerState.pageCount
diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt
index daeb6b853d..df64f093ec 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/ui/Components.kt
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -105,7 +106,7 @@ fun <T> OptionsCard(card: OptionsCardData<T>, callbacks: Callbacks<T>) {
 
         Spacer(modifier = Modifier.height(LocalDimensions.current.xsSpacing))
 
-        CellNoMargin {
+        Cell {
             LazyColumn(
                 modifier = Modifier.heightIn(max = 5000.dp)
             ) {
@@ -257,32 +258,19 @@ fun PrewviewItemButton() {
 
 @Composable
 fun Cell(
-    padding: Dp = 0.dp,
-    margin: Dp = LocalDimensions.current.spacing,
-    content: @Composable () -> Unit
-) {
-    CellWithPaddingAndMargin(padding, margin) { content() }
-}
-@Composable
-fun CellNoMargin(content: @Composable () -> Unit) {
-    CellWithPaddingAndMargin(padding = 0.dp, margin = 0.dp) { content() }
-}
-
-@Composable
-fun CellWithPaddingAndMargin(
-    padding: Dp = LocalDimensions.current.spacing,
-    margin: Dp = LocalDimensions.current.spacing,
+    modifier: Modifier = Modifier,
     content: @Composable () -> Unit
 ) {
     Box(
-        modifier = Modifier
-            .padding(horizontal = margin)
-            .background(color = LocalColors.current.backgroundSecondary,
-                shape = MaterialTheme.shapes.small)
+        modifier = modifier
+            .background(
+                color = LocalColors.current.backgroundSecondary,
+                shape = MaterialTheme.shapes.small
+            )
             .wrapContentHeight()
             .fillMaxWidth(),
     ) {
-        Box(Modifier.padding(padding)) { content() }
+        content()
     }
 }
 
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/app/src/main/java/org/thoughtcrime/securesms/ui/components/DropDown.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/components/DropDown.kt
new file mode 100644
index 0000000000..de7f437356
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/ui/components/DropDown.kt
@@ -0,0 +1,109 @@
+package org.thoughtcrime.securesms.ui.components
+
+import androidx.compose.foundation.border
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuBox
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.MenuDefaults
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import org.thoughtcrime.securesms.ui.theme.LocalColors
+import org.thoughtcrime.securesms.ui.theme.LocalType
+import org.thoughtcrime.securesms.ui.theme.PreviewTheme
+import org.thoughtcrime.securesms.ui.theme.bold
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun DropDown(
+    modifier: Modifier = Modifier,
+    selectedText: String,
+    values: List<String>,
+    onValueSelected: (String) -> Unit
+) {
+    var expanded by remember { mutableStateOf(false) }
+
+    ExposedDropdownMenuBox(
+        modifier = modifier,
+        expanded = expanded,
+        onExpandedChange = {
+            expanded = !expanded
+        }
+    ) {
+        TextField(
+            value = selectedText,
+            onValueChange = {},
+            readOnly = true,
+            trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
+            modifier = Modifier
+                .menuAnchor()
+                .border(
+                    1.dp,
+                    color = LocalColors.current.borders,
+                    shape = MaterialTheme.shapes.medium
+                ),
+            shape = MaterialTheme.shapes.medium,
+            colors = ExposedDropdownMenuDefaults.textFieldColors(
+                focusedContainerColor = LocalColors.current.backgroundSecondary,
+                unfocusedContainerColor = LocalColors.current.backgroundSecondary,
+                disabledIndicatorColor = Color.Transparent,
+                errorIndicatorColor = Color.Transparent,
+                focusedIndicatorColor = Color.Transparent,
+                unfocusedIndicatorColor = Color.Transparent,
+                disabledTrailingIconColor = LocalColors.current.primary,
+                errorTrailingIconColor = LocalColors.current.primary,
+                focusedTrailingIconColor = LocalColors.current.primary,
+                unfocusedTrailingIconColor = LocalColors.current.primary,
+                disabledTextColor = LocalColors.current.text,
+                errorTextColor = LocalColors.current.text,
+                focusedTextColor = LocalColors.current.text,
+                unfocusedTextColor = LocalColors.current.text
+            ),
+            textStyle = LocalType.current.base.bold()
+        )
+
+        ExposedDropdownMenu(
+            expanded = expanded,
+            onDismissRequest = { expanded = false }
+        ) {
+            values.forEach { item ->
+                DropdownMenuItem(
+                    text = {
+                        Text(
+                            text = item,
+                            style = LocalType.current.base
+                        )
+                    },
+                    colors = MenuDefaults.itemColors(
+                        textColor = LocalColors.current.text
+                    ),
+                    onClick = {
+                        expanded = false
+                        onValueSelected(item)
+                    }
+                )
+            }
+        }
+    }
+}
+
+@Preview
+@Composable
+fun PreviewDropDown() {
+    PreviewTheme {
+        DropDown(
+            selectedText = "Hello",
+            values = listOf("First Item", "Second Item", "Third Item"),
+            onValueSelected = {})
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/SessionTypography.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/SessionTypography.kt
index 602affa6af..50c3957cbd 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/SessionTypography.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/SessionTypography.kt
@@ -8,11 +8,11 @@ import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.sp
 
 
-fun TextStyle.bold() = TextStyle.Default.copy(
+fun TextStyle.bold() = copy(
     fontWeight = FontWeight.Bold
 )
 
-fun TextStyle.monospace() = TextStyle.Default.copy(
+fun TextStyle.monospace() = copy(
     fontFamily = FontFamily.Monospace
 )
 
diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ActivityUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ActivityUtilities.kt
index c3b7eaca96..f50ac33e28 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/util/ActivityUtilities.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/util/ActivityUtilities.kt
@@ -53,7 +53,7 @@ fun AppCompatActivity.push(intent: Intent, isForResult: Boolean = false) {
     } else {
         startActivity(intent)
     }
-    overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out)
+    overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
 }
 
 fun AppCompatActivity.show(intent: Intent, isForResult: Boolean = false) {
@@ -108,5 +108,5 @@ data class ThemeState (
 )
 
 inline fun <reified T: Activity> Activity.show() = Intent(this, T::class.java).also(::startActivity).let { overridePendingTransition(R.anim.slide_from_bottom, R.anim.fade_scale_out) }
-inline fun <reified T: Activity> Activity.push(modify: Intent.() -> Unit = {}) = Intent(this, T::class.java).also(modify).also(::startActivity).let { overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out) }
+inline fun <reified T: Activity> Activity.push(modify: Intent.() -> Unit = {}) = Intent(this, T::class.java).also(modify).also(::startActivity).let { overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) }
 inline fun <reified T: Activity> Context.start(modify: Intent.() -> Unit = {}) = Intent(this, T::class.java).also(modify).apply { addFlags(FLAG_ACTIVITY_SINGLE_TOP) }.let(::startActivity)
diff --git a/app/src/main/res/anim/fade_scale_in.xml b/app/src/main/res/anim/fade_scale_in.xml
new file mode 100644
index 0000000000..3e004af2c2
--- /dev/null
+++ b/app/src/main/res/anim/fade_scale_in.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/decelerate_interpolator">
+
+    <scale
+        android:duration="250"
+        android:fromXScale="0.85"
+        android:fromYScale="0.85"
+        android:toXScale="1.0"
+        android:toYScale="1.0"
+        android:pivotX="50%"
+        android:pivotY="50%" />
+
+    <alpha
+        android:duration="250"
+        android:fromAlpha="0.6"
+        android:toAlpha="1" />
+
+</set>
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_to_bottom.xml b/app/src/main/res/anim/slide_to_bottom.xml
new file mode 100644
index 0000000000..0f62dfa487
--- /dev/null
+++ b/app/src/main/res/anim/slide_to_bottom.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/decelerate_interpolator">
+
+    <translate
+        android:duration="250"
+        android:fromYDelta="0%"
+        android:toYDelta="100%" />
+
+</set>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml
new file mode 100644
index 0000000000..9fd7185331
--- /dev/null
+++ b/app/src/main/res/drawable/ic_settings.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32dp"
+    android:height="32dp"
+    android:viewportWidth="50"
+    android:viewportHeight="50">
+  <path
+      android:pathData="M25,15.916C19.991,15.916 15.916,19.991 15.916,25C15.916,30.009 19.991,34.084 25,34.084C30.009,34.084 34.084,30.009 34.084,25C34.084,19.991 30.009,15.916 25,15.916ZM25,31.813C21.243,31.813 18.187,28.757 18.187,25C18.187,21.243 21.243,18.187 25,18.187C28.757,18.187 31.813,21.243 31.813,25C31.813,28.757 28.757,31.813 25,31.813Z"
+      android:fillColor="#000000"/>
+  <path
+      android:pathData="M43.106,20.673L40.185,20.038C39.931,19.261 39.617,18.502 39.245,17.772L40.862,15.257C41.151,14.807 41.088,14.217 40.71,13.84L36.16,9.29C35.783,8.913 35.193,8.849 34.743,9.138L32.228,10.755C31.498,10.383 30.739,10.069 29.962,9.815L29.327,6.894C29.213,6.372 28.751,6 28.217,6H21.783C21.249,6 20.787,6.372 20.673,6.894L20.038,9.815C19.261,10.069 18.502,10.383 17.772,10.755L15.257,9.138C14.807,8.849 14.217,8.913 13.84,9.29L9.29,13.84C8.913,14.217 8.849,14.807 9.138,15.257L10.755,17.772C10.383,18.502 10.069,19.261 9.815,20.038L6.894,20.673C6.372,20.787 6,21.249 6,21.783V28.217C6,28.751 6.372,29.213 6.894,29.327L9.815,29.962C10.069,30.739 10.383,31.498 10.755,32.228L9.138,34.743C8.849,35.193 8.913,35.783 9.29,36.16L13.84,40.71C14.217,41.088 14.807,41.151 15.257,40.862L17.772,39.245C18.502,39.617 19.261,39.931 20.038,40.185L20.673,43.106C20.787,43.628 21.249,44 21.783,44H28.217C28.751,44 29.213,43.628 29.327,43.106L29.962,40.185C30.739,39.931 31.498,39.617 32.228,39.245L34.743,40.862C35.193,41.151 35.783,41.088 36.16,40.71L40.71,36.16C41.088,35.783 41.151,35.193 40.862,34.743L39.245,32.228C39.617,31.498 39.931,30.739 40.185,29.962L43.106,29.327C43.628,29.213 44,28.751 44,28.217V21.783C44,21.249 43.628,20.787 43.106,20.673ZM41.729,27.302L39.051,27.884C38.64,27.974 38.312,28.283 38.199,28.689C37.904,29.744 37.481,30.765 36.94,31.723C36.734,32.089 36.746,32.541 36.974,32.895L38.457,35.201L35.202,38.457L32.895,36.974C32.541,36.746 32.089,36.734 31.723,36.94C30.765,37.481 29.744,37.904 28.689,38.199C28.283,38.312 27.974,38.64 27.884,39.051L27.302,41.729H22.698L22.116,39.051C22.026,38.64 21.717,38.312 21.311,38.199C20.256,37.904 19.235,37.481 18.277,36.94C17.911,36.734 17.459,36.747 17.105,36.974L14.799,38.457L11.543,35.201L13.026,32.895C13.254,32.541 13.267,32.089 13.06,31.723C12.519,30.765 12.096,29.744 11.802,28.689C11.689,28.283 11.361,27.974 10.949,27.884L8.271,27.302V22.698L10.949,22.116C11.36,22.026 11.689,21.717 11.802,21.311C12.096,20.256 12.519,19.235 13.059,18.277C13.267,17.911 13.254,17.459 13.026,17.105L11.543,14.799L14.798,11.543L17.105,13.026C17.459,13.254 17.911,13.267 18.277,13.059C19.235,12.519 20.256,12.096 21.311,11.802C21.717,11.689 22.026,11.36 22.116,10.949L22.698,8.271H27.302L27.884,10.949C27.974,11.36 28.283,11.689 28.689,11.802C29.744,12.096 30.765,12.519 31.723,13.059C32.089,13.267 32.541,13.253 32.895,13.026L35.201,11.543L38.457,14.799L36.974,17.105C36.746,17.459 36.733,17.911 36.94,18.277C37.481,19.235 37.904,20.256 38.199,21.311C38.312,21.717 38.639,22.026 39.051,22.116L41.729,22.698V27.302Z"
+      android:fillColor="#000000"/>
+</vector>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 537038921d..d31a433436 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -787,6 +787,7 @@
     <string name="activity_settings_display_name_edit_text_hint">Enter a display name</string>
     <string name="activity_settings_display_name_missing_error">Please pick a display name</string>
     <string name="activity_settings_display_name_too_long_error">Please pick a shorter display name</string>
+    <string name="activity_settings_debug_button_title">Debug Menu</string>
     <string name="activity_settings_privacy_button_title">Privacy</string>
     <string name="activity_settings_notifications_button_title">Notifications</string>
     <string name="activity_settings_message_requests_button_title">Message Requests</string>
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
diff --git a/libsession/src/main/java/org/session/libsession/utilities/Environment.kt b/libsession/src/main/java/org/session/libsession/utilities/Environment.kt
new file mode 100644
index 0000000000..a5a878cae4
--- /dev/null
+++ b/libsession/src/main/java/org/session/libsession/utilities/Environment.kt
@@ -0,0 +1,5 @@
+package org.session.libsession.utilities
+
+enum class Environment(val label: String) {
+    MAIN_NET("Mainnet"),  TEST_NET("Testnet")
+}
\ No newline at end of file
diff --git a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt
index 5bf109843d..7a60b41bc5 100644
--- a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt
+++ b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt
@@ -18,6 +18,7 @@ import org.session.libsession.utilities.TextSecurePreferences.Companion.AUTOPLAY
 import org.session.libsession.utilities.TextSecurePreferences.Companion.CALL_NOTIFICATIONS_ENABLED
 import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_DARK
 import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_LIGHT
+import org.session.libsession.utilities.TextSecurePreferences.Companion.ENVIRONMENT
 import org.session.libsession.utilities.TextSecurePreferences.Companion.FOLLOW_SYSTEM_SETTINGS
 import org.session.libsession.utilities.TextSecurePreferences.Companion.HIDE_PASSWORD
 import org.session.libsession.utilities.TextSecurePreferences.Companion.LAST_VACUUM_TIME
@@ -190,6 +191,8 @@ interface TextSecurePreferences {
     fun setHidePassword(value: Boolean)
     fun getLastVersionCheck(): Long
     fun setLastVersionCheck()
+    fun getEnvironment(): Environment
+    fun setEnvironment(value: Environment)
 
     companion object {
         val TAG = TextSecurePreferences::class.simpleName
@@ -277,6 +280,7 @@ interface TextSecurePreferences {
         const val FINGERPRINT_KEY_GENERATED = "fingerprint_key_generated"
         const val SELECTED_ACCENT_COLOR = "selected_accent_color"
         const val LAST_VERSION_CHECK = "pref_last_version_check"
+        const val ENVIRONMENT = "debug_environment"
 
         const val HAS_RECEIVED_LEGACY_CONFIG = "has_received_legacy_config"
         const val HAS_FORCED_NEW_CONFIG = "has_forced_new_config"
@@ -1554,6 +1558,17 @@ class AppTextSecurePreferences @Inject constructor(
         setLongPreference(LAST_VERSION_CHECK, System.currentTimeMillis())
     }
 
+    override fun getEnvironment(): Environment {
+        val environment = getStringPreference(ENVIRONMENT, null)
+        return if (environment != null) {
+            Environment.valueOf(environment)
+        } else Environment.MAIN_NET
+    }
+
+    override fun setEnvironment(value: Environment) {
+        setStringPreference(ENVIRONMENT, value.name)
+    }
+
     override fun setShownCallNotification(): Boolean {
         val previousValue = getBooleanPreference(SHOWN_CALL_NOTIFICATION, false)
         if (previousValue) return false