summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorsaber safavi <saber.safavi@codic.se>2022-10-11 09:33:57 +0200
committersaber safavi <saber.safavi@codic.se>2022-10-12 09:16:53 +0200
commit387da8a74043c5d6050cb72ad04ed8b7738fc8b9 (patch)
tree84831b8e3ee10579451023a8f3d86d23acea136f /android
parente552d907ad63d2cbb868c881fd3a5eb45019fa7d (diff)
downloadmullvadvpn-387da8a74043c5d6050cb72ad04ed8b7738fc8b9.tar.xz
mullvadvpn-387da8a74043c5d6050cb72ad04ed8b7738fc8b9.zip
Add notification permission for android 13
Also prevent foreground service notification from being dismissed
Diffstat (limited to 'android')
-rw-r--r--android/app/src/main/AndroidManifest.xml1
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/VpnServiceModule.kt9
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt7
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt26
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt16
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt16
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SdkUtils.kt9
8 files changed, 76 insertions, 11 deletions
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 59d85b30d4..279a6f1858 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-feature android:name="android.hardware.touchscreen"
android:required="false" />
<uses-feature android:name="android.hardware.faketouch"
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/VpnServiceModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/VpnServiceModule.kt
new file mode 100644
index 0000000000..12df45f923
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/VpnServiceModule.kt
@@ -0,0 +1,9 @@
+package net.mullvad.mullvadvpn.di
+
+import androidx.core.app.NotificationManagerCompat
+import org.koin.android.ext.koin.androidContext
+import org.koin.dsl.module
+
+val vpnServiceModule = module {
+ single { NotificationManagerCompat.from(androidContext()) }
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
index 0a6c29b44e..4753294376 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
@@ -12,12 +12,14 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
+import net.mullvad.mullvadvpn.di.vpnServiceModule
import net.mullvad.mullvadvpn.model.Settings
import net.mullvad.mullvadvpn.model.TunnelState
import net.mullvad.mullvadvpn.service.endpoint.ServiceEndpoint
import net.mullvad.mullvadvpn.service.notifications.AccountExpiryNotification
import net.mullvad.mullvadvpn.ui.MainActivity
import net.mullvad.talpid.TalpidVpnService
+import org.koin.core.context.loadKoinModules
class MullvadVpnService : TalpidVpnService() {
companion object {
@@ -66,6 +68,7 @@ class MullvadVpnService : TalpidVpnService() {
super.onCreate()
Log.d(TAG, "Initializing service")
+ loadKoinModules(vpnServiceModule)
daemonInstance = DaemonInstance(this)
keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt
index ae4fcc75ff..cdad8e327f 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt
@@ -16,6 +16,7 @@ import net.mullvad.mullvadvpn.service.endpoint.AccountCache
import net.mullvad.mullvadvpn.util.Intermittent
import net.mullvad.mullvadvpn.util.JobTracker
import net.mullvad.mullvadvpn.util.SdkUtils
+import net.mullvad.mullvadvpn.util.SdkUtils.isNotificationPermissionGranted
import org.joda.time.DateTime
import org.joda.time.Duration
@@ -69,8 +70,10 @@ class AccountExpiryNotification(
val durationUntilExpiry = expiryDate?.remainingTime()
if (accountCache.isNewAccount.not() && durationUntilExpiry?.isCloseToExpiry() == true) {
- val notification = build(expiryDate, durationUntilExpiry)
- channel.notificationManager.notify(NOTIFICATION_ID, notification)
+ if (context.isNotificationPermissionGranted()) {
+ val notification = build(expiryDate, durationUntilExpiry)
+ channel.notificationManager.notify(NOTIFICATION_ID, notification)
+ }
jobTracker.newUiJob("scheduleUpdate") { scheduleUpdate() }
} else {
channel.notificationManager.cancel(NOTIFICATION_ID)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt
index cb875d0d6b..052866b549 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt
@@ -41,33 +41,43 @@ class NotificationChannel(
fun buildNotification(
intent: PendingIntent,
title: String,
- deleteIntent: PendingIntent? = null
+ deleteIntent: PendingIntent? = null,
+ isOngoing: Boolean = false
): Notification {
- return buildNotification(intent, title, emptyList(), deleteIntent)
+ return buildNotification(intent, title, emptyList(), deleteIntent, isOngoing)
}
fun buildNotification(
intent: PendingIntent,
title: Int,
- deleteIntent: PendingIntent? = null
+ deleteIntent: PendingIntent? = null,
+ isOngoing: Boolean = false
): Notification {
- return buildNotification(intent, title, emptyList(), deleteIntent)
+ return buildNotification(intent, title, emptyList(), deleteIntent, isOngoing)
}
fun buildNotification(
pendingIntent: PendingIntent,
title: Int,
actions: List<NotificationCompat.Action>,
- deleteIntent: PendingIntent? = null
+ deleteIntent: PendingIntent? = null,
+ isOngoing: Boolean = false
): Notification {
- return buildNotification(pendingIntent, context.getString(title), actions, deleteIntent)
+ return buildNotification(
+ pendingIntent,
+ context.getString(title),
+ actions,
+ deleteIntent,
+ isOngoing
+ )
}
private fun buildNotification(
pendingIntent: PendingIntent,
title: String,
actions: List<NotificationCompat.Action>,
- deleteIntent: PendingIntent? = null
+ deleteIntent: PendingIntent? = null,
+ isOngoing: Boolean = false
): Notification {
val builder = NotificationCompat.Builder(context, id)
.setSmallIcon(R.drawable.small_logo_black)
@@ -75,7 +85,7 @@ class NotificationChannel(
.setContentTitle(title)
.setContentIntent(pendingIntent)
.setVisibility(visibility)
-
+ .setOngoing(isOngoing)
for (action in actions) {
builder.addAction(action)
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt
index aee8115c1e..ed14fceae9 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt
@@ -55,6 +55,15 @@ class TunnelStateNotification(val context: Context) {
}
}
+ private val shouldDisplayOngoingNotification: Boolean
+ get() = when (tunnelState) {
+ is TunnelState.Connected -> true
+ is TunnelState.Disconnected,
+ is TunnelState.Connecting,
+ is TunnelState.Disconnecting,
+ is TunnelState.Error -> false
+ }
+
private var reconnecting = false
private var showingReconnecting = false
@@ -98,7 +107,12 @@ class TunnelStateNotification(val context: Context) {
emptyList()
}
- return channel.buildNotification(pendingIntent, notificationText, actions)
+ return channel.buildNotification(
+ pendingIntent,
+ notificationText,
+ actions,
+ isOngoing = shouldDisplayOngoingNotification
+ )
}
private fun buildAction(): NotificationCompat.Action {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
index 0697a5e5a5..a72d74c917 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
@@ -1,5 +1,6 @@
package net.mullvad.mullvadvpn.ui
+import android.Manifest
import android.app.Activity
import android.app.UiModeManager
import android.content.Intent
@@ -9,6 +10,8 @@ import android.net.VpnService
import android.os.Bundle
import android.util.Log
import android.view.WindowManager
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
@@ -33,6 +36,7 @@ import net.mullvad.mullvadvpn.ui.fragments.DeviceRevokedFragment
import net.mullvad.mullvadvpn.ui.serviceconnection.AccountRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.DeviceRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
+import net.mullvad.mullvadvpn.util.SdkUtils.isNotificationPermissionGranted
import net.mullvad.mullvadvpn.util.UNKNOWN_STATE_DEBOUNCE_DELAY_MILLISECONDS
import net.mullvad.mullvadvpn.util.addDebounceForUnknownState
import org.koin.android.ext.android.getKoin
@@ -40,6 +44,11 @@ import org.koin.core.context.loadKoinModules
open class MainActivity : FragmentActivity() {
val problemReport = MullvadProblemReport()
+ private var requestNotificationPermissionLauncher: ActivityResultLauncher<String> =
+ registerForActivityResult(ActivityResultContracts.RequestPermission()) {
+ // NotificationManager.areNotificationsEnabled is used to check the state rather than
+ // handling the callback value.
+ }
private var visibleSecureScreens = HashSet<Fragment>()
@@ -80,6 +89,7 @@ open class MainActivity : FragmentActivity() {
setContentView(R.layout.main)
launchDeviceStateHandler()
+ checkForNotificationPermission()
}
override fun onStart() {
@@ -258,6 +268,12 @@ open class MainActivity : FragmentActivity() {
}
}
+ private fun checkForNotificationPermission() {
+ if (isNotificationPermissionGranted().not()) {
+ requestNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+ }
+ }
+
companion object {
private const val LOGIN_DELAY_MILLIS = 1000L
private const val LOGIN_AWAIT_EXPIRY_MILLIS = 1000L
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SdkUtils.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SdkUtils.kt
index 7f1ba6bd74..c6aa4d9457 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SdkUtils.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SdkUtils.kt
@@ -1,6 +1,9 @@
package net.mullvad.mullvadvpn.util
+import android.Manifest
import android.app.PendingIntent
+import android.content.Context
+import android.content.pm.PackageManager
import android.net.VpnService
import android.os.Build
import android.service.quicksettings.Tile
@@ -14,6 +17,12 @@ object SdkUtils {
}
}
+ fun Context.isNotificationPermissionGranted(): Boolean {
+ return (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) ||
+ checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) ==
+ PackageManager.PERMISSION_GRANTED
+ }
+
fun VpnService.Builder.setMeteredIfSupported(isMetered: Boolean) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
this.setMetered(isMetered)