diff --git a/client/android/openvpn.tar.gz b/client/android/openvpn.tar.gz
deleted file mode 100644
index 92a6a613..00000000
Binary files a/client/android/openvpn.tar.gz and /dev/null differ
diff --git a/client/android/res/drawable/ic_amnezia_round.xml b/client/android/res/drawable/ic_amnezia_round.xml
index c3158ddd..32df5ca4 100644
--- a/client/android/res/drawable/ic_amnezia_round.xml
+++ b/client/android/res/drawable/ic_amnezia_round.xml
@@ -1,11 +1,10 @@
-
+ android:width="256dp"
+ android:height="256dp"
+ android:viewportWidth="256"
+ android:viewportHeight="256">
+ android:pathData="M121.6,3.7c-0.3,2.1 -0.8,9.1 -1.2,15.6 -0.3,6.4 -1,12 -1.5,12.3 -0.5,0.3 -0.9,1.3 -0.9,2.3 0,2.6 -4.8,7.1 -7.6,7.1 -1.3,-0 -2.4,0.4 -2.4,0.9 0,0.5 -1,1.1 -2.2,1.3 -1.6,0.2 -2.4,1.1 -2.6,3 -0.3,2.2 -0.9,2.7 -4.3,3.3 -2.1,0.4 -3.9,1 -3.9,1.5 0,0.4 -2.9,1.3 -6.5,2 -3.6,0.7 -6.5,1.6 -6.5,2.1 0,0.4 -2,1.1 -4.5,1.5 -4,0.6 -4.7,1.1 -6.5,4.8 -3.6,7.2 -5.1,9.6 -6,9.6 -0.5,-0 -1.7,1.4 -2.5,3.1 -1.4,2.5 -2.1,3 -4.7,2.7 -2.7,-0.3 -3.2,0.1 -4.6,3.6 -0.9,2.2 -3.1,5.3 -4.9,6.9 -1.8,1.6 -3.3,3.4 -3.3,4 0,0.5 -1.3,3.7 -2.9,7.1 -1.5,3.4 -3.1,7.1 -3.5,8.3 -0.4,1.3 -1.1,2.3 -1.5,2.3 -0.4,-0 -1.5,2.4 -2.6,5.2 -1,2.9 -2.1,5.7 -2.5,6.3 -0.4,0.5 -1.3,3.4 -2.1,6.2 -0.7,2.9 -1.6,5.3 -2,5.3 -1.1,-0 -3.1,15.4 -3.5,27.4l-0.4,11 -4.3,4.5c-2.4,2.6 -6.5,6.3 -9.2,8.4 -11.9,8.9 -13.2,11.3 -5.7,10.2 2.6,-0.4 5.2,-1.1 5.7,-1.5 1.6,-1.4 14.4,-3.2 16.9,-2.4 2.2,0.7 6.6,4.9 6.6,6.4 0,0.5 5.2,6.3 11.6,13l11.7,12.1 -0.6,9.6c-0.4,5.2 -1.1,10.1 -1.7,10.8 -1.2,1.4 -2.7,14.5 -1.6,14.5 0.3,-0 1.9,-1.1 3.5,-2.5 1.8,-1.7 3.4,-4.7 4.6,-8.8 0.9,-3.4 2.1,-7.1 2.6,-8.2 0.5,-1.1 1.3,-3.2 1.9,-4.8 0.5,-1.5 1.6,-2.7 2.5,-2.7 0.8,-0 1.5,-0.5 1.5,-1 0,-1.6 4.3,-1.2 8.6,0.9 12.6,6 15.3,7.1 17.7,7.1 1.5,-0 2.7,0.4 2.7,0.8 0,0.4 6.2,1.6 13.8,2.7 7.5,1 14.1,2.2 14.7,2.7 1.2,1.1 12.1,1 14.3,-0.1 0.9,-0.5 4.4,-1.5 7.7,-2.1 3.3,-0.7 7.8,-2 10,-3 2.3,-1 5.4,-2.2 7,-2.6 1.7,-0.4 3.5,-1 4.1,-1.5 1.5,-1.2 13.3,-3.9 17,-3.9 2.3,-0 3.3,0.5 3.7,1.9 0.2,1 1.7,2.5 3.2,3.2 1.8,1 3.4,3.1 4.9,6.7 2.3,5.7 8.7,13.2 11.2,13.2 1.5,-0 1.6,-1 0.9,-8.2 -0.4,-4.6 -1,-8.5 -1.4,-8.8 -0.8,-0.5 -2.6,-9.5 -3.7,-18.8 -0.7,-6 1.1,-15.1 3.2,-15.9 0.8,-0.3 1.4,-1.1 1.4,-1.8 0,-0.6 2.1,-3.7 4.8,-6.8 10.3,-12.3 9.6,-10.6 10.2,-25.8 0.3,-7.5 1.1,-14.3 1.7,-15 0.6,-0.8 1.4,-3.2 1.8,-5.4 0.6,-3.4 4.2,-10.3 7.4,-14.1 0.5,-0.6 1.2,-2.5 1.6,-4.2l0.7,-3.2 -6.8,-0 -6.7,-0 0.6,-4.7c0.3,-2.6 1,-4.9 1.6,-5.1 0.6,-0.2 1.1,-0.9 1.1,-1.5 0,-1.4 10.4,-11.7 11.8,-11.7 0.5,-0 1.6,-0.8 2.6,-1.9 0.9,-1 3.4,-2.6 5.6,-3.6 2.2,-1 4,-2.2 4,-2.7 0,-0.4 0.7,-0.8 1.5,-0.8 0.9,-0 1.5,-0.9 1.5,-2.1 0,-2 -0.3,-2.1 -5.7,-1.4 -3.2,0.4 -6.2,1 -6.8,1.5 -1.8,1.3 -14.5,3.3 -17.7,2.6 -1.7,-0.3 -4.1,-1.6 -5.2,-2.9 -3.9,-4 -10.4,-9.4 -12.2,-10.1 -1.3,-0.5 -1.5,-1.4 -1,-4.5 0.6,-4.1 -0.3,-5.1 -4.4,-5.1 -1.1,-0 -3.9,-1.9 -6.4,-4.3 -4.5,-4.3 -8,-7.2 -15.1,-12.6 -2.2,-1.7 -4.7,-3.6 -5.5,-4.3 -0.8,-0.7 -2.2,-1.8 -3,-2.3 -0.9,-0.6 -3.2,-2 -5.1,-3.2 -2.8,-1.9 -4.8,-2.3 -11.1,-2.3 -4.2,-0 -8,-0.5 -8.3,-1 -0.3,-0.6 -1.6,-1 -2.8,-1 -1.2,-0 -2.9,-0.6 -3.8,-1.3 -2.7,-2 -6.8,-10.9 -8,-17.2 -0.7,-3.3 -1.6,-6.5 -2,-7 -0.4,-0.6 -1.3,-3.3 -1.9,-6 -1.5,-7 -2.9,-9.5 -5.6,-9.5 -1.8,-0 -2.3,0.7 -2.8,3.7zM162.5,77.5c3.2,1.6 7.5,3.9 9.5,5.2 1.9,1.3 4.3,2.9 5.4,3.6 4.9,3.2 11.3,8.8 13.4,11.6 3.5,4.7 3.4,8.1 -0.3,10 -1.6,0.9 -3.5,2.1 -4.1,2.7 -0.7,0.6 -3.3,1.7 -5.8,2.4 -2.6,0.7 -4.9,1.7 -5.2,2.1 -0.3,0.5 -2.9,0.9 -5.9,0.9 -4.9,-0 -5.6,-0.3 -7.9,-3.3 -1.4,-1.8 -3.3,-5.3 -4.1,-7.8 -0.9,-2.4 -2.2,-5.8 -2.9,-7.4 -0.8,-1.7 -1.9,-5.7 -2.6,-9 -0.7,-3.3 -1.6,-6.2 -2,-6.5 -0.4,-0.3 -1,-2.2 -1.4,-4.4l-0.7,-3.9 4.3,0.5c2.4,0.3 7,1.8 10.3,3.3zM103,77.6c0,0.9 -0.6,3 -1.4,4.7 -1.2,2.7 -6.5,19.3 -8.3,26.2 -0.3,1.1 -0.9,2.4 -1.3,3 -0.4,0.5 -1.5,4.1 -2.5,8 -1,3.8 -2.1,7.4 -2.5,8 -0.4,0.5 -1.3,3.5 -2,6.5 -0.7,3 -1.6,5.7 -1.9,6 -0.3,0.3 -1.1,1.4 -1.7,2.4 -0.6,1.1 -1.9,2.4 -3,3 -1,0.6 -2.1,1.4 -2.4,1.7 -0.3,0.3 -2.2,1.2 -4.2,2 -2.1,0.7 -3.8,1.6 -3.8,2 0,0.4 -3.1,1.5 -7,2.5 -3.8,1 -7,2.2 -7,2.6 0,0.4 -1.9,0.8 -4.2,0.8l-4.1,-0 0.6,-9.8c0.9,-13 2.4,-18.3 8.4,-29.7 4.2,-7.9 9.4,-14.4 18.4,-22.9 11.2,-10.5 11.4,-10.6 12.8,-10.6 0.6,-0 1.4,-0.6 1.7,-1.3 0.2,-0.7 2.5,-2.2 4.9,-3.2 2.5,-1.1 4.5,-2.3 4.5,-2.8 0,-0.4 1.4,-0.7 3,-0.7 2.1,-0 3,0.5 3,1.6zM126.9,78.2c0.7,1.3 2.3,4.1 3.6,6.3 1.4,2.2 3.5,6 4.6,8.5 1.2,2.5 2.5,4.7 2.9,5 0.4,0.3 1.3,2.3 2,4.5 0.7,2.2 1.7,4.5 2.2,5.2 0.6,0.7 1.2,3.9 1.5,7.3 0.5,5.4 0.3,6.2 -1.8,8.4 -3.6,3.5 -9.8,6.3 -17,7.6 -3.5,0.6 -8.3,1.5 -10.7,2.1 -2.3,0.5 -5.4,0.9 -6.7,0.9 -2.8,-0 -4.3,-2.3 -2.8,-4.3 0.6,-0.6 1.6,-3.5 2.3,-6.2 0.7,-2.8 1.6,-5.5 2,-6 0.4,-0.6 1.5,-4.5 2.5,-8.8 1,-4.2 2.1,-7.7 2.5,-7.7 0.4,-0 1,-1.9 1.4,-4.2 0.4,-2.3 1.3,-5.6 2.1,-7.2 0.7,-1.7 1.9,-4.9 2.5,-7.1 2.1,-6.8 4.7,-8.4 6.9,-4.3zM207.6,123.1c0.8,1.3 0.7,26.1 -0.2,32.9 -0.3,2.5 -0.9,5.4 -1.4,6.5 -0.5,1.1 -1.3,4.8 -1.9,8.2 -0.7,3.4 -1.8,7.6 -2.7,9.5 -5.5,12.3 -7.1,13.3 -10.2,6.6 -5.1,-10.8 -7.1,-15.8 -7.7,-19.2 -0.4,-2 -1,-3.6 -1.4,-3.6 -0.4,-0 -1.3,-2.1 -2.1,-4.7 -0.7,-2.6 -1.9,-5.8 -2.6,-7.2 -1.6,-3.1 -3.9,-15.5 -3.1,-17.4 0.8,-2.1 14.2,-8.3 21.2,-9.8 3.3,-0.7 6.4,-1.6 7,-2.1 1.3,-1.2 4.3,-1 5.1,0.3zM156.2,145c3.7,2.9 5.5,7.4 6.7,17 0.8,6.4 1.9,10.1 4.5,15 7.8,14.8 9.8,19.6 10.3,24.7 0.7,6.6 -0.7,9.5 -5.3,11.3 -1.9,0.8 -3.4,1.7 -3.4,2.1 0,0.4 -2.1,1.5 -4.7,2.5 -2.7,1 -5,2.1 -5.3,2.5 -1.6,2.2 -7.7,2.9 -25.9,2.9 -19.7,-0 -28.9,-0.9 -34.6,-3.4 -1.6,-0.8 -4.5,-1.7 -6.3,-2.1 -1.8,-0.4 -5.2,-2.5 -7.5,-4.7l-4.2,-4 0.1,-8.9c0.1,-4.9 0.6,-9.5 1.2,-10.2 0.5,-0.6 1.3,-3 1.6,-5.1 1.1,-6.1 6.4,-14.5 13.6,-21.6 8,-7.8 8.8,-8.5 13.7,-10.7 2.1,-1 4,-2 4.3,-2.3 0.3,-0.3 2.5,-1.2 5,-1.9 2.5,-0.7 5.4,-1.7 6.5,-2.2 2.6,-1.2 13.3,-2.6 21.1,-2.8 4.7,-0.1 6.7,0.4 8.6,1.9zM68,173.6c0,3 -3.2,15.4 -4.1,15.9 -0.5,0.4 -0.9,1.6 -0.9,2.9 0,1.2 -0.6,3.1 -1.4,4.1 -1.4,1.9 -1.5,1.9 -4.9,0.1 -3.4,-1.9 -8.7,-10.3 -8.7,-13.8 0,-3.3 6.8,-8.8 10.9,-8.8 1.2,-0 2.1,-0.5 2.1,-1 0,-0.6 1.6,-1 3.5,-1 2.5,-0 3.5,0.4 3.5,1.6z"
+ android:fillColor="#000000"
+ android:strokeColor="#00000000"/>
diff --git a/client/android/src/debug/res/mipmap-anydpi-v26/vpnicon.xml b/client/android/src/debug/res/mipmap-anydpi-v26/vpnicon.xml
deleted file mode 100644
index 172bc624..00000000
--- a/client/android/src/debug/res/mipmap-anydpi-v26/vpnicon.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/client/android/src/debug/res/mipmap-anydpi-v26/vpnicon_round.xml b/client/android/src/debug/res/mipmap-anydpi-v26/vpnicon_round.xml
deleted file mode 100644
index 7e0a9c7b..00000000
--- a/client/android/src/debug/res/mipmap-anydpi-v26/vpnicon_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/client/android/src/debug/res/mipmap-hdpi/vpnicon.png b/client/android/src/debug/res/mipmap-hdpi/vpnicon.png
deleted file mode 100644
index fa762712..00000000
Binary files a/client/android/src/debug/res/mipmap-hdpi/vpnicon.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-hdpi/vpnicon_foreground.png b/client/android/src/debug/res/mipmap-hdpi/vpnicon_foreground.png
deleted file mode 100644
index 849f6ffd..00000000
Binary files a/client/android/src/debug/res/mipmap-hdpi/vpnicon_foreground.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-hdpi/vpnicon_round.png b/client/android/src/debug/res/mipmap-hdpi/vpnicon_round.png
deleted file mode 100644
index 0fc3bb7f..00000000
Binary files a/client/android/src/debug/res/mipmap-hdpi/vpnicon_round.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-mdpi/vpnicon.png b/client/android/src/debug/res/mipmap-mdpi/vpnicon.png
deleted file mode 100644
index b5d3fe52..00000000
Binary files a/client/android/src/debug/res/mipmap-mdpi/vpnicon.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-mdpi/vpnicon_foreground.png b/client/android/src/debug/res/mipmap-mdpi/vpnicon_foreground.png
deleted file mode 100644
index 198212fd..00000000
Binary files a/client/android/src/debug/res/mipmap-mdpi/vpnicon_foreground.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-mdpi/vpnicon_round.png b/client/android/src/debug/res/mipmap-mdpi/vpnicon_round.png
deleted file mode 100644
index d6ce6de9..00000000
Binary files a/client/android/src/debug/res/mipmap-mdpi/vpnicon_round.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-xhdpi/vpnicon.png b/client/android/src/debug/res/mipmap-xhdpi/vpnicon.png
deleted file mode 100644
index b6f63054..00000000
Binary files a/client/android/src/debug/res/mipmap-xhdpi/vpnicon.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-xhdpi/vpnicon_foreground.png b/client/android/src/debug/res/mipmap-xhdpi/vpnicon_foreground.png
deleted file mode 100644
index bc00fe12..00000000
Binary files a/client/android/src/debug/res/mipmap-xhdpi/vpnicon_foreground.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-xhdpi/vpnicon_round.png b/client/android/src/debug/res/mipmap-xhdpi/vpnicon_round.png
deleted file mode 100644
index ba4aa7b7..00000000
Binary files a/client/android/src/debug/res/mipmap-xhdpi/vpnicon_round.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-xxhdpi/vpnicon.png b/client/android/src/debug/res/mipmap-xxhdpi/vpnicon.png
deleted file mode 100644
index e6d3c7ed..00000000
Binary files a/client/android/src/debug/res/mipmap-xxhdpi/vpnicon.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-xxhdpi/vpnicon_foreground.png b/client/android/src/debug/res/mipmap-xxhdpi/vpnicon_foreground.png
deleted file mode 100644
index 4f281d05..00000000
Binary files a/client/android/src/debug/res/mipmap-xxhdpi/vpnicon_foreground.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-xxhdpi/vpnicon_round.png b/client/android/src/debug/res/mipmap-xxhdpi/vpnicon_round.png
deleted file mode 100644
index deb1e253..00000000
Binary files a/client/android/src/debug/res/mipmap-xxhdpi/vpnicon_round.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-xxxhdpi/vpnicon.png b/client/android/src/debug/res/mipmap-xxxhdpi/vpnicon.png
deleted file mode 100644
index be451734..00000000
Binary files a/client/android/src/debug/res/mipmap-xxxhdpi/vpnicon.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-xxxhdpi/vpnicon_foreground.png b/client/android/src/debug/res/mipmap-xxxhdpi/vpnicon_foreground.png
deleted file mode 100644
index 8b8bad14..00000000
Binary files a/client/android/src/debug/res/mipmap-xxxhdpi/vpnicon_foreground.png and /dev/null differ
diff --git a/client/android/src/debug/res/mipmap-xxxhdpi/vpnicon_round.png b/client/android/src/debug/res/mipmap-xxxhdpi/vpnicon_round.png
deleted file mode 100644
index ec01eaee..00000000
Binary files a/client/android/src/debug/res/mipmap-xxxhdpi/vpnicon_round.png and /dev/null differ
diff --git a/client/android/src/debug/res/values/vpnicon_background.xml b/client/android/src/debug/res/values/vpnicon_background.xml
deleted file mode 100644
index 5f343ea1..00000000
--- a/client/android/src/debug/res/values/vpnicon_background.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- #000000
-
\ No newline at end of file
diff --git a/client/android/src/org/amnezia/vpn/VPNService.kt b/client/android/src/org/amnezia/vpn/VPNService.kt
index b824610b..d6efc732 100644
--- a/client/android/src/org/amnezia/vpn/VPNService.kt
+++ b/client/android/src/org/amnezia/vpn/VPNService.kt
@@ -377,11 +377,11 @@ class VPNService : android.net.VpnService() {
val wgConfig: String = wireguard_conf!!.toWgUserspaceString()
val builder = Builder()
setupBuilder(wireguard_conf, builder)
- builder.setSession("mvpn0")
+ builder.setSession("avpn0")
builder.establish().use { tun ->
if (tun == null)return
Log.i(tag, "Go backend " + wgVersion())
- currentTunnelHandle = wgTurnOn("mvpn0", tun.detachFd(), wgConfig)
+ currentTunnelHandle = wgTurnOn("avpn0", tun.detachFd(), wgConfig)
}
if (currentTunnelHandle < 0) {
Log.e(tag, "Activation Error Code -> $currentTunnelHandle")
diff --git a/client/client.pro b/client/client.pro
index 69f081a2..de376dd2 100644
--- a/client/client.pro
+++ b/client/client.pro
@@ -39,6 +39,7 @@ HEADERS += \
managementserver.h \
protocols/protocols_defs.h \
settings.h \
+ ui/notificationhandler.h \
ui/models/containers_model.h \
ui/models/protocols_model.h \
ui/pages.h \
@@ -63,6 +64,7 @@ HEADERS += \
ui/pages_logic/protocols/ShadowSocksLogic.h \
ui/property_helper.h \
ui/models/servers_model.h \
+ ui/systemtray_notificationhandler.h \
ui/uilogic.h \
ui/qautostart.h \
ui/models/sites_model.h \
@@ -90,6 +92,7 @@ SOURCES += \
managementserver.cpp \
protocols/protocols_defs.cpp \
settings.cpp \
+ ui/notificationhandler.cpp \
ui/models/containers_model.cpp \
ui/models/protocols_model.cpp \
ui/pages_logic/AppSettingsLogic.cpp \
@@ -112,6 +115,7 @@ SOURCES += \
ui/pages_logic/protocols/PageProtocolLogicBase.cpp \
ui/pages_logic/protocols/ShadowSocksLogic.cpp \
ui/models/servers_model.cpp \
+ ui/systemtray_notificationhandler.cpp \
ui/uilogic.cpp \
ui/qautostart.cpp \
ui/models/sites_model.cpp \
@@ -199,9 +203,16 @@ android {
INCLUDEPATH += platforms/android
- HEADERS += protocols/android_vpnprotocol.h \
+ HEADERS += \
+ platforms/android/android_controller.h \
+ platforms/android/android_notificationhandler.h \
+ protocols/android_vpnprotocol.h
+
+ SOURCES += \
+ platforms/android/android_controller.cpp \
+ platforms/android/android_notificationhandler.cpp \
+ protocols/android_vpnprotocol.cpp
- SOURCES += protocols/android_vpnprotocol.cpp \
DISTFILES += \
android/AndroidManifest.xml \
diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp
index 9375cd1d..06479316 100644
--- a/client/core/servercontroller.cpp
+++ b/client/core/servercontroller.cpp
@@ -1,6 +1,7 @@
#include "servercontroller.h"
#include
+#include
#include
#include
#include
@@ -10,6 +11,7 @@
#include
#include
#include
+#include
#include "sftpchannel.h"
#include "sshconnectionmanager.h"
@@ -134,12 +136,7 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
{
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
- QString mkdir = "sudo docker exec -i $CONTAINER_NAME mkdir -p /opt/amnezia/";
- ErrorCode e = runScript(credentials,
- replaceVars(mkdir, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
- if (e) return e;
-
- e = uploadTextFileToContainer(container, credentials, script, fileName);
+ ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
if (e) return e;
QString runner = QString("sudo docker exec -i $CONTAINER_NAME bash %1 ").arg(fileName);
@@ -167,6 +164,14 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
stdOut += data + "\n";
};
+ // mkdir
+ QFileInfo fi(path);
+ QString mkdir = "sudo docker exec -i $CONTAINER_NAME mkdir -p " + fi.absoluteDir().absolutePath();
+ e = runScript(credentials,
+ replaceVars(mkdir, genVarsForScript(credentials, container)));
+ if (e) return e;
+
+
if (overwriteMode == QSsh::SftpOverwriteMode::SftpOverwriteExisting) {
e = runScript(credentials,
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
diff --git a/client/images/icon_src.svg b/client/images/icon_src.svg
new file mode 100644
index 00000000..b27d1360
--- /dev/null
+++ b/client/images/icon_src.svg
@@ -0,0 +1,67 @@
+
+
+
diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp
new file mode 100644
index 00000000..6b2c1aa0
--- /dev/null
+++ b/client/platforms/android/android_controller.cpp
@@ -0,0 +1,334 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "android_controller.h"
+#include "core/errorstrings.h"
+
+// Binder Codes for VPNServiceBinder
+// See also - VPNServiceBinder.kt
+// Actions that are Requestable
+const int ACTION_ACTIVATE = 1;
+const int ACTION_DEACTIVATE = 2;
+const int ACTION_REGISTERLISTENER = 3;
+const int ACTION_REQUEST_STATISTIC = 4;
+const int ACTION_REQUEST_GET_LOG = 5;
+const int ACTION_REQUEST_CLEANUP_LOG = 6;
+const int ACTION_RESUME_ACTIVATE = 7;
+const int ACTION_SET_NOTIFICATION_TEXT = 8;
+const int ACTION_SET_NOTIFICATION_FALLBACK = 9;
+
+// Event Types that will be Dispatched after registration
+const int EVENT_INIT = 0;
+const int EVENT_CONNECTED = 1;
+const int EVENT_DISCONNECTED = 2;
+const int EVENT_STATISTIC_UPDATE = 3;
+const int EVENT_BACKEND_LOGS = 4;
+const int EVENT_ACTIVATION_ERROR = 5;
+
+namespace {
+AndroidController* s_instance = nullptr;
+
+constexpr auto PERMISSIONHELPER_CLASS =
+ "org/amnezia/vpn/qt/VPNPermissionHelper";
+
+} // namespace
+
+AndroidController::AndroidController():
+ m_binder(this)
+{
+
+}
+
+AndroidController* AndroidController::instance() {
+ if (!s_instance) s_instance = new AndroidController();
+ return s_instance;
+}
+
+bool AndroidController::initialize()
+{
+ qDebug() << "Initializing";
+
+ // Hook in the native implementation for startActivityForResult into the JNI
+ JNINativeMethod methods[]{{"startActivityForResult",
+ "(Landroid/content/Intent;)V",
+ reinterpret_cast(startActivityForResult)}};
+ QAndroidJniObject javaClass(PERMISSIONHELPER_CLASS);
+ QAndroidJniEnvironment env;
+ jclass objectClass = env->GetObjectClass(javaClass.object());
+ env->RegisterNatives(objectClass, methods,
+ sizeof(methods) / sizeof(methods[0]));
+ env->DeleteLocalRef(objectClass);
+
+ auto appContext = QtAndroid::androidActivity().callObjectMethod(
+ "getApplicationContext", "()Landroid/content/Context;");
+
+ QAndroidJniObject::callStaticMethod(
+ "org/amnezia/vpn/VPNService", "startService",
+ "(Landroid/content/Context;)V", appContext.object());
+
+ // Start the VPN Service (if not yet) and Bind to it
+ const bool bindResult = QtAndroid::bindService(
+ QAndroidIntent(appContext.object(), "org.amnezia.vpn.VPNService"),
+ *this, QtAndroid::BindFlag::AutoCreate);
+ qDebug() << "Binding to the service..." << bindResult;
+
+ return bindResult;
+}
+
+ErrorCode AndroidController::start()
+{
+
+ //qDebug().noquote() << "AndroidController::start" << QJsonDocument(m_rawConfig).toJson();
+ qDebug() << "Prompting for VPN permission";
+ auto appContext = QtAndroid::androidActivity().callObjectMethod(
+ "getApplicationContext", "()Landroid/content/Context;");
+ QAndroidJniObject::callStaticMethod(
+ PERMISSIONHELPER_CLASS, "startService", "(Landroid/content/Context;)V",
+ appContext.object());
+
+ QAndroidParcel sendData;
+ sendData.writeData(QJsonDocument(m_vpnConfig).toJson());
+ bool activateResult = false;
+ while (!activateResult){
+ activateResult = m_serviceBinder.transact(ACTION_ACTIVATE, sendData, nullptr);
+ }
+
+ return activateResult ? NoError : UnknownError;
+}
+
+void AndroidController::stop() {
+ qDebug() << "AndroidController::stop";
+
+// if (reason != ReasonNone) {
+// // Just show that we're disconnected
+// // we're doing the actual disconnect once
+// // the vpn-service has the new server ready in Action->Activate
+// emit disconnected();
+// qCritical() << "deactivation skipped for Switching";
+// return;
+// }
+
+ QAndroidParcel nullData;
+ m_serviceBinder.transact(ACTION_DEACTIVATE, nullData, nullptr);
+}
+
+// Activates the tunnel that is currently set
+// in the VPN Service
+void AndroidController::resumeStart() {
+ QAndroidParcel nullData;
+ m_serviceBinder.transact(ACTION_RESUME_ACTIVATE, nullData, nullptr);
+}
+
+/*
+ * Sets the current notification text that is shown
+ */
+void AndroidController::setNotificationText(const QString& title,
+ const QString& message,
+ int timerSec) {
+ QJsonObject args;
+ args["title"] = title;
+ args["message"] = message;
+ args["sec"] = timerSec;
+ QJsonDocument doc(args);
+ QAndroidParcel data;
+ data.writeData(doc.toJson());
+ m_serviceBinder.transact(ACTION_SET_NOTIFICATION_TEXT, data, nullptr);
+}
+
+/*
+ * Sets fallback Notification text that should be shown in case the VPN
+ * switches into the Connected state without the app open
+ * e.g via always-on vpn
+ */
+void AndroidController::setFallbackConnectedNotification() {
+ QJsonObject args;
+ args["title"] = tr("AmneziaVPN");
+ //% "Ready for you to connect"
+ //: Refers to the app - which is currently running the background and waiting
+ args["message"] = tr("VPN Connected");
+ QJsonDocument doc(args);
+ QAndroidParcel data;
+ data.writeData(doc.toJson());
+ m_serviceBinder.transact(ACTION_SET_NOTIFICATION_FALLBACK, data, nullptr);
+}
+
+void AndroidController::checkStatus() {
+ qDebug() << "check status";
+
+ QAndroidParcel nullParcel;
+ m_serviceBinder.transact(ACTION_REQUEST_STATISTIC, nullParcel, nullptr);
+}
+
+void AndroidController::getBackendLogs(std::function&& a_callback) {
+ qDebug() << "get logs";
+
+ m_logCallback = std::move(a_callback);
+ QAndroidParcel nullData, replyData;
+ m_serviceBinder.transact(ACTION_REQUEST_GET_LOG, nullData, &replyData);
+}
+
+void AndroidController::cleanupBackendLogs() {
+ qDebug() << "cleanup logs";
+
+ QAndroidParcel nullParcel;
+ m_serviceBinder.transact(ACTION_REQUEST_CLEANUP_LOG, nullParcel, nullptr);
+}
+
+void AndroidController::onServiceConnected(
+ const QString& name, const QAndroidBinder& serviceBinder) {
+ qDebug() << "Server " + name + " connected";
+
+ Q_UNUSED(name);
+
+ m_serviceBinder = serviceBinder;
+
+ // Send the Service our Binder to recive incoming Events
+ QAndroidParcel binderParcel;
+ binderParcel.writeBinder(m_binder);
+ m_serviceBinder.transact(ACTION_REGISTERLISTENER, binderParcel, nullptr);
+}
+
+void AndroidController::onServiceDisconnected(const QString& name) {
+ qDebug() << "Server disconnected";
+ m_serviceConnected = false;
+ Q_UNUSED(name);
+ // TODO: Maybe restart? Or crash?
+}
+
+const QJsonObject &AndroidController::vpnConfig() const
+{
+ return m_vpnConfig;
+}
+
+void AndroidController::setVpnConfig(const QJsonObject &newVpnConfig)
+{
+ m_vpnConfig = newVpnConfig;
+}
+
+
+/**
+ * @brief AndroidController::VPNBinder::onTransact
+ * @param code the Event-Type we get From the VPNService See
+ * @param data - Might contain UTF-8 JSON in case the Event has a payload
+ * @param reply - always null
+ * @param flags - unused
+ * @return Returns true is the code was a valid Event Code
+ */
+bool AndroidController::VPNBinder::onTransact(int code,
+ const QAndroidParcel& data,
+ const QAndroidParcel& reply,
+ QAndroidBinder::CallType flags) {
+ Q_UNUSED(data);
+ Q_UNUSED(reply);
+ Q_UNUSED(flags);
+
+ QJsonDocument doc;
+ QString buffer;
+ switch (code) {
+ case EVENT_INIT:
+ qDebug() << "Transact: init";
+ doc = QJsonDocument::fromJson(data.readData());
+ emit m_controller->initialized(
+ true, doc.object()["connected"].toBool(),
+ QDateTime::fromMSecsSinceEpoch(
+ doc.object()["time"].toVariant().toLongLong()));
+ // Pass a localised version of the Fallback string for the Notification
+ m_controller->setFallbackConnectedNotification();
+
+ break;
+ case EVENT_CONNECTED:
+ qDebug() << "Transact: connected";
+ emit m_controller->connectionStateChanged(VpnProtocol::Connected);
+ break;
+ case EVENT_DISCONNECTED:
+ qDebug() << "Transact: disconnected";
+ emit m_controller->connectionStateChanged(VpnProtocol::Disconnected);
+ break;
+ case EVENT_STATISTIC_UPDATE:
+ qDebug() << "Transact:: update";
+
+ // Data is here a JSON String
+ doc = QJsonDocument::fromJson(data.readData());
+ // TODO update counters
+// emit m_controller->statusUpdated(doc.object()["endpoint"].toString(),
+// doc.object()["deviceIpv4"].toString(),
+// doc.object()["totalTX"].toInt(),
+// doc.object()["totalRX"].toInt());
+ break;
+ case EVENT_BACKEND_LOGS:
+ qDebug() << "Transact: backend logs";
+
+ buffer = readUTF8Parcel(data);
+ if (m_controller->m_logCallback) {
+ m_controller->m_logCallback(buffer);
+ }
+ break;
+ case EVENT_ACTIVATION_ERROR:
+ qDebug() << "Transact: error";
+ emit m_controller->connectionStateChanged(VpnProtocol::Error);
+
+ default:
+ qWarning() << "Transact: Invalid!";
+ break;
+ }
+
+ return true;
+}
+
+QString AndroidController::VPNBinder::readUTF8Parcel(QAndroidParcel data) {
+ // 106 is the Code for UTF-8
+ return QTextCodec::codecForMib(106)->toUnicode(data.readData());
+}
+
+const int ACTIVITY_RESULT_OK = 0xffffffff;
+/**
+ * @brief Starts the Given intent in Context of the QTActivity
+ * @param env
+ * @param intent
+ */
+void AndroidController::startActivityForResult(JNIEnv *env, jobject, jobject intent)
+{
+ qDebug() << "start activity";
+ Q_UNUSED(env);
+ QtAndroid::startActivity(intent, 1337,
+ [](int receiverRequestCode, int resultCode,
+ const QAndroidJniObject& data) {
+ // Currently this function just used in
+ // VPNService.kt::checkPersmissions. So the result
+ // we're getting is if the User gave us the
+ // Vpn.bind permission. In case of NO we should
+ // abort.
+ Q_UNUSED(receiverRequestCode);
+ Q_UNUSED(data);
+
+ AndroidController* controller =
+ AndroidController::instance();
+ if (!controller) {
+ return;
+ }
+
+ if (resultCode == ACTIVITY_RESULT_OK) {
+ qDebug() << "VPN PROMPT RESULT - Accepted";
+ controller->resumeStart();
+ return;
+ }
+ // If the request got rejected abort the current
+ // connection.
+ qWarning() << "VPN PROMPT RESULT - Rejected";
+ emit controller->connectionStateChanged(VpnProtocol::Disconnected);
+ });
+ return;
+}
diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h
new file mode 100644
index 00000000..97b088c7
--- /dev/null
+++ b/client/platforms/android/android_controller.h
@@ -0,0 +1,84 @@
+#ifndef ANDROID_CONTROLLER_H
+#define ANDROID_CONTROLLER_H
+
+#include
+#include
+
+#include "protocols/vpnprotocol.h"
+using namespace amnezia;
+
+
+
+class AndroidController : public QObject, public QAndroidServiceConnection
+{
+ Q_OBJECT
+
+public:
+ explicit AndroidController();
+ static AndroidController* instance();
+
+ virtual ~AndroidController() override = default;
+
+ bool initialize();
+
+ ErrorCode start();
+ void stop();
+ void resumeStart();
+
+ void checkStatus();
+ void setNotificationText(const QString& title, const QString& message, int timerSec);
+ void setFallbackConnectedNotification();
+ void getBackendLogs(std::function&& callback);
+ void cleanupBackendLogs();
+
+ // from QAndroidServiceConnection
+ void onServiceConnected(const QString& name, const QAndroidBinder& serviceBinder) override;
+ void onServiceDisconnected(const QString& name) override;
+
+ const QJsonObject &vpnConfig() const;
+ void setVpnConfig(const QJsonObject &newVpnConfig);
+
+signals:
+ void connectionStateChanged(VpnProtocol::ConnectionState state);
+
+ // This signal is emitted when the controller is initialized. Note that the
+ // VPN tunnel can be already active. In this case, "connected" should be set
+ // to true and the "connectionDate" should be set to the activation date if
+ // known.
+ // If "status" is set to false, the backend service is considered unavailable.
+ void initialized(bool status, bool connected,
+ const QDateTime& connectionDate);
+
+protected slots:
+
+protected:
+
+
+private:
+ //Protocol m_protocol;
+ QJsonObject m_vpnConfig;
+
+ bool m_serviceConnected = false;
+ std::function m_logCallback;
+
+ QAndroidBinder m_serviceBinder;
+ class VPNBinder : public QAndroidBinder {
+ public:
+ VPNBinder(AndroidController* controller) : m_controller(controller) {}
+
+ bool onTransact(int code, const QAndroidParcel& data,
+ const QAndroidParcel& reply,
+ QAndroidBinder::CallType flags) override;
+
+ QString readUTF8Parcel(QAndroidParcel data);
+
+ private:
+ AndroidController* m_controller = nullptr;
+ };
+
+ VPNBinder m_binder;
+
+ static void startActivityForResult(JNIEnv* env, jobject /*thiz*/, jobject intent);
+};
+
+#endif // ANDROID_CONTROLLER_H
diff --git a/client/platforms/android/android_notificationhandler.cpp b/client/platforms/android/android_notificationhandler.cpp
new file mode 100644
index 00000000..2bd26b7f
--- /dev/null
+++ b/client/platforms/android/android_notificationhandler.cpp
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "android_notificationhandler.h"
+#include "platforms/android/android_controller.h"
+
+AndroidNotificationHandler::AndroidNotificationHandler(QObject* parent)
+ : NotificationHandler(parent) {
+}
+AndroidNotificationHandler::~AndroidNotificationHandler() {
+}
+
+void AndroidNotificationHandler::notify(NotificationHandler::Message type,
+ const QString& title,
+ const QString& message, int timerMsec) {
+ Q_UNUSED(type);
+ qDebug() << "Send notification - " << message;
+ AndroidController::instance()->setNotificationText(title, message,
+ timerMsec / 1000);
+}
diff --git a/client/platforms/android/android_notificationhandler.h b/client/platforms/android/android_notificationhandler.h
new file mode 100644
index 00000000..e3e7325e
--- /dev/null
+++ b/client/platforms/android/android_notificationhandler.h
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ANDROIDNOTIFICATIONHANDLER_H
+#define ANDROIDNOTIFICATIONHANDLER_H
+
+#include "ui/notificationhandler.h"
+
+#include
+
+class AndroidNotificationHandler final : public NotificationHandler {
+ Q_DISABLE_COPY_MOVE(AndroidNotificationHandler)
+
+ public:
+ AndroidNotificationHandler(QObject* parent);
+ ~AndroidNotificationHandler();
+
+ protected:
+ void notify(Message type, const QString& title, const QString& message,
+ int timerMsec) override;
+};
+
+#endif // ANDROIDNOTIFICATIONHANDLER_H
diff --git a/client/protocols/android_vpnprotocol.cpp b/client/protocols/android_vpnprotocol.cpp
index 0b986ab5..41818dd5 100644
--- a/client/protocols/android_vpnprotocol.cpp
+++ b/client/protocols/android_vpnprotocol.cpp
@@ -17,338 +17,25 @@
#include "android_vpnprotocol.h"
#include "core/errorstrings.h"
-// Binder Codes for VPNServiceBinder
-// See also - VPNServiceBinder.kt
-// Actions that are Requestable
-const int ACTION_ACTIVATE = 1;
-const int ACTION_DEACTIVATE = 2;
-const int ACTION_REGISTERLISTENER = 3;
-const int ACTION_REQUEST_STATISTIC = 4;
-const int ACTION_REQUEST_GET_LOG = 5;
-const int ACTION_REQUEST_CLEANUP_LOG = 6;
-const int ACTION_RESUME_ACTIVATE = 7;
-const int ACTION_SET_NOTIFICATION_TEXT = 8;
-const int ACTION_SET_NOTIFICATION_FALLBACK = 9;
+#include "platforms/android/android_controller.h"
-// Event Types that will be Dispatched after registration
-const int EVENT_INIT = 0;
-const int EVENT_CONNECTED = 1;
-const int EVENT_DISCONNECTED = 2;
-const int EVENT_STATISTIC_UPDATE = 3;
-const int EVENT_BACKEND_LOGS = 4;
-const int EVENT_ACTIVATION_ERROR = 5;
-
-namespace {
-AndroidVpnProtocol* s_instance = nullptr;
-
-constexpr auto PERMISSIONHELPER_CLASS =
- "org/amnezia/vpn/qt/VPNPermissionHelper";
-
-} // namespace
AndroidVpnProtocol::AndroidVpnProtocol(Protocol protocol, const QJsonObject &configuration, QObject* parent)
: VpnProtocol(configuration, parent),
- m_protocol(protocol),
- m_binder(this)
+ m_protocol(protocol)
{
}
-AndroidVpnProtocol* AndroidVpnProtocol::instance() {
- return s_instance;
-}
-
-bool AndroidVpnProtocol::initialize()
-{
- qDebug() << "Initializing";
-
- // Hook in the native implementation for startActivityForResult into the JNI
- JNINativeMethod methods[]{{"startActivityForResult",
- "(Landroid/content/Intent;)V",
- reinterpret_cast(startActivityForResult)}};
- QAndroidJniObject javaClass(PERMISSIONHELPER_CLASS);
- QAndroidJniEnvironment env;
- jclass objectClass = env->GetObjectClass(javaClass.object());
- env->RegisterNatives(objectClass, methods,
- sizeof(methods) / sizeof(methods[0]));
- env->DeleteLocalRef(objectClass);
-
- auto appContext = QtAndroid::androidActivity().callObjectMethod(
- "getApplicationContext", "()Landroid/content/Context;");
-
- QAndroidJniObject::callStaticMethod(
- "org/amnezia/vpn/VPNService", "startService",
- "(Landroid/content/Context;)V", appContext.object());
-
- // Start the VPN Service (if not yet) and Bind to it
- const bool bindResult = QtAndroid::bindService(
- QAndroidIntent(appContext.object(), "org.amnezia.vpn.VPNService"),
- *this, QtAndroid::BindFlag::AutoCreate);
- qDebug() << "Binding to the service..." << bindResult;
-
- return bindResult;
-}
-
ErrorCode AndroidVpnProtocol::start()
{
-
- //qDebug().noquote() << "AndroidVpnProtocol::start" << QJsonDocument(m_rawConfig).toJson();
- qDebug() << "Prompting for VPN permission";
- auto appContext = QtAndroid::androidActivity().callObjectMethod(
- "getApplicationContext", "()Landroid/content/Context;");
- QAndroidJniObject::callStaticMethod(
- PERMISSIONHELPER_CLASS, "startService", "(Landroid/content/Context;)V",
- appContext.object());
-
-
- // QJsonObject jServer;
- // jServer["ipv4AddrIn"] = server.ipv4AddrIn();
- // jServer["ipv4Gateway"] = server.ipv4Gateway();
- // jServer["ipv6AddrIn"] = server.ipv6AddrIn();
- // jServer["ipv6Gateway"] = server.ipv6Gateway();
- // jServer["publicKey"] = server.publicKey();
- // jServer["port"] = (int)server.choosePort();
-
- // QJsonArray allowedIPs;
- // foreach (auto item, allowedIPAddressRanges) {
- // QJsonValue val;
- // val = item.toString();
- // allowedIPs.append(val);
- // }
-
- // QJsonArray excludedApps;
- // foreach (auto appID, vpnDisabledApps) {
- // excludedApps.append(QJsonValue(appID));
- // }
-
- // QJsonObject args;
- // args["device"] = jDevice;
- // args["keys"] = jKeys;
- // args["server"] = jServer;
- // args["reason"] = (int)reason;
- // args["allowedIPs"] = allowedIPs;
- // args["excludedApps"] = excludedApps;
- // args["dns"] = dns.toString();
-
- QAndroidParcel sendData;
- sendData.writeData(QJsonDocument(m_rawConfig).toJson());
- bool activateResult = false;
- while (!activateResult){
- activateResult = m_serviceBinder.transact(ACTION_ACTIVATE, sendData, nullptr);
- }
-
- return activateResult ? NoError : UnknownError;
+ AndroidController::instance()->setVpnConfig(m_rawConfig);
+ return AndroidController::instance()->start();
}
-// Activates the tunnel that is currently set
-// in the VPN Service
-void AndroidVpnProtocol::resume_start() {
- QAndroidParcel nullData;
- m_serviceBinder.transact(ACTION_RESUME_ACTIVATE, nullData, nullptr);
-}
-
-void AndroidVpnProtocol::stop() {
- qDebug() << "deactivation";
-
-// if (reason != ReasonNone) {
-// // Just show that we're disconnected
-// // we're doing the actual disconnect once
-// // the vpn-service has the new server ready in Action->Activate
-// emit disconnected();
-// logger.warning() << "deactivation skipped for Switching";
-// return;
-// }
-
- QAndroidParcel nullData;
- m_serviceBinder.transact(ACTION_DEACTIVATE, nullData, nullptr);
-}
-
-/*
- * Sets the current notification text that is shown
- */
-void AndroidVpnProtocol::setNotificationText(const QString& title,
- const QString& message,
- int timerSec) {
- QJsonObject args;
- args["title"] = title;
- args["message"] = message;
- args["sec"] = timerSec;
- QJsonDocument doc(args);
- QAndroidParcel data;
- data.writeData(doc.toJson());
- m_serviceBinder.transact(ACTION_SET_NOTIFICATION_TEXT, data, nullptr);
-}
-
-/*
- * Sets fallback Notification text that should be shown in case the VPN
- * switches into the Connected state without the app open
- * e.g via always-on vpn
- */
-void AndroidVpnProtocol::setFallbackConnectedNotification() {
- QJsonObject args;
- args["title"] = qtTrId("vpn.main.productName");
- //% "Ready for you to connect"
- //: Refers to the app - which is currently running the background and waiting
- args["message"] = qtTrId("vpn.android.notification.isIDLE");
- QJsonDocument doc(args);
- QAndroidParcel data;
- data.writeData(doc.toJson());
- m_serviceBinder.transact(ACTION_SET_NOTIFICATION_FALLBACK, data, nullptr);
-}
-
-void AndroidVpnProtocol::checkStatus() {
- qDebug() << "check status";
-
- QAndroidParcel nullParcel;
- m_serviceBinder.transact(ACTION_REQUEST_STATISTIC, nullParcel, nullptr);
-}
-
-void AndroidVpnProtocol::getBackendLogs(std::function&& a_callback) {
- qDebug() << "get logs";
-
- m_logCallback = std::move(a_callback);
- QAndroidParcel nullData, replyData;
- m_serviceBinder.transact(ACTION_REQUEST_GET_LOG, nullData, &replyData);
-}
-
-void AndroidVpnProtocol::cleanupBackendLogs() {
- qDebug() << "cleanup logs";
-
- QAndroidParcel nullParcel;
- m_serviceBinder.transact(ACTION_REQUEST_CLEANUP_LOG, nullParcel, nullptr);
-}
-
-void AndroidVpnProtocol::onServiceConnected(
- const QString& name, const QAndroidBinder& serviceBinder) {
- qDebug() << "Server " + name + " connected";
-
- Q_UNUSED(name);
-
- m_serviceBinder = serviceBinder;
-
- // Send the Service our Binder to recive incoming Events
- QAndroidParcel binderParcel;
- binderParcel.writeBinder(m_binder);
- m_serviceBinder.transact(ACTION_REGISTERLISTENER, binderParcel, nullptr);
-}
-
-void AndroidVpnProtocol::onServiceDisconnected(const QString& name) {
- qDebug() << "Server disconnected";
- m_serviceConnected = false;
- Q_UNUSED(name);
- // TODO: Maybe restart? Or crash?
-}
-
-
-/**
- * @brief AndroidController::VPNBinder::onTransact
- * @param code the Event-Type we get From the VPNService See
- * @param data - Might contain UTF-8 JSON in case the Event has a payload
- * @param reply - always null
- * @param flags - unused
- * @return Returns true is the code was a valid Event Code
- */
-bool AndroidVpnProtocol::VPNBinder::onTransact(int code,
- const QAndroidParcel& data,
- const QAndroidParcel& reply,
- QAndroidBinder::CallType flags) {
- Q_UNUSED(data);
- Q_UNUSED(reply);
- Q_UNUSED(flags);
-
- QJsonDocument doc;
- QString buffer;
- switch (code) {
- case EVENT_INIT:
- qDebug() << "Transact: init";
- doc = QJsonDocument::fromJson(data.readData());
- emit m_controller->initialized(
- true, doc.object()["connected"].toBool(),
- QDateTime::fromMSecsSinceEpoch(
- doc.object()["time"].toVariant().toLongLong()));
- // Pass a localised version of the Fallback string for the Notification
- m_controller->setFallbackConnectedNotification();
-
- break;
- case EVENT_CONNECTED:
- qDebug() << "Transact: connected";
- m_controller->setConnectionState(Connected);
- break;
- case EVENT_DISCONNECTED:
- qDebug() << "Transact: disconnected";
- m_controller->setConnectionState(Disconnected);
- break;
- case EVENT_STATISTIC_UPDATE:
- qDebug() << "Transact:: update";
-
- // Data is here a JSON String
- doc = QJsonDocument::fromJson(data.readData());
- // TODO update counters
-// emit m_controller->statusUpdated(doc.object()["endpoint"].toString(),
-// doc.object()["deviceIpv4"].toString(),
-// doc.object()["totalTX"].toInt(),
-// doc.object()["totalRX"].toInt());
- break;
- case EVENT_BACKEND_LOGS:
- qDebug() << "Transact: backend logs";
-
- buffer = readUTF8Parcel(data);
- if (m_controller->m_logCallback) {
- m_controller->m_logCallback(buffer);
- }
- break;
- case EVENT_ACTIVATION_ERROR:
- m_controller->setConnectionState(Error);
- default:
- qWarning() << "Transact: Invalid!";
- break;
- }
-
- return true;
-}
-
-QString AndroidVpnProtocol::VPNBinder::readUTF8Parcel(QAndroidParcel data) {
- // 106 is the Code for UTF-8
- return QTextCodec::codecForMib(106)->toUnicode(data.readData());
-}
-
-const int ACTIVITY_RESULT_OK = 0xffffffff;
-/**
- * @brief Starts the Given intent in Context of the QTActivity
- * @param env
- * @param intent
- */
-void AndroidVpnProtocol::startActivityForResult(JNIEnv *env, jobject, jobject intent)
+void AndroidVpnProtocol::stop()
{
- qDebug() << "start activity";
- Q_UNUSED(env);
- QtAndroid::startActivity(intent, 1337,
- [](int receiverRequestCode, int resultCode,
- const QAndroidJniObject& data) {
- // Currently this function just used in
- // VPNService.kt::checkPersmissions. So the result
- // we're getting is if the User gave us the
- // Vpn.bind permission. In case of NO we should
- // abort.
- Q_UNUSED(receiverRequestCode);
- Q_UNUSED(data);
-
- AndroidVpnProtocol* controller =
- AndroidVpnProtocol::instance();
- if (!controller) {
- return;
- }
-
- if (resultCode == ACTIVITY_RESULT_OK) {
- qDebug() << "VPN PROMPT RESULT - Accepted";
- controller->resume_start();
- return;
- }
- // If the request got rejected abort the current
- // connection.
- qWarning() << "VPN PROMPT RESULT - Rejected";
- controller->setConnectionState(Disconnected);
-
- });
- return;
+ qDebug() << "AndroidVpnProtocol::stop()";
+ AndroidController::instance()->stop();
}
+
diff --git a/client/protocols/android_vpnprotocol.h b/client/protocols/android_vpnprotocol.h
index 23365eee..ede6ffd8 100644
--- a/client/protocols/android_vpnprotocol.h
+++ b/client/protocols/android_vpnprotocol.h
@@ -11,38 +11,16 @@ using namespace amnezia;
-class AndroidVpnProtocol : public VpnProtocol,
- public QAndroidServiceConnection
+class AndroidVpnProtocol : public VpnProtocol
{
Q_OBJECT
public:
explicit AndroidVpnProtocol(Protocol protocol, const QJsonObject& configuration, QObject* parent = nullptr);
- static AndroidVpnProtocol* instance();
-
virtual ~AndroidVpnProtocol() override = default;
- bool initialize();
-
- virtual ErrorCode start() override;
- virtual void stop() override;
-
- void resume_start();
-
- void checkStatus();
-
- void setNotificationText(const QString& title, const QString& message,
- int timerSec);
- void setFallbackConnectedNotification();
-
- void getBackendLogs(std::function&& callback);
-
- void cleanupBackendLogs();
-
- // from QAndroidServiceConnection
- void onServiceConnected(const QString& name,
- const QAndroidBinder& serviceBinder) override;
- void onServiceDisconnected(const QString& name) override;
+ ErrorCode start() override;
+ void stop() override;
signals:
@@ -55,28 +33,6 @@ protected:
private:
Protocol m_protocol;
- bool m_serviceConnected = false;
- std::function m_logCallback;
-
- QAndroidBinder m_serviceBinder;
- class VPNBinder : public QAndroidBinder {
- public:
- VPNBinder(AndroidVpnProtocol* controller) : m_controller(controller) {}
-
- bool onTransact(int code, const QAndroidParcel& data,
- const QAndroidParcel& reply,
- QAndroidBinder::CallType flags) override;
-
- QString readUTF8Parcel(QAndroidParcel data);
-
- private:
- AndroidVpnProtocol* m_controller = nullptr;
- };
-
- VPNBinder m_binder;
-
- static void startActivityForResult(JNIEnv* env, jobject /*thiz*/,
- jobject intent);
};
#endif // ANDROID_VPNPROTOCOL_H
diff --git a/client/protocols/vpnprotocol.h b/client/protocols/vpnprotocol.h
index c6bfc3b2..6db5e29e 100644
--- a/client/protocols/vpnprotocol.h
+++ b/client/protocols/vpnprotocol.h
@@ -48,23 +48,16 @@ signals:
void timeoutTimerEvent();
void protocolError(amnezia::ErrorCode e);
- // This signal is emitted when the controller is initialized. Note that the
- // VPN tunnel can be already active. In this case, "connected" should be set
- // to true and the "connectionDate" should be set to the activation date if
- // known.
- // If "status" is set to false, the backend service is considered unavailable.
- void initialized(bool status, bool connected,
- const QDateTime& connectionDate);
-protected slots:
- virtual void onTimeout();
+public slots:
+ virtual void onTimeout(); // todo: remove?
+
+ void setBytesChanged(quint64 receivedBytes, quint64 sentBytes);
+ void setConnectionState(VpnProtocol::ConnectionState state);
protected:
void startTimeoutTimer();
void stopTimeoutTimer();
- virtual void setBytesChanged(quint64 receivedBytes, quint64 sentBytes);
- virtual void setConnectionState(VpnProtocol::ConnectionState state);
-
ConnectionState m_connectionState;
QString m_routeGateway;
QString m_vpnLocalAddress;
diff --git a/client/ui/notificationhandler.cpp b/client/ui/notificationhandler.cpp
new file mode 100644
index 00000000..7a2387a9
--- /dev/null
+++ b/client/ui/notificationhandler.cpp
@@ -0,0 +1,124 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include
+#include "notificationhandler.h"
+
+#if defined(Q_OS_IOS)
+# include "platforms/ios/iosnotificationhandler.h"
+#elif defined(Q_OS_ANDROID)
+# include "platforms/android/android_notificationhandler.h"
+#else
+
+# if defined(Q_OS_LINUX)
+# include "platforms/linux/linuxsystemtraynotificationhandler.h"
+# endif
+
+# include "systemtraynotificationhandler.h"
+#endif
+
+// static
+NotificationHandler* NotificationHandler::create(QObject* parent) {
+#if defined(Q_OS_IOS)
+ return new IOSNotificationHandler(parent);
+#elif defined(Q_OS_ANDROID)
+ return new AndroidNotificationHandler(parent);
+#else
+
+# if defined(Q_OS_LINUX)
+ if (LinuxSystemTrayNotificationHandler::requiredCustomImpl()) {
+ return new LinuxSystemTrayNotificationHandler(parent);
+ }
+# endif
+
+ return new SystemTrayNotificationHandler(parent);
+#endif
+}
+
+namespace {
+NotificationHandler* s_instance = nullptr;
+} // namespace
+
+// static
+NotificationHandler* NotificationHandler::instance() {
+ Q_ASSERT(s_instance);
+ return s_instance;
+}
+
+NotificationHandler::NotificationHandler(QObject* parent) : QObject(parent) {
+ Q_ASSERT(!s_instance);
+ s_instance = this;
+}
+
+NotificationHandler::~NotificationHandler() {
+ Q_ASSERT(s_instance == this);
+ s_instance = nullptr;
+}
+
+void NotificationHandler::showVpnStateNotification(VpnProtocol::ConnectionState state)
+{
+ if (state != VpnProtocol::Connected && state != VpnProtocol::Disconnected) {
+ return;
+ }
+
+ QString title;
+ QString message;
+
+ switch (state) {
+ case VpnProtocol::ConnectionState::Connected:
+ m_connected = true;
+
+ title = tr("AmneziaVPN");
+ message = tr("VPN Connected");
+ break;
+
+ case VpnProtocol::ConnectionState::Disconnected:
+ if (m_connected) {
+ m_connected = false;
+ title = tr("AmneziaVPN");
+ message = tr("VPN Disconnected");
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ Q_ASSERT(title.isEmpty() == message.isEmpty());
+
+ if (!title.isEmpty()) {
+ notifyInternal(VpnState, title, message, 2000);
+ }
+}
+
+void NotificationHandler::unsecuredNetworkNotification(const QString& networkName) {
+ qDebug() << "Unsecured network notification shown";
+
+
+ QString title = tr("AmneziaVPN notification");
+ QString message = tr("Unsucured network detected: ") + networkName;
+
+ notifyInternal(UnsecuredNetwork, title, message, 2000);
+}
+
+void NotificationHandler::notifyInternal(Message type, const QString& title,
+ const QString& message,
+ int timerMsec) {
+ m_lastMessage = type;
+
+ emit notificationShown(title, message);
+ notify(type, title, message, timerMsec);
+}
+
+void NotificationHandler::messageClickHandle() {
+ qDebug() << "Message clicked";
+
+ if (m_lastMessage == VpnState) {
+ qCritical() << "Random message clicked received";
+ return;
+ }
+
+ emit notificationClicked(m_lastMessage);
+ m_lastMessage = VpnState;
+}
diff --git a/client/ui/notificationhandler.h b/client/ui/notificationhandler.h
new file mode 100644
index 00000000..67ab46cf
--- /dev/null
+++ b/client/ui/notificationhandler.h
@@ -0,0 +1,61 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NOTIFICATIONHANDLER_H
+#define NOTIFICATIONHANDLER_H
+
+#include
+#include "protocols/vpnprotocol.h"
+
+class QMenu;
+
+class NotificationHandler : public QObject {
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(NotificationHandler)
+
+public:
+ enum Message {
+ VpnState,
+ UnsecuredNetwork
+ };
+
+ static NotificationHandler* create(QObject* parent);
+
+ static NotificationHandler* instance();
+
+ virtual ~NotificationHandler();
+
+ void unsecuredNetworkNotification(const QString& networkName);
+
+ void messageClickHandle();
+
+public slots:
+ void showVpnStateNotification(VpnProtocol::ConnectionState state);
+
+signals:
+ void notificationShown(const QString& title, const QString& message);
+
+ void notificationClicked(Message message);
+
+protected:
+ explicit NotificationHandler(QObject* parent);
+
+ virtual void notify(Message type, const QString& title,
+ const QString& message, int timerMsec) = 0;
+
+private:
+ virtual void notifyInternal(Message type, const QString& title,
+ const QString& message, int timerMsec);
+
+protected:
+ Message m_lastMessage = VpnState;
+
+private:
+
+ // We want to show a 'disconnected' notification only if we were actually
+ // connected.
+ bool m_connected = false;
+};
+
+#endif // NOTIFICATIONHANDLER_H
diff --git a/client/ui/systemtray_notificationhandler.cpp b/client/ui/systemtray_notificationhandler.cpp
new file mode 100644
index 00000000..25d0253b
--- /dev/null
+++ b/client/ui/systemtray_notificationhandler.cpp
@@ -0,0 +1,127 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include
+#include "systemtray_notificationhandler.h"
+
+
+#ifdef MVPN_MACOS
+# include "platforms/macos/macosutils.h"
+#endif
+
+#include
+#include
+
+
+SystemTrayNotificationHandler::SystemTrayNotificationHandler(QObject* parent)
+ : NotificationHandler(parent) {
+
+// MozillaVPN* vpn = MozillaVPN::instance();
+// Q_ASSERT(vpn);
+
+// connect(vpn, &MozillaVPN::stateChanged, this,
+// &SystemTrayNotificationHandler::updateContextMenu);
+
+// connect(vpn->currentServer(), &ServerData::changed, this,
+// &SystemTrayNotificationHandler::updateContextMenu);
+
+// connect(vpn->controller(), &Controller::stateChanged, this,
+// &SystemTrayNotificationHandler::updateContextMenu);
+
+// connect(vpn->statusIcon(), &StatusIcon::iconChanged, this,
+// &SystemTrayNotificationHandler::updateIcon);
+
+// m_systemTrayIcon.setToolTip(qtTrId("vpn.main.productName"));
+
+// // Status label
+// m_statusLabel = m_menu.addAction("");
+// m_statusLabel->setEnabled(false);
+
+// m_lastLocationLabel =
+// m_menu.addAction("", vpn->controller(), &Controller::activate);
+// m_lastLocationLabel->setEnabled(false);
+
+// m_disconnectAction =
+// m_menu.addAction("", vpn->controller(), &Controller::deactivate);
+
+// m_separator = m_menu.addSeparator();
+
+// m_showHideLabel = m_menu.addAction(
+// "", this, &SystemTrayNotificationHandler::showHideWindow);
+
+// m_menu.addSeparator();
+
+// m_helpMenu = m_menu.addMenu("");
+
+// m_preferencesAction = m_menu.addAction("", vpn, &MozillaVPN::requestSettings);
+
+// m_menu.addSeparator();
+
+// m_quitAction = m_menu.addAction("", vpn->controller(), &Controller::quit);
+// m_systemTrayIcon.setContextMenu(&m_menu);
+
+// updateIcon(vpn->statusIcon()->iconString());
+
+// connect(QmlEngineHolder::instance()->window(), &QWindow::visibleChanged, this,
+// &SystemTrayNotificationHandler::updateContextMenu);
+
+// connect(&m_systemTrayIcon, &QSystemTrayIcon::activated, this,
+// &SystemTrayNotificationHandler::maybeActivated);
+
+// connect(&m_systemTrayIcon, &QSystemTrayIcon::messageClicked, this,
+// &SystemTrayNotificationHandler::messageClickHandle);
+
+// retranslate();
+
+// m_systemTrayIcon.show();
+}
+
+SystemTrayNotificationHandler::~SystemTrayNotificationHandler() {
+}
+
+void SystemTrayNotificationHandler::notify(NotificationHandler::Message type,
+ const QString& title,
+ const QString& message,
+ int timerMsec) {
+ Q_UNUSED(type);
+
+// QIcon icon(Constants::LOGO_URL);
+// m_systemTrayIcon.showMessage(title, message, icon, timerMsec);
+}
+
+void SystemTrayNotificationHandler::updateIcon(const QString& icon) {
+ QIcon trayIconMask(icon);
+ trayIconMask.setIsMask(true);
+ m_systemTrayIcon.setIcon(trayIconMask);
+}
+
+void SystemTrayNotificationHandler::showHideWindow() {
+// QmlEngineHolder* engine = QmlEngineHolder::instance();
+// if (engine->window()->isVisible()) {
+// engine->hideWindow();
+//#ifdef MVPN_MACOS
+// MacOSUtils::hideDockIcon();
+//#endif
+// } else {
+// engine->showWindow();
+//#ifdef MVPN_MACOS
+// MacOSUtils::showDockIcon();
+//#endif
+// }
+}
+
+void SystemTrayNotificationHandler::maybeActivated(
+ QSystemTrayIcon::ActivationReason reason) {
+ qDebug() << "Activated";
+
+#if defined(MVPN_WINDOWS) || defined(MVPN_LINUX)
+ if (reason == QSystemTrayIcon::DoubleClick ||
+ reason == QSystemTrayIcon::Trigger) {
+ QmlEngineHolder* engine = QmlEngineHolder::instance();
+ engine->showWindow();
+ }
+#else
+ Q_UNUSED(reason);
+#endif
+}
diff --git a/client/ui/systemtray_notificationhandler.h b/client/ui/systemtray_notificationhandler.h
new file mode 100644
index 00000000..1bd228de
--- /dev/null
+++ b/client/ui/systemtray_notificationhandler.h
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SYSTEMTRAY_NOTIFICATIONHANDLER_H
+#define SYSTEMTRAY_NOTIFICATIONHANDLER_H
+
+#include "notificationhandler.h"
+
+#include
+#include
+
+class SystemTrayNotificationHandler : public NotificationHandler {
+ public:
+ explicit SystemTrayNotificationHandler(QObject* parent);
+ ~SystemTrayNotificationHandler();
+
+ protected:
+ virtual void notify(Message type, const QString& title,
+ const QString& message, int timerMsec) override;
+
+ private:
+ void updateIcon(const QString& icon);
+
+ void updateContextMenu();
+
+ void showHideWindow();
+
+ void maybeActivated(QSystemTrayIcon::ActivationReason reason);
+
+ private:
+ QMenu m_menu;
+ QSystemTrayIcon m_systemTrayIcon;
+
+ QAction* m_statusLabel = nullptr;
+ QAction* m_lastLocationLabel = nullptr;
+ QAction* m_disconnectAction = nullptr;
+ QAction* m_separator = nullptr;
+ QAction* m_preferencesAction = nullptr;
+ QAction* m_showHideLabel = nullptr;
+ QAction* m_quitAction = nullptr;
+ QMenu* m_helpMenu = nullptr;
+};
+
+#endif // SYSTEMTRAY_NOTIFICATIONHANDLER_H
diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp
index fc08c392..d4c13d71 100644
--- a/client/ui/uilogic.cpp
+++ b/client/ui/uilogic.cpp
@@ -44,6 +44,10 @@
#include "ui/macos_util.h"
#endif
+#ifdef Q_OS_ANDROID
+#include "platforms/android/android_controller.h"
+#endif
+
#include "pages_logic/AppSettingsLogic.h"
#include "pages_logic/GeneralSettingsLogic.h"
#include "pages_logic/NetworkSettingsLogic.h"
@@ -128,9 +132,18 @@ UiLogic::~UiLogic()
void UiLogic::initalizeUiLogic()
{
+ if (!AndroidController::instance()->initialize()) {
+ qDebug() << QString("Init failed") ;
+ emit VpnProtocol::Error;
+ return;
+ }
+
qDebug() << "UiLogic::initalizeUiLogic()";
setupTray();
+ notificationHandler = NotificationHandler::create(this);
+
+ connect(m_vpnConnection, &VpnConnection::connectionStateChanged, notificationHandler, &NotificationHandler::showVpnStateNotification);
// if (QOperatingSystemVersion::current() <= QOperatingSystemVersion::Windows7) {
// needToHideCustomTitlebar = true;
diff --git a/client/ui/uilogic.h b/client/ui/uilogic.h
index 3a33ea45..21c77681 100644
--- a/client/ui/uilogic.h
+++ b/client/ui/uilogic.h
@@ -16,6 +16,7 @@
#include "models/containers_model.h"
#include "models/protocols_model.h"
+#include "notificationhandler.h"
#include "settings.h"
class AppSettingsLogic;
@@ -218,6 +219,8 @@ private:
QThread m_vpnConnectionThread;
Settings m_settings;
+ NotificationHandler* notificationHandler;
+
// QRegExpValidator m_ipAddressValidator;
// QRegExpValidator m_ipAddressPortValidator;
diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp
index bd913dbb..f6225874 100644
--- a/client/vpnconnection.cpp
+++ b/client/vpnconnection.cpp
@@ -2,6 +2,7 @@
#include
#include
#include
+#include
#include
#include
@@ -260,11 +261,8 @@ void VpnConnection::connectToVpn(int serverIndex,
#else
Protocol proto = ContainerProps::defaultProtocol(container);
AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(proto, m_vpnConfiguration);
- if (!androidVpnProtocol->initialize()) {
- qDebug() << QString("Init failed") ;
- emit VpnProtocol::Error;
- return;
- }
+ connect(AndroidController::instance(), &AndroidController::connectionStateChanged, androidVpnProtocol, &AndroidVpnProtocol::setConnectionState);
+
m_vpnProtocol.reset(androidVpnProtocol);
#endif