summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorDavid Göransson <david.goransson90@gmail.com>2023-10-09 09:35:21 +0200
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2023-10-13 11:03:58 +0200
commitc07ad9f01246937018ad4ae8021afa208641bfa1 (patch)
tree2d54fc8f1f9656b70c7b2cfbe0d8e3722eb8d058 /android
parentd4f8725e7d508af530cd1b3134f20aa73733c96a (diff)
downloadmullvadvpn-c07ad9f01246937018ad4ae8021afa208641bfa1.tar.xz
mullvadvpn-c07ad9f01246937018ad4ae8021afa208641bfa1.zip
Add device name and time left
Diffstat (limited to 'android')
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt47
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt116
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt7
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/OutOfTimeScreen.kt16
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/ConnectUiState.kt8
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/OutOfTimeUiState.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt10
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DateExtensions.kt5
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt27
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ConnectViewModel.kt14
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/OutOfTimeViewModel.kt22
-rw-r--r--android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountExpiry.kt17
-rw-r--r--android/lib/resource/src/main/res/values-da/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-de/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-es/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-fi/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-fr/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-it/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-ja/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-ko/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-my/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-nb/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-nl/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-pl/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-pt/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-ru/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-sv/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-th/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-tr/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-zh-rCN/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values-zh-rTW/strings.xml2
-rw-r--r--android/lib/resource/src/main/res/values/strings.xml2
34 files changed, 298 insertions, 44 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 {
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountExpiry.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountExpiry.kt
index f5738ec21d..f856ef8c89 100644
--- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountExpiry.kt
+++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountExpiry.kt
@@ -1,28 +1,15 @@
package net.mullvad.mullvadvpn.model
import android.os.Parcelable
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.DurationUnit
import kotlinx.parcelize.Parcelize
import org.joda.time.DateTime
sealed class AccountExpiry : Parcelable {
- @Parcelize
- data class Available(val expiryDateTime: DateTime) : AccountExpiry() {
- override fun daysLeft(): Int =
- (expiryDateTime.toInstant().millis - DateTime.now().toInstant().millis)
- .milliseconds
- .toInt(DurationUnit.DAYS)
- }
+ @Parcelize data class Available(val expiryDateTime: DateTime) : AccountExpiry()
- @Parcelize
- data object Missing : AccountExpiry()
+ @Parcelize data object Missing : AccountExpiry()
fun date(): DateTime? {
return (this as? Available)?.expiryDateTime
}
-
- open fun daysLeft(): Int? {
- return (this as? Available)?.daysLeft()
- }
}
diff --git a/android/lib/resource/src/main/res/values-da/strings.xml b/android/lib/resource/src/main/res/values-da/strings.xml
index bb26081112..b98455f7d9 100644
--- a/android/lib/resource/src/main/res/values-da/strings.xml
+++ b/android/lib/resource/src/main/res/values-da/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Skift placering</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">Slå VPN til/fra</string>
+ <string name="top_bar_device_name">Enhedsnavn: %1$s</string>
+ <string name="top_bar_time_left">Resterende tid: %1$s</string>
<string name="try_again">Prøv igen</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">Hvilken TCP-port UDP-over-TCP tilsløringsprotokollen skal forbinde til på VPN-serveren.</string>
diff --git a/android/lib/resource/src/main/res/values-de/strings.xml b/android/lib/resource/src/main/res/values-de/strings.xml
index 357e4209e3..19120efa7b 100644
--- a/android/lib/resource/src/main/res/values-de/strings.xml
+++ b/android/lib/resource/src/main/res/values-de/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Ort wechseln</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">VPN umschalten</string>
+ <string name="top_bar_device_name">Gerätename: %1$s</string>
+ <string name="top_bar_time_left">Verbleibende Zeit: %1$s</string>
<string name="try_again">Erneut versuchen</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">Mit welchem TCP-Port sich das UDP-über-TCP-Verschleierungsprotokoll auf dem VPN-Server verbinden soll.</string>
diff --git a/android/lib/resource/src/main/res/values-es/strings.xml b/android/lib/resource/src/main/res/values-es/strings.xml
index 1ca4ad0fa5..e5c5f7d657 100644
--- a/android/lib/resource/src/main/res/values-es/strings.xml
+++ b/android/lib/resource/src/main/res/values-es/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Cambiar ubicación</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">Alternar VPN</string>
+ <string name="top_bar_device_name">Nombre del dispositivo: %1$s</string>
+ <string name="top_bar_time_left">Tiempo restante: %1$s</string>
<string name="try_again">Volver a intentarlo</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">El puerto TCP al que se conectará el protocolo de ofuscación de UDP sobre TCP en el servidor VPN.</string>
diff --git a/android/lib/resource/src/main/res/values-fi/strings.xml b/android/lib/resource/src/main/res/values-fi/strings.xml
index eb02e63756..379d8dd4bb 100644
--- a/android/lib/resource/src/main/res/values-fi/strings.xml
+++ b/android/lib/resource/src/main/res/values-fi/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Vaihda sijaintia</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">Vaihda VPN:ää</string>
+ <string name="top_bar_device_name">Laitteen nimi: %1$s</string>
+ <string name="top_bar_time_left">Aikaa jäljellä: %1$s</string>
<string name="try_again">Yritä uudelleen</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">Määrittää, mihin VPN-palvelimen TCP-porttiin \"UDP TCP:n kautta\" -hämäysteknologia-protokollan tulee muodostaa yhteys.</string>
diff --git a/android/lib/resource/src/main/res/values-fr/strings.xml b/android/lib/resource/src/main/res/values-fr/strings.xml
index da970c6d89..9da5482c92 100644
--- a/android/lib/resource/src/main/res/values-fr/strings.xml
+++ b/android/lib/resource/src/main/res/values-fr/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Changer de localisation</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">Activer/désactiver le VPN</string>
+ <string name="top_bar_device_name">Nom de l\'appareil : %1$s</string>
+ <string name="top_bar_time_left">Temps restant : %1$s</string>
<string name="try_again">Réessayer</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">Le port TCP auquel le protocole de dissimulation UDP sur TCP doit se connecter sur le serveur VPN.</string>
diff --git a/android/lib/resource/src/main/res/values-it/strings.xml b/android/lib/resource/src/main/res/values-it/strings.xml
index c988e760cf..e91aaecdb9 100644
--- a/android/lib/resource/src/main/res/values-it/strings.xml
+++ b/android/lib/resource/src/main/res/values-it/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Cambia posizione</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">Attiva/disattiva VPN</string>
+ <string name="top_bar_device_name">Nome del dispositivo: %1$s</string>
+ <string name="top_bar_time_left">Tempo rimasto: %1$s</string>
<string name="try_again">Riprova</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">A quale porta TCP deve connettersi il protocollo di offuscamento UDP-over-TCP sul server VPN.</string>
diff --git a/android/lib/resource/src/main/res/values-ja/strings.xml b/android/lib/resource/src/main/res/values-ja/strings.xml
index 8c9ef84739..3112ec2b1c 100644
--- a/android/lib/resource/src/main/res/values-ja/strings.xml
+++ b/android/lib/resource/src/main/res/values-ja/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">場所を切り替える</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">VPNの切り替え</string>
+ <string name="top_bar_device_name">デバイス名: %1$s</string>
+ <string name="top_bar_time_left">残り時間: %1$s</string>
<string name="try_again">再試行</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">UDP-over-TCP難読化プロトコルで接続する必要のあるVPNサーバーのTCPポートです。</string>
diff --git a/android/lib/resource/src/main/res/values-ko/strings.xml b/android/lib/resource/src/main/res/values-ko/strings.xml
index 209023a64e..b535966911 100644
--- a/android/lib/resource/src/main/res/values-ko/strings.xml
+++ b/android/lib/resource/src/main/res/values-ko/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">위치 전환</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">VPN 전환</string>
+ <string name="top_bar_device_name">장치 이름: %1$s</string>
+ <string name="top_bar_time_left">남은 시간: %1$s</string>
<string name="try_again">다시 시도</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">UDP-over-TCP 난독 처리 프로토콜이 VPN 서버에서 연결해야 하는 TCP 포트입니다.</string>
diff --git a/android/lib/resource/src/main/res/values-my/strings.xml b/android/lib/resource/src/main/res/values-my/strings.xml
index 0aef2a2c2e..6a0f2ba377 100644
--- a/android/lib/resource/src/main/res/values-my/strings.xml
+++ b/android/lib/resource/src/main/res/values-my/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">တည်နေရာ ပြောင်းရန်</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">VPN ရွေးသုံးရန်</string>
+ <string name="top_bar_device_name">စက်အမည်- %1$s</string>
+ <string name="top_bar_time_left">ကျန်သည့် အချိန်- %1$s</string>
<string name="try_again">ထပ်ကြိုးစားရန်</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">VPN ဆာဗာကို ဖွင့်ရန် ၎င်း TCP ပေါ့တ် UDP-over-TCP Obfuscation ပရိုတိုကောလ်နှင့် ချိတ်ဆက်ထားသင့်ပါသည်။</string>
diff --git a/android/lib/resource/src/main/res/values-nb/strings.xml b/android/lib/resource/src/main/res/values-nb/strings.xml
index 2b0e370bb3..6872608306 100644
--- a/android/lib/resource/src/main/res/values-nb/strings.xml
+++ b/android/lib/resource/src/main/res/values-nb/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Bytt plassering</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">Velg VPN</string>
+ <string name="top_bar_device_name">Enhetsnavn: %1$s</string>
+ <string name="top_bar_time_left">Tid igjen: %1$s</string>
<string name="try_again">Prøv på nytt</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">TCP-porten som UDP-over-TCP-tilsløringen skal koble til på VPN-serveren.</string>
diff --git a/android/lib/resource/src/main/res/values-nl/strings.xml b/android/lib/resource/src/main/res/values-nl/strings.xml
index cdbaa554c3..005f1c6907 100644
--- a/android/lib/resource/src/main/res/values-nl/strings.xml
+++ b/android/lib/resource/src/main/res/values-nl/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Locatie wijzigen</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">VPN in-/uitschakelen</string>
+ <string name="top_bar_device_name">Apparaatnaam: %1$s</string>
+ <string name="top_bar_time_left">Resterende tijd: %1$s</string>
<string name="try_again">Probeer het opnieuw</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">Met welke TCP-poort moet het UDP-over-TCP-obfuscatieprotocol verbinding maken op de VPN-server.</string>
diff --git a/android/lib/resource/src/main/res/values-pl/strings.xml b/android/lib/resource/src/main/res/values-pl/strings.xml
index 2e2e6ee267..98b69a66a8 100644
--- a/android/lib/resource/src/main/res/values-pl/strings.xml
+++ b/android/lib/resource/src/main/res/values-pl/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Zmień lokalizację</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">Przełącz VPN</string>
+ <string name="top_bar_device_name">Nazwa urządzenia: %1$s</string>
+ <string name="top_bar_time_left">Pozostało: %1$s</string>
<string name="try_again">Spróbuj ponownie</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">Port TCP, z którym powinien łączyć się protokół zaciemniania UDP-przez-TCP na serwerze VPN.</string>
diff --git a/android/lib/resource/src/main/res/values-pt/strings.xml b/android/lib/resource/src/main/res/values-pt/strings.xml
index 2fee06cab6..5dd4fd61ea 100644
--- a/android/lib/resource/src/main/res/values-pt/strings.xml
+++ b/android/lib/resource/src/main/res/values-pt/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Alterar local</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">Alternar VPN</string>
+ <string name="top_bar_device_name">Nome do dispositivo: %1$s</string>
+ <string name="top_bar_time_left">Tempo restante: %1$s</string>
<string name="try_again">Tentar novamente</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">A que porta TCP o protocolo de ofuscação UDP sobre TCP deve ligar-se no servidor VPN.</string>
diff --git a/android/lib/resource/src/main/res/values-ru/strings.xml b/android/lib/resource/src/main/res/values-ru/strings.xml
index 0fb01c88ad..7b9acc9195 100644
--- a/android/lib/resource/src/main/res/values-ru/strings.xml
+++ b/android/lib/resource/src/main/res/values-ru/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Сменить местоположение</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">Включение VPN</string>
+ <string name="top_bar_device_name">Имя устройства: %1$s</string>
+ <string name="top_bar_time_left">Осталось времени: %1$s</string>
<string name="try_again">Повторить попытку</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">TCP-порт, к которому должен подключаться протокол обфускации UDP через TCP на VPN-сервере.</string>
diff --git a/android/lib/resource/src/main/res/values-sv/strings.xml b/android/lib/resource/src/main/res/values-sv/strings.xml
index c65809dc5d..d8183b2435 100644
--- a/android/lib/resource/src/main/res/values-sv/strings.xml
+++ b/android/lib/resource/src/main/res/values-sv/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Växla plats</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">Växla VPN</string>
+ <string name="top_bar_device_name">Enhetsnamn: %1$s</string>
+ <string name="top_bar_time_left">Tid kvar: %1$s</string>
<string name="try_again">Försök igen</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">Vilken TCP-port som UDP-över-TCP-obfuskeringsprotokoll bör ansluta till på VPN-servern.</string>
diff --git a/android/lib/resource/src/main/res/values-th/strings.xml b/android/lib/resource/src/main/res/values-th/strings.xml
index 3f01840e0a..7afc8a7b44 100644
--- a/android/lib/resource/src/main/res/values-th/strings.xml
+++ b/android/lib/resource/src/main/res/values-th/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">สลับตำแหน่ง</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">เปิด/ปิด VPN</string>
+ <string name="top_bar_device_name">ชื่ออุปกรณ์: %1$s</string>
+ <string name="top_bar_time_left">เหลือเวลา: %1$s</string>
<string name="try_again">ลองอีกครั้ง</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">พอร์ต TCP ใดที่โพรโทคอลการทำให้ข้อมูลยุ่งเหยิง UDP-ผ่าน-TCP ควรเชื่อมต่อบนเซิร์ฟเวอร์ VPN</string>
diff --git a/android/lib/resource/src/main/res/values-tr/strings.xml b/android/lib/resource/src/main/res/values-tr/strings.xml
index 08ff5f47e6..908f9ce2d9 100644
--- a/android/lib/resource/src/main/res/values-tr/strings.xml
+++ b/android/lib/resource/src/main/res/values-tr/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">Konum değiştir</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">VPN\'i aç/kapat</string>
+ <string name="top_bar_device_name">Cihaz adı: %1$s</string>
+ <string name="top_bar_time_left">Kalan süre: %1$s</string>
<string name="try_again">Tekrar dene</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">TCP üzerinden UDP gizleme protokolünün VPN sunucusunda hangi TCP portuna bağlanması gerekiyor.</string>
diff --git a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml
index 174262c638..667ffbcde9 100644
--- a/android/lib/resource/src/main/res/values-zh-rCN/strings.xml
+++ b/android/lib/resource/src/main/res/values-zh-rCN/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">切换位置</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">切换 VPN</string>
+ <string name="top_bar_device_name">设备名称:%1$s</string>
+ <string name="top_bar_time_left">剩余时间:%1$s</string>
<string name="try_again">重试</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">UDP-over-TCP 混淆协议应连接到 VPN 服务器上的哪个 TCP 端口。</string>
diff --git a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml
index 70b0d42c55..5378b92550 100644
--- a/android/lib/resource/src/main/res/values-zh-rTW/strings.xml
+++ b/android/lib/resource/src/main/res/values-zh-rTW/strings.xml
@@ -175,6 +175,8 @@
<string name="switch_location">切換位置</string>
<string name="tcp">TCP</string>
<string name="toggle_vpn">切換 VPN</string>
+ <string name="top_bar_device_name">裝置名稱:%1$s</string>
+ <string name="top_bar_time_left">剩餘時間:%1$s</string>
<string name="try_again">再試一次</string>
<string name="udp">UDP</string>
<string name="udp_over_tcp_port_info">UDP-over-TCP 混淆通訊協定應連線到 VPN 伺服器上的哪個 TCP 連接埠。</string>
diff --git a/android/lib/resource/src/main/res/values/strings.xml b/android/lib/resource/src/main/res/values/strings.xml
index bc9630e974..c9c837d38d 100644
--- a/android/lib/resource/src/main/res/values/strings.xml
+++ b/android/lib/resource/src/main/res/values/strings.xml
@@ -224,4 +224,6 @@
<string name="verifying_voucher">Verifying voucher…</string>
<string name="added_to_your_account">%s was added to your account.</string>
<string name="less_than_one_day">less than one day</string>
+ <string name="top_bar_time_left">Time left: %s</string>
+ <string name="top_bar_device_name">Device name: %s</string>
</resources>