diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialog.kt deleted file mode 100644 index a30e525e6d59..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialog.kt +++ /dev/null @@ -1,119 +0,0 @@ -package net.mullvad.mullvadvpn.compose.dialog - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.button.NegativeButton -import net.mullvad.mullvadvpn.compose.button.PrimaryButton -import net.mullvad.mullvadvpn.compose.test.CUSTOM_PORT_DIALOG_INPUT_TEST_TAG -import net.mullvad.mullvadvpn.compose.textfield.CustomPortTextField -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.lib.theme.Dimens -import net.mullvad.mullvadvpn.lib.theme.color.AlphaDescription -import net.mullvad.mullvadvpn.model.PortRange -import net.mullvad.mullvadvpn.util.asString -import net.mullvad.mullvadvpn.util.isPortInValidRanges - -@Preview -@Composable -private fun PreviewCustomPortDialog() { - AppTheme { - CustomPortDialog( - onSave = {}, - onReset = {}, - customPort = "", - allowedPortRanges = listOf(PortRange(10, 10), PortRange(40, 50)), - showReset = true, - onDismissRequest = {} - ) - } -} - -@Composable -fun CustomPortDialog( - customPort: String, - allowedPortRanges: List, - showReset: Boolean, - onSave: (customPortString: String) -> Unit, - onReset: () -> Unit, - onDismissRequest: () -> Unit -) { - val port = remember { mutableStateOf(customPort) } - - AlertDialog( - title = { - Text( - text = stringResource(id = R.string.custom_port_dialog_title), - style = MaterialTheme.typography.headlineSmall - ) - }, - confirmButton = { - Column(verticalArrangement = Arrangement.spacedBy(Dimens.buttonSpacing)) { - PrimaryButton( - text = stringResource(id = R.string.custom_port_dialog_submit), - onClick = { onSave(port.value) }, - isEnabled = - port.value.isNotEmpty() && - allowedPortRanges.isPortInValidRanges(port.value.toIntOrNull() ?: 0) - ) - if (showReset) { - NegativeButton( - text = stringResource(R.string.custom_port_dialog_remove), - onClick = onReset - ) - } - PrimaryButton( - text = stringResource(id = R.string.cancel), - onClick = onDismissRequest - ) - } - }, - text = { - Column { - CustomPortTextField( - value = port.value, - onSubmit = { input -> - if ( - input.isNotEmpty() && - allowedPortRanges.isPortInValidRanges(input.toIntOrNull() ?: 0) - ) { - onSave(input) - } - }, - onValueChanged = { input -> port.value = input }, - isValidValue = - port.value.isNotEmpty() && - allowedPortRanges.isPortInValidRanges(port.value.toIntOrNull() ?: 0), - maxCharLength = 5, - modifier = Modifier.testTag(CUSTOM_PORT_DIALOG_INPUT_TEST_TAG).fillMaxWidth() - ) - Spacer(modifier = Modifier.height(Dimens.smallPadding)) - Text( - text = - stringResource( - id = R.string.custom_port_dialog_valid_ranges, - allowedPortRanges.asString() - ), - color = MaterialTheme.colorScheme.onBackground.copy(alpha = AlphaDescription), - style = MaterialTheme.typography.bodySmall - ) - } - }, - containerColor = MaterialTheme.colorScheme.background, - titleContentColor = MaterialTheme.colorScheme.onBackground, - onDismissRequest = onDismissRequest - ) -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt deleted file mode 100644 index d99f9e62b7ee..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt +++ /dev/null @@ -1,78 +0,0 @@ -package net.mullvad.mullvadvpn.compose.dialog - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.button.NegativeButton -import net.mullvad.mullvadvpn.compose.button.PrimaryButton -import net.mullvad.mullvadvpn.compose.component.HtmlText -import net.mullvad.mullvadvpn.compose.component.textResource -import net.mullvad.mullvadvpn.model.Device - -@Preview -@Composable -private fun PreviewShowDeviceRemovalDialog() { - DeviceRemovalDialog( - onDismiss = {}, - onConfirm = {}, - device = Device("test", "test", byteArrayOf(), "test") - ) -} - -@Composable -fun DeviceRemovalDialog(onDismiss: () -> Unit, onConfirm: () -> Unit, device: Device) { - AlertDialog( - onDismissRequest = onDismiss, - title = { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(top = 0.dp).fillMaxWidth() - ) { - Image( - painter = painterResource(id = R.drawable.icon_alert), - contentDescription = "Remove", - modifier = Modifier.width(50.dp).height(50.dp) - ) - } - }, - text = { - val htmlFormattedDialogText = - textResource( - id = R.string.max_devices_confirm_removal_description, - device.displayName() - ) - - HtmlText(htmlFormattedString = htmlFormattedDialogText, textSize = 16.sp.value) - }, - dismissButton = { - NegativeButton( - onClick = onConfirm, - text = stringResource(id = R.string.confirm_removal) - ) - }, - confirmButton = { - PrimaryButton( - modifier = Modifier.focusRequester(FocusRequester()), - onClick = onDismiss, - text = stringResource(id = R.string.back) - ) - }, - containerColor = MaterialTheme.colorScheme.background - ) -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoadingScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoadingScreen.kt deleted file mode 100644 index 022ca06c53a6..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoadingScreen.kt +++ /dev/null @@ -1,86 +0,0 @@ -package net.mullvad.mullvadvpn.compose.screen - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.compositeOver -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import net.mullvad.mullvadvpn.R -import net.mullvad.mullvadvpn.compose.component.ScaffoldWithTopBar -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.lib.theme.Dimens -import net.mullvad.mullvadvpn.lib.theme.color.AlphaDescription - -@Preview -@Composable -private fun PreviewLoadingScreen() { - AppTheme { LoadingScreen() } -} - -@Composable -fun LoadingScreen(onSettingsCogClicked: () -> Unit = {}) { - val backgroundColor = MaterialTheme.colorScheme.primary - - ScaffoldWithTopBar( - topBarColor = backgroundColor, - statusBarColor = backgroundColor, - navigationBarColor = backgroundColor, - onSettingsClicked = onSettingsCogClicked, - onAccountClicked = null, - isIconAndLogoVisible = false, - content = { - Box( - contentAlignment = Alignment.Center, - modifier = - Modifier.background(backgroundColor) - .padding(it) - .padding(bottom = it.calculateTopPadding()) - .fillMaxSize() - ) { - Column( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Image( - painter = painterResource(id = R.drawable.launch_logo), - contentDescription = "", - modifier = Modifier.size(120.dp) - ) - Image( - painter = painterResource(id = R.drawable.logo_text), - contentDescription = "", - alpha = 0.6f, - modifier = Modifier.padding(top = 12.dp).height(18.dp) - ) - Text( - text = stringResource(id = R.string.connecting_to_daemon), - fontSize = 13.sp, - color = - MaterialTheme.colorScheme.onPrimary - .copy(alpha = AlphaDescription) - .compositeOver(backgroundColor), - modifier = - Modifier.padding(top = 12.dp).padding(horizontal = Dimens.sideMargin), - textAlign = TextAlign.Center - ) - } - } - } - ) -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/RedeemVoucherDialogScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/RedeemVoucherDialogScreen.kt deleted file mode 100644 index 1db18b01a31b..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/RedeemVoucherDialogScreen.kt +++ /dev/null @@ -1,32 +0,0 @@ -package net.mullvad.mullvadvpn.compose.screen - -import android.content.res.Configuration -import androidx.compose.runtime.Composable -import androidx.compose.ui.tooling.preview.Devices -import androidx.compose.ui.tooling.preview.Preview -import net.mullvad.mullvadvpn.compose.dialog.RedeemVoucherDialog -import net.mullvad.mullvadvpn.compose.state.VoucherDialogUiState -import net.mullvad.mullvadvpn.lib.theme.AppTheme - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.PIXEL_3) -@Composable -private fun PreviewRedeemVoucherDialogScreen() { - AppTheme { - RedeemVoucherDialogScreen( - uiState = VoucherDialogUiState.INITIAL, - onVoucherInputChange = {}, - onRedeem = {}, - onDismiss = {} - ) - } -} - -@Composable -internal fun RedeemVoucherDialogScreen( - uiState: VoucherDialogUiState, - onVoucherInputChange: (String) -> Unit = {}, - onRedeem: (voucherCode: String) -> Unit, - onDismiss: (isTimeAdded: Boolean) -> Unit -) { - RedeemVoucherDialog(uiState, onVoucherInputChange, onRedeem, onDismiss) -} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/TimingConstant.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/TimingConstant.kt deleted file mode 100644 index 2a09964a809d..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/constant/TimingConstant.kt +++ /dev/null @@ -1,3 +0,0 @@ -package net.mullvad.mullvadvpn.constant - -const val MINIMUM_LOADING_TIME_MILLIS = 500L diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginState.kt deleted file mode 100644 index cece17826792..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LoginState.kt +++ /dev/null @@ -1,8 +0,0 @@ -package net.mullvad.mullvadvpn.ui - -enum class LoginState { - Initial, - InProgress, - Success, - Failure, -} 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 deleted file mode 100644 index e2e2f5c44c63..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/extension/ContextExtensions.kt +++ /dev/null @@ -1,23 +0,0 @@ -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 - val clipData = ClipData.newPlainText(clipboardLabel, content) - clipboard.setPrimaryClip(clipData) -} 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 5225368dacbb..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt +++ /dev/null @@ -1,56 +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.constant.IS_PLAY_BUILD -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( - showSitePayment = IS_PLAY_BUILD.not(), - uiState = state, - uiSideEffect = vm.uiSideEffect, - enterTransitionEndAction = vm.enterTransitionEndAction, - onRedeemVoucherClick = { openRedeemVoucherFragment() }, - onManageAccountClick = vm::onManageAccountClick, - onLogoutClick = vm::onLogoutClick, - onPurchaseBillingProductClick = vm::startBillingPayment, - onClosePurchaseResultDialog = vm::onClosePurchaseResultDialog, - 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 532787ff4f44..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt +++ /dev/null @@ -1,111 +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.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.util.appendHideNavOnPlayBuild -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() { - val intent = - Intent( - Intent.ACTION_VIEW, - Uri.parse( - requireContext().getString(R.string.download_url).appendHideNavOnPlayBuild() - ) - ) - .apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK } - requireContext().startActivity(intent) - } - - 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 ab6de40dc2c3..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceListFragment.kt +++ /dev/null @@ -1,91 +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 android.widget.Toast -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.platform.ComposeView -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.R -import net.mullvad.mullvadvpn.compose.screen.DeviceListScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -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 onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - deviceListViewModel.accountToken = arguments?.getString(ACCOUNT_TOKEN_ARGUMENT_KEY) - - return inflater.inflate(R.layout.fragment_compose, container, false).apply { - findViewById(R.id.compose_view).setContent { - AppTheme { - val state = deviceListViewModel.uiState.collectAsState().value - DeviceListScreen( - state = state, - onBackClick = { openLoginView(doTriggerAutoLogin = false) }, - onContinueWithLogin = { openLoginView(doTriggerAutoLogin = true) }, - onSettingsClicked = this@DeviceListFragment::openSettings, - onDeviceRemovalClicked = deviceListViewModel::stageDeviceForRemoval, - onDismissDeviceRemovalDialog = deviceListViewModel::clearStagedDevice, - onConfirmDeviceRemovalDialog = - deviceListViewModel::confirmRemovalOfStagedDevice - ) - } - } - } - } - - 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 openLoginView(doTriggerAutoLogin: Boolean) { - parentActivity()?.clearBackStack() - val loginFragment = - LoginFragment().apply { - if (doTriggerAutoLogin && deviceListViewModel.accountToken != null) { - arguments = - Bundle().apply { - putString(ACCOUNT_TOKEN_ARGUMENT_KEY, deviceListViewModel.accountToken) - } - } - } - parentFragmentManager.beginTransaction().apply { - replace(R.id.main_fragment, loginFragment) - commitAllowingStateLoss() - } - } - - 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/FilterFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/FilterFragment.kt deleted file mode 100644 index 17357d8cbfd4..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/FilterFragment.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.FilterScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.viewmodel.FilterViewModel -import org.koin.androidx.viewmodel.ext.android.viewModel - -class FilterFragment : Fragment() { - - 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 - FilterScreen( - uiState = state, - onSelectedOwnership = vm::setSelectedOwnership, - onAllProviderCheckChange = vm::setAllProviders, - onSelectedProviders = vm::setSelectedProvider, - uiCloseAction = vm.uiSideEffect, - onBackClick = { activity?.onBackPressedDispatcher?.onBackPressed() }, - onApplyClick = vm::onApplyButtonClicked - ) - } - } - } - } -} 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 d2f0cbfb6e89..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoadingFragment.kt +++ /dev/null @@ -1,30 +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.compose.screen.LoadingScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -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 { LoadingScreen(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 92d58066ee0b..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt +++ /dev/null @@ -1,89 +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.LaunchedEffect -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.compose.screen.LoginScreen -import net.mullvad.mullvadvpn.lib.theme.AppTheme -import net.mullvad.mullvadvpn.model.AccountToken -import net.mullvad.mullvadvpn.ui.MainActivity -import net.mullvad.mullvadvpn.viewmodel.LoginUiSideEffect -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() - LaunchedEffect(Unit) { - vm.uiSideEffect.collect { - when (it) { - LoginUiSideEffect.NavigateToWelcome, - LoginUiSideEffect - .NavigateToConnect -> {} // TODO Fix when we redo navigation - is LoginUiSideEffect.TooManyDevices -> { - navigateToDeviceListFragment(it.accountToken) - } - } - } - } - 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 5a1ae49e1a19..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.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.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, - onPurchaseBillingProductClick = vm::startBillingPayment, - onClosePurchaseResultDialog = vm::onClosePurchaseResultDialog - ) - } - } - } - } - - 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 ed4538201363..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/PrivacyDisclaimerFragment.kt +++ /dev/null @@ -1,56 +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.endpoint.getApiEndpointConfigurationExtras -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( - apiEndpointConfiguration = activity?.intent?.getApiEndpointConfigurationExtras() - ) - } - - 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 fd489e563f40..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ProblemReportFragment.kt +++ /dev/null @@ -1,57 +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() }, - updateEmail = vm::updateEmail, - updateDescription = vm::updateDescription - ) { - 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 64fdee71f625..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SelectLocationFragment.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.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.ui.MainActivity -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() }, - removeOwnershipFilter = vm::removeOwnerFilter, - removeProviderFilter = vm::removeProviderFilter, - onFilterClick = ::openFilterView - ) - } - } - } - } - - private fun openFilterView() { - (context as? MainActivity)?.openFilter() - } - - 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 5c5e0c83f85b..000000000000 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt +++ /dev/null @@ -1,68 +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, - onPurchaseBillingProductClick = vm::startBillingPayment, - onClosePurchaseResultDialog = vm::onClosePurchaseResultDialog - ) - } - } - } - } - - 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/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemModelTest.kt deleted file mode 100644 index e0b1c5274c5e..000000000000 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemModelTest.kt +++ /dev/null @@ -1,164 +0,0 @@ -package net.mullvad.mullvadvpn.viewmodel - -import androidx.lifecycle.viewModelScope -import app.cash.turbine.test -import io.mockk.MockKAnnotations -import io.mockk.coEvery -import io.mockk.impl.annotations.MockK -import io.mockk.verify -import kotlin.test.assertEquals -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.test.runTest -import net.mullvad.mullvadvpn.dataproxy.MullvadProblemReport -import net.mullvad.mullvadvpn.dataproxy.SendProblemReportResult -import net.mullvad.mullvadvpn.dataproxy.UserReport -import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule -import net.mullvad.mullvadvpn.repository.ProblemReportRepository -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test - -class ReportProblemModelTest { - @get:Rule val testCoroutineRule = TestCoroutineRule() - - @MockK private lateinit var mockMullvadProblemReport: MullvadProblemReport - @MockK(relaxed = true) private lateinit var mockProblemReportRepository: ProblemReportRepository - - private val problemReportFlow = MutableStateFlow(UserReport("", "")) - - private lateinit var viewModel: ReportProblemViewModel - - @Before - fun setUp() { - MockKAnnotations.init(this) - coEvery { mockMullvadProblemReport.collectLogs() } returns true - coEvery { mockProblemReportRepository.problemReport } returns problemReportFlow - viewModel = ReportProblemViewModel(mockMullvadProblemReport, mockProblemReportRepository) - } - - @After - fun tearDown() { - viewModel.viewModelScope.coroutineContext.cancel() - } - - @Test - fun sendReportFailedToCollectLogs() = runTest { - // Arrange - coEvery { mockMullvadProblemReport.sendReport(any()) } returns - SendProblemReportResult.Error.CollectLog - val email = "my@email.com" - - // Act, Assert - viewModel.uiState.test { - assertEquals(null, awaitItem().sendingState) - viewModel.sendReport(email, "My description") - assertEquals(SendingReportUiState.Sending, awaitItem().sendingState) - assertEquals( - SendingReportUiState.Error(SendProblemReportResult.Error.CollectLog), - awaitItem().sendingState - ) - } - } - - @Test - fun sendReportFailedToSendReport() = runTest { - // Arrange - coEvery { mockMullvadProblemReport.sendReport(any()) } returns - SendProblemReportResult.Error.SendReport - val email = "my@email.com" - - // Act, Assert - viewModel.uiState.test { - assertEquals(null, awaitItem().sendingState) - viewModel.sendReport(email, "My description") - assertEquals(SendingReportUiState.Sending, awaitItem().sendingState) - assertEquals( - SendingReportUiState.Error(SendProblemReportResult.Error.SendReport), - awaitItem().sendingState - ) - } - } - - @Test - fun sendReportWithoutEmailSuccessfully() = runTest { - // Arrange - coEvery { mockMullvadProblemReport.sendReport(any()) } returns - SendProblemReportResult.Success - val email = "" - - // Act, Assert - viewModel.uiState.test { - assertEquals(ReportProblemUiState(false, null), awaitItem()) - viewModel.sendReport(email, "My description") - assertEquals(ReportProblemUiState(true, null), awaitItem()) - viewModel.sendReport(email, "My description") - assertEquals(ReportProblemUiState(false, SendingReportUiState.Sending), awaitItem()) - assertEquals( - ReportProblemUiState(false, SendingReportUiState.Success(null)), - awaitItem() - ) - } - } - - @Test - fun sendReportSuccessfully() = runTest { - // Arrange - coEvery { mockMullvadProblemReport.collectLogs() } returns true - coEvery { mockMullvadProblemReport.sendReport(any()) } returns - SendProblemReportResult.Success - val email = "my@email.com" - - // Act, Assert - viewModel.uiState.test { - assertEquals(awaitItem(), ReportProblemUiState(false, null)) - viewModel.sendReport(email, "My description") - - assertEquals(awaitItem(), ReportProblemUiState(false, SendingReportUiState.Sending)) - assertEquals( - awaitItem(), - ReportProblemUiState(false, SendingReportUiState.Success(email)) - ) - } - } - - @Test - fun testUpdateEmail() = runTest { - // Arrange - val email = "my@email.com" - - // Act - viewModel.updateEmail(email) - - // Assert - verify { mockProblemReportRepository.setEmail(email) } - } - - @Test - fun testUpdateDescription() = runTest { - // Arrange - val description = "My description" - - // Act - viewModel.updateDescription(description) - - // Assert - verify { mockProblemReportRepository.setDescription(description) } - } - - @Test - fun testUpdateProblemReport() = runTest { - // Arrange - val userReport = UserReport("my@email.com", "My description") - - // Act, Assert - viewModel.uiState.test { - awaitItem() - problemReportFlow.value = userReport - val result = awaitItem() - assertEquals(userReport.email, result.email) - assertEquals(userReport.description, result.description) - } - } -} diff --git a/android/lib/resource/src/main/res/anim/do_nothing.xml b/android/lib/resource/src/main/res/anim/do_nothing.xml deleted file mode 100644 index 8cb6866d6db7..000000000000 --- a/android/lib/resource/src/main/res/anim/do_nothing.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/android/lib/resource/src/main/res/anim/fragment_enter_from_bottom.xml b/android/lib/resource/src/main/res/anim/fragment_enter_from_bottom.xml deleted file mode 100644 index 337392e881b4..000000000000 --- a/android/lib/resource/src/main/res/anim/fragment_enter_from_bottom.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/android/lib/resource/src/main/res/anim/fragment_enter_from_right.xml b/android/lib/resource/src/main/res/anim/fragment_enter_from_right.xml deleted file mode 100644 index 5ba3b5c3f8f6..000000000000 --- a/android/lib/resource/src/main/res/anim/fragment_enter_from_right.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/android/lib/resource/src/main/res/anim/fragment_exit_to_bottom.xml b/android/lib/resource/src/main/res/anim/fragment_exit_to_bottom.xml deleted file mode 100644 index dc1261114ae4..000000000000 --- a/android/lib/resource/src/main/res/anim/fragment_exit_to_bottom.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/android/lib/resource/src/main/res/anim/fragment_exit_to_left.xml b/android/lib/resource/src/main/res/anim/fragment_exit_to_left.xml deleted file mode 100644 index 9ffa2c987758..000000000000 --- a/android/lib/resource/src/main/res/anim/fragment_exit_to_left.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/android/lib/resource/src/main/res/anim/fragment_exit_to_right.xml b/android/lib/resource/src/main/res/anim/fragment_exit_to_right.xml deleted file mode 100644 index d79420098292..000000000000 --- a/android/lib/resource/src/main/res/anim/fragment_exit_to_right.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/android/lib/resource/src/main/res/anim/fragment_half_enter_from_left.xml b/android/lib/resource/src/main/res/anim/fragment_half_enter_from_left.xml deleted file mode 100644 index 67e7b7364e37..000000000000 --- a/android/lib/resource/src/main/res/anim/fragment_half_enter_from_left.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/android/lib/resource/src/main/res/anim/fragment_half_exit_to_left.xml b/android/lib/resource/src/main/res/anim/fragment_half_exit_to_left.xml deleted file mode 100644 index bfac81df2ecf..000000000000 --- a/android/lib/resource/src/main/res/anim/fragment_half_exit_to_left.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - -