diff options
| author | saber safavi <saber.safavi@codic.se> | 2022-10-11 09:33:57 +0200 |
|---|---|---|
| committer | saber safavi <saber.safavi@codic.se> | 2022-10-12 09:16:53 +0200 |
| commit | 387da8a74043c5d6050cb72ad04ed8b7738fc8b9 (patch) | |
| tree | 84831b8e3ee10579451023a8f3d86d23acea136f /android/app/src | |
| parent | e552d907ad63d2cbb868c881fd3a5eb45019fa7d (diff) | |
| download | mullvadvpn-387da8a74043c5d6050cb72ad04ed8b7738fc8b9.tar.xz mullvadvpn-387da8a74043c5d6050cb72ad04ed8b7738fc8b9.zip | |
Add notification permission for android 13
Also prevent foreground service notification from being dismissed
Diffstat (limited to 'android/app/src')
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) |
