aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/src/main/java/com/wireguard/android/QuickTileService.kt
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/main/java/com/wireguard/android/QuickTileService.kt')
-rw-r--r--ui/src/main/java/com/wireguard/android/QuickTileService.kt130
1 files changed, 88 insertions, 42 deletions
diff --git a/ui/src/main/java/com/wireguard/android/QuickTileService.kt b/ui/src/main/java/com/wireguard/android/QuickTileService.kt
index 5099668e..a8650b78 100644
--- a/ui/src/main/java/com/wireguard/android/QuickTileService.kt
+++ b/ui/src/main/java/com/wireguard/android/QuickTileService.kt
@@ -1,15 +1,18 @@
/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
+ * Copyright © 2017-2025 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android
+import android.app.PendingIntent
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.Icon
+import android.net.Uri
import android.os.Build
import android.os.IBinder
+import android.provider.Settings
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import android.util.Log
@@ -20,7 +23,9 @@ import com.wireguard.android.activity.MainActivity
import com.wireguard.android.activity.TunnelToggleActivity
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.model.ObservableTunnel
+import com.wireguard.android.util.applicationScope
import com.wireguard.android.widget.SlashDrawable
+import kotlinx.coroutines.launch
/**
* Service that maintains the application's custom Quick Settings tile. This service is bound by the
@@ -40,38 +45,66 @@ class QuickTileService : TileService() {
var ret: IBinder? = null
try {
ret = super.onBind(intent)
- } catch (e: Exception) {
+ } catch (e: Throwable) {
Log.d(TAG, "Failed to bind to TileService", e)
}
return ret
}
override fun onClick() {
- if (tunnel != null) {
- unlockAndRun {
- val tile = qsTile
- if (tile != null) {
- tile.icon = if (tile.icon == iconOn) iconOff else iconOn
- tile.updateTile()
- }
- tunnel!!.setStateAsync(Tunnel.State.TOGGLE).whenComplete { _, t ->
- if (t == null) {
- updateTile()
+ applicationScope.launch {
+ if (tunnel == null) {
+ Application.getTunnelManager().getTunnels()
+ updateTile()
+ }
+ when (val tunnel = tunnel) {
+ null -> {
+ Log.d(TAG, "No tunnel set, so launching main activity")
+ val intent = Intent(this@QuickTileService, MainActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ startActivityAndCollapse(PendingIntent.getActivity(this@QuickTileService, 0, intent, PendingIntent.FLAG_IMMUTABLE))
} else {
- val toggleIntent = Intent(this, TunnelToggleActivity::class.java)
- toggleIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- startActivity(toggleIntent)
+ @Suppress("DEPRECATION")
+ startActivityAndCollapse(intent)
+ }
+ }
+
+ else -> {
+ unlockAndRun {
+ applicationScope.launch {
+ try {
+ tunnel.setStateAsync(Tunnel.State.TOGGLE)
+ updateTile()
+ } catch (e: Throwable) {
+ Log.d(TAG, "Failed to set state, so falling back", e)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && !Settings.canDrawOverlays(this@QuickTileService)) {
+ Log.d(TAG, "Need overlay permissions")
+ val permissionIntent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName"))
+ permissionIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ startActivityAndCollapse(
+ PendingIntent.getActivity(
+ this@QuickTileService,
+ 0,
+ permissionIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ )
+ return@launch
+ }
+ val toggleIntent = Intent(this@QuickTileService, TunnelToggleActivity::class.java)
+ toggleIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ startActivity(toggleIntent)
+ }
+ }
}
}
}
- } else {
- val intent = Intent(this, MainActivity::class.java)
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- startActivityAndCollapse(intent)
}
}
override fun onCreate() {
+ isAdded = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
iconOn = Icon.createWithResource(this, R.drawable.ic_tile)
iconOff = iconOn
@@ -81,55 +114,64 @@ class QuickTileService : TileService() {
icon.setAnimationEnabled(false) /* Unfortunately we can't have animations, since Icons are marshaled. */
icon.setSlashed(false)
var b = Bitmap.createBitmap(icon.intrinsicWidth, icon.intrinsicHeight, Bitmap.Config.ARGB_8888)
- ?: return
var c = Canvas(b)
icon.setBounds(0, 0, c.width, c.height)
icon.draw(c)
iconOn = Icon.createWithBitmap(b)
icon.setSlashed(true)
b = Bitmap.createBitmap(icon.intrinsicWidth, icon.intrinsicHeight, Bitmap.Config.ARGB_8888)
- ?: return
c = Canvas(b)
icon.setBounds(0, 0, c.width, c.height)
icon.draw(c)
iconOff = Icon.createWithBitmap(b)
}
+ override fun onDestroy() {
+ super.onDestroy()
+ isAdded = false
+ }
+
override fun onStartListening() {
Application.getTunnelManager().addOnPropertyChangedCallback(onTunnelChangedCallback)
- if (tunnel != null) tunnel!!.addOnPropertyChangedCallback(onStateChangedCallback)
+ tunnel?.addOnPropertyChangedCallback(onStateChangedCallback)
updateTile()
}
override fun onStopListening() {
- if (tunnel != null) tunnel!!.removeOnPropertyChangedCallback(onStateChangedCallback)
+ tunnel?.removeOnPropertyChangedCallback(onStateChangedCallback)
Application.getTunnelManager().removeOnPropertyChangedCallback(onTunnelChangedCallback)
}
+ override fun onTileAdded() {
+ isAdded = true
+ }
+
+ override fun onTileRemoved() {
+ isAdded = false
+ }
+
private fun updateTile() {
// Update the tunnel.
val newTunnel = Application.getTunnelManager().lastUsedTunnel
if (newTunnel != tunnel) {
- if (tunnel != null) tunnel!!.removeOnPropertyChangedCallback(onStateChangedCallback)
+ tunnel?.removeOnPropertyChangedCallback(onStateChangedCallback)
tunnel = newTunnel
- if (tunnel != null) tunnel!!.addOnPropertyChangedCallback(onStateChangedCallback)
+ tunnel?.addOnPropertyChangedCallback(onStateChangedCallback)
}
// Update the tile contents.
- val label: String
- val state: Int
- val tile = qsTile
- if (tunnel != null) {
- label = tunnel!!.name
- state = if (tunnel!!.state == Tunnel.State.UP) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
- } else {
- label = getString(R.string.app_name)
- state = Tile.STATE_INACTIVE
- }
- if (tile == null) return
- tile.label = label
- if (tile.state != state) {
- tile.icon = if (state == Tile.STATE_ACTIVE) iconOn else iconOff
- tile.state = state
+ val tile = qsTile ?: return
+
+ when (val tunnel = tunnel) {
+ null -> {
+ tile.label = getString(R.string.app_name)
+ tile.state = Tile.STATE_INACTIVE
+ tile.icon = iconOff
+ }
+ else -> {
+ tile.label = tunnel.name
+ tile.state = if (tunnel.state == Tunnel.State.UP) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
+ tile.icon = if (tunnel.state == Tunnel.State.UP) iconOn else iconOff
+ }
}
tile.updateTile()
}
@@ -140,19 +182,23 @@ class QuickTileService : TileService() {
sender.removeOnPropertyChangedCallback(this)
return
}
- if (propertyId != 0 && propertyId != BR.state) return
+ if (propertyId != 0 && propertyId != BR.state)
+ return
updateTile()
}
}
private inner class OnTunnelChangedCallback : OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable, propertyId: Int) {
- if (propertyId != 0 && propertyId != BR.lastUsedTunnel) return
+ if (propertyId != 0 && propertyId != BR.lastUsedTunnel)
+ return
updateTile()
}
}
companion object {
private const val TAG = "WireGuard/QuickTileService"
+ var isAdded: Boolean = false
+ private set
}
}