diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt index 6ad7617f40c6..66df9890b708 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt @@ -7,5 +7,5 @@ import com.ramcosta.composedestinations.DestinationsNavHost @Composable fun MullvadApp() { - DestinationsNavHost(modifier = Modifier.fillMaxSize(), navGraph = NavGraphs.root) {} + DestinationsNavHost(modifier = Modifier, navGraph = NavGraphs.root) {} } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/extension/ContextExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/extension/ContextExtensions.kt index e2e2f5c44c63..f747ae169dd7 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/extension/ContextExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/extension/ContextExtensions.kt @@ -3,18 +3,6 @@ package net.mullvad.mullvadvpn.ui.extension import android.content.ClipData import android.content.ClipboardManager import android.content.Context -import androidx.fragment.app.Fragment -import net.mullvad.mullvadvpn.ui.MainActivity - -fun Fragment.requireMainActivity(): MainActivity { - return if (this.activity is MainActivity) { - this.activity as MainActivity - } else { - throw IllegalStateException( - "Fragment $this not attached to ${MainActivity::class.simpleName}." - ) - } -} fun Context.copyToClipboard(content: String, clipboardLabel: String) { val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt deleted file mode 100644 index efdc0783a3ce..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt +++ /dev/null @@ -1,52 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.AccountScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.viewmodel.AccountViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class AccountFragment : BaseFragment() { - private val vm by viewModel() - - @OptIn(ExperimentalMaterial3Api::class) - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val state = vm.uiState.collectAsState().value - AccountScreen( - uiState = state, - uiSideEffect = vm.uiSideEffect, - enterTransitionEndAction = vm.enterTransitionEndAction, - onRedeemVoucherClick = { openRedeemVoucherFragment() }, - onManageAccountClick = vm::onManageAccountClick, - onLogoutClick = vm::onLogoutClick, - onBackClick = { activity?.onBackPressedDispatcher?.onBackPressed() } - ) - } - } - } - } - - override fun onEnterTransitionAnimationEnd() { - vm.onTransitionAnimationEnd() - } - - private fun openRedeemVoucherFragment() { - val transaction = parentFragmentManager.beginTransaction() - transaction.addToBackStack(null) - RedeemVoucherDialogFragment().show(transaction, null) - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/BaseFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/BaseFragment.kt deleted file mode 100644 index 99b9b42f0982..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/BaseFragment.kt +++ /dev/null @@ -1,71 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.view.animation.Animation -import android.view.animation.AnimationUtils -import androidx.annotation.LayoutRes -import androidx.core.view.ViewCompat -import androidx.fragment.app.Fragment -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.util.transitionFinished - -abstract class BaseFragment : Fragment { - constructor() : super() - - constructor(@LayoutRes contentLayoutId: Int) : super(contentLayoutId) - - protected var transitionFinishedFlow: Flow = emptyFlow() - private set - - override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? { - val zAdjustment = - if (animationsToAdjustZorder.contains(nextAnim)) { - 1f - } else { - 0f - } - ViewCompat.setTranslationZ(requireView(), zAdjustment) - val anim = - if (nextAnim != 0 && enter) { - AnimationUtils.loadAnimation(context, nextAnim)?.apply { - transitionFinishedFlow = transitionFinished() - } - } else { - super.onCreateAnimation(transit, enter, nextAnim) - } - anim?.let { - anim.setAnimationListener( - object : Animation.AnimationListener { - override fun onAnimationStart(animation: Animation?) {} - - override fun onAnimationRepeat(animation: Animation?) {} - - override fun onAnimationEnd(animation: Animation?) { - if (enter) { - onEnterTransitionAnimationEnd() - } - } - }, - ) - } - ?: run { - if (enter) { - onEnterTransitionAnimationEnd() - } - } - return anim - } - - open fun onEnterTransitionAnimationEnd() {} - - companion object { - private val animationsToAdjustZorder = - listOf( - R.anim.fragment_enter_from_right, - R.anim.fragment_exit_to_right, - R.anim.fragment_enter_from_bottom, - R.anim.fragment_exit_to_bottom - ) - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt deleted file mode 100644 index ee4ae501791b..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt +++ /dev/null @@ -1,98 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import kotlinx.coroutines.flow.MutableStateFlow -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.ConnectScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.ui.MainActivity -import net.mullvad.mullvadvpn.viewmodel.ConnectViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class ConnectFragment : BaseFragment() { - - // Injected dependencies - private val connectViewModel: ConnectViewModel by viewModel() - private val _setNavigationBar = MutableStateFlow(false) - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - val view = inflater.inflate(R.layout.fragment_compose, container, false) - - view.findViewById(R.id.compose_view).setContent { - AppTheme { - val state = connectViewModel.uiState.collectAsState().value - val drawNavbar = _setNavigationBar.collectAsState() - ConnectScreen( - uiState = state, - uiSideEffect = connectViewModel.uiSideEffect, - drawNavigationBar = drawNavbar.value, - onDisconnectClick = connectViewModel::onDisconnectClick, - onReconnectClick = connectViewModel::onReconnectClick, - onConnectClick = connectViewModel::onConnectClick, - onCancelClick = connectViewModel::onCancelClick, - onSwitchLocationClick = ::openSwitchLocationScreen, - onToggleTunnelInfo = connectViewModel::toggleTunnelInfoExpansion, - onUpdateVersionClick = { openDownloadUrl() }, - onManageAccountClick = connectViewModel::onManageAccountClick, - onOpenOutOfTimeScreen = ::openOutOfTimeScreen, - onSettingsClick = ::openSettingsView, - onAccountClick = ::openAccountView, - onDismissNewDeviceClick = connectViewModel::dismissNewDeviceNotification, - ) - } - } - - return view - } - - private fun openDownloadUrl() {} - - private fun openSwitchLocationScreen() { - parentFragmentManager.beginTransaction().apply { - setCustomAnimations( - R.anim.fragment_enter_from_bottom, - R.anim.do_nothing, - R.anim.do_nothing, - R.anim.fragment_exit_to_bottom - ) - replace(R.id.main_fragment, SelectLocationFragment()) - addToBackStack(null) - commitAllowingStateLoss() - } - } - - private fun openOutOfTimeScreen() { - parentFragmentManager.beginTransaction().apply { - replace(R.id.main_fragment, OutOfTimeFragment()) - commitAllowingStateLoss() - } - } - - private fun openSettingsView() { - (context as? MainActivity)?.openSettings() - } - - private fun openAccountView() { - (context as? MainActivity)?.openAccount() - } - - override fun onPause() { - super.onPause() - _setNavigationBar.value = false - } - - // TODO Temporary fix for handling in & out animations until we have Compose Navigation - override fun onEnterTransitionAnimationEnd() { - super.onEnterTransitionAnimationEnd() - _setNavigationBar.value = true - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceListFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceListFragment.kt deleted file mode 100644 index 13d172cf4188..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceListFragment.kt +++ /dev/null @@ -1,40 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.widget.Toast -import androidx.fragment.app.Fragment -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.flowWithLifecycle -import androidx.lifecycle.lifecycleScope -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import net.mullvad.mullvadvpn.ui.MainActivity -import net.mullvad.mullvadvpn.viewmodel.DeviceListViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class DeviceListFragment : Fragment() { - - private val deviceListViewModel by viewModel() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - lifecycleScope.launchUiSubscriptionsOnResume() - } - - override fun onResume() { - super.onResume() - deviceListViewModel.clearStagedDevice() - } - - private fun CoroutineScope.launchUiSubscriptionsOnResume() = launch { - deviceListViewModel.toastMessages - .flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED) - .collect { Toast.makeText(context, it, Toast.LENGTH_SHORT).show() } - } - - private fun parentActivity(): MainActivity? { - return (context as? MainActivity) - } - - private fun openSettings() = parentActivity()?.openSettings() -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceRevokedFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceRevokedFragment.kt deleted file mode 100644 index 4b744d568a00..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceRevokedFragment.kt +++ /dev/null @@ -1,42 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import androidx.fragment.app.Fragment -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.DeviceRevokedScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.ui.MainActivity -import net.mullvad.mullvadvpn.viewmodel.DeviceRevokedViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class DeviceRevokedFragment : Fragment() { - private val deviceRevokedViewModel: DeviceRevokedViewModel by viewModel() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val state = deviceRevokedViewModel.uiState.collectAsState().value - DeviceRevokedScreen( - state = state, - onSettingsClicked = this@DeviceRevokedFragment::openSettingsView, - onGoToLoginClicked = deviceRevokedViewModel::onGoToLoginClicked - ) - } - } - } - } - - private fun openSettingsView() { - (context as? MainActivity)?.openSettings() - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/FragmentArgumentConstant.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/FragmentArgumentConstant.kt deleted file mode 100644 index 7b066b12462c..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/FragmentArgumentConstant.kt +++ /dev/null @@ -1,3 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -const val ACCOUNT_TOKEN_ARGUMENT_KEY = "accountToken" diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoadingFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoadingFragment.kt deleted file mode 100644 index 49b651af18e1..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoadingFragment.kt +++ /dev/null @@ -1,28 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.ui.platform.ComposeView -import androidx.fragment.app.Fragment -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.ui.MainActivity - -class LoadingFragment : Fragment() { - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - // AppTheme { SplashScreen(this@LoadingFragment::openSettings) } - } - } - } - - private fun openSettings() { - (context as? MainActivity)?.openSettings() - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt deleted file mode 100644 index 2dfa6df7cbf1..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt +++ /dev/null @@ -1,76 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.platform.ComposeView -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.model.AccountToken -import net.mullvad.mullvadvpn.ui.MainActivity -import net.mullvad.mullvadvpn.viewmodel.LoginViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class LoginFragment : BaseFragment() { - private val vm: LoginViewModel by viewModel() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - - // TODO: Remove this when we have a better solution for login after clearing max devices - val accountTokenArgument = arguments?.getString(ACCOUNT_TOKEN_ARGUMENT_KEY) - if (accountTokenArgument != null) { - // Login and set initial TextField value - vm.onAccountNumberChange(accountTokenArgument) - vm.login(accountTokenArgument) - } - - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val uiState by vm.uiState.collectAsState() - /* - LoginScreen( - uiState, - vm::login, - vm::createAccount, - vm::clearAccountHistory, - vm::onAccountNumberChange, - ::openSettingsView - ) - */ - } - } - } - } - - private fun navigateToDeviceListFragment(accountToken: AccountToken) { - val deviceFragment = - DeviceListFragment().apply { - arguments = - Bundle().apply { putString(ACCOUNT_TOKEN_ARGUMENT_KEY, accountToken.value) } - } - - parentFragmentManager.beginTransaction().apply { - setCustomAnimations( - R.anim.fragment_enter_from_right, - R.anim.fragment_exit_to_left, - R.anim.fragment_half_enter_from_left, - R.anim.fragment_exit_to_right - ) - replace(R.id.main_fragment, deviceFragment) - addToBackStack(null) - commitAllowingStateLoss() - } - } - - private fun openSettingsView() { - (context as? MainActivity)?.openSettings() - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.kt deleted file mode 100644 index 53df05c5f3fe..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.kt +++ /dev/null @@ -1,67 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.OutOfTimeScreen -import net.mullvad.mullvadvpn.constant.IS_PLAY_BUILD -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.ui.MainActivity -import net.mullvad.mullvadvpn.viewmodel.OutOfTimeViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class OutOfTimeFragment : BaseFragment() { - - private val vm by viewModel() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val state = vm.uiState.collectAsState().value - OutOfTimeScreen( - showSitePayment = IS_PLAY_BUILD.not(), - uiState = state, - uiSideEffect = vm.uiSideEffect, - onSitePaymentClick = vm::onSitePaymentClick, - onRedeemVoucherClick = ::openRedeemVoucherFragment, - onSettingsClick = ::openSettingsView, - onAccountClick = ::openAccountView, - openConnectScreen = ::advanceToConnectScreen, - onDisconnectClick = vm::onDisconnectClick - ) - } - } - } - } - - private fun openRedeemVoucherFragment() { - val transaction = parentFragmentManager.beginTransaction() - transaction.addToBackStack(null) - RedeemVoucherDialogFragment { wasSuccessful -> if (wasSuccessful) advanceToConnectScreen() } - .show(transaction, null) - } - - private fun advanceToConnectScreen() { - parentFragmentManager.beginTransaction().apply { - replace(R.id.main_fragment, ConnectFragment()) - commitAllowingStateLoss() - } - } - - private fun openSettingsView() { - (context as? MainActivity)?.openSettings() - } - - private fun openAccountView() { - (context as? MainActivity)?.openAccount() - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/PrivacyDisclaimerFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/PrivacyDisclaimerFragment.kt deleted file mode 100644 index baa5c149d6dd..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/PrivacyDisclaimerFragment.kt +++ /dev/null @@ -1,53 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.ui.platform.ComposeView -import androidx.fragment.app.Fragment -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.PrivacyDisclaimerScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.ui.MainActivity -import net.mullvad.mullvadvpn.util.appendHideNavOnPlayBuild -import net.mullvad.mullvadvpn.viewmodel.PrivacyDisclaimerViewModel -import org.koin.android.ext.android.inject - -class PrivacyDisclaimerFragment : Fragment() { - - private val privacyDisclaimerViewModel: PrivacyDisclaimerViewModel by inject() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - PrivacyDisclaimerScreen( - onPrivacyPolicyLinkClicked = { openPrivacyPolicy() }, - onAcceptClicked = { handleAcceptedPrivacyDisclaimer() } - ) - } - } - } - } - - private fun handleAcceptedPrivacyDisclaimer() { - privacyDisclaimerViewModel.setPrivacyDisclosureAccepted() - (activity as? MainActivity)?.initializeStateHandlerAndServiceConnection() - } - - private fun openPrivacyPolicy() { - val privacyPolicyUrlIntent = - Intent( - Intent.ACTION_VIEW, - Uri.parse(getString(R.string.privacy_policy_url).appendHideNavOnPlayBuild()) - ) - context?.startActivity(privacyPolicyUrlIntent) - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ProblemReportFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ProblemReportFragment.kt deleted file mode 100644 index 397039719c0e..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ProblemReportFragment.kt +++ /dev/null @@ -1,55 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.ReportProblemScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.viewmodel.ReportProblemViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class ProblemReportFragment : BaseFragment() { - private val vm by viewModel() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val uiState = vm.uiState.collectAsState().value - ReportProblemScreen( - uiState, - onSendReport = { email, description -> vm.sendReport(email, description) }, - onDismissNoEmailDialog = vm::dismissConfirmNoEmail, - onClearSendResult = vm::clearSendResult, - onNavigateToViewLogs = { showLogs() } - ) { - activity?.onBackPressed() - } - } - } - } - } - - private fun showLogs() { - parentFragmentManager.beginTransaction().apply { - setCustomAnimations( - R.anim.fragment_enter_from_right, - R.anim.fragment_half_exit_to_left, - R.anim.fragment_half_enter_from_left, - R.anim.fragment_exit_to_right - ) - replace(R.id.main_fragment, ViewLogsFragment()) - addToBackStack(null) - commitAllowingStateLoss() - } - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/RedeemVoucherDialogFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/RedeemVoucherDialogFragment.kt deleted file mode 100644 index 2730fde5479d..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/RedeemVoucherDialogFragment.kt +++ /dev/null @@ -1,48 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.app.Dialog -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import androidx.fragment.app.DialogFragment -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.RedeemVoucherDialogScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.viewmodel.VoucherDialogViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class RedeemVoucherDialogFragment(val onDialogDismiss: (Boolean) -> Unit = {}) : DialogFragment() { - - private val vm by viewModel() - private lateinit var voucherDialog: Dialog - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - RedeemVoucherDialogScreen( - uiState = vm.uiState.collectAsState().value, - onVoucherInputChange = { vm.onVoucherInputChange(it) }, - onRedeem = { vm.onRedeem(it) }, - onDismiss = { - onDismiss(voucherDialog) - onDialogDismiss(it) - } - ) - } - } - } - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - voucherDialog = super.onCreateDialog(savedInstanceState) - return voucherDialog - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SelectLocationFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SelectLocationFragment.kt deleted file mode 100644 index d1c4ac72bfbf..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SelectLocationFragment.kt +++ /dev/null @@ -1,44 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.SelectLocationScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.viewmodel.SelectLocationViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class SelectLocationFragment : BaseFragment() { - - private val vm by viewModel() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val state = vm.uiState.collectAsState().value - SelectLocationScreen( - uiState = state, - uiCloseAction = vm.uiCloseAction, - enterTransitionEndAction = vm.enterTransitionEndAction, - onSelectRelay = vm::selectRelay, - onSearchTermInput = vm::onSearchTermInput, - onBackClick = { activity?.onBackPressedDispatcher?.onBackPressed() }, - ) - } - } - } - } - - override fun onEnterTransitionAnimationEnd() { - vm.onTransitionAnimationEnd() - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SettingsFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SettingsFragment.kt deleted file mode 100644 index e5faf6bb1171..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SettingsFragment.kt +++ /dev/null @@ -1,72 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import androidx.fragment.app.Fragment -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.SettingsScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.viewmodel.SettingsViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class SettingsFragment : BaseFragment() { - private val vm by viewModel() - - @OptIn(ExperimentalMaterial3Api::class) - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val state = vm.uiState.collectAsState().value - SettingsScreen( - uiState = state, - enterTransitionEndAction = vm.enterTransitionEndAction, - onVpnSettingCellClick = { openVpnSettingsFragment() }, - onSplitTunnelingCellClick = { openSplitTunnelingFragment() }, - onReportProblemCellClick = { openReportProblemFragment() }, - onBackClick = { activity?.onBackPressed() } - ) - } - } - } - } - - override fun onEnterTransitionAnimationEnd() { - vm.onTransitionAnimationEnd() - } - - private fun openFragment(fragment: Fragment) { - parentFragmentManager.beginTransaction().apply { - setCustomAnimations( - R.anim.fragment_enter_from_right, - R.anim.fragment_exit_to_left, - R.anim.fragment_half_enter_from_left, - R.anim.fragment_exit_to_right - ) - replace(R.id.main_fragment, fragment) - addToBackStack(null) - commitAllowingStateLoss() - } - } - - private fun openVpnSettingsFragment() { - openFragment(VpnSettingsFragment()) - } - - private fun openSplitTunnelingFragment() { - openFragment(SplitTunnelingFragment()) - } - - private fun openReportProblemFragment() { - openFragment(ProblemReportFragment()) - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragment.kt deleted file mode 100644 index 7004303ae8c1..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragment.kt +++ /dev/null @@ -1,44 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.applist.ApplicationsIconManager -import net.mullvad.mullvadvpn.compose.screen.SplitTunnelingScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.viewmodel.SplitTunnelingViewModel -import org.koin.android.ext.android.inject -import org.koin.androidx.viewmodel.ext.android.viewModel - -class SplitTunnelingFragment : BaseFragment() { - private val viewModel: SplitTunnelingViewModel by viewModel() - private val applicationsIconManager: ApplicationsIconManager by inject() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val state = viewModel.uiState.collectAsState().value - SplitTunnelingScreen( - uiState = state, - onShowSystemAppsClick = viewModel::onShowSystemAppsClick, - onExcludeAppClick = viewModel::onExcludeAppClick, - onIncludeAppClick = viewModel::onIncludeAppClick, - onBackClick = { activity?.onBackPressedDispatcher?.onBackPressed() }, - onResolveIcon = { packageName -> - applicationsIconManager.getAppIcon(packageName) - } - ) - } - } - } - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ViewLogsFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ViewLogsFragment.kt deleted file mode 100644 index 21931ab87638..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ViewLogsFragment.kt +++ /dev/null @@ -1,36 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.ViewLogsScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.viewmodel.ViewLogsViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class ViewLogsFragment : BaseFragment() { - private val vm by viewModel() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val uiState = vm.uiState.collectAsState() - ViewLogsScreen( - uiState = uiState.value, - onBackClick = { activity?.onBackPressed() } - ) - } - } - } - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt deleted file mode 100644 index 49d43e6b2770..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/VpnSettingsFragment.kt +++ /dev/null @@ -1,69 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.VpnSettingsScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.viewmodel.VpnSettingsViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class VpnSettingsFragment : BaseFragment() { - private val vm by viewModel() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val state = vm.uiState.collectAsState().value - VpnSettingsScreen( - uiState = state, - onMtuCellClick = vm::onMtuCellClick, - onSaveMtuClick = vm::onSaveMtuClick, - onRestoreMtuClick = vm::onRestoreMtuClick, - onCancelMtuDialogClick = vm::onCancelDialogClick, - onToggleAutoConnect = vm::onToggleAutoConnect, - onToggleLocalNetworkSharing = vm::onToggleLocalNetworkSharing, - onToggleDnsClick = vm::onToggleDnsClick, - onToggleBlockAds = vm::onToggleBlockAds, - onToggleBlockTrackers = vm::onToggleBlockTrackers, - onToggleBlockMalware = vm::onToggleBlockMalware, - onToggleBlockAdultContent = vm::onToggleBlockAdultContent, - onToggleBlockGambling = vm::onToggleBlockGambling, - onToggleBlockSocialMedia = vm::onToggleBlockSocialMedia, - onDnsClick = vm::onDnsClick, - onDnsInputChange = vm::onDnsInputChange, - onSaveDnsClick = vm::onSaveDnsClick, - onRemoveDnsClick = vm::onRemoveDnsClick, - onCancelDnsDialogClick = vm::onCancelDns, - onLocalNetworkSharingInfoClick = vm::onLocalNetworkSharingInfoClick, - onContentsBlockersInfoClick = vm::onContentsBlockerInfoClick, - onCustomDnsInfoClick = vm::onCustomDnsInfoClick, - onMalwareInfoClick = vm::onMalwareInfoClick, - onDismissInfoClick = vm::onDismissInfoClick, - onBackClick = { activity?.onBackPressedDispatcher?.onBackPressed() }, - onStopEvent = vm::onStopEvent, - toastMessagesSharedFlow = vm.toastMessages, - onSelectObfuscationSetting = vm::onSelectObfuscationSetting, - onObfuscationInfoClick = vm::onObfuscationInfoClick, - onSelectQuantumResistanceSetting = vm::onSelectQuantumResistanceSetting, - onQuantumResistanceInfoClicked = vm::onQuantumResistanceInfoClicked, - onWireguardPortSelected = vm::onWireguardPortSelected, - onWireguardPortInfoClicked = vm::onWireguardPortInfoClicked, - onShowCustomPortDialog = vm::onShowCustomPortDialog, - onCancelCustomPortDialogClick = vm::onCancelDialogClick, - onCloseCustomPortDialog = vm::onCancelDialogClick - ) - } - } - } - } -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt deleted file mode 100644 index d04c5de53afd..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt +++ /dev/null @@ -1,66 +0,0 @@ -package net.mullvad.mullvadvpn.ui.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.screen.WelcomeScreen -import net.mullvad.mullvadvpn.constant.IS_PLAY_BUILD -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.ui.MainActivity -import net.mullvad.mullvadvpn.viewmodel.WelcomeViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class WelcomeFragment : BaseFragment() { - - private val vm by viewModel() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val state = vm.uiState.collectAsState().value - WelcomeScreen( - showSitePayment = IS_PLAY_BUILD.not(), - uiState = state, - uiSideEffect = vm.uiSideEffect, - onSitePaymentClick = vm::onSitePaymentClick, - onRedeemVoucherClick = ::openRedeemVoucherFragment, - onSettingsClick = ::openSettingsView, - onAccountClick = ::openAccountView, - openConnectScreen = ::advanceToConnectScreen - ) - } - } - } - } - - private fun openRedeemVoucherFragment() { - val transaction = parentFragmentManager.beginTransaction() - transaction.addToBackStack(null) - RedeemVoucherDialogFragment { wasSuccessful -> if (wasSuccessful) advanceToConnectScreen() } - .show(transaction, null) - } - - private fun advanceToConnectScreen() { - parentFragmentManager.beginTransaction().apply { - replace(R.id.main_fragment, ConnectFragment()) - commitAllowingStateLoss() - } - } - - private fun openSettingsView() { - (context as? MainActivity)?.openSettings() - } - - private fun openAccountView() { - (context as? MainActivity)?.openAccount() - } -} diff --git a/android/app/src/main/res/layout/fragment_compose.xml b/android/app/src/main/res/layout/fragment_compose.xml deleted file mode 100644 index a3a147d9971f..000000000000 --- a/android/app/src/main/res/layout/fragment_compose.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/android/app/src/main/res/layout/main.xml b/android/app/src/main/res/layout/main.xml deleted file mode 100644 index 8e7356e766ee..000000000000 --- a/android/app/src/main/res/layout/main.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/android/gradle/verification-metadata.xml b/android/gradle/verification-metadata.xml deleted file mode 100644 index 5d658e00d097..000000000000 --- a/android/gradle/verification-metadata.xml +++ /dev/null @@ -1,4024 +0,0 @@ - - - - - true - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -