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 d4f5a70c..91109890 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 \
@@ -94,6 +95,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 \
@@ -133,6 +135,8 @@ TRANSLATIONS = \
translations/amneziavpn_ru.ts
win32 {
+ DEFINES += MVPN_WINDOWS
+
OTHER_FILES += platform_win/vpnclient.rc
RC_FILE = platform_win/vpnclient.rc
@@ -168,6 +172,8 @@ win32 {
}
macx {
+ DEFINES += MVPN_MACOS
+
ICON = $$PWD/images/app.icns
HEADERS += ui/macos_util.h
@@ -180,6 +186,8 @@ macx {
}
linux:!android {
+ DEFINES += MVPN_LINUX
+
LIBS += /usr/lib/x86_64-linux-gnu/libcrypto.a
LIBS += /usr/lib/x86_64-linux-gnu/libssl.a
}
@@ -187,6 +195,7 @@ linux:!android {
win32|macx|linux:!android {
HEADERS += \
+ ui/systemtray_notificationhandler.h \
protocols/openvpnprotocol.h \
protocols/ikev2_vpn_protocol.h \
protocols/openvpnovercloakprotocol.h \
@@ -194,6 +203,7 @@ win32|macx|linux:!android {
protocols/wireguardprotocol.h \
SOURCES += \
+ ui/systemtray_notificationhandler.cpp \
protocols/openvpnprotocol.cpp \
protocols/ikev2_vpn_protocol.cpp \
protocols/openvpnovercloakprotocol.cpp \
@@ -203,12 +213,20 @@ win32|macx|linux:!android {
android {
QT += androidextras
+ DEFINES += MVPN_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 a5d32be0..1e4c9c8d 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/main.cpp b/client/main.cpp
index d3251f1e..817e885f 100644
--- a/client/main.cpp
+++ b/client/main.cpp
@@ -116,12 +116,12 @@ int main(int argc, char *argv[])
app.setQuitOnLastWindowClosed(false);
- qRegisterMetaType("VpnProtocol::ConnectionState");
+ qRegisterMetaType("VpnProtocol::VpnConnectionState");
qRegisterMetaType("ServerCredentials");
qRegisterMetaType("DockerContainer");
qRegisterMetaType("TransportProto");
- qRegisterMetaType("Protocol");
+ qRegisterMetaType("Proto");
qRegisterMetaType("ServiceType");
qRegisterMetaType("Page");
qRegisterMetaType("ConnectionState");
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..a4734414
--- /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::VpnConnectionState 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 3f8141b0..66e2eaf5 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(Proto 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 55ac0fdd..cdcff2c6 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(Proto 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:
Proto 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/ikev2_vpn_protocol.cpp b/client/protocols/ikev2_vpn_protocol.cpp
index 42e34ee8..8f5d4cd3 100644
--- a/client/protocols/ikev2_vpn_protocol.cpp
+++ b/client/protocols/ikev2_vpn_protocol.cpp
@@ -241,7 +241,7 @@ void Ikev2Protocol::newConnectionStateEventReceived(UINT unMsg, tagRASCONNSTATE
void Ikev2Protocol::readIkev2Configuration(const QJsonObject &configuration)
{
- m_config = configuration.value(ProtocolProps::key_proto_config_data(Protocol::Ikev2)).toObject();
+ m_config = configuration.value(ProtocolProps::key_proto_config_data(Proto::Ikev2)).toObject();
}
ErrorCode Ikev2Protocol::start()
diff --git a/client/protocols/openvpnovercloakprotocol.cpp b/client/protocols/openvpnovercloakprotocol.cpp
index f2ac4512..01afb4ca 100644
--- a/client/protocols/openvpnovercloakprotocol.cpp
+++ b/client/protocols/openvpnovercloakprotocol.cpp
@@ -78,7 +78,7 @@ ErrorCode OpenVpnOverCloakProtocol::start()
m_ckProcess.waitForStarted();
if (m_ckProcess.state() == QProcess::ProcessState::Running) {
- setConnectionState(ConnectionState::Connecting);
+ setConnectionState(VpnConnectionState::Connecting);
return OpenVpnProtocol::start();
}
@@ -113,5 +113,5 @@ QString OpenVpnOverCloakProtocol::cloakExecPath()
void OpenVpnOverCloakProtocol::readCloakConfiguration(const QJsonObject &configuration)
{
- m_cloakConfig = configuration.value(ProtocolProps::key_proto_config_data(Protocol::Cloak)).toObject();
+ m_cloakConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::Cloak)).toObject();
}
diff --git a/client/protocols/openvpnprotocol.cpp b/client/protocols/openvpnprotocol.cpp
index df57e365..75e56674 100644
--- a/client/protocols/openvpnprotocol.cpp
+++ b/client/protocols/openvpnprotocol.cpp
@@ -86,8 +86,8 @@ void OpenVpnProtocol::killOpenVpnProcess()
void OpenVpnProtocol::readOpenVpnConfiguration(const QJsonObject &configuration)
{
- if (configuration.contains(ProtocolProps::key_proto_config_data(Protocol::OpenVpn))) {
- QJsonObject jConfig = configuration.value(ProtocolProps::key_proto_config_data(Protocol::OpenVpn)).toObject();
+ if (configuration.contains(ProtocolProps::key_proto_config_data(Proto::OpenVpn))) {
+ QJsonObject jConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::OpenVpn)).toObject();
m_configFile.open();
m_configFile.write(jConfig.value(config_key::config).toString().toUtf8());
@@ -167,7 +167,7 @@ ErrorCode OpenVpnProtocol::start()
return lastError();
}
- setConnectionState(ConnectionState::Connecting);
+ setConnectionState(VpnConnectionState::Connecting);
m_openVpnProcess = IpcClient::CreatePrivilegedProcess();
@@ -201,7 +201,7 @@ ErrorCode OpenVpnProtocol::start()
});
connect(m_openVpnProcess.data(), &PrivilegedProcess::finished, this, [&]() {
- setConnectionState(ConnectionState::Disconnected);
+ setConnectionState(VpnConnectionState::Disconnected);
});
m_openVpnProcess->start();
diff --git a/client/protocols/shadowsocksvpnprotocol.cpp b/client/protocols/shadowsocksvpnprotocol.cpp
index 702a35d8..e2465c1f 100644
--- a/client/protocols/shadowsocksvpnprotocol.cpp
+++ b/client/protocols/shadowsocksvpnprotocol.cpp
@@ -75,7 +75,7 @@ ErrorCode ShadowSocksVpnProtocol::start()
m_ssProcess.waitForStarted();
if (m_ssProcess.state() == QProcess::ProcessState::Running) {
- setConnectionState(ConnectionState::Connecting);
+ setConnectionState(VpnConnectionState::Connecting);
return OpenVpnProtocol::start();
}
@@ -112,5 +112,5 @@ QString ShadowSocksVpnProtocol::shadowSocksExecPath()
void ShadowSocksVpnProtocol::readShadowSocksConfiguration(const QJsonObject &configuration)
{
- m_shadowSocksConfig = configuration.value(ProtocolProps::key_proto_config_data(Protocol::ShadowSocks)).toObject();
+ m_shadowSocksConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::ShadowSocks)).toObject();
}
diff --git a/client/protocols/vpnprotocol.h b/client/protocols/vpnprotocol.h
index 8310d825..4b6876d5 100644
--- a/client/protocols/vpnprotocol.h
+++ b/client/protocols/vpnprotocol.h
@@ -48,24 +48,18 @@ 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::VpnConnectionState state);
protected:
void startTimeoutTimer();
void stopTimeoutTimer();
- virtual void setBytesChanged(quint64 receivedBytes, quint64 sentBytes);
- virtual void setConnectionState(VpnProtocol::VpnConnectionState state);
-
VpnConnectionState m_connectionState;
+
QString m_routeGateway;
QString m_vpnLocalAddress;
QString m_vpnGateway;
diff --git a/client/protocols/wireguardprotocol.cpp b/client/protocols/wireguardprotocol.cpp
index d08843e7..d650cb3f 100644
--- a/client/protocols/wireguardprotocol.cpp
+++ b/client/protocols/wireguardprotocol.cpp
@@ -61,7 +61,7 @@ void WireguardProtocol::stop()
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol Stop errorOccurred" << error;
- setConnectionState(ConnectionState::Disconnected);
+ setConnectionState(VpnConnectionState::Disconnected);
});
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::stateChanged, this, [this](QProcess::ProcessState newState) {
@@ -79,7 +79,7 @@ void WireguardProtocol::stop()
void WireguardProtocol::readWireguardConfiguration(const QJsonObject &configuration)
{
- QJsonObject jConfig = configuration.value(ProtocolProps::key_proto_config_data(Protocol::WireGuard)).toObject();
+ QJsonObject jConfig = configuration.value(ProtocolProps::key_proto_config_data(Proto::WireGuard)).toObject();
if (!m_configFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qCritical() << "Failed to save wireguard config to" << m_configFile.fileName();
@@ -93,7 +93,7 @@ void WireguardProtocol::readWireguardConfiguration(const QJsonObject &configurat
m_configFileName = m_configFile.fileName();
qDebug().noquote() << QString("Set config data") << m_configFileName;
- qDebug().noquote() << QString("Set config data") << configuration.value(ProtocolProps::key_proto_config_data(Protocol::WireGuard)).toString().toUtf8();
+ qDebug().noquote() << QString("Set config data") << configuration.value(ProtocolProps::key_proto_config_data(Proto::WireGuard)).toString().toUtf8();
}
@@ -151,7 +151,7 @@ ErrorCode WireguardProtocol::start()
return lastError();
}
- setConnectionState(ConnectionState::Connecting);
+ setConnectionState(VpnConnectionState::Connecting);
m_wireguardStartProcess = IpcClient::CreatePrivilegedProcess();
@@ -178,7 +178,7 @@ ErrorCode WireguardProtocol::start()
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol errorOccurred" << error;
- setConnectionState(ConnectionState::Disconnected);
+ setConnectionState(VpnConnectionState::Disconnected);
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::stateChanged, this, [this](QProcess::ProcessState newState) {
@@ -186,7 +186,7 @@ ErrorCode WireguardProtocol::start()
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::finished, this, [this]() {
- setConnectionState(ConnectionState::Connected);
+ setConnectionState(VpnConnectionState::Connected);
});
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyRead, this, [this]() {
diff --git a/client/ui/notificationhandler.cpp b/client/ui/notificationhandler.cpp
new file mode 100644
index 00000000..81f6430a
--- /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 "systemtray_notificationhandler.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::setConnectionState(VpnProtocol::VpnConnectionState state)
+{
+ if (state != VpnProtocol::Connected && state != VpnProtocol::Disconnected) {
+ return;
+ }
+
+ QString title;
+ QString message;
+
+ switch (state) {
+ case VpnProtocol::VpnConnectionState::Connected:
+ m_connected = true;
+
+ title = tr("AmneziaVPN");
+ message = tr("VPN Connected");
+ break;
+
+ case VpnProtocol::VpnConnectionState::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..b87e9575
--- /dev/null
+++ b/client/ui/notificationhandler.h
@@ -0,0 +1,64 @@
+/* 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:
+ virtual void setConnectionState(VpnProtocol::VpnConnectionState state);
+
+signals:
+ void notificationShown(const QString& title, const QString& message);
+ void notificationClicked(Message message);
+
+ void raiseRequested();
+ void connectRequested();
+ void disconnectRequested();
+
+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/pages_logic/VpnLogic.cpp b/client/ui/pages_logic/VpnLogic.cpp
index 7c2b9305..1d66547d 100644
--- a/client/ui/pages_logic/VpnLogic.cpp
+++ b/client/ui/pages_logic/VpnLogic.cpp
@@ -88,8 +88,6 @@ void VpnLogic::onConnectionStateChanged(VpnProtocol::VpnConnectionState state)
bool pbConnectVisible = false;
set_labelStateText(VpnProtocol::textConnectionState(state));
- uiLogic()->setTrayState(state);
-
switch (state) {
case VpnProtocol::Disconnected:
onBytesChanged(0,0);
diff --git a/client/ui/systemtray_notificationhandler.cpp b/client/ui/systemtray_notificationhandler.cpp
new file mode 100644
index 00000000..9b10f0de
--- /dev/null
+++ b/client/ui/systemtray_notificationhandler.cpp
@@ -0,0 +1,181 @@
+/* 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 Q_OS_MACOS
+# include "platforms/macos/macosutils.h"
+#endif
+
+#include
+#include
+#include
+#include
+
+#include "defines.h"
+
+
+SystemTrayNotificationHandler::SystemTrayNotificationHandler(QObject* parent) :
+ NotificationHandler(parent),
+ m_systemTrayIcon(parent)
+
+{
+ m_systemTrayIcon.show();
+ connect(&m_systemTrayIcon, &QSystemTrayIcon::activated, this, &SystemTrayNotificationHandler::onTrayActivated);
+
+
+ m_menu.addAction(QIcon(":/images/tray/application.png"), tr("Show") + " " + APPLICATION_NAME, this, [this](){
+ emit raiseRequested();
+ });
+ m_menu.addSeparator();
+ m_trayActionConnect = m_menu.addAction(tr("Connect"), this, [this](){ emit connectRequested(); });
+ m_trayActionDisconnect = m_menu.addAction(tr("Disconnect"), this, [this](){ emit disconnectRequested(); });
+
+ m_menu.addSeparator();
+
+ m_menu.addAction(QIcon(":/images/tray/link.png"), tr("Visit Website"), [&](){
+ QDesktopServices::openUrl(QUrl("https://amnezia.org"));
+ });
+
+ m_menu.addAction(QIcon(":/images/tray/cancel.png"), tr("Quit") + " " + APPLICATION_NAME, this, [&](){
+ qApp->quit();
+ });
+
+ m_systemTrayIcon.setContextMenu(&m_menu);
+ setTrayState(VpnProtocol::Disconnected);
+
+
+// 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::setConnectionState(VpnProtocol::VpnConnectionState state)
+{
+ setTrayState(state);
+ NotificationHandler::setConnectionState(state);
+}
+
+void SystemTrayNotificationHandler::setTrayIcon(const QString &iconPath)
+{
+ QIcon trayIconMask(QPixmap(iconPath).scaled(128,128));
+ trayIconMask.setIsMask(true);
+ m_systemTrayIcon.setIcon(trayIconMask);
+}
+
+void SystemTrayNotificationHandler::onTrayActivated(QSystemTrayIcon::ActivationReason reason)
+{
+#ifndef Q_OS_MAC
+ if(reason == QSystemTrayIcon::DoubleClick || reason == QSystemTrayIcon::Trigger) {
+ emit raiseRequested();
+ }
+#endif
+}
+
+void SystemTrayNotificationHandler::setTrayState(VpnProtocol::VpnConnectionState state)
+{
+ qDebug() << "SystemTrayNotificationHandler::setTrayState" << state;
+ QString resourcesPath = ":/images/tray/%1";
+
+ switch (state) {
+ case VpnProtocol::Disconnected:
+ setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
+ m_trayActionConnect->setEnabled(true);
+ m_trayActionDisconnect->setEnabled(false);
+ break;
+ case VpnProtocol::Preparing:
+ setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
+ m_trayActionConnect->setEnabled(false);
+ m_trayActionDisconnect->setEnabled(true);
+ break;
+ case VpnProtocol::Connecting:
+ setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
+ m_trayActionConnect->setEnabled(false);
+ m_trayActionDisconnect->setEnabled(true);
+ break;
+ case VpnProtocol::Connected:
+ setTrayIcon(QString(resourcesPath).arg(ConnectedTrayIconName));
+ m_trayActionConnect->setEnabled(false);
+ m_trayActionDisconnect->setEnabled(true);
+ break;
+ case VpnProtocol::Disconnecting:
+ setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
+ m_trayActionConnect->setEnabled(false);
+ m_trayActionDisconnect->setEnabled(true);
+ break;
+ case VpnProtocol::Reconnecting:
+ setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
+ m_trayActionConnect->setEnabled(false);
+ m_trayActionDisconnect->setEnabled(true);
+ break;
+ case VpnProtocol::Error:
+ setTrayIcon(QString(resourcesPath).arg(ErrorTrayIconName));
+ m_trayActionConnect->setEnabled(true);
+ m_trayActionDisconnect->setEnabled(false);
+ break;
+ case VpnProtocol::Unknown:
+ default:
+ m_trayActionConnect->setEnabled(false);
+ m_trayActionDisconnect->setEnabled(true);
+ setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
+ }
+
+ //#ifdef Q_OS_MAC
+ // // Get theme from current user (note, this app can be launched as root application and in this case this theme can be different from theme of real current user )
+ // bool darkTaskBar = MacOSFunctions::instance().isMenuBarUseDarkTheme();
+ // darkTaskBar = forceUseBrightIcons ? true : darkTaskBar;
+ // resourcesPath = ":/images_mac/tray_icon/%1";
+ // useIconName = useIconName.replace(".png", darkTaskBar ? "@2x.png" : " dark@2x.png");
+ //#endif
+}
+
+
+void SystemTrayNotificationHandler::notify(NotificationHandler::Message type,
+ const QString& title,
+ const QString& message,
+ int timerMsec) {
+ Q_UNUSED(type);
+
+ QIcon icon(ConnectedTrayIconName);
+ m_systemTrayIcon.showMessage(title, message, icon, timerMsec);
+}
+
+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
+// }
+}
+
diff --git a/client/ui/systemtray_notificationhandler.h b/client/ui/systemtray_notificationhandler.h
new file mode 100644
index 00000000..46563bde
--- /dev/null
+++ b/client/ui/systemtray_notificationhandler.h
@@ -0,0 +1,49 @@
+/* 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 {
+ Q_OBJECT
+
+public:
+ explicit SystemTrayNotificationHandler(QObject* parent);
+ ~SystemTrayNotificationHandler();
+
+ void setConnectionState(VpnProtocol::VpnConnectionState state) override;
+
+protected:
+ virtual void notify(Message type, const QString& title,
+ const QString& message, int timerMsec) override;
+
+private:
+ void showHideWindow();
+
+ void setTrayState(VpnProtocol::VpnConnectionState state);
+ void onTrayActivated(QSystemTrayIcon::ActivationReason reason);
+
+ void setTrayIcon(const QString &iconPath);
+
+private:
+ QMenu m_menu;
+ QSystemTrayIcon m_systemTrayIcon;
+
+ QAction* m_trayActionConnect = nullptr;
+ QAction* m_trayActionDisconnect = nullptr;
+ QAction* m_preferencesAction = nullptr;
+ QAction* m_statusLabel = nullptr;
+ QAction* m_separator = nullptr;
+
+ const QString ConnectedTrayIconName = "active.png";
+ const QString DisconnectedTrayIconName = "default.png";
+ const QString ErrorTrayIconName = "error.png";
+};
+
+#endif // SYSTEMTRAY_NOTIFICATIONHANDLER_H
diff --git a/client/ui/uilogic.cpp b/client/ui/uilogic.cpp
index fd14b975..f3cbed04 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"
@@ -94,7 +98,7 @@ UiLogic::UiLogic(QObject *parent) :
m_protocolLogicMap.insert(Proto::OpenVpn, new OpenVpnLogic(this));
m_protocolLogicMap.insert(Proto::ShadowSocks, new ShadowSocksLogic(this));
m_protocolLogicMap.insert(Proto::Cloak, new CloakLogic(this));
- //m_protocolLogicMap->insert(Protocol::WireGuard, new WireguardLogic(this));
+ //m_protocolLogicMap->insert(Proto::WireGuard, new WireguardLogic(this));
m_protocolLogicMap.insert(Proto::Dns, new OtherProtocolsLogic(this));
m_protocolLogicMap.insert(Proto::Sftp, new OtherProtocolsLogic(this));
@@ -104,8 +108,6 @@ UiLogic::UiLogic(QObject *parent) :
UiLogic::~UiLogic()
{
- m_tray = nullptr;
-
emit hide();
if (m_vpnConnection->connectionState() != VpnProtocol::VpnConnectionState::Disconnected) {
@@ -128,9 +130,22 @@ UiLogic::~UiLogic()
void UiLogic::initalizeUiLogic()
{
- qDebug() << "UiLogic::initalizeUiLogic()";
- setupTray();
+#ifdef Q_OS_ANDROID
+ if (!AndroidController::instance()->initialize()) {
+ qDebug() << QString("Init failed") ;
+ emit VpnProtocol::Error;
+ return;
+ }
+#endif
+ qDebug() << "UiLogic::initalizeUiLogic()";
+
+ m_notificationHandler = NotificationHandler::create(qmlRoot());
+
+ connect(m_vpnConnection, &VpnConnection::connectionStateChanged, m_notificationHandler, &NotificationHandler::setConnectionState);
+ connect(m_notificationHandler, &NotificationHandler::raiseRequested, this, &UiLogic::raise);
+ connect(m_notificationHandler, &NotificationHandler::connectRequested, vpnLogic(), &VpnLogic::onConnect);
+ connect(m_notificationHandler, &NotificationHandler::disconnectRequested, vpnLogic(), &VpnLogic::onDisconnect);
// if (QOperatingSystemVersion::current() <= QOperatingSystemVersion::Windows7) {
// needToHideCustomTitlebar = true;
@@ -161,7 +176,7 @@ void UiLogic::initalizeUiLogic()
selectedServerIndex = m_settings.defaultServerIndex();
//goToPage(Page::ServerContainers, true, false);
//goToPage(Page::NewServerProtocols, true, false);
- //onGotoProtocolPage(Protocol::OpenVpn);
+ //onGotoProtocolPage(Proto::OpenVpn);
//ui->pushButton_general_settings_exit->hide();
@@ -600,27 +615,8 @@ ErrorCode UiLogic::doInstallAction(const std::function &action,
return ErrorCode::NoError;
}
-void UiLogic::setupTray()
+PageProtocolLogicBase *UiLogic::protocolLogic(Proto p)
{
- m_tray = new QSystemTrayIcon(qmlRoot());
- setTrayState(VpnProtocol::Disconnected);
-
- m_tray->show();
-
- connect(m_tray, &QSystemTrayIcon::activated, this, &UiLogic::onTrayActivated);
-}
-
-void UiLogic::setTrayIcon(const QString &iconPath)
-{
- if (m_tray) m_tray->setIcon(QIcon(QPixmap(iconPath).scaled(128,128)));
-}
-
-void UiLogic::onTrayActivated(QSystemTrayIcon::ActivationReason reason)
-{
- emit raise();
-}
-
-PageProtocolLogicBase *UiLogic::protocolLogic(Proto p) {
PageProtocolLogicBase *logic = m_protocolLogicMap.value(p);
if (logic) return logic;
else {
@@ -639,57 +635,23 @@ void UiLogic::setQmlRoot(QObject *newQmlRoot)
m_qmlRoot = newQmlRoot;
}
+NotificationHandler *UiLogic::notificationHandler() const
+{
+ return m_notificationHandler;
+}
+
PageEnumNS::Page UiLogic::currentPage()
{
return static_cast(currentPageValue());
}
-void UiLogic::setTrayState(VpnProtocol::VpnConnectionState state)
-{
- QString resourcesPath = ":/images/tray/%1";
-
- switch (state) {
- case VpnProtocol::Disconnected:
- setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
- break;
- case VpnProtocol::Preparing:
- setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
- break;
- case VpnProtocol::Connecting:
- setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
- break;
- case VpnProtocol::Connected:
- setTrayIcon(QString(resourcesPath).arg(ConnectedTrayIconName));
- break;
- case VpnProtocol::Disconnecting:
- setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
- break;
- case VpnProtocol::Reconnecting:
- setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
- break;
- case VpnProtocol::Error:
- setTrayIcon(QString(resourcesPath).arg(ErrorTrayIconName));
- break;
- case VpnProtocol::Unknown:
- default:
- setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
- }
-
- //#ifdef Q_OS_MAC
- // // Get theme from current user (note, this app can be launched as root application and in this case this theme can be different from theme of real current user )
- // bool darkTaskBar = MacOSFunctions::instance().isMenuBarUseDarkTheme();
- // darkTaskBar = forceUseBrightIcons ? true : darkTaskBar;
- // resourcesPath = ":/images_mac/tray_icon/%1";
- // useIconName = useIconName.replace(".png", darkTaskBar ? "@2x.png" : " dark@2x.png");
- //#endif
-}
-
-
bool UiLogic::saveTextFile(const QString& desc, const QString& ext, const QString& data)
{
QString fileName = QFileDialog::getSaveFileName(nullptr, desc,
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), ext);
+ if (fileName.isEmpty()) return false;
+
QSaveFile save(fileName);
save.open(QIODevice::WriteOnly);
save.write(data.toUtf8());
@@ -705,6 +667,8 @@ bool UiLogic::saveBinaryFile(const QString &desc, const QString &ext, const QStr
QString fileName = QFileDialog::getSaveFileName(nullptr, desc,
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), ext);
+ if (fileName.isEmpty()) return false;
+
QSaveFile save(fileName);
save.open(QIODevice::WriteOnly);
save.write(QByteArray::fromBase64(data.toUtf8()));
diff --git a/client/ui/uilogic.h b/client/ui/uilogic.h
index 2b9b91bf..9eefb381 100644
--- a/client/ui/uilogic.h
+++ b/client/ui/uilogic.h
@@ -6,7 +6,6 @@
#include
#include
#include
-#include
#include "property_helper.h"
#include "pages.h"
@@ -16,6 +15,7 @@
#include "models/containers_model.h"
#include "models/protocols_model.h"
+#include "notificationhandler.h"
#include "settings.h"
class AppSettingsLogic;
@@ -107,12 +107,8 @@ public:
void setDialogConnectErrorText(const QString &dialogConnectErrorText);
signals:
- void trayIconUrlChanged();
- void trayActionDisconnectEnabledChanged();
- void trayActionConnectEnabledChanged();
void dialogConnectErrorTextChanged();
-
void goToPage(PageEnumNS::Page page, bool reset = true, bool slide = true);
void goToProtocolPage(Proto protocol, bool reset = true, bool slide = true);
void goToShareProtocolPage(Proto protocol, bool reset = true, bool slide = true);
@@ -126,17 +122,12 @@ signals:
void raise();
private:
- QSystemTrayIcon *m_tray;
-
QString m_dialogConnectErrorText;
private slots:
// containers - INOUT arg
void installServer(QMap &containers);
- void setTrayState(VpnProtocol::VpnConnectionState state);
- void onTrayActivated(QSystemTrayIcon::ActivationReason reason);
-
private:
PageEnumNS::Page currentPage();
struct ProgressFunc {
@@ -171,9 +162,6 @@ private:
const ButtonFunc& button,
const LabelFunc& info);
- void setupTray();
- void setTrayIcon(const QString &iconPath);
-
public:
AppSettingsLogic *appSettingsLogic() { return m_appSettingsLogic; }
@@ -195,6 +183,8 @@ public:
QObject *qmlRoot() const;
void setQmlRoot(QObject *newQmlRoot);
+ NotificationHandler *notificationHandler() const;
+
private:
QObject *m_qmlRoot{nullptr};
@@ -218,6 +208,8 @@ private:
QThread m_vpnConnectionThread;
Settings m_settings;
+ NotificationHandler* m_notificationHandler;
+
// QRegExpValidator m_ipAddressValidator;
// QRegExpValidator m_ipAddressPortValidator;
@@ -230,10 +222,6 @@ private:
// void showEvent(QShowEvent *event) override;
// void hideEvent(QHideEvent *event) override;
- const QString ConnectedTrayIconName = "active.png";
- const QString DisconnectedTrayIconName = "default.png";
- const QString ErrorTrayIconName = "error.png";
-
// QStack pagesStack;
int selectedServerIndex = -1; // server index to use when proto settings page opened
diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp
index e08e6c02..5837ce58 100644
--- a/client/vpnconnection.cpp
+++ b/client/vpnconnection.cpp
@@ -12,7 +12,8 @@
#include
#ifdef Q_OS_ANDROID
-#include
+#include "android_controller.h"
+#include "protocols/android_vpnprotocol.h"
#endif
#ifdef Q_OS_IOS
@@ -261,11 +262,8 @@ void VpnConnection::connectToVpn(int serverIndex,
#elif defined Q_OS_ANDROID
Proto 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);
#elif defined Q_OS_IOS
Proto proto = ContainerProps::defaultProtocol(container);
@@ -279,7 +277,7 @@ void VpnConnection::connectToVpn(int serverIndex,
#endif
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
- connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::ConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::ConnectionState)));
+ connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::VpnConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::VpnConnectionState)));
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
ServerController::disconnectFromHost(credentials);