diff options
| author | David Göransson <david.goransson90@gmail.com> | 2023-10-09 09:35:21 +0200 |
|---|---|---|
| committer | Jonatan Rhodin <jonatan.rhodin@mullvad.net> | 2023-10-13 11:03:58 +0200 |
| commit | c07ad9f01246937018ad4ae8021afa208641bfa1 (patch) | |
| tree | 2d54fc8f1f9656b70c7b2cfbe0d8e3722eb8d058 /android/app/src | |
| parent | d4f8725e7d508af530cd1b3134f20aa73733c96a (diff) | |
| download | mullvadvpn-c07ad9f01246937018ad4ae8021afa208641bfa1.tar.xz mullvadvpn-c07ad9f01246937018ad4ae8021afa208641bfa1.zip | |
Add device name and time left
Diffstat (limited to 'android/app/src')
13 files changed, 256 insertions, 29 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt index eb4d0d19a5..332c841d87 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt @@ -1,5 +1,6 @@ package net.mullvad.mullvadvpn.compose.component +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxSize @@ -67,6 +68,52 @@ fun ScaffoldWithTopBar( } @Composable +fun ScaffoldWithTopBarAndDeviceName( + topBarColor: Color, + statusBarColor: Color, + navigationBarColor: Color, + modifier: Modifier = Modifier, + iconTintColor: Color = MaterialTheme.colorScheme.onPrimary.copy(alpha = AlphaTopBar), + onSettingsClicked: (() -> Unit)?, + onAccountClicked: (() -> Unit)?, + isIconAndLogoVisible: Boolean = true, + snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, + deviceName: String?, + timeLeft: Int?, + content: @Composable (PaddingValues) -> Unit, +) { + val systemUiController = rememberSystemUiController() + LaunchedEffect(key1 = statusBarColor, key2 = navigationBarColor) { + systemUiController.setStatusBarColor(statusBarColor) + systemUiController.setNavigationBarColor(navigationBarColor) + } + + Scaffold( + modifier = modifier, + topBar = { + Column { + MullvadTopBarWithDeviceName( + containerColor = topBarColor, + iconTintColor = iconTintColor, + onSettingsClicked = onSettingsClicked, + onAccountClicked = onAccountClicked, + isIconAndLogoVisible = isIconAndLogoVisible, + deviceName = deviceName, + daysLeftUntilExpiry = timeLeft + ) + } + }, + snackbarHost = { + SnackbarHost( + snackbarHostState, + snackbar = { snackbarData -> MullvadSnackbar(snackbarData = snackbarData) } + ) + }, + content = content + ) +} + +@Composable fun MullvadSnackbar(snackbarData: SnackbarData) { Snackbar(snackbarData = snackbarData, contentColor = MaterialTheme.colorScheme.secondary) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt index 3c5e0e1bb7..93e1e291a2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt @@ -2,9 +2,18 @@ package net.mullvad.mullvadvpn.compose.component +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -13,16 +22,19 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MediumTopAppBar +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -206,3 +218,107 @@ fun MullvadMediumTopBar( actions = actions ) } + +@Preview +@Composable +fun PreviewMullvadTopBarWithLongDeviceName() { + AppTheme { + Surface { + MullvadTopBarWithDeviceName( + containerColor = MaterialTheme.colorScheme.error, + iconTintColor = MaterialTheme.colorScheme.onError, + onSettingsClicked = null, + onAccountClicked = null, + deviceName = "Superstitious Hippopotamus with extra weight", + daysLeftUntilExpiry = 1 + ) + } + } +} + +@Preview +@Composable +fun PreviewMullvadTopBarWithShortDeviceName() { + AppTheme { + Surface { + MullvadTopBarWithDeviceName( + containerColor = MaterialTheme.colorScheme.error, + iconTintColor = MaterialTheme.colorScheme.onError, + onSettingsClicked = null, + onAccountClicked = null, + deviceName = "Fit Ant", + daysLeftUntilExpiry = 1 + ) + } + } +} + +@Composable +fun MullvadTopBarWithDeviceName( + containerColor: Color, + onSettingsClicked: (() -> Unit)?, + onAccountClicked: (() -> Unit)?, + iconTintColor: Color, + isIconAndLogoVisible: Boolean = true, + deviceName: String?, + daysLeftUntilExpiry: Int? +) { + Column { + MullvadTopBar( + containerColor, + onSettingsClicked, + onAccountClicked, + Modifier, + iconTintColor, + isIconAndLogoVisible, + ) + + // Align animation of extra row with the rest of the Topbar + val appBarContainerColor by + animateColorAsState( + targetValue = containerColor, + animationSpec = spring(stiffness = Spring.StiffnessMediumLow), + label = "ColorAnimation" + ) + Row( + modifier = + Modifier.background(appBarContainerColor) + .padding( + bottom = Dimens.smallPadding, + start = Dimens.mediumPadding, + end = Dimens.mediumPadding + ) + .fillMaxWidth() + .animateContentSize(), + horizontalArrangement = Arrangement.spacedBy(Dimens.mediumPadding) + ) { + Text( + modifier = Modifier.weight(1f, fill = false), + text = + deviceName?.let { + stringResource(id = R.string.top_bar_device_name, deviceName) + } + ?: "", + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.bodySmall + ) + if (daysLeftUntilExpiry != null) { + Text( + text = + stringResource( + id = R.string.top_bar_time_left, + pluralStringResource( + id = R.plurals.days, + daysLeftUntilExpiry, + daysLeftUntilExpiry + ) + ), + style = MaterialTheme.typography.bodySmall + ) + } else { + Spacer(Modifier) + } + } + } +} 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 index 4cbbc0d292..1ac8873fc3 100644 --- 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 @@ -57,10 +57,7 @@ fun ShowDeviceRemovalDialog(onDismiss: () -> Unit, onConfirm: () -> Unit, device }, text = { val htmlFormattedDialogText = - textResource( - id = R.string.max_devices_confirm_removal_description, - device.name - ) + textResource(id = R.string.max_devices_confirm_removal_description, device.name) HtmlText(htmlFormattedString = htmlFormattedDialogText, textSize = 16.sp.value) }, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt index 69d849183e..9374f4ab9a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt @@ -35,6 +35,7 @@ import net.mullvad.mullvadvpn.compose.button.SwitchLocationButton import net.mullvad.mullvadvpn.compose.component.ConnectionStatusText import net.mullvad.mullvadvpn.compose.component.LocationInfo import net.mullvad.mullvadvpn.compose.component.Notification +import net.mullvad.mullvadvpn.compose.component.ScaffoldWithTopBarAndDeviceName import net.mullvad.mullvadvpn.compose.component.ScaffoldWithTopBar import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar import net.mullvad.mullvadvpn.compose.state.ConnectUiState @@ -107,7 +108,7 @@ fun ConnectScreen( } } - ScaffoldWithTopBar( + ScaffoldWithTopBarAndDeviceName( topBarColor = if (uiState.tunnelUiState.isSecured()) { MaterialTheme.colorScheme.inversePrimary @@ -129,7 +130,9 @@ fun ConnectScreen( } .copy(alpha = AlphaTopBar), onSettingsClicked = onSettingsClick, - onAccountClicked = onAccountClick + onAccountClicked = onAccountClick, + deviceName = uiState.deviceName, + timeLeft = uiState.daysLeftUntilExpiry ) { Column( verticalArrangement = Arrangement.Bottom, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt index 7e76040ac4..4036d9547c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt @@ -194,8 +194,7 @@ fun DeviceListScreen( Column { state.deviceUiItems.forEach { deviceUiState -> ListItem( - text = - deviceUiState.device.name, + text = deviceUiState.device.name, subText = deviceUiState.device.created.parseAsDateTime()?.let { creationDate -> diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt index 49de23228c..994e45b556 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt @@ -28,7 +28,7 @@ import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.compose.button.ActionButton import net.mullvad.mullvadvpn.compose.button.RedeemVoucherButton import net.mullvad.mullvadvpn.compose.button.SitePaymentButton -import net.mullvad.mullvadvpn.compose.component.ScaffoldWithTopBar +import net.mullvad.mullvadvpn.compose.component.ScaffoldWithTopBarAndDeviceName import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar import net.mullvad.mullvadvpn.compose.extensions.createOpenAccountPageHook import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState @@ -47,7 +47,7 @@ private fun PreviewOutOfTimeScreenDisconnected() { AppTheme { OutOfTimeScreen( showSitePayment = true, - uiState = OutOfTimeUiState(tunnelState = TunnelState.Disconnected), + uiState = OutOfTimeUiState(tunnelState = TunnelState.Disconnected, "Heroic Frog"), uiSideEffect = MutableSharedFlow<OutOfTimeViewModel.UiSideEffect>().asSharedFlow() ) } @@ -59,7 +59,8 @@ private fun PreviewOutOfTimeScreenConnecting() { AppTheme { OutOfTimeScreen( showSitePayment = true, - uiState = OutOfTimeUiState(tunnelState = TunnelState.Connecting(null, null)), + uiState = + OutOfTimeUiState(tunnelState = TunnelState.Connecting(null, null), "Strong Rabbit"), uiSideEffect = MutableSharedFlow<OutOfTimeViewModel.UiSideEffect>().asSharedFlow() ) } @@ -76,7 +77,8 @@ private fun PreviewOutOfTimeScreenError() { tunnelState = TunnelState.Error( ErrorState(cause = ErrorStateCause.IsOffline, isBlocking = true) - ) + ), + deviceName = "Stable Horse" ), uiSideEffect = MutableSharedFlow<OutOfTimeViewModel.UiSideEffect>().asSharedFlow() ) @@ -106,7 +108,7 @@ fun OutOfTimeScreen( } } val scrollState = rememberScrollState() - ScaffoldWithTopBar( + ScaffoldWithTopBarAndDeviceName( topBarColor = if (uiState.tunnelState.isSecured()) { MaterialTheme.colorScheme.inversePrimary @@ -128,7 +130,9 @@ fun OutOfTimeScreen( } .copy(alpha = AlphaTopBar), onSettingsClicked = onSettingsClick, - onAccountClicked = onAccountClick + onAccountClicked = onAccountClick, + deviceName = uiState.deviceName, + timeLeft = null ) { Column( verticalArrangement = Arrangement.Bottom, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt index 3c9c7352fe..93b9df5b7a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt @@ -14,7 +14,9 @@ data class ConnectUiState( val outAddress: String, val showLocation: Boolean, val connectNotificationState: ConnectNotificationState, - val isTunnelInfoExpanded: Boolean + val isTunnelInfoExpanded: Boolean, + val deviceName: String?, + val daysLeftUntilExpiry: Int? ) { companion object { val INITIAL = @@ -27,7 +29,9 @@ data class ConnectUiState( outAddress = "", showLocation = false, isTunnelInfoExpanded = false, - connectNotificationState = ConnectNotificationState.HideNotification + connectNotificationState = ConnectNotificationState.HideNotification, + deviceName = null, + daysLeftUntilExpiry = null ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/OutOfTimeUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/OutOfTimeUiState.kt index cc19ac7ca8..f7794e5a55 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/OutOfTimeUiState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/OutOfTimeUiState.kt @@ -2,4 +2,7 @@ package net.mullvad.mullvadvpn.compose.state import net.mullvad.mullvadvpn.model.TunnelState -data class OutOfTimeUiState(val tunnelState: TunnelState = TunnelState.Disconnected) +data class OutOfTimeUiState( + val tunnelState: TunnelState = TunnelState.Disconnected, + val deviceName: String +) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt index 63fcf17ad2..7134f7b7d2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt @@ -83,19 +83,21 @@ val uiModule = module { viewModel { ChangelogViewModel(get(), BuildConfig.VERSION_CODE, BuildConfig.ALWAYS_SHOW_CHANGELOG) } - viewModel { ConnectViewModel(get(), BuildConfig.ENABLE_IN_APP_VERSION_NOTIFICATIONS, get()) } + viewModel { + ConnectViewModel(get(), BuildConfig.ENABLE_IN_APP_VERSION_NOTIFICATIONS, get(), get()) + } viewModel { DeviceListViewModel(get(), get()) } viewModel { DeviceRevokedViewModel(get(), get()) } viewModel { LoginViewModel(get(), get()) } - viewModel { OutOfTimeViewModel(get(), get()) } viewModel { PrivacyDisclaimerViewModel(get()) } - viewModel { ReportProblemViewModel(get()) } viewModel { SelectLocationViewModel(get()) } viewModel { SettingsViewModel(get(), get()) } - viewModel { ViewLogsViewModel(get()) } viewModel { VoucherDialogViewModel(get(), get()) } viewModel { VpnSettingsViewModel(get(), get(), get(), get()) } viewModel { WelcomeViewModel(get(), get(), get()) } + viewModel { ReportProblemViewModel(get()) } + viewModel { ViewLogsViewModel(get()) } + viewModel { OutOfTimeViewModel(get(), get(), get()) } } const val SELF_PACKAGE_NAME = "SELF_PACKAGE_NAME" diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DateExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DateExtensions.kt index d3be3e09aa..e11434257a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DateExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DateExtensions.kt @@ -1,6 +1,8 @@ package net.mullvad.mullvadvpn.util import java.text.DateFormat +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.DurationUnit import org.joda.time.DateTime import org.joda.time.format.ISODateTimeFormat @@ -8,3 +10,6 @@ fun DateTime.formatDate(): String = ISODateTimeFormat.date().print(this) fun DateTime.toExpiryDateString(): String = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).format(this.toDate()) + +fun DateTime.daysFromNow() = + (toInstant().millis - DateTime.now().toInstant().millis).milliseconds.toInt(DurationUnit.DAYS) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt index d18e4f8fc9..e782f6f439 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt @@ -97,3 +97,30 @@ inline fun <T1, T2, T3, T4, T5, T6, T7, R> combine( ) } } + +inline fun <T1, T2, T3, T4, T5, T6, T7, T8, R> combine( + flow: Flow<T1>, + flow2: Flow<T2>, + flow3: Flow<T3>, + flow4: Flow<T4>, + flow5: Flow<T5>, + flow6: Flow<T6>, + flow7: Flow<T7>, + flow8: Flow<T8>, + crossinline transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8) -> R +): Flow<R> { + return kotlinx.coroutines.flow.combine(flow, flow2, flow3, flow4, flow5, flow6, flow7, flow8) { + args: Array<*> -> + @Suppress("UNCHECKED_CAST") + transform( + args[0] as T1, + args[1] as T2, + args[2] as T3, + args[3] as T4, + args[4] as T5, + args[5] as T6, + args[6] as T7, + args[7] as T8 + ) + } +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt index 01a1c84896..01ba71ff86 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -24,6 +25,7 @@ import net.mullvad.mullvadvpn.compose.state.ConnectUiState import net.mullvad.mullvadvpn.model.AccountExpiry import net.mullvad.mullvadvpn.model.TunnelState import net.mullvad.mullvadvpn.repository.AccountRepository +import net.mullvad.mullvadvpn.repository.DeviceRepository import net.mullvad.mullvadvpn.ui.VersionInfo import net.mullvad.mullvadvpn.ui.serviceconnection.ConnectionProxy import net.mullvad.mullvadvpn.ui.serviceconnection.LocationInfoCache @@ -36,6 +38,7 @@ import net.mullvad.mullvadvpn.ui.serviceconnection.connectionProxy import net.mullvad.mullvadvpn.util.appVersionCallbackFlow import net.mullvad.mullvadvpn.util.callbackFlowFromNotifier import net.mullvad.mullvadvpn.util.combine +import net.mullvad.mullvadvpn.util.daysFromNow import net.mullvad.mullvadvpn.util.toInAddress import net.mullvad.mullvadvpn.util.toOutAddress import net.mullvad.talpid.tunnel.ActionAfterDisconnect @@ -47,6 +50,7 @@ class ConnectViewModel( private val serviceConnectionManager: ServiceConnectionManager, private val isVersionInfoNotificationEnabled: Boolean, accountRepository: AccountRepository, + private val deviceRepository: DeviceRepository, ) : ViewModel() { private val _uiSideEffect = MutableSharedFlow<UiSideEffect>(extraBufferCapacity = 1) val uiSideEffect = _uiSideEffect.asSharedFlow() @@ -74,7 +78,8 @@ class ConnectViewModel( serviceConnection.connectionProxy.tunnelUiStateFlow(), serviceConnection.connectionProxy.tunnelRealStateFlow(), accountRepository.accountExpiryState, - _isTunnelInfoExpanded + _isTunnelInfoExpanded, + deviceRepository.deviceState.map { it.deviceName() } ) { location, relayLocation, @@ -82,7 +87,8 @@ class ConnectViewModel( tunnelUiState, tunnelRealState, accountExpiry, - isTunnelInfoExpanded -> + isTunnelInfoExpanded, + deviceName -> if (tunnelRealState.isTunnelErrorStateDueToExpiredAccount()) { _uiSideEffect.tryEmit(UiSideEffect.OpenOutOfTimeView) } @@ -124,7 +130,9 @@ class ConnectViewModel( tunnelUiState = tunnelUiState, versionInfo = versionInfo, accountExpiry = accountExpiry - ) + ), + deviceName = deviceName, + daysLeftUntilExpiry = accountExpiry.date()?.daysFromNow() ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt index 8a789f62fd..b1df2d2225 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt @@ -12,13 +12,13 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState import net.mullvad.mullvadvpn.constant.ACCOUNT_EXPIRY_POLL_INTERVAL import net.mullvad.mullvadvpn.model.TunnelState import net.mullvad.mullvadvpn.repository.AccountRepository +import net.mullvad.mullvadvpn.repository.DeviceRepository import net.mullvad.mullvadvpn.ui.serviceconnection.ConnectionProxy import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionState @@ -31,7 +31,8 @@ import org.joda.time.DateTime class OutOfTimeViewModel( private val accountRepository: AccountRepository, private val serviceConnectionManager: ServiceConnectionManager, - private val pollAccountExpiry: Boolean = true + private val deviceRepository: DeviceRepository, + private val pollAccountExpiry: Boolean = true, ) : ViewModel() { private val _uiSideEffect = MutableSharedFlow<UiSideEffect>(extraBufferCapacity = 1) @@ -47,10 +48,21 @@ class OutOfTimeViewModel( } } .flatMapLatest { serviceConnection -> - serviceConnection.connectionProxy.tunnelStateFlow() + kotlinx.coroutines.flow.combine( + serviceConnection.connectionProxy.tunnelStateFlow(), + deviceRepository.deviceState + ) { tunnelState, deviceState -> + OutOfTimeUiState( + tunnelState = tunnelState, + deviceName = deviceState.deviceName() ?: "", + ) + } } - .map { tunnelState -> OutOfTimeUiState(tunnelState = tunnelState) } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), OutOfTimeUiState()) + .stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(), + OutOfTimeUiState(deviceName = "") + ) init { viewModelScope.launch { |
