diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-06-08 11:32:50 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2020-06-08 11:32:50 -0300 |
| commit | 3a35547e4c4a00755b14d05f67ae5e636138f81b (patch) | |
| tree | a98efa895b28d20d5198e4706fe64fe7850389a9 /android | |
| parent | 05be799e63292f87d96f5a85f3cc97f94b578e08 (diff) | |
| parent | 6b88c972af86ee7d941e995795ddf46c418d3b05 (diff) | |
| download | mullvadvpn-3a35547e4c4a00755b14d05f67ae5e636138f81b.tar.xz mullvadvpn-3a35547e4c4a00755b14d05f67ae5e636138f81b.zip | |
Merge branch 'show-time-running-out-banner'
Diffstat (limited to 'android')
5 files changed, 98 insertions, 53 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectFragment.kt index 36cf498e40..1ef0b3ac62 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectFragment.kt @@ -102,6 +102,10 @@ class ConnectFragment : ServiceDependentFragment(OnNoService.GoToLaunchScreen) { } else if (expiry != null) { scheduleNextAccountExpiryCheck(expiry) } + + jobTracker.newUiJob("updateAccountExpiry") { + notificationBanner.accountExpiry = expiry + } } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/NotificationBanner.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/NotificationBanner.kt index 447837fefd..82a45cb347 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/NotificationBanner.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/NotificationBanner.kt @@ -18,10 +18,12 @@ import net.mullvad.mullvadvpn.dataproxy.AppVersionInfoCache import net.mullvad.mullvadvpn.model.KeygenEvent import net.mullvad.mullvadvpn.model.TunnelState import net.mullvad.mullvadvpn.service.MullvadDaemon +import net.mullvad.mullvadvpn.util.TimeLeftFormatter import net.mullvad.talpid.tunnel.ActionAfterDisconnect import net.mullvad.talpid.tunnel.ErrorState import net.mullvad.talpid.tunnel.ErrorStateCause import net.mullvad.talpid.tunnel.ParameterGenerationError +import org.joda.time.DateTime class NotificationBanner( val parentView: View, @@ -29,12 +31,18 @@ class NotificationBanner( val versionInfoCache: AppVersionInfoCache, val daemon: MullvadDaemon ) { - enum class ExternalLink { Download, KeyManagement } + enum class ExternalLink { + BuyMoreTime, + Download, + KeyManagement + } private val resources = context.resources + private val timeLeftFormatter = TimeLeftFormatter(resources) - private val keyManagementUrl = context.getString(R.string.wg_key_url) + private val buyMoreTimeUrl = context.getString(R.string.account_url) private val downloadUrl = Uri.parse(context.getString(R.string.download_url)) + private val keyManagementUrl = context.getString(R.string.wg_key_url) private val errorImage = resources.getDrawable(R.drawable.icon_notification_error, null) private val warningImage = resources.getDrawable(R.drawable.icon_notification_warning, null) @@ -56,7 +64,7 @@ class NotificationBanner( private var externalLink: ExternalLink? = null private var visible = false - private val keyManagementController = BlockingController( + private val clickController = BlockingController( object : BlockableView { override fun setEnabled(enabled: Boolean) { if (enabled) { @@ -68,13 +76,20 @@ class NotificationBanner( } } - override fun onClick(): Job { - return GlobalScope.launch(Dispatchers.Default) { - val token = daemon.getWwwAuthToken() - val url = Uri.parse(keyManagementUrl + "?token=" + token) + override fun onClick() = GlobalScope.launch(Dispatchers.Default) { + buildUrl()?.let { url -> context.startActivity(Intent(Intent.ACTION_VIEW, url)) } } + + private fun buildUrl() = when (externalLink) { + ExternalLink.BuyMoreTime -> Uri.parse(buyMoreTimeUrl + buildUrlTokenParameter()) + ExternalLink.Download -> downloadUrl + ExternalLink.KeyManagement -> Uri.parse(keyManagementUrl + buildUrlTokenParameter()) + null -> null + } + + private fun buildUrlTokenParameter() = "?token=${daemon.getWwwAuthToken()}" } ) @@ -82,20 +97,12 @@ class NotificationBanner( newListener?.invoke(height) } - var keyState: KeygenEvent? = null - set(value) { - field = value - update() - } - - var tunnelState: TunnelState = TunnelState.Disconnected() - set(value) { - field = value - update() - } + var accountExpiry by observable<DateTime?>(null) { _, _, _ -> update() } + var keyState by observable<KeygenEvent?>(null) { _, _, _ -> update() } + var tunnelState by observable<TunnelState>(TunnelState.Disconnected()) { _, _, _ -> update() } init { - banner.setOnClickListener { onClick() } + banner.setOnClickListener { clickController.action() } } fun onResume() { @@ -107,12 +114,16 @@ class NotificationBanner( fun onPause() { versionInfoCache.onUpdate = null updateJob?.cancel() - keyManagementController.onPause() + clickController.onPause() } private fun update() { externalLink = null - updateBasedOnTunnelState() || updateBasedOnKeyState() || updateBasedOnVersionInfo() + + updateBasedOnTunnelState() || + updateBasedOnKeyState() || + updateBasedOnVersionInfo() || + updateBasedOnAccountExpiry() } private fun updateBasedOnKeyState(): Boolean { @@ -153,9 +164,7 @@ class NotificationBanner( } private fun updateBasedOnVersionInfo(): Boolean { - if (!versionInfoCache.isOutdated && versionInfoCache.isSupported) { - hide() - } else { + if (versionInfoCache.isOutdated || !versionInfoCache.isSupported) { val title: Int val statusImage: Drawable val template: Int @@ -176,6 +185,25 @@ class NotificationBanner( externalLink = ExternalLink.Download show(statusImage, title, description) + + return true + } else { + return false + } + } + + private fun updateBasedOnAccountExpiry(): Boolean { + val expiry = accountExpiry + val threeDaysFromNow = DateTime.now().plusDays(3) + + if (expiry != null && expiry.isBefore(threeDaysFromNow)) { + val timeLeft = timeLeftFormatter.format(expiry) + + externalLink = ExternalLink.BuyMoreTime + + show(warningImage, R.string.account_credit_expires_soon, timeLeft) + } else { + hide() } return true @@ -277,17 +305,4 @@ class NotificationBanner( return measuredHeight } } - - private fun onClick() { - val externalLink = this.externalLink - - when (externalLink) { - ExternalLink.Download -> { - context.startActivity(Intent(Intent.ACTION_VIEW, this.downloadUrl)) - } - ExternalLink.KeyManagement -> { - this.keyManagementController.action() - } - } - } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/RemainingTimeLabel.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/RemainingTimeLabel.kt index f20f6974fd..20a5c62e6e 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/RemainingTimeLabel.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ui/RemainingTimeLabel.kt @@ -4,12 +4,13 @@ import android.content.Context import android.view.View import android.widget.TextView import net.mullvad.mullvadvpn.R +import net.mullvad.mullvadvpn.util.TimeLeftFormatter import org.joda.time.DateTime import org.joda.time.Duration -import org.joda.time.PeriodType class RemainingTimeLabel(val context: Context, val view: View) { private val resources = context.resources + private val formatter = TimeLeftFormatter(resources) private val expiredColor = resources.getColor(R.color.red) private val normalColor = resources.getColor(R.color.white60) @@ -32,21 +33,7 @@ class RemainingTimeLabel(val context: Context, val view: View) { label.setText(R.string.out_of_time) label.setTextColor(expiredColor) } else { - val remainingTimeInfo = - remainingTime.toPeriodTo(expiry, PeriodType.yearMonthDayTime()) - - if (remainingTimeInfo.years > 0) { - label.setText(getRemainingText(R.plurals.years_left, remainingTimeInfo.years)) - } else if (remainingTimeInfo.months >= 3) { - label.setText(getRemainingText(R.plurals.months_left, remainingTimeInfo.months)) - } else if (remainingTimeInfo.months > 0 || remainingTimeInfo.days >= 1) { - label.setText( - getRemainingText(R.plurals.days_left, remainingTime.standardDays.toInt()) - ) - } else { - label.setText(R.string.less_than_a_day_left) - } - + label.setText(formatter.format(expiry, remainingTime)) label.setTextColor(normalColor) } } else { diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/util/TimeLeftFormatter.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/util/TimeLeftFormatter.kt new file mode 100644 index 0000000000..c3a6aaa1cb --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/util/TimeLeftFormatter.kt @@ -0,0 +1,38 @@ +package net.mullvad.mullvadvpn.util + +import android.content.res.Resources +import net.mullvad.mullvadvpn.R +import org.joda.time.DateTime +import org.joda.time.Duration +import org.joda.time.PeriodType + +class TimeLeftFormatter(val resources: Resources) { + fun format(accountExpiry: DateTime): String { + val remainingTime = Duration(DateTime.now(), accountExpiry) + + return format(accountExpiry, remainingTime) + } + + fun format(accountExpiry: DateTime, remainingTime: Duration): String { + if (remainingTime.isShorterThan(Duration.ZERO)) { + return resources.getString(R.string.out_of_time) + } else { + val remainingTimeInfo = + remainingTime.toPeriodTo(accountExpiry, PeriodType.yearMonthDayTime()) + + if (remainingTimeInfo.years > 0) { + return getRemainingText(R.plurals.years_left, remainingTimeInfo.years) + } else if (remainingTimeInfo.months >= 3) { + return getRemainingText(R.plurals.months_left, remainingTimeInfo.months) + } else if (remainingTimeInfo.months > 0 || remainingTimeInfo.days >= 1) { + return getRemainingText(R.plurals.days_left, remainingTime.standardDays.toInt()) + } else { + return resources.getString(R.string.less_than_a_day_left) + } + } + } + + private fun getRemainingText(pluralId: Int, quantity: Int): String { + return resources.getQuantityString(pluralId, quantity, quantity) + } +} diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml index 86cca30416..1bdf764ac4 100644 --- a/android/src/main/res/values/strings.xml +++ b/android/src/main/res/values/strings.xml @@ -130,6 +130,7 @@ <string name="unsupported_version">Unsupported version</string> <string name="unsupported_version_description">You are running an unsupported app version. Please upgrade to %1$s now to ensure your security</string> + <string name="account_credit_expires_soon">Account credit expires soon</string> <string name="select_location">Select location</string> <string name="select_location_description">While connected, your real location is masked with a private and secure location in the selected region</string> |
