summaryrefslogtreecommitdiffhomepage
path: root/android/src
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2020-03-18 10:58:26 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2020-03-18 10:58:26 -0300
commitb4d7858e0d16f23b1d32029924e20aa863a26b80 (patch)
tree01b0ae8153bdb426250e0015cfd6ad1cc0c2e1e2 /android/src
parentb94272f83c2af3ec0657ced3758949dcb7c16081 (diff)
parent6fedeae09c0620b6b4277a255373c3c16f053501 (diff)
downloadmullvadvpn-b4d7858e0d16f23b1d32029924e20aa863a26b80.tar.xz
mullvadvpn-b4d7858e0d16f23b1d32029924e20aa863a26b80.zip
Merge branch 'quick-tile'
Diffstat (limited to 'android/src')
-rw-r--r--android/src/main/AndroidManifest.xml9
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt48
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt2
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt86
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt5
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/Persistence.kt57
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateListener.kt36
-rw-r--r--android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateUpdater.kt29
-rw-r--r--android/src/main/res/drawable-hdpi/small_logo_black.png (renamed from android/src/main/res/drawable-hdpi/notification.png)bin755 -> 755 bytes
-rw-r--r--android/src/main/res/drawable-hdpi/small_logo_white.pngbin0 -> 846 bytes
-rw-r--r--android/src/main/res/drawable-mdpi/small_logo_black.png (renamed from android/src/main/res/drawable-mdpi/notification.png)bin505 -> 505 bytes
-rw-r--r--android/src/main/res/drawable-mdpi/small_logo_white.pngbin0 -> 636 bytes
-rw-r--r--android/src/main/res/drawable-xhdpi/small_logo_black.png (renamed from android/src/main/res/drawable-xhdpi/notification.png)bin1000 -> 1000 bytes
-rw-r--r--android/src/main/res/drawable-xhdpi/small_logo_white.pngbin0 -> 1047 bytes
-rw-r--r--android/src/main/res/drawable-xxhdpi/small_logo_black.png (renamed from android/src/main/res/drawable-xxhdpi/notification.png)bin1570 -> 1570 bytes
-rw-r--r--android/src/main/res/drawable-xxhdpi/small_logo_white.pngbin0 -> 1598 bytes
-rw-r--r--android/src/main/res/drawable-xxxhdpi/small_logo_black.png (renamed from android/src/main/res/drawable-xxxhdpi/notification.png)bin2143 -> 2143 bytes
-rw-r--r--android/src/main/res/drawable-xxxhdpi/small_logo_white.pngbin0 -> 2120 bytes
18 files changed, 271 insertions, 1 deletions
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index 8f472d7f5b..cbd48f152e 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -27,5 +27,14 @@
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
+ <service android:name="net.mullvad.mullvadvpn.service.MullvadTileService"
+ android:label="@string/app_name"
+ android:icon="@drawable/small_logo_black"
+ android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
+
+ <intent-filter>
+ <action android:name="android.service.quicksettings.action.QS_TILE" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt
index 556517720d..e4b67e2f1b 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt
@@ -3,6 +3,7 @@ package net.mullvad.mullvadvpn.model
import net.mullvad.talpid.net.TunnelEndpoint
import net.mullvad.talpid.tunnel.ActionAfterDisconnect
import net.mullvad.talpid.tunnel.ErrorState
+import net.mullvad.talpid.tunnel.ErrorStateCause
sealed class TunnelState() {
class Disconnected() : TunnelState()
@@ -10,4 +11,51 @@ sealed class TunnelState() {
class Connected(val endpoint: TunnelEndpoint, val location: GeoIpLocation?) : TunnelState()
class Disconnecting(val actionAfterDisconnect: ActionAfterDisconnect) : TunnelState()
class Error(val errorState: ErrorState) : TunnelState()
+
+ companion object {
+ const val DISCONNECTED = "disconnected"
+ const val CONNECTING = "connecting"
+ const val CONNECTED = "connected"
+ const val RECONNECTING = "reconnecting"
+ const val DISCONNECTING = "disconnecting"
+ const val BLOCKING = "blocking"
+ const val ERROR = "error"
+
+ fun fromString(description: String, endpoint: TunnelEndpoint?): TunnelState {
+ return when (description) {
+ DISCONNECTED -> TunnelState.Disconnected()
+ CONNECTING -> TunnelState.Connecting(endpoint, null)
+ CONNECTED -> TunnelState.Connected(endpoint!!, null)
+ RECONNECTING -> TunnelState.Disconnecting(ActionAfterDisconnect.Reconnect)
+ DISCONNECTING -> TunnelState.Disconnecting(ActionAfterDisconnect.Nothing)
+ BLOCKING -> TunnelState.Error(ErrorState(ErrorStateCause.StartTunnelError(), true))
+ ERROR -> {
+ TunnelState.Error(ErrorState(ErrorStateCause.SetFirewallPolicyError(), false))
+ }
+ else -> {
+ TunnelState.Error(ErrorState(ErrorStateCause.SetFirewallPolicyError(), false))
+ }
+ }
+ }
+ }
+
+ override fun toString() = when (this) {
+ is TunnelState.Disconnected -> DISCONNECTED
+ is TunnelState.Connecting -> CONNECTING
+ is TunnelState.Connected -> CONNECTED
+ is TunnelState.Disconnecting -> {
+ if (actionAfterDisconnect == ActionAfterDisconnect.Reconnect) {
+ RECONNECTING
+ } else {
+ DISCONNECTING
+ }
+ }
+ is TunnelState.Error -> {
+ if (errorState.isBlocking) {
+ BLOCKING
+ } else {
+ ERROR
+ }
+ }
+ }
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt
index 733a885b09..aabd9771a3 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt
@@ -252,7 +252,7 @@ class ForegroundNotificationManager(
PendingIntent.getActivity(service, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val builder = NotificationCompat.Builder(service, CHANNEL_ID)
- .setSmallIcon(R.drawable.notification)
+ .setSmallIcon(R.drawable.small_logo_black)
.setColor(badgeColor)
.setContentTitle(service.getString(notificationText))
.setContentIntent(pendingIntent)
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt
new file mode 100644
index 0000000000..e8eb8a3a82
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt
@@ -0,0 +1,86 @@
+package net.mullvad.mullvadvpn.service
+
+import android.content.Intent
+import android.graphics.drawable.Icon
+import android.service.quicksettings.Tile
+import android.service.quicksettings.TileService
+import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.model.TunnelState
+import net.mullvad.mullvadvpn.service.tunnelstate.TunnelStateListener
+import net.mullvad.talpid.tunnel.ActionAfterDisconnect
+
+class MullvadTileService : TileService() {
+ private var secured = false
+ set(value) {
+ if (field != value) {
+ field = value
+ updateTileState()
+ }
+ }
+
+ private lateinit var listener: TunnelStateListener
+ private lateinit var securedIcon: Icon
+ private lateinit var unsecuredIcon: Icon
+
+ override fun onCreate() {
+ super.onCreate()
+
+ listener = TunnelStateListener(this)
+ securedIcon = Icon.createWithResource(this, R.drawable.small_logo_white)
+ unsecuredIcon = Icon.createWithResource(this, R.drawable.small_logo_black)
+ }
+
+ override fun onStartListening() {
+ super.onStartListening()
+
+ listener.onStateChange = { state ->
+ secured = when (state) {
+ is TunnelState.Disconnected -> false
+ is TunnelState.Connecting -> true
+ is TunnelState.Connected -> true
+ is TunnelState.Disconnecting -> {
+ state.actionAfterDisconnect == ActionAfterDisconnect.Reconnect
+ }
+ is TunnelState.Error -> {
+ state.errorState.isBlocking
+ }
+ }
+ }
+
+ updateTileState()
+ }
+
+ override fun onClick() {
+ super.onClick()
+
+ val tunnelActionKey = if (secured) {
+ KEY_DISCONNECT_ACTION
+ } else {
+ KEY_CONNECT_ACTION
+ }
+
+ val intent = Intent(tunnelActionKey).setPackage("net.mullvad.mullvadvpn")
+
+ sendBroadcast(intent)
+ }
+
+ override fun onStopListening() {
+ super.onStartListening()
+
+ listener.onStateChange = null
+ }
+
+ private fun updateTileState() {
+ qsTile.apply {
+ if (secured) {
+ state = Tile.STATE_ACTIVE
+ icon = securedIcon
+ } else {
+ state = Tile.STATE_INACTIVE
+ icon = unsecuredIcon
+ }
+
+ updateTile()
+ }
+ }
+}
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 cdfdbac287..49a58662d1 100644
--- a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt
@@ -9,6 +9,7 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.dataproxy.ConnectionProxy
+import net.mullvad.mullvadvpn.service.tunnelstate.TunnelStateUpdater
import net.mullvad.talpid.TalpidVpnService
import net.mullvad.talpid.util.EventNotifier
@@ -29,6 +30,7 @@ class MullvadVpnService : TalpidVpnService() {
private var startDaemonJob: Job? = null
private lateinit var notificationManager: ForegroundNotificationManager
+ private lateinit var tunnelStateUpdater: TunnelStateUpdater
var shouldConnect = false
set(value) {
@@ -56,7 +58,10 @@ class MullvadVpnService : TalpidVpnService() {
override fun onCreate() {
super.onCreate()
+
notificationManager = ForegroundNotificationManager(this, serviceNotifier)
+ tunnelStateUpdater = TunnelStateUpdater(this, serviceNotifier)
+
setUp()
}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/Persistence.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/Persistence.kt
new file mode 100644
index 0000000000..f1366abc66
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/Persistence.kt
@@ -0,0 +1,57 @@
+package net.mullvad.mullvadvpn.service.tunnelstate
+
+import android.content.Context
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener
+import java.net.InetSocketAddress
+import net.mullvad.mullvadvpn.model.TunnelState
+import net.mullvad.talpid.net.Endpoint
+import net.mullvad.talpid.net.TransportProtocol
+import net.mullvad.talpid.net.TunnelEndpoint
+
+private const val SHARED_PREFERENCES = "tunnel_state"
+private const val KEY_TUNNEL_STATE = "tunnel_state"
+
+// TODO: Maybe replace using this with actually persisting the endpoint information
+private val dummyTunnelEndpoint = TunnelEndpoint(Endpoint(
+ InetSocketAddress.createUnresolved("dummy", 53),
+ TransportProtocol.Tcp
+))
+
+internal class Persistence(context: Context) {
+ val sharedPreferences =
+ context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE)
+
+ var state
+ get() = loadState()
+ set(value) {
+ persistState(value)
+ }
+
+ var listener: OnSharedPreferenceChangeListener? = null
+ set(value) {
+ if (value != field) {
+ if (field != null) {
+ sharedPreferences.unregisterOnSharedPreferenceChangeListener(field)
+ }
+
+ if (value != null) {
+ sharedPreferences.registerOnSharedPreferenceChangeListener(value)
+ }
+
+ field = value
+ }
+ }
+
+ private fun loadState(): TunnelState {
+ val description = sharedPreferences.getString(KEY_TUNNEL_STATE, TunnelState.DISCONNECTED)!!
+
+ return TunnelState.fromString(description, dummyTunnelEndpoint)
+ }
+
+ private fun persistState(state: TunnelState) {
+ sharedPreferences
+ .edit()
+ .putString(KEY_TUNNEL_STATE, state.toString())
+ .commit()
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateListener.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateListener.kt
new file mode 100644
index 0000000000..7f7832d3e4
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateListener.kt
@@ -0,0 +1,36 @@
+package net.mullvad.mullvadvpn.service.tunnelstate
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener
+import net.mullvad.mullvadvpn.model.TunnelState
+
+class TunnelStateListener(context: Context) {
+ private val persistence = Persistence(context)
+
+ private val listener = object : OnSharedPreferenceChangeListener {
+ override fun onSharedPreferenceChanged(preferences: SharedPreferences, key: String) {
+ state = persistence.state
+ }
+ }
+
+ var state = persistence.state
+ private set(value) {
+ if (field != value) {
+ field = value
+ onStateChange?.invoke(value)
+ }
+ }
+
+ var onStateChange: ((TunnelState) -> Unit)? = null
+ set(value) {
+ field = value
+
+ if (value == null) {
+ persistence.listener = null
+ } else {
+ persistence.listener = listener
+ state = persistence.state
+ }
+ }
+}
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateUpdater.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateUpdater.kt
new file mode 100644
index 0000000000..133c81506e
--- /dev/null
+++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/service/tunnelstate/TunnelStateUpdater.kt
@@ -0,0 +1,29 @@
+package net.mullvad.mullvadvpn.service.tunnelstate
+
+import android.content.Context
+import net.mullvad.mullvadvpn.dataproxy.ConnectionProxy
+import net.mullvad.mullvadvpn.service.ServiceInstance
+import net.mullvad.talpid.util.EventNotifier
+
+class TunnelStateUpdater(context: Context, serviceNotifier: EventNotifier<ServiceInstance?>) {
+ private val persistence = Persistence(context)
+
+ private var connectionProxy: ConnectionProxy? = null
+ private var stateSubscriptionId: Int? = null
+
+ init {
+ serviceNotifier.subscribe { serviceInstance ->
+ onNewServiceInstance(serviceInstance)
+ }
+ }
+
+ private fun onNewServiceInstance(serviceInstance: ServiceInstance?) {
+ stateSubscriptionId?.let { id -> connectionProxy?.onStateChange?.unsubscribe(id) }
+
+ connectionProxy = serviceInstance?.connectionProxy?.apply {
+ stateSubscriptionId = onStateChange.subscribe { newState ->
+ persistence.state = newState
+ }
+ }
+ }
+}
diff --git a/android/src/main/res/drawable-hdpi/notification.png b/android/src/main/res/drawable-hdpi/small_logo_black.png
index a6aa370201..a6aa370201 100644
--- a/android/src/main/res/drawable-hdpi/notification.png
+++ b/android/src/main/res/drawable-hdpi/small_logo_black.png
Binary files differ
diff --git a/android/src/main/res/drawable-hdpi/small_logo_white.png b/android/src/main/res/drawable-hdpi/small_logo_white.png
new file mode 100644
index 0000000000..e590941f60
--- /dev/null
+++ b/android/src/main/res/drawable-hdpi/small_logo_white.png
Binary files differ
diff --git a/android/src/main/res/drawable-mdpi/notification.png b/android/src/main/res/drawable-mdpi/small_logo_black.png
index d1a7dc4d5f..d1a7dc4d5f 100644
--- a/android/src/main/res/drawable-mdpi/notification.png
+++ b/android/src/main/res/drawable-mdpi/small_logo_black.png
Binary files differ
diff --git a/android/src/main/res/drawable-mdpi/small_logo_white.png b/android/src/main/res/drawable-mdpi/small_logo_white.png
new file mode 100644
index 0000000000..792c7746e8
--- /dev/null
+++ b/android/src/main/res/drawable-mdpi/small_logo_white.png
Binary files differ
diff --git a/android/src/main/res/drawable-xhdpi/notification.png b/android/src/main/res/drawable-xhdpi/small_logo_black.png
index 7ce258b490..7ce258b490 100644
--- a/android/src/main/res/drawable-xhdpi/notification.png
+++ b/android/src/main/res/drawable-xhdpi/small_logo_black.png
Binary files differ
diff --git a/android/src/main/res/drawable-xhdpi/small_logo_white.png b/android/src/main/res/drawable-xhdpi/small_logo_white.png
new file mode 100644
index 0000000000..21f3f411b3
--- /dev/null
+++ b/android/src/main/res/drawable-xhdpi/small_logo_white.png
Binary files differ
diff --git a/android/src/main/res/drawable-xxhdpi/notification.png b/android/src/main/res/drawable-xxhdpi/small_logo_black.png
index edebf879d3..edebf879d3 100644
--- a/android/src/main/res/drawable-xxhdpi/notification.png
+++ b/android/src/main/res/drawable-xxhdpi/small_logo_black.png
Binary files differ
diff --git a/android/src/main/res/drawable-xxhdpi/small_logo_white.png b/android/src/main/res/drawable-xxhdpi/small_logo_white.png
new file mode 100644
index 0000000000..0cfb2f889e
--- /dev/null
+++ b/android/src/main/res/drawable-xxhdpi/small_logo_white.png
Binary files differ
diff --git a/android/src/main/res/drawable-xxxhdpi/notification.png b/android/src/main/res/drawable-xxxhdpi/small_logo_black.png
index b2b88998cb..b2b88998cb 100644
--- a/android/src/main/res/drawable-xxxhdpi/notification.png
+++ b/android/src/main/res/drawable-xxxhdpi/small_logo_black.png
Binary files differ
diff --git a/android/src/main/res/drawable-xxxhdpi/small_logo_white.png b/android/src/main/res/drawable-xxxhdpi/small_logo_white.png
new file mode 100644
index 0000000000..18b6f286f0
--- /dev/null
+++ b/android/src/main/res/drawable-xxxhdpi/small_logo_white.png
Binary files differ