diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt index b360613eaaed..dad6ea5b5681 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt @@ -1,6 +1,9 @@ package net.mullvad.mullvadvpn.service import android.app.Service +import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED +import android.net.VpnService +import android.os.Build import kotlin.properties.Delegates.observable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope @@ -90,11 +93,22 @@ class ForegroundNotificationManager( } fun showOnForeground() { - service.startForeground( - TunnelStateNotification.NOTIFICATION_ID, - tunnelStateNotification.build() - ) - + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + if (VpnService.prepare(service) == null) { + service.startForeground( + TunnelStateNotification.NOTIFICATION_ID, + tunnelStateNotification.build(), + FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED + ) + } else { + return + } + } else { + service.startForeground( + TunnelStateNotification.NOTIFICATION_ID, + tunnelStateNotification.build(), + ) + } onForeground = true } diff --git a/android/tile/src/main/kotlin/net/mullvad/mullvadvpn/tile/MullvadTileService.kt b/android/tile/src/main/kotlin/net/mullvad/mullvadvpn/tile/MullvadTileService.kt index 2a51c4e0e7b5..d8b1c85a6bf4 100644 --- a/android/tile/src/main/kotlin/net/mullvad/mullvadvpn/tile/MullvadTileService.kt +++ b/android/tile/src/main/kotlin/net/mullvad/mullvadvpn/tile/MullvadTileService.kt @@ -1,7 +1,11 @@ package net.mullvad.mullvadvpn.tile +import android.annotation.SuppressLint +import android.app.PendingIntent import android.content.Intent import android.graphics.drawable.Icon +import android.net.VpnService +import android.os.Build import android.service.quicksettings.Tile import android.service.quicksettings.TileService import android.util.Log @@ -17,7 +21,9 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeoutOrNull import net.mullvad.mullvadvpn.lib.common.constant.KEY_CONNECT_ACTION import net.mullvad.mullvadvpn.lib.common.constant.KEY_DISCONNECT_ACTION +import net.mullvad.mullvadvpn.lib.common.constant.MAIN_ACTIVITY_CLASS import net.mullvad.mullvadvpn.lib.common.constant.VPN_SERVICE_CLASS +import net.mullvad.mullvadvpn.lib.common.util.SdkUtils import net.mullvad.mullvadvpn.lib.common.util.SdkUtils.setSubtitleIfSupported import net.mullvad.mullvadvpn.model.ServiceResult import net.mullvad.mullvadvpn.model.TunnelState @@ -74,7 +80,27 @@ class MullvadTileService : TileService() { scope?.cancel() } + @SuppressLint("StartActivityAndCollapseDeprecated") private fun toggleTunnel() { + val isSetup = VpnService.prepare(applicationContext) == null + // TODO This logic should be more advanced, we should ensure user has an account setup etc. + if (!isSetup) { + Log.d("MullvadTileService", "VPN service not setup, starting main activity") + + val intent = + Intent().apply { + setClassName(applicationContext.packageName, MAIN_ACTIVITY_CLASS) + flags = + Intent.FLAG_ACTIVITY_CLEAR_TOP or + Intent.FLAG_ACTIVITY_SINGLE_TOP or + Intent.FLAG_ACTIVITY_NEW_TASK + action = Intent.ACTION_MAIN + } + startActivityAndCollapseCompat(intent) + return + } else { + Log.d("MullvadTileService", "VPN service is setup") + } val intent = Intent().apply { setClassName(applicationContext.packageName, VPN_SERVICE_CLASS) @@ -90,6 +116,22 @@ class MullvadTileService : TileService() { startForegroundService(intent) } + @SuppressLint("StartActivityAndCollapseDeprecated") + private fun MullvadTileService.startActivityAndCollapseCompat(intent: Intent) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + val pendingIntent = + PendingIntent.getActivity( + applicationContext, + 0, + intent, + SdkUtils.getSupportedPendingIntentFlags() + ) + startActivityAndCollapse(pendingIntent) + } else { + startActivityAndCollapse(intent) + } + } + @OptIn(FlowPreview::class) private fun CoroutineScope.launchListenToTunnelState() = launch { ServiceConnection(this@MullvadTileService, this)