summaryrefslogtreecommitdiffhomepage
path: root/android/src
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2020-06-22 09:53:33 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2020-06-22 09:53:33 -0300
commit6a8d202fcef9fdfd32f873d25550d78bff3ffeb6 (patch)
treee4b51f0056cfcc3b3522fd45bb772727671459f7 /android/src
parent9f59403aaaad0c31120046d75acfa472f6b96ad2 (diff)
parent33399bc388ee48b111624fa6c0154f9d00d99960 (diff)
downloadmullvadvpn-6a8d202fcef9fdfd32f873d25550d78bff3ffeb6.tar.xz
mullvadvpn-6a8d202fcef9fdfd32f873d25550d78bff3ffeb6.zip
Merge branch 'time-running-out-notification'
Diffstat (limited to 'android/src')
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt49
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt100
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt14
-rw-r--r--android/src/main/res/values/plurals.xml8
-rw-r--r--android/src/main/res/values/strings.xml6
5 files changed, 160 insertions, 17 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
index 0998ddcc1c..795f5596df 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
@@ -8,15 +8,19 @@ import android.os.Binder
import android.os.IBinder
import android.util.Log
import java.io.File
+import kotlin.properties.Delegates.observable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.model.Settings
+import net.mullvad.mullvadvpn.service.notifications.AccountExpiryNotification
import net.mullvad.mullvadvpn.service.tunnelstate.TunnelStateUpdater
import net.mullvad.mullvadvpn.ui.MainActivity
import net.mullvad.talpid.TalpidVpnService
import net.mullvad.talpid.util.EventNotifier
+import net.mullvad.talpid.util.autoSubscribable
+import org.joda.time.DateTime
private const val RELAYS_FILE = "relays.json"
@@ -41,13 +45,32 @@ class MullvadVpnService : TalpidVpnService() {
private var startDaemonJob: Job? = null
- private var instance: ServiceInstance? = null
- set(value) {
- if (field != value) {
- field?.onDestroy()
- field = value
- serviceNotifier.notify(value)
+ private var instance by observable<ServiceInstance?>(null) { _, oldInstance, newInstance ->
+ if (newInstance != oldInstance) {
+ oldInstance?.onDestroy()
+
+ accountExpiryNotification = newInstance?.daemon?.let { daemon ->
+ AccountExpiryNotification(this, daemon)
}
+
+ accountNumberEvents = newInstance?.accountCache?.onAccountNumberChange
+ accountExpiryEvents = newInstance?.accountCache?.onAccountExpiryChange
+
+ serviceNotifier.notify(newInstance)
+ }
+ }
+
+ private var accountNumberEvents by autoSubscribable<String?>(this, null) { accountNumber ->
+ loggedIn = accountNumber != null
+ }
+
+ private var accountExpiryEvents by autoSubscribable<DateTime?>(this, null) { expiry ->
+ accountExpiryNotification?.accountExpiry = expiry
+ }
+
+ private var accountExpiryNotification
+ by observable<AccountExpiryNotification?>(null) { _, oldNotification, _ ->
+ oldNotification?.accountExpiry = null
}
private lateinit var keyguardManager: KeyguardManager
@@ -69,11 +92,9 @@ class MullvadVpnService : TalpidVpnService() {
}
}
- private var isBound = false
- set(value) {
- field = value
- notificationManager.lockedToForeground = value
- }
+ private var isBound by observable(false) { _, _, isBound ->
+ notificationManager.lockedToForeground = isBound
+ }
override fun onCreate() {
super.onCreate()
@@ -195,11 +216,7 @@ class MullvadVpnService : TalpidVpnService() {
}
private fun setUpInstance(daemon: MullvadDaemon, settings: Settings) {
- val settingsListener = SettingsListener(daemon, settings).apply {
- accountNumberNotifier.subscribe(this@MullvadVpnService) { accountNumber ->
- loggedIn = accountNumber != null
- }
- }
+ val settingsListener = SettingsListener(daemon, settings)
val connectionProxy = ConnectionProxy(this, daemon).apply {
when (pendingAction) {
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt
new file mode 100644
index 0000000000..34ac1d5a7d
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt
@@ -0,0 +1,100 @@
+package net.mullvad.mullvadvpn.service.notifications
+
+import android.app.Notification
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import kotlin.properties.Delegates.observable
+import kotlinx.coroutines.delay
+import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.service.MullvadDaemon
+import net.mullvad.mullvadvpn.util.JobTracker
+import org.joda.time.DateTime
+import org.joda.time.Duration
+
+class AccountExpiryNotification(val context: Context, val daemon: MullvadDaemon) {
+ companion object {
+ val NOTIFICATION_ID: Int = 2
+ val REMAINING_TIME_FOR_REMINDERS = Duration.standardDays(2)
+ val TIME_BETWEEN_CHECKS: Long = 12 /* h */ * 60 /* min */ * 60 /* s */ * 1000 /* ms */
+ }
+
+ private val jobTracker = JobTracker()
+ private val resources = context.resources
+
+ private val buyMoreTimeUrl = resources.getString(R.string.account_url)
+
+ private val channel = NotificationChannel(
+ context,
+ "mullvad_account_time",
+ R.string.account_time_notification_channel_name,
+ R.string.account_time_notification_channel_description,
+ NotificationManager.IMPORTANCE_HIGH
+ )
+
+ var accountExpiry by observable<DateTime?>(null) { _, oldValue, newValue ->
+ if (oldValue != newValue) {
+ jobTracker.newUiJob("update") { update(newValue) }
+ }
+ }
+
+ private suspend fun update(accountExpiry: DateTime?) {
+ val remainingTime = accountExpiry?.let { expiry -> Duration(DateTime.now(), expiry) }
+
+ if (remainingTime != null && remainingTime.isShorterThan(REMAINING_TIME_FOR_REMINDERS)) {
+ val notification = build(accountExpiry, remainingTime)
+
+ channel.notificationManager.notify(NOTIFICATION_ID, notification)
+
+ jobTracker.newUiJob("scheduleUpdate") { scheduleUpdate() }
+ } else {
+ channel.notificationManager.cancel(NOTIFICATION_ID)
+ jobTracker.cancelJob("scheduleUpdate")
+ }
+ }
+
+ private suspend fun scheduleUpdate() {
+ delay(TIME_BETWEEN_CHECKS)
+ update(accountExpiry)
+ }
+
+ private suspend fun build(expiry: DateTime, remainingTime: Duration): Notification {
+ val url = jobTracker.runOnBackground {
+ Uri.parse("$buyMoreTimeUrl?token=${daemon.getWwwAuthToken()}")
+ }
+
+ val intent = Intent(Intent.ACTION_VIEW, url)
+ val flags = PendingIntent.FLAG_UPDATE_CURRENT
+ val pendingIntent = PendingIntent.getActivity(context, 1, intent, flags)
+
+ return channel.buildNotification(pendingIntent, format(expiry, remainingTime))
+ }
+
+ private fun format(expiry: DateTime, remainingTime: Duration): String {
+ if (remainingTime.isShorterThan(Duration.ZERO)) {
+ return resources.getString(R.string.account_credit_has_expired)
+ } else {
+ val remainingTimeInfo = remainingTime.toPeriodTo(expiry)
+
+ if (remainingTimeInfo.days >= 1) {
+ return getRemainingText(
+ R.plurals.account_credit_expires_in_days,
+ remainingTime.standardDays.toInt()
+ )
+ } else if (remainingTimeInfo.hours >= 1) {
+ return getRemainingText(
+ R.plurals.account_credit_expires_in_hours,
+ remainingTime.standardHours.toInt()
+ )
+ } else {
+ return resources.getString(R.string.account_credit_expires_in_a_few_minutes)
+ }
+ }
+ }
+
+ private fun getRemainingText(pluralId: Int, quantity: Int): String {
+ return resources.getQuantityString(pluralId, quantity, quantity)
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt
index 2546c8955f..037a01033d 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt
@@ -37,6 +37,10 @@ class NotificationChannel(
}
}
+ fun buildNotification(intent: PendingIntent, title: String): Notification {
+ return buildNotification(intent, title, emptyList())
+ }
+
fun buildNotification(intent: PendingIntent, title: Int): Notification {
return buildNotification(intent, title, emptyList())
}
@@ -46,10 +50,18 @@ class NotificationChannel(
title: Int,
actions: List<NotificationCompat.Action>
): Notification {
+ return buildNotification(pendingIntent, context.getString(title), actions)
+ }
+
+ fun buildNotification(
+ pendingIntent: PendingIntent,
+ title: String,
+ actions: List<NotificationCompat.Action>
+ ): Notification {
val builder = NotificationCompat.Builder(context, id)
.setSmallIcon(R.drawable.small_logo_black)
.setColor(badgeColor)
- .setContentTitle(context.getString(title))
+ .setContentTitle(title)
.setContentIntent(pendingIntent)
for (action in actions) {
diff --git a/android/src/main/res/values/plurals.xml b/android/src/main/res/values/plurals.xml
index 679c4f46db..224cc9cb79 100644
--- a/android/src/main/res/values/plurals.xml
+++ b/android/src/main/res/values/plurals.xml
@@ -32,4 +32,12 @@
<item quantity="one">a year ago</item>
<item quantity="other">%d years ago</item>
</plurals>
+ <plurals name="account_credit_expires_in_days">
+ <item quantity="one">Account credit expires in a day</item>
+ <item quantity="other">Account credit expires in %d days</item>
+ </plurals>
+ <plurals name="account_credit_expires_in_hours">
+ <item quantity="one">Account credit expires in an hour</item>
+ <item quantity="other">Account credit expires in %d hours</item>
+ </plurals>
</resources>
diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml
index 1bdf764ac4..48921c07a3 100644
--- a/android/src/main/res/values/strings.xml
+++ b/android/src/main/res/values/strings.xml
@@ -10,6 +10,9 @@
<string name="foreground_notification_channel_name">VPN tunnel status</string>
<string name="foreground_notification_channel_description">Shows current VPN tunnel
status</string>
+ <string name="account_time_notification_channel_name">Account time reminders</string>
+ <string name="account_time_notification_channel_description">Shows reminders when the account
+ time is about to expire</string>
<string name="connecting_to_daemon">Connecting to Mullvad system service...</string>
<string name="login_title">Login</string>
<string name="login_description">Enter your account number</string>
@@ -41,6 +44,9 @@
<string name="settings_account">Account</string>
<string name="less_than_a_day_left">less than a day left</string>
<string name="less_than_a_minute_ago">less than a minute ago</string>
+ <string name="account_credit_expires_in_a_few_minutes">Account credit expires in a few
+ minutes</string>
+ <string name="account_credit_has_expired">Account credit has expired</string>
<string name="out_of_time">Out of time</string>
<string name="no_more_vpn_time_left">You have no more VPN time left on this account. Either buy
credit on our website or redeem a voucher.</string>