diff --git a/mailbox-android/src/main/AndroidManifest.xml b/mailbox-android/src/main/AndroidManifest.xml index 30bcdd515ab75017ad760198f5ecfed04d71b2bf..cb70c9be8fe1298cc5fe8c92c7c02837517978b1 100644 --- a/mailbox-android/src/main/AndroidManifest.xml +++ b/mailbox-android/src/main/AndroidManifest.xml @@ -22,8 +22,6 @@ android:supportsRtl="true" android:theme="@style/Theme.BriarMailbox"> - <service android:name=".android.MailboxService" /> - <activity android:name=".android.ui.MainActivity" android:exported="true" @@ -35,13 +33,28 @@ </intent-filter> </activity> <activity - android:name=".android.ui.WipeCompleteActivity" + android:name=".android.ui.settings.SettingsActivity" + android:exported="false" + android:label="@string/prefs_title" /> + <activity + android:name=".android.ui.wipe.WipeCompleteActivity" + android:excludeFromRecents="true" + android:launchMode="singleInstance" + android:process=":briar_mailbox_wipe_complete" + android:theme="@style/Theme.BriarMailbox.NoActionBar" /> + <activity + android:name=".android.ui.startup.StartupFailureActivity" android:excludeFromRecents="true" + android:exported="false" + android:finishOnTaskLaunch="true" + android:label="@string/startup_failed_activity_title" android:launchMode="singleInstance" - android:process=":briar_mailbox_wipe_complete" /> + android:process=":briar_mailbox_startup_failure" + android:theme="@style/Theme.BriarMailbox.NoActionBar" /> - <receiver android:name=".core.system.AlarmReceiver" /> + <service android:name=".android.MailboxService" /> + <receiver android:name=".core.system.AlarmReceiver" /> <receiver android:name=".android.StartReceiver" android:exported="false"> @@ -50,15 +63,6 @@ <action android:name="android.intent.action.MY_PACKAGE_REPLACED" /> </intent-filter> </receiver> - - <activity - android:name=".android.ui.StartupFailureActivity" - android:excludeFromRecents="true" - android:exported="false" - android:finishOnTaskLaunch="true" - android:label="@string/startup_failed_activity_title" - android:launchMode="singleInstance" - android:process=":briar_mailbox_startup_failure" /> </application> -</manifest> +</manifest> \ No newline at end of file diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxPreferences.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxPreferences.kt index ce7a571f30e5f8fb1a02bc45e4aef6539373fd3a..859e43c988775bb00d8cee6bd8cd3369af2863e5 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxPreferences.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxPreferences.kt @@ -24,7 +24,7 @@ import android.content.SharedPreferences import androidx.core.content.edit import androidx.preference.PreferenceManager.getDefaultSharedPreferences import dagger.hilt.android.qualifiers.ApplicationContext -import org.briarproject.mailbox.android.ui.WipeCompleteFragment +import org.briarproject.mailbox.android.ui.wipe.WipeCompleteFragment import javax.inject.Inject class MailboxPreferences @Inject constructor(@ApplicationContext val context: Context) { diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt index 5d94053ba090f1db43cd022dedb0236384c94ab5..2ab01db223fa2b1d26a1aa00e3c2d2645c6986ef 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/MailboxService.kt @@ -35,10 +35,10 @@ import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager import org.briarproject.mailbox.R import org.briarproject.mailbox.android.MailboxNotificationManager.Companion.NOTIFICATION_MAIN_ID import org.briarproject.mailbox.android.StatusManager.Starting -import org.briarproject.mailbox.android.ui.StartupFailureActivity -import org.briarproject.mailbox.android.ui.StartupFailureActivity.Companion.EXTRA_START_RESULT -import org.briarproject.mailbox.android.ui.StartupFailureActivity.StartupFailure -import org.briarproject.mailbox.android.ui.WipeCompleteActivity +import org.briarproject.mailbox.android.ui.startup.StartupFailureActivity +import org.briarproject.mailbox.android.ui.startup.StartupFailureActivity.Companion.EXTRA_START_RESULT +import org.briarproject.mailbox.android.ui.startup.StartupFailureActivity.StartupFailure +import org.briarproject.mailbox.android.ui.wipe.WipeCompleteActivity import org.briarproject.mailbox.core.lifecycle.LifecycleManager import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.CLOCK_ERROR import org.briarproject.mailbox.core.lifecycle.LifecycleManager.StartResult.LIFECYCLE_REUSE diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/BackFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/BackFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..01ec85c71a5428bbe93fae8742ded3ba31a34e35 --- /dev/null +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/BackFragment.kt @@ -0,0 +1,34 @@ +package org.briarproject.mailbox.android.ui + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import androidx.annotation.CallSuper +import androidx.core.view.MenuProvider +import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle.State.RESUMED + +/** + * A fragment that allows back navigation via the action bar. + */ +abstract class BackFragment : Fragment(), MenuProvider { + @CallSuper + override fun onViewCreated(v: View, savedInstanceState: Bundle?) { + // only needed for up/back navigation in action bar + requireActivity().addMenuProvider(this, viewLifecycleOwner, RESUMED) + } + + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + } + + @CallSuper + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + if (menuItem.itemId == android.R.id.home) { + requireActivity().onBackPressedDispatcher.onBackPressed() + return true + } + return false + } +} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/MailboxViewModel.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/MailboxViewModel.kt index 841a0794760f8a21f59c69c107d20ddcff71ef41..40203d840bc90e2367161e85fe870a7f6cac2d30 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/MailboxViewModel.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/MailboxViewModel.kt @@ -37,6 +37,7 @@ import org.briarproject.mailbox.core.lifecycle.LifecycleManager.LifecycleState import org.briarproject.mailbox.core.settings.MetadataManager import org.briarproject.mailbox.core.system.AndroidExecutor import org.briarproject.mailbox.core.system.DozeWatchdog +import org.briarproject.mailbox.core.tor.TorPlugin import org.briarproject.mailbox.core.util.LogUtils.info import org.slf4j.LoggerFactory.getLogger import javax.inject.Inject @@ -51,6 +52,7 @@ class MailboxViewModel @Inject constructor( metadataManager: MetadataManager, private val mailboxPreferences: MailboxPreferences, private val androidExecutor: AndroidExecutor, + private val torPlugin: TorPlugin, handle: SavedStateHandle, ) : AndroidViewModel(app) { @@ -116,6 +118,10 @@ class MailboxViewModel @Inject constructor( MailboxService.stopService(getApplication()) } + fun onSettingsChanged() { + torPlugin.onSettingsChanged() + } + /** * Called from the status fragment's unlink button. */ diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/MainActivity.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/MainActivity.kt index fb99d10f6622c005c622ff8e6757df693e19aec4..d2a600fa33bc5ab390307be8feea96ba36555056 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/MainActivity.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/MainActivity.kt @@ -23,12 +23,18 @@ import android.Manifest.permission.POST_NOTIFICATIONS import android.content.Intent import android.os.Build.VERSION.SDK_INT import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem import androidx.activity.result.contract.ActivityResultContracts.RequestPermission import androidx.activity.viewModels +import androidx.annotation.UiThread import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.content.PermissionChecker.PERMISSION_GRANTED import androidx.core.content.PermissionChecker.checkSelfPermission +import androidx.core.view.MenuProvider +import androidx.lifecycle.Lifecycle.State.RESUMED import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import dagger.hilt.android.AndroidEntryPoint @@ -57,12 +63,14 @@ import org.briarproject.mailbox.android.StatusManager.Stopped import org.briarproject.mailbox.android.StatusManager.Stopping import org.briarproject.mailbox.android.StatusManager.Undecided import org.briarproject.mailbox.android.StatusManager.Wiping +import org.briarproject.mailbox.android.ui.settings.SettingsActivity +import org.briarproject.mailbox.android.ui.wipe.WipeCompleteActivity import org.briarproject.mailbox.core.lifecycle.LifecycleManager.LifecycleState.NOT_STARTED import org.briarproject.mailbox.core.util.LogUtils.info import org.slf4j.LoggerFactory.getLogger @AndroidEntryPoint -class MainActivity : AppCompatActivity() { +class MainActivity : AppCompatActivity(), MenuProvider { companion object { private val LOG = getLogger(MainActivity::class.java) @@ -87,6 +95,8 @@ class MainActivity : AppCompatActivity() { LOG.info("onCreate()") setContentView(R.layout.activity_main) + addMenuProvider(this, this, RESUMED) + LOG.info { "do we have a saved instance state? " + (savedInstanceState != null) } hadBeenStartedOnSave = savedInstanceState?.getBoolean(BUNDLE_LIFECYCLE_HAS_STARTED) ?: false @@ -101,8 +111,7 @@ class MainActivity : AppCompatActivity() { supportActionBar?.setDisplayHomeAsUpEnabled(true) getString(R.string.link_text_title) } - R.id.statusFragment -> getString(R.string.app_name) - else -> "" + else -> getString(R.string.app_name) } } @@ -111,6 +120,19 @@ class MainActivity : AppCompatActivity() { } } + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + menuInflater.inflate(R.menu.main_actions, menu) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + if (menuItem.itemId == R.id.action_settings) { + startActivity(Intent(this, SettingsActivity::class.java)) + return true + } + return false + } + + @UiThread private fun onAppStateChanged(state: MailboxAppState) { // Catch the situation where we come back to the activity after a remote wipe has happened // while the app was in the background and gets restored from the recent app list after @@ -122,21 +144,20 @@ class MainActivity : AppCompatActivity() { startActivity(Intent(this, WipeCompleteActivity::class.java)) return } + val currentDestId = nav.currentDestination?.id when (state) { Undecided -> supportActionBar?.hide() // hide action bar until we need it - NeedOnboarding -> { + NeedOnboarding -> if (currentDestId == R.id.initFragment) { supportActionBar?.hide() - if (nav.currentDestination?.id == R.id.initFragment) - nav.navigate(actionGlobalOnboardingContainer()) + nav.navigate(actionGlobalOnboardingContainer()) } - NeedsDozeExemption -> { + NeedsDozeExemption -> if (currentDestId != R.id.doNotKillMeFragment) { supportActionBar?.hide() - if (nav.currentDestination?.id != R.id.doNotKillMeFragment) - nav.navigate(actionGlobalDoNotKillMeFragment()) + nav.navigate(actionGlobalDoNotKillMeFragment()) } NotStarted -> { askForNotificationPermission() - supportActionBar?.hide() + supportActionBar?.show() nav.navigate(actionGlobalStartupFragment()) } // It is important to navigate here from various fragments. The normal case is @@ -144,47 +165,40 @@ class MainActivity : AppCompatActivity() { // However, when the service got killed and the app has been restored with a different // UI state such as the qr code screen or the status screen, then we also want to // navigate to the startup fragment. - is Starting -> { - supportActionBar?.hide() - if (nav.currentDestination?.id != R.id.startupFragment) - nav.navigate(actionGlobalStartupFragment()) + is Starting -> if (currentDestId != R.id.startupFragment) { + supportActionBar?.show() + nav.navigate(actionGlobalStartupFragment()) } - is StartedSettingUp -> { + is StartedSettingUp -> if (currentDestId != R.id.qrCodeFragment && + currentDestId != R.id.qrCodeLinkFragment + ) { supportActionBar?.show() - if (nav.currentDestination?.id != R.id.qrCodeFragment && - nav.currentDestination?.id != R.id.qrCodeLinkFragment - ) nav.navigate(actionGlobalQrCodeFragment()) + nav.navigate(actionGlobalQrCodeFragment()) } - StartedSetupComplete -> { - if (nav.currentDestination?.id == R.id.qrCodeFragment) { - supportActionBar?.hide() - nav.navigate(actionGlobalSetupCompleteFragment()) - } else if (nav.currentDestination?.id != R.id.statusFragment && - nav.currentDestination?.id != R.id.setupCompleteFragment - ) { - supportActionBar?.show() - nav.navigate(actionGlobalStatusFragment()) - } + StartedSetupComplete -> if (currentDestId == R.id.qrCodeFragment) { + supportActionBar?.hide() + nav.navigate(actionGlobalSetupCompleteFragment()) + } else if (currentDestId != R.id.statusFragment && + currentDestId != R.id.setupCompleteFragment + ) { + supportActionBar?.show() + nav.navigate(actionGlobalStatusFragment()) } - ErrorNoNetwork -> { + ErrorNoNetwork -> if (currentDestId != R.id.noNetworkFragment) { supportActionBar?.hide() - if (nav.currentDestination?.id != R.id.noNetworkFragment) - nav.navigate(actionGlobalNoNetworkFragment()) + nav.navigate(actionGlobalNoNetworkFragment()) } - ErrorClockSkew -> { + ErrorClockSkew -> if (currentDestId != R.id.clockSkewFragment) { supportActionBar?.hide() - if (nav.currentDestination?.id != R.id.clockSkewFragment) - nav.navigate(actionGlobalClockSkewFragment()) + nav.navigate(actionGlobalClockSkewFragment()) } - Stopping -> { + Stopping -> if (currentDestId != R.id.stoppingFragment) { supportActionBar?.hide() - if (nav.currentDestination?.id != R.id.stoppingFragment) - nav.navigate(actionGlobalStoppingFragment()) + nav.navigate(actionGlobalStoppingFragment()) } - Wiping -> { + Wiping -> if (nav.currentDestination?.id != R.id.wipingFragment) { supportActionBar?.hide() - if (nav.currentDestination?.id != R.id.wipingFragment) - nav.navigate(actionGlobalWipingFragment()) + nav.navigate(actionGlobalWipingFragment()) } Stopped -> {} // nothing to do but needs to be exhaustive for Kotlin 1.7 } diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/settings/SettingsActivity.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/settings/SettingsActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..b81c7bf49687ee665a9c909fa834d35850c252bc --- /dev/null +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/settings/SettingsActivity.kt @@ -0,0 +1,41 @@ +package org.briarproject.mailbox.android.ui.settings + +import android.os.Bundle +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import androidx.annotation.CallSuper +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.MenuProvider +import androidx.lifecycle.Lifecycle.State.RESUMED +import dagger.hilt.android.AndroidEntryPoint +import org.briarproject.mailbox.databinding.ActivitySettingsBinding + +@AndroidEntryPoint +class SettingsActivity : AppCompatActivity(), MenuProvider { + + private lateinit var binding: ActivitySettingsBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivitySettingsBinding.inflate(layoutInflater) + setContentView(binding.root) + + // only needed for up/back navigation in action bar + addMenuProvider(this, this, RESUMED) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + } + + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + } + + @CallSuper + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + if (menuItem.itemId == android.R.id.home) { + onBackPressedDispatcher.onBackPressed() + return true + } + return false + } +} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/settings/SettingsFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/settings/SettingsFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..8f773c762abe9050c8ad889fa639b31ffd8f42df --- /dev/null +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/settings/SettingsFragment.kt @@ -0,0 +1,166 @@ +package org.briarproject.mailbox.android.ui.settings + +import android.os.Bundle +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.SwitchPreferenceCompat +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.briarproject.mailbox.R +import org.briarproject.mailbox.android.ui.MailboxViewModel +import org.briarproject.mailbox.core.settings.SettingsManager +import org.briarproject.mailbox.core.system.LocationUtils +import org.briarproject.mailbox.core.tor.NetworkManager +import org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_AUTO +import org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_AUTO_DEFAULT +import org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE +import org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_MEEK +import org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_OBFS4 +import org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_OBFS4_DEFAULT +import org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_SNOWFLAKE +import org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_VANILLA +import org.briarproject.mailbox.core.tor.TorPlugin +import org.briarproject.onionwrapper.CircumventionProvider +import org.briarproject.onionwrapper.CircumventionProvider.BridgeType.DEFAULT_OBFS4 +import org.briarproject.onionwrapper.CircumventionProvider.BridgeType.MEEK +import org.briarproject.onionwrapper.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4 +import org.briarproject.onionwrapper.CircumventionProvider.BridgeType.SNOWFLAKE +import org.briarproject.onionwrapper.CircumventionProvider.BridgeType.VANILLA +import javax.inject.Inject + +@AndroidEntryPoint +class SettingsFragment : PreferenceFragmentCompat() { + + private val viewModel: MailboxViewModel by activityViewModels() + + private lateinit var autoPref: SwitchPreferenceCompat + private lateinit var usePref: SwitchPreferenceCompat + private lateinit var snowflakePref: SwitchPreferenceCompat + private lateinit var meekPref: SwitchPreferenceCompat + private lateinit var obfs4Pref: SwitchPreferenceCompat + private lateinit var obfs4DefaultPref: SwitchPreferenceCompat + private lateinit var vanillaPref: SwitchPreferenceCompat + private lateinit var brideTypePrefs: List<Preference> + private lateinit var bridgeTypesCategory: PreferenceCategory + + @Inject + lateinit var settingsManager: SettingsManager + + @Inject + lateinit var torSettingsStore: TorSettingsStore + + @Inject + lateinit var torPlugin: TorPlugin + + @Inject + lateinit var locationUtils: LocationUtils + + @Inject + lateinit var circumventionProvider: CircumventionProvider + + @Inject + lateinit var networkManager: NetworkManager + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.preferences, rootKey) + torSettingsStore.setOnSettingsLoadedCallback { settings -> + lifecycleScope.launch(Dispatchers.Main) { + onAutoChanged(settings.getBoolean(BRIDGE_AUTO, BRIDGE_AUTO_DEFAULT)) + // we set the store for persistence of settings only after setting up auto value + preferenceManager.preferenceDataStore = torSettingsStore + } + } + autoPref = findPreference(BRIDGE_AUTO)!! + usePref = findPreference(BRIDGE_USE)!! + snowflakePref = findPreference(BRIDGE_USE_SNOWFLAKE)!! + meekPref = findPreference(BRIDGE_USE_MEEK)!! + obfs4Pref = findPreference(BRIDGE_USE_OBFS4)!! + obfs4DefaultPref = findPreference(BRIDGE_USE_OBFS4_DEFAULT)!! + vanillaPref = findPreference(BRIDGE_USE_VANILLA)!! + brideTypePrefs = listOf( + snowflakePref, meekPref, obfs4Pref, obfs4DefaultPref, vanillaPref + ) + bridgeTypesCategory = findPreference("bridgeTypesCategory")!! + autoPref.setOnPreferenceChangeListener { _, newValue -> + onAutoChanged(newValue as Boolean) + true + } + usePref.setOnPreferenceChangeListener { _, newValue -> + onUseBridgesChanged(newValue as Boolean) + true + } + } + + override fun onDestroy() { + super.onDestroy() + // apply settings only when user is leaving settings to prevent Tor changes on each toggle + viewModel.onSettingsChanged() + } + + private fun onAutoChanged(auto: Boolean) { + autoPref.isChecked = auto + val country = locationUtils.currentCountry + val doBridgesWork = circumventionProvider.doBridgesWork(country) + // if automatic mode is on, we show what Tor is using, otherwise we show what user has set + if (auto) { + setIsPersistent(false) + usePref.isChecked = doBridgesWork + onUseBridgesChanged(doBridgesWork) + val autoTypes = if (networkManager.networkStatus.isIpv6Only) { + listOf(MEEK, SNOWFLAKE) + } else { + circumventionProvider.getSuitableBridgeTypes(country) + } + snowflakePref.isChecked = autoTypes.contains(SNOWFLAKE) + meekPref.isChecked = autoTypes.contains(MEEK) + obfs4Pref.isChecked = autoTypes.contains(NON_DEFAULT_OBFS4) + obfs4DefaultPref.isChecked = autoTypes.contains(DEFAULT_OBFS4) + vanillaPref.isChecked = autoTypes.contains(VANILLA) + } else { + setIsPersistent(true) + val useBridges = + torSettingsStore.getBooleanAndStoreDefault(usePref, BRIDGE_USE, doBridgesWork) + onUseBridgesChanged(useBridges) + val customTypes = torPlugin.customBridgeTypes + torSettingsStore.getBooleanAndStoreDefault( + pref = snowflakePref, + key = BRIDGE_USE_SNOWFLAKE, + defaultValue = customTypes.contains(SNOWFLAKE) + ) + torSettingsStore.getBooleanAndStoreDefault( + pref = meekPref, + key = BRIDGE_USE_MEEK, + defaultValue = customTypes.contains(MEEK), + ) + torSettingsStore.getBooleanAndStoreDefault( + pref = obfs4Pref, + key = BRIDGE_USE_OBFS4, + defaultValue = customTypes.contains(NON_DEFAULT_OBFS4), + ) + torSettingsStore.getBooleanAndStoreDefault( + pref = obfs4DefaultPref, + key = BRIDGE_USE_OBFS4_DEFAULT, + defaultValue = customTypes.contains(DEFAULT_OBFS4), + ) + torSettingsStore.getBooleanAndStoreDefault( + pref = vanillaPref, + key = BRIDGE_USE_VANILLA, + defaultValue = customTypes.contains(VANILLA), + ) + } + } + + private fun onUseBridgesChanged(useBridges: Boolean) { + brideTypePrefs.forEach { it.isVisible = useBridges } + bridgeTypesCategory.isVisible = useBridges + } + + private fun setIsPersistent(enable: Boolean) { + usePref.isPersistent = enable + brideTypePrefs.forEach { pref -> pref.isPersistent = enable } + } +} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/settings/SettingsStore.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/settings/SettingsStore.kt new file mode 100644 index 0000000000000000000000000000000000000000..e8f608a88247806c31d75dfd917828579ba086a6 --- /dev/null +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/settings/SettingsStore.kt @@ -0,0 +1,94 @@ +package org.briarproject.mailbox.android.ui.settings + +import androidx.core.util.Consumer +import androidx.preference.PreferenceDataStore +import androidx.preference.TwoStatePreference +import org.briarproject.mailbox.core.db.DbException +import org.briarproject.mailbox.core.lifecycle.IoExecutor +import org.briarproject.mailbox.core.settings.Settings +import org.briarproject.mailbox.core.settings.SettingsManager +import org.briarproject.mailbox.core.tor.TorConstants.SETTINGS_NAMESPACE +import org.briarproject.mailbox.core.util.LogUtils.info +import org.briarproject.mailbox.core.util.LogUtils.logDuration +import org.briarproject.mailbox.core.util.LogUtils.logException +import org.briarproject.mailbox.core.util.LogUtils.now +import org.slf4j.LoggerFactory.getLogger +import java.util.concurrent.Executor +import javax.inject.Inject +import javax.inject.Singleton + +private val LOG = getLogger(SettingsStore::class.java) + +@Singleton +class TorSettingsStore @Inject constructor( + settingsManager: SettingsManager, + @IoExecutor + ioExecutor: Executor, +) : SettingsStore(settingsManager, ioExecutor, SETTINGS_NAMESPACE) { + fun getBooleanAndStoreDefault( + pref: TwoStatePreference, + key: String, + defaultValue: Boolean, + ): Boolean { + val s = requireNotNull(settings) + val value = if (s.containsKey(key)) { + s.getBoolean(key, defaultValue) + } else { + putBoolean(key, defaultValue) + defaultValue + } + pref.isChecked = value + return value + } +} + +/** + * This is only for storing settings. We still need to retrieve the current value ourselves. + */ +open class SettingsStore( + private val settingsManager: SettingsManager, + private val dbExecutor: Executor, + private val namespace: String, +) : PreferenceDataStore() { + + @Volatile + protected var settings: Settings? = null + private var callback: Consumer<Settings>? = null + + init { + dbExecutor.execute { + settings = settingsManager.getSettings(namespace) + callback?.accept(settings) + } + } + + fun setOnSettingsLoadedCallback(callback: Consumer<Settings>) { + this.callback = callback + if (settings != null) callback.accept(settings) + } + + override fun putBoolean(key: String, value: Boolean) { + LOG.info { "Store bool setting: $key=$value" } + val s = Settings().apply { + putBoolean(key, value) + } + settings!!.putBoolean(key, value) + storeSettings(s) + } + + override fun getBoolean(key: String, defaultValue: Boolean): Boolean { + return settings!!.getBoolean(key, defaultValue) + } + + private fun storeSettings(s: Settings) { + dbExecutor.execute { + try { + val start: Long = now() + settingsManager.mergeSettings(s, namespace) + logDuration(LOG, start) { "Merging $namespace settings" } + } catch (e: DbException) { + logException(LOG, e, "Error storing settings: ") + } + } + } +} diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/QrCodeFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/setup/QrCodeFragment.kt similarity index 94% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/QrCodeFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/setup/QrCodeFragment.kt index db5a5893b04454565b44a662e9a5f50e13f8fec0..b947a9ba94c04d267514e37be0732f29531e32a4 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/QrCodeFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/setup/QrCodeFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.setup import android.os.Bundle import android.view.LayoutInflater @@ -37,6 +37,8 @@ import dagger.hilt.android.AndroidEntryPoint import org.briarproject.mailbox.R import org.briarproject.mailbox.android.StatusManager.MailboxAppState import org.briarproject.mailbox.android.StatusManager.StartedSettingUp +import org.briarproject.mailbox.android.ui.MailboxViewModel +import org.briarproject.mailbox.android.ui.launchAndRepeatWhileStarted @AndroidEntryPoint class QrCodeFragment : Fragment(), MenuProvider { diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/QrCodeLinkFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/setup/QrCodeLinkFragment.kt similarity index 82% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/QrCodeLinkFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/setup/QrCodeLinkFragment.kt index 652356162267247c22ba8a0027e13b73d594428a..cd421ebda40626c4790ef13987583004d8fce130 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/QrCodeLinkFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/setup/QrCodeLinkFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.setup import android.content.ActivityNotFoundException import android.content.ClipData @@ -28,9 +28,6 @@ import android.content.Intent.EXTRA_TEXT import android.os.Build.VERSION.SDK_INT import android.os.Bundle import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.TextView @@ -38,18 +35,18 @@ import android.widget.Toast import android.widget.Toast.LENGTH_LONG import android.widget.Toast.LENGTH_SHORT import androidx.core.content.ContextCompat.getSystemService -import androidx.core.view.MenuProvider -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Lifecycle import com.google.android.material.button.MaterialButton import dagger.hilt.android.AndroidEntryPoint import org.briarproject.mailbox.R import org.briarproject.mailbox.android.StatusManager.MailboxAppState import org.briarproject.mailbox.android.StatusManager.StartedSettingUp +import org.briarproject.mailbox.android.ui.BackFragment +import org.briarproject.mailbox.android.ui.MailboxViewModel +import org.briarproject.mailbox.android.ui.launchAndRepeatWhileStarted @AndroidEntryPoint -class QrCodeLinkFragment : Fragment(), MenuProvider { +class QrCodeLinkFragment : BackFragment() { private val viewModel: MailboxViewModel by activityViewModels() private lateinit var linkView: TextView @@ -65,29 +62,16 @@ class QrCodeLinkFragment : Fragment(), MenuProvider { } override fun onViewCreated(v: View, savedInstanceState: Bundle?) { + super.onViewCreated(v, savedInstanceState) linkView = v.findViewById(R.id.linkView) shareButton = v.findViewById(R.id.shareButton) copyButton = v.findViewById(R.id.copyButton) - // only needed for up/back navigation in action bar - requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED) - launchAndRepeatWhileStarted { viewModel.appState.collect { onAppStateChanged(it) } } } - override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { - } - - override fun onMenuItemSelected(menuItem: MenuItem): Boolean { - if (menuItem.itemId == android.R.id.home) { - requireActivity().onBackPressedDispatcher.onBackPressed() - return true - } - return false - } - private fun onAppStateChanged(state: MailboxAppState) { if (state is StartedSettingUp) { linkView.text = state.link diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/SetupCompleteFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/setup/SetupCompleteFragment.kt similarity index 97% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/SetupCompleteFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/setup/SetupCompleteFragment.kt index 54d420512eda53a2a2557263d1b2859e2f0c8673..19fa4f38beadf81e2ecb56ffbf6d67c469465944 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/SetupCompleteFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/setup/SetupCompleteFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.setup import android.os.Bundle import android.view.LayoutInflater diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/InitFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/InitFragment.kt similarity index 97% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/InitFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/InitFragment.kt index 0c6962e14f99e649f75ceca6458fc478d3dbb93c..ee8496180ff042e681493f9d6bcae0250db87682 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/InitFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/InitFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.startup import android.os.Bundle import android.view.LayoutInflater diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingContainerFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/OnboardingContainerFragment.kt similarity index 96% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingContainerFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/OnboardingContainerFragment.kt index 67ebc5dabd2e86b20f458ba29084e176e4b33f40..8d05855cba28000791a971fed06a1d313e450cf2 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingContainerFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/OnboardingContainerFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.startup import android.os.Bundle import android.view.LayoutInflater @@ -27,6 +27,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 +import org.briarproject.mailbox.android.ui.MailboxViewModel import org.briarproject.mailbox.databinding.FragmentOnboardingContainerBinding class OnboardingContainerFragment : Fragment() { diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/OnboardingFragment.kt similarity index 97% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/OnboardingFragment.kt index ed45b3838f3c3e130a2ddacb26be533f4f2c10b7..140cecd0e58ea001cfa7e25bedfce990d847ae98 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/OnboardingFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/OnboardingFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.startup import android.os.Bundle import android.view.LayoutInflater @@ -31,6 +31,7 @@ import androidx.core.widget.ImageViewCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import org.briarproject.mailbox.R +import org.briarproject.mailbox.android.ui.MailboxViewModel import org.briarproject.mailbox.databinding.FragmentOnboardingBinding class Onboarding0Fragment : OnboardingFragment( diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFailureActivity.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/StartupFailureActivity.kt similarity index 84% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFailureActivity.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/StartupFailureActivity.kt index 866665aa389a75cf929a8cca4e6d83ddaabfc8ad..271c2a780cae969f8bad53c3063e5f9814f2d88d 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFailureActivity.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/StartupFailureActivity.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.startup import android.content.Intent import android.os.Bundle @@ -25,9 +25,9 @@ import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import dagger.hilt.android.AndroidEntryPoint import org.briarproject.mailbox.R -import org.briarproject.mailbox.android.ui.StartupFailureActivity.StartupFailure.CLOCK_ERROR -import org.briarproject.mailbox.android.ui.StartupFailureActivity.StartupFailure.LIFECYCLE_REUSE -import org.briarproject.mailbox.android.ui.StartupFailureActivity.StartupFailure.SERVICE_ERROR +import org.briarproject.mailbox.android.ui.startup.StartupFailureActivity.StartupFailure.CLOCK_ERROR +import org.briarproject.mailbox.android.ui.startup.StartupFailureActivity.StartupFailure.LIFECYCLE_REUSE +import org.briarproject.mailbox.android.ui.startup.StartupFailureActivity.StartupFailure.SERVICE_ERROR @AndroidEntryPoint class StartupFailureActivity : AppCompatActivity() { diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/StartupFragment.kt similarity index 93% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/StartupFragment.kt index 8dcd70430e1d18e69d59befbb74c9121aa4526b3..5a464f0ec1b35d4e057a9edd10d47d1edcba9f4d 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StartupFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/startup/StartupFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.startup import android.os.Bundle import android.view.LayoutInflater @@ -33,6 +33,8 @@ import dagger.hilt.android.AndroidEntryPoint import org.briarproject.mailbox.R import org.briarproject.mailbox.android.StatusManager.MailboxAppState import org.briarproject.mailbox.android.StatusManager.Starting +import org.briarproject.mailbox.android.ui.MailboxViewModel +import org.briarproject.mailbox.android.ui.launchAndRepeatWhileStarted @AndroidEntryPoint class StartupFragment : Fragment() { diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/ClockSkewFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/ClockSkewFragment.kt similarity index 96% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/ClockSkewFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/ClockSkewFragment.kt index c3d4189374830cba87bbdc387955b46f21fb724b..40cbe2c7eb4b811cff0bc0ecb321cf62336a03a1 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/ClockSkewFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/ClockSkewFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.status import android.os.Bundle import android.view.LayoutInflater diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/NoNetworkFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/NoNetworkFragment.kt similarity index 96% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/NoNetworkFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/NoNetworkFragment.kt index 43f4aaa2c8827651bcf5ed21ee7b601d06aca02c..8e4e48b880469dff990485531dec49506a5280ca 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/NoNetworkFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/NoNetworkFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.status import android.os.Bundle import android.view.LayoutInflater diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StatusFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/StatusFragment.kt similarity index 95% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StatusFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/StatusFragment.kt index 0c9e215588c31df68145f07c6068fe5528f22f7b..fd36123d49ab874023c2e76da58503edd1124a0c 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StatusFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/StatusFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.status import android.os.Bundle import android.view.LayoutInflater @@ -32,6 +32,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import org.briarproject.mailbox.R import org.briarproject.mailbox.android.UiUtils.formatDate +import org.briarproject.mailbox.android.ui.MailboxViewModel +import org.briarproject.mailbox.android.ui.launchAndRepeatWhileStarted @AndroidEntryPoint class StatusFragment : Fragment() { diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StoppingFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/StoppingFragment.kt similarity index 96% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StoppingFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/StoppingFragment.kt index 92e0d05d80201fd8a14c7f6cdd643cfe959f5622..f72dc0d1f0ae314aa1486f81f6ab6767440b213f 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/StoppingFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/status/StoppingFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.status import android.os.Bundle import android.view.LayoutInflater diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/WipeCompleteActivity.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/wipe/WipeCompleteActivity.kt similarity index 96% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/WipeCompleteActivity.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/wipe/WipeCompleteActivity.kt index aa95f8888f299a3de9138e4251cc8be4e253669d..d03549d91f61234771927d59d86a85c9530f3e43 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/WipeCompleteActivity.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/wipe/WipeCompleteActivity.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.wipe import android.os.Bundle import androidx.appcompat.app.AppCompatActivity diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/WipeCompleteFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/wipe/WipeCompleteFragment.kt similarity index 95% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/WipeCompleteFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/wipe/WipeCompleteFragment.kt index ce898e8b15114d42a9c9ce25207d8498ca341a1e..58aef8338dfcc68d3d83519bfca950cfd97c9c5d 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/WipeCompleteFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/wipe/WipeCompleteFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.wipe import android.os.Bundle import android.view.LayoutInflater @@ -29,6 +29,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import dagger.hilt.android.AndroidEntryPoint import org.briarproject.mailbox.R +import org.briarproject.mailbox.android.ui.MailboxViewModel import org.briarproject.mailbox.core.system.AndroidExecutor import org.briarproject.mailbox.core.system.System import org.slf4j.LoggerFactory.getLogger diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/WipingFragment.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/wipe/WipingFragment.kt similarity index 96% rename from mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/WipingFragment.kt rename to mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/wipe/WipingFragment.kt index 885e6b6165197516b3a015481b60a928874cb1a5..e0cc0de593a902dddc9d6bd1746fa3313f9ab2c4 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/WipingFragment.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/android/ui/wipe/WipingFragment.kt @@ -17,7 +17,7 @@ * */ -package org.briarproject.mailbox.android.ui +package org.briarproject.mailbox.android.ui.wipe import android.os.Bundle import android.view.LayoutInflater diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt index 2e3c5c404dd4c0e538867952ef785b5b0083e6b8..2dff2c815c7e368bcfa3e765020465b1a3e1e4ba 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt @@ -78,9 +78,17 @@ internal class AndroidTorModule { throw UnsupportedOperationException() } + override fun onSettingsChanged() { + throw UnsupportedOperationException() + } + override fun getHiddenServiceAddress(): String { throw UnsupportedOperationException() } + + override fun getCustomBridgeTypes(): MutableList<CircumventionProvider.BridgeType> { + throw UnsupportedOperationException() + } } } return AndroidTorPlugin( diff --git a/mailbox-android/src/main/res/layout/activity_settings.xml b/mailbox-android/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..ccd386d2e631617c3e1bb618572ef5773eb09196 --- /dev/null +++ b/mailbox-android/src/main/res/layout/activity_settings.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/fragmentContainer" + android:name="org.briarproject.mailbox.android.ui.settings.SettingsFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".android.ui.settings.SettingsActivity" /> diff --git a/mailbox-android/src/main/res/layout/activity_wipe_complete.xml b/mailbox-android/src/main/res/layout/activity_wipe_complete.xml index 272c018facf89437f29cd04f17d59097327875e8..cc458d355985b94f168b36a4ef131c4d096008b0 100644 --- a/mailbox-android/src/main/res/layout/activity_wipe_complete.xml +++ b/mailbox-android/src/main/res/layout/activity_wipe_complete.xml @@ -2,7 +2,7 @@ <androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/fragmentContainer" - android:name="org.briarproject.mailbox.android.ui.WipeCompleteFragment" + android:name="org.briarproject.mailbox.android.ui.wipe.WipeCompleteFragment" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".android.ui.WipeCompleteActivity" /> + tools:context=".android.ui.wipe.WipeCompleteActivity" /> diff --git a/mailbox-android/src/main/res/menu/main_actions.xml b/mailbox-android/src/main/res/menu/main_actions.xml new file mode 100644 index 0000000000000000000000000000000000000000..ff816139fee668d8d7e7844eeff36d7194ddce34 --- /dev/null +++ b/mailbox-android/src/main/res/menu/main_actions.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + <item + android:id="@+id/action_settings" + android:title="@string/prefs_title" + app:showAsAction="never" /> +</menu> diff --git a/mailbox-android/src/main/res/navigation/nav_main.xml b/mailbox-android/src/main/res/navigation/nav_main.xml index fbb0eee8f501fe1631ceb4be70b7f03dffaae3cf..a727bfb4e76b2217a65114a7929591ec67b80ec6 100644 --- a/mailbox-android/src/main/res/navigation/nav_main.xml +++ b/mailbox-android/src/main/res/navigation/nav_main.xml @@ -7,12 +7,12 @@ <fragment android:id="@+id/initFragment" - android:name="org.briarproject.mailbox.android.ui.InitFragment" + android:name="org.briarproject.mailbox.android.ui.startup.InitFragment" android:label="InitFragment" tools:layout="@layout/fragment_init" /> <fragment android:id="@+id/onboardingFragment" - android:name="org.briarproject.mailbox.android.ui.OnboardingContainerFragment" + android:name="org.briarproject.mailbox.android.ui.startup.OnboardingContainerFragment" android:label="OnboardingContainerFragment" tools:layout="@layout/fragment_onboarding" /> <fragment @@ -22,12 +22,12 @@ tools:layout="@layout/fragment_dont_kill_me" /> <fragment android:id="@+id/startupFragment" - android:name="org.briarproject.mailbox.android.ui.StartupFragment" + android:name="org.briarproject.mailbox.android.ui.startup.StartupFragment" android:label="StartupFragment" tools:layout="@layout/fragment_startup" /> <fragment android:id="@+id/qrCodeFragment" - android:name="org.briarproject.mailbox.android.ui.QrCodeFragment" + android:name="org.briarproject.mailbox.android.ui.setup.QrCodeFragment" android:label="@string/link_title" tools:layout="@layout/fragment_qr"> <action @@ -36,37 +36,37 @@ </fragment> <fragment android:id="@+id/qrCodeLinkFragment" - android:name="org.briarproject.mailbox.android.ui.QrCodeLinkFragment" + android:name="org.briarproject.mailbox.android.ui.setup.QrCodeLinkFragment" android:label="@string/link_title" tools:layout="@layout/fragment_qr_link" /> <fragment android:id="@+id/setupCompleteFragment" - android:name="org.briarproject.mailbox.android.ui.SetupCompleteFragment" + android:name="org.briarproject.mailbox.android.ui.setup.SetupCompleteFragment" android:label="SetupCompleteFragment" tools:layout="@layout/fragment_setup_complete" /> <fragment android:id="@+id/statusFragment" - android:name="org.briarproject.mailbox.android.ui.StatusFragment" + android:name="org.briarproject.mailbox.android.ui.status.StatusFragment" android:label="StatusFragment" tools:layout="@layout/fragment_status" /> <fragment android:id="@+id/noNetworkFragment" - android:name="org.briarproject.mailbox.android.ui.NoNetworkFragment" + android:name="org.briarproject.mailbox.android.ui.status.NoNetworkFragment" android:label="NoNetworkFragment" tools:layout="@layout/fragment_no_network" /> <fragment android:id="@+id/clockSkewFragment" - android:name="org.briarproject.mailbox.android.ui.ClockSkewFragment" + android:name="org.briarproject.mailbox.android.ui.status.ClockSkewFragment" android:label="ClockSkewFragment" tools:layout="@layout/fragment_clock_skew" /> <fragment android:id="@+id/stoppingFragment" - android:name="org.briarproject.mailbox.android.ui.StoppingFragment" + android:name="org.briarproject.mailbox.android.ui.status.StoppingFragment" android:label="StoppingFragment" tools:layout="@layout/fragment_stopping" /> <fragment android:id="@+id/wipingFragment" - android:name="org.briarproject.mailbox.android.ui.WipingFragment" + android:name="org.briarproject.mailbox.android.ui.wipe.WipingFragment" android:label="WipingFragment" tools:layout="@layout/fragment_wiping" /> <action diff --git a/mailbox-android/src/main/res/values/strings.xml b/mailbox-android/src/main/res/values/strings.xml index d6cdf41f190048688dd6d2ebfa62f90e0b0854fb..c6caacd17879006ef68e0de40300ed2e81870f18 100644 --- a/mailbox-android/src/main/res/values/strings.xml +++ b/mailbox-android/src/main/res/values/strings.xml @@ -79,4 +79,17 @@ <string name="startup_failed_lifecycle_reuse">Mailbox has already stopped.\n\nPlease close the app and try again.</string> <string name="startup_failed_clock_error">Mailbox was unable to start because your device\'s clock is wrong.\n\nPlease set your device\'s clock to the right time and try again.</string> + <!-- Preference --> + <string name="prefs_title">Settings</string> + <string name="prefs_tor_category_title">Tor circumvention settings</string> + <string name="prefs_tor_auto_title">Automatic bridge selection</string> + <string name="prefs_tor_auto_summary">Bridges are chosen based on your location</string> + <string name="prefs_tor_bridges_title">Use bridges</string> + <string name="prefs_bridges_category_title">Bridge types</string> + <string name="prefs_bridges_snowflake_title" translatable="false">Snowflake</string> + <string name="prefs_bridges_vanilla_title" translatable="false">Vanilla</string> + <string name="prefs_bridges_obfs4_title" translatable="false">Obfs4</string> + <string name="prefs_bridges_obfs_builtin_title">Obfs4 from Tor Browser</string> + <string name="prefs_bridges_meek_title" translatable="false">Meek</string> + </resources> diff --git a/mailbox-android/src/main/res/values/themes.xml b/mailbox-android/src/main/res/values/themes.xml index 892878df15c459ee8d1946233793b8365691433f..1f8f089329e8268de6d91a5b6444067bef1bd513 100644 --- a/mailbox-android/src/main/res/values/themes.xml +++ b/mailbox-android/src/main/res/values/themes.xml @@ -19,4 +19,10 @@ <style name="Theme.BriarMailbox.Dialog.Destructive" parent="Theme.BriarMailbox.Dialog"> <item name="buttonBarPositiveButtonStyle">@style/TextButtonDestructive</item> </style> + + <style name="Theme.BriarMailbox.NoActionBar" parent="Theme.BriarMailboxBase"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> + </style> + </resources> diff --git a/mailbox-android/src/main/res/xml/preferences.xml b/mailbox-android/src/main/res/xml/preferences.xml new file mode 100644 index 0000000000000000000000000000000000000000..217a859d4f7cc2f5de7db4ed39eeee730dc4a8c3 --- /dev/null +++ b/mailbox-android/src/main/res/xml/preferences.xml @@ -0,0 +1,66 @@ +<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"> + + <PreferenceCategory + app:iconSpaceReserved="false" + app:title="@string/prefs_tor_category_title"> + + <SwitchPreferenceCompat + app:defaultValue="true" + app:disableDependentsState="true" + app:iconSpaceReserved="false" + app:key="bridgeAuto" + app:summary="@string/prefs_tor_auto_summary" + app:title="@string/prefs_tor_auto_title" /> + + <SwitchPreferenceCompat + app:defaultValue="false" + app:dependency="bridgeAuto" + app:iconSpaceReserved="false" + app:key="bridgeUse" + app:title="@string/prefs_tor_bridges_title" /> + + </PreferenceCategory> + + <PreferenceCategory + app:key="bridgeTypesCategory" + app:iconSpaceReserved="false" + app:title="@string/prefs_bridges_category_title"> + + <SwitchPreferenceCompat + app:defaultValue="false" + app:dependency="bridgeUse" + app:iconSpaceReserved="false" + app:key="bridgeUseSnowflake" + app:title="@string/prefs_bridges_snowflake_title" /> + + <SwitchPreferenceCompat + app:defaultValue="false" + app:dependency="bridgeUse" + app:iconSpaceReserved="false" + app:key="bridgeUseMeek" + app:title="@string/prefs_bridges_meek_title" /> + + <SwitchPreferenceCompat + app:defaultValue="false" + app:dependency="bridgeUse" + app:iconSpaceReserved="false" + app:key="bridgeUseObfs4" + app:title="@string/prefs_bridges_obfs4_title" /> + + <SwitchPreferenceCompat + app:defaultValue="false" + app:dependency="bridgeUse" + app:iconSpaceReserved="false" + app:key="bridgeUseObfs4Default" + app:title="@string/prefs_bridges_obfs_builtin_title" /> + + <SwitchPreferenceCompat + app:defaultValue="false" + app:dependency="bridgeUse" + app:iconSpaceReserved="false" + app:key="bridgeUseVanilla" + app:title="@string/prefs_bridges_vanilla_title" /> + + </PreferenceCategory> + +</PreferenceScreen> diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/AbstractTorPlugin.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/AbstractTorPlugin.java index 8f2d8d3fbe424103a278af26adb4eb36efaf3d0f..ca56fadcb0d96b66d8772265dd859a87970908e2 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/AbstractTorPlugin.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/AbstractTorPlugin.java @@ -54,14 +54,25 @@ import kotlinx.coroutines.flow.StateFlow; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_AUTO; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_AUTO_DEFAULT; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_MEEK; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_OBFS4; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_OBFS4_DEFAULT; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_SNOWFLAKE; +import static org.briarproject.mailbox.core.tor.TorConstants.BRIDGE_USE_VANILLA; import static org.briarproject.mailbox.core.tor.TorConstants.HS_ADDRESS_V3; import static org.briarproject.mailbox.core.tor.TorConstants.HS_PRIVATE_KEY_V3; import static org.briarproject.mailbox.core.tor.TorConstants.SETTINGS_NAMESPACE; import static org.briarproject.mailbox.core.util.LogUtils.info; import static org.briarproject.mailbox.core.util.LogUtils.logException; import static org.briarproject.mailbox.core.util.PrivacyUtils.scrubOnion; +import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.DEFAULT_OBFS4; import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.MEEK; +import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4; import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.SNOWFLAKE; +import static org.briarproject.onionwrapper.CircumventionProvider.BridgeType.VANILLA; import static org.slf4j.LoggerFactory.getLogger; public abstract class AbstractTorPlugin implements TorPlugin, EventListener { @@ -89,6 +100,8 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { protected final PluginState state = new PluginState(); + private volatile Settings settings = null; + AbstractTorPlugin(Executor ioExecutor, SettingsManager settingsManager, NetworkManager networkManager, @@ -139,6 +152,13 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { @Override public void startService() throws ServiceException { if (used.getAndSet(true)) throw new IllegalStateException(); + // Load the settings + try { + settings = settingsManager.getSettings(SETTINGS_NAMESPACE); + } catch (DbException e) { + logException(LOG, e, "Error while retrieving settings"); + settings = new Settings(); + } // Start Tor try { tor.start(); @@ -168,14 +188,7 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { private void publishHiddenService(int port) { if (!tor.isTorRunning()) return; - Settings s; - try { - s = settingsManager.getSettings(SETTINGS_NAMESPACE); - } catch (DbException e) { - logException(LOG, e, "Error while retrieving settings"); - s = new Settings(); - } - String privateKey3 = s.get(HS_PRIVATE_KEY_V3); + String privateKey3 = settings.get(HS_PRIVATE_KEY_V3); createV3HiddenService(port, privateKey3); } @@ -197,6 +210,8 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { s.put(HS_PRIVATE_KEY_V3, hsProps.privKey); try { settingsManager.mergeSettings(s, SETTINGS_NAMESPACE); + // update cached settings with merge result + settings = settingsManager.getSettings(SETTINGS_NAMESPACE); } catch (DbException e) { logException(LOG, e, "Error while merging settings"); } @@ -204,9 +219,8 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { } @Nullable - public String getHiddenServiceAddress() throws DbException { - Settings s = settingsManager.getSettings(SETTINGS_NAMESPACE); - return s.get(HS_ADDRESS_V3); + public String getHiddenServiceAddress() { + return settings == null ? null : settings.get(HS_ADDRESS_V3); } private void enableBridges(List<BridgeType> bridgeTypes, String countryCode) @@ -240,6 +254,19 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { } } + @Override + public void onSettingsChanged() { + ioExecutor.execute(() -> { + try { + settings = settingsManager.getSettings(SETTINGS_NAMESPACE); + } catch (DbException e) { + logException(LOG, e, "Error while retrieving settings"); + settings = new Settings(); + } + updateConnectionStatus(networkManager.getNetworkStatus()); + }); + } + private void updateConnectionStatus(NetworkStatus status) { connectionStatusExecutor.execute(() -> { if (!tor.isTorRunning()) return; @@ -247,7 +274,6 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { boolean wifi = status.isWifi(); boolean ipv6Only = status.isIpv6Only(); String country = locationUtils.getCurrentCountry(); - boolean bridgesWork = circumventionProvider.doBridgesWork(country); if (LOG.isInfoEnabled()) { LOG.info("Online: " + online + ", wifi: " + wifi @@ -264,19 +290,7 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { } else { LOG.info("Enabling network"); enableNetwork = true; - if (bridgesWork) { - if (ipv6Only) { - bridgeTypes = asList(MEEK, SNOWFLAKE); - } else { - bridgeTypes = circumventionProvider - .getSuitableBridgeTypes(country); - } - if (LOG.isInfoEnabled()) { - LOG.info("Using bridge types " + bridgeTypes); - } - } else { - LOG.info("Not using bridges"); - } + bridgeTypes = getBridgeTypes(country, ipv6Only); if (wifi) { LOG.info("Enabling connection padding"); enableConnectionPadding = true; @@ -298,6 +312,78 @@ public abstract class AbstractTorPlugin implements TorPlugin, EventListener { }); } + @Override + public List<BridgeType> getCustomBridgeTypes() { + return getCustomBridgeTypes(locationUtils.getCurrentCountry(), + networkManager.getNetworkStatus().isIpv6Only()); + } + + private List<BridgeType> getCustomBridgeTypes(String country, + boolean ipv6Only) { + List<BridgeType> defaultTypes; + if (ipv6Only) { + defaultTypes = asList(MEEK, SNOWFLAKE); + } else { + defaultTypes = + circumventionProvider.getSuitableBridgeTypes(country); + } + ArrayList<BridgeType> types = new ArrayList<>(); + if (settings.getBoolean(BRIDGE_USE_SNOWFLAKE, + defaultTypes.contains(SNOWFLAKE))) { + types.add(SNOWFLAKE); + } + if (settings.getBoolean(BRIDGE_USE_MEEK, defaultTypes.contains(MEEK))) { + types.add(MEEK); + } + if (settings.getBoolean(BRIDGE_USE_OBFS4, + defaultTypes.contains(NON_DEFAULT_OBFS4))) { + types.add(NON_DEFAULT_OBFS4); + } + if (settings.getBoolean(BRIDGE_USE_OBFS4_DEFAULT, + defaultTypes.contains(DEFAULT_OBFS4))) { + types.add(DEFAULT_OBFS4); + } + if (settings.getBoolean(BRIDGE_USE_VANILLA, + defaultTypes.contains(VANILLA))) { + types.add(VANILLA); + } + return types; + } + + private List<BridgeType> getBridgeTypes(String country, boolean ipv6Only) { + List<BridgeType> bridgeTypes = emptyList(); + boolean bridgesNeeded = + circumventionProvider.doBridgesWork(country); + boolean bridgeAuto = + settings.getBoolean(BRIDGE_AUTO, BRIDGE_AUTO_DEFAULT); + if (bridgeAuto) { + if (bridgesNeeded) { + if (ipv6Only) { + bridgeTypes = asList(MEEK, SNOWFLAKE); + } else { + bridgeTypes = circumventionProvider + .getSuitableBridgeTypes(country); + } + if (LOG.isInfoEnabled()) { + LOG.info("Using bridge types " + bridgeTypes); + } + } else { + LOG.info("Not using bridges"); + } + } else { + boolean useBridges = settings.getBoolean(BRIDGE_USE, bridgesNeeded); + if (useBridges) { + bridgeTypes = getCustomBridgeTypes(country, ipv6Only); + if (LOG.isInfoEnabled()) { + LOG.info("Using bridge types " + bridgeTypes); + } + } else { + LOG.info("Not using bridges"); + } + } + return bridgeTypes; + } + @ThreadSafe private class PluginState { diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorConstants.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorConstants.java index ca6b6633ecfe7eb554e85c5016f63bf91271f57d..0079982fc3cd972792cfdc4ad06f807fb17d950e 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorConstants.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorConstants.java @@ -25,6 +25,24 @@ public interface TorConstants { String SETTINGS_NAMESPACE = "Tor"; String HS_PRIVATE_KEY_V3 = "onionPrivKey3"; String HS_ADDRESS_V3 = "onionAddress3"; + /** + * Whether circumvention bridge handling should be handled automatically. + */ + String BRIDGE_AUTO = "bridgeAuto"; + /** + * Whether automatic circumvention handling is enabled by default or not. + */ + boolean BRIDGE_AUTO_DEFAULT = true; + /** + * Whether bridges should be used for circumvention. + * Only consider when {@link #BRIDGE_AUTO} is false. + */ + String BRIDGE_USE = "bridgeUse"; + String BRIDGE_USE_SNOWFLAKE = "bridgeUseSnowflake"; + String BRIDGE_USE_MEEK = "bridgeUseMeek"; + String BRIDGE_USE_OBFS4 = "bridgeUseObfs4"; + String BRIDGE_USE_OBFS4_DEFAULT = "bridgeUseObfs4Default"; + String BRIDGE_USE_VANILLA = "bridgeUseVanilla"; int SOCKS_PORT = 59054; int CONTROL_PORT = 59055; diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java index bbc28f42779005a40bd06206cb6c34c8939a2c85..39c907e5d6e9b4a5a6406eb9d90b607f566ef49a 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java @@ -2,13 +2,38 @@ package org.briarproject.mailbox.core.tor; import org.briarproject.mailbox.core.db.DbException; import org.briarproject.mailbox.core.lifecycle.Service; +import org.briarproject.mailbox.core.settings.Settings; +import org.briarproject.nullsafety.NotNullByDefault; +import org.briarproject.onionwrapper.CircumventionProvider.BridgeType; + +import java.util.List; + +import javax.annotation.Nullable; import kotlinx.coroutines.flow.StateFlow; +@NotNullByDefault public interface TorPlugin extends Service { StateFlow<TorPluginState> getState(); + /** + * Call this whenever {@link Settings} in + * {@link TorConstants#SETTINGS_NAMESPACE} have changed. + */ + void onSettingsChanged(); + + /** + * This is only available after {@link #startService()} has returned. + * Otherwise returns null. + */ + @Nullable String getHiddenServiceAddress() throws DbException; + /** + * Get a list of bridge types that Tor will be using with current settings, + * country and {@link NetworkStatus}. + */ + List<BridgeType> getCustomBridgeTypes(); + } diff --git a/mailbox-lib/src/main/java/org/briarproject/mailbox/core/tor/FakeTorPlugin.kt b/mailbox-lib/src/main/java/org/briarproject/mailbox/core/tor/FakeTorPlugin.kt index d56ec5cfb1c9207dba95387effb3d229e71b87fc..5d387bb8c7ecff90b3b8cdc21b52362c27aaa014 100644 --- a/mailbox-lib/src/main/java/org/briarproject/mailbox/core/tor/FakeTorPlugin.kt +++ b/mailbox-lib/src/main/java/org/briarproject/mailbox/core/tor/FakeTorPlugin.kt @@ -2,6 +2,7 @@ package org.briarproject.mailbox.core.tor import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import org.briarproject.onionwrapper.CircumventionProvider.BridgeType import javax.inject.Inject class FakeTorPlugin @Inject constructor() : TorPlugin { @@ -20,5 +21,9 @@ class FakeTorPlugin @Inject constructor() : TorPlugin { return state } + override fun onSettingsChanged() { + } + override fun getHiddenServiceAddress(): String? = null + override fun getCustomBridgeTypes(): List<BridgeType> = emptyList() }