Merge branch 'dev' of github.com:amnezia-vpn/desktop-client into feature/qt6-client-management-panel

This commit is contained in:
vladimir.kuznetsov 2023-02-27 19:59:01 +03:00
commit 4171afe275
55 changed files with 820 additions and 337 deletions

View file

@ -3,15 +3,70 @@
#include "ui/uilogic.h"
#include "ui/pages_logic/StartPageLogic.h"
#ifdef Q_OS_ANDROID
#include <QJniEnvironment>
#include <QJniObject>
#include "../../platforms/android/androidutils.h"
#endif
using namespace amnezia;
using namespace PageEnumNS;
namespace {
QrDecoderLogic* mInstance = nullptr;
constexpr auto CLASSNAME = "org.amnezia.vpn.qt.CameraActivity";
}
QrDecoderLogic::QrDecoderLogic(UiLogic *logic, QObject *parent):
PageLogicBase(logic, parent)
{
mInstance = this;
#if (defined(Q_OS_ANDROID))
AndroidUtils::runOnAndroidThreadAsync([]() {
JNINativeMethod methods[]{
{"passDataToDecoder", "(Ljava/lang/String;)V", reinterpret_cast<void*>(onNewDataChunk)},
};
QJniObject javaClass(CLASSNAME);
QJniEnvironment env;
jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());
env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0]));
env->DeleteLocalRef(objectClass);
});
#endif
}
void QrDecoderLogic::stopDecodingQr()
{
#if (defined(Q_OS_ANDROID))
QJniObject::callStaticMethod<void>(CLASSNAME, "stopQrCodeReader", "()V");
#endif
emit stopDecode();
}
#ifdef Q_OS_ANDROID
void QrDecoderLogic::onNewDataChunk(JNIEnv *env, jobject thiz, jstring data)
{
Q_UNUSED(thiz);
const char* buffer = env->GetStringUTFChars(data, nullptr);
if (!buffer) {
return;
}
QString parcelBody(buffer);
env->ReleaseStringUTFChars(data, buffer);
if (mInstance != nullptr) {
if (!mInstance->m_detectingEnabled) {
mInstance->onUpdatePage();
}
mInstance->onDetectedQrCode(parcelBody);
}
}
#endif
void QrDecoderLogic::onUpdatePage()
{
m_chunks.clear();
@ -24,7 +79,6 @@ void QrDecoderLogic::onUpdatePage()
void QrDecoderLogic::onDetectedQrCode(const QString &code)
{
//qDebug() << code;
if (!detectingEnabled()) return;
// check if chunk received
@ -32,12 +86,12 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code)
QDataStream s(&ba, QIODevice::ReadOnly);
qint16 magic; s >> magic;
if (magic == amnezia::qrMagicCode) {
quint8 chunksCount; s >> chunksCount;
if (totalChunksCount() != chunksCount) {
m_chunks.clear();
}
set_totalChunksCount(chunksCount);
quint8 chunkId; s >> chunkId;
@ -46,6 +100,7 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code)
if (m_chunks.size() == totalChunksCount()) {
QByteArray data;
for (int i = 0; i < totalChunksCount(); ++i) {
data.append(m_chunks.value(i));
}
@ -53,21 +108,18 @@ void QrDecoderLogic::onDetectedQrCode(const QString &code)
bool ok = uiLogic()->pageLogic<StartPageLogic>()->importConnectionFromQr(data);
if (ok) {
set_detectingEnabled(false);
emit stopDecode();
}
else {
stopDecodingQr();
} else {
m_chunks.clear();
set_totalChunksCount(0);
set_receivedChunksCount(0);
}
}
}
else {
} else {
bool ok = uiLogic()->pageLogic<StartPageLogic>()->importConnectionFromQr(ba);
if (ok) {
set_detectingEnabled(false);
emit stopDecode();
stopDecodingQr();
}
}
}

View file

@ -3,6 +3,10 @@
#include "PageLogicBase.h"
#ifdef Q_OS_ANDROID
#include "jni.h"
#endif
class UiLogic;
class QrDecoderLogic : public PageLogicBase
@ -16,10 +20,17 @@ public:
Q_INVOKABLE void onUpdatePage() override;
Q_INVOKABLE void onDetectedQrCode(const QString &code);
#ifdef Q_OS_ANDROID
static void onNewDataChunk(JNIEnv *env, jobject thiz, jstring data);
#endif
public:
explicit QrDecoderLogic(UiLogic *uiLogic, QObject *parent = nullptr);
~QrDecoderLogic() = default;
private:
void stopDecodingQr();
signals:
void startDecode();
void stopDecode();

View file

@ -13,6 +13,7 @@
#ifdef Q_OS_ANDROID
#include <QJniObject>
#include "../../platforms/android/androidutils.h"
#include "../../platforms/android/android_controller.h"
#endif
namespace {
@ -184,6 +185,13 @@ void StartPageLogic::onPushButtonImportOpenFile()
}
}
#ifdef Q_OS_ANDROID
void StartPageLogic::startQrDecoder()
{
AndroidController::instance()->startQrReaderActivity();
}
#endif
bool StartPageLogic::importConnection(const QJsonObject &profile)
{
ServerCredentials credentials;

View file

@ -31,6 +31,10 @@ public:
Q_INVOKABLE void onPushButtonImport();
Q_INVOKABLE void onPushButtonImportOpenFile();
#ifdef Q_OS_ANDROID
Q_INVOKABLE void startQrDecoder();
#endif
bool importConnection(const QJsonObject &profile);
bool importConnectionFromCode(QString code);
bool importConnectionFromQr(const QByteArray &data);

View file

@ -51,9 +51,15 @@ void OpenVpnLogic::updateProtocolPage(const QJsonObject &openvpnConfig, DockerCo
set_lineEditSubnetText(openvpnConfig.value(config_key::subnet_address).
toString(protocols::openvpn::defaultSubnetAddress));
QString trasnsport = openvpnConfig.value(config_key::transport_proto).
toString(protocols::openvpn::defaultTransportProto);
QString trasnsport;
if (container == DockerContainer::ShadowSocks || container == DockerContainer::Cloak) {
trasnsport = "tcp";
set_radioButtonUdpEnabled(false);
set_radioButtonTcpEnabled(false);
} else {
trasnsport = openvpnConfig.value(config_key::transport_proto).
toString(protocols::openvpn::defaultTransportProto);
}
set_radioButtonUdpChecked(trasnsport == protocols::openvpn::defaultTransportProto);
set_radioButtonTcpChecked(trasnsport != protocols::openvpn::defaultTransportProto);
@ -80,12 +86,6 @@ void OpenVpnLogic::updateProtocolPage(const QJsonObject &openvpnConfig, DockerCo
toString(protocols::openvpn::defaultAdditionalServerConfig);
set_textAreaAdditionalServerConfig(additionalServerConfig);
if (container == DockerContainer::ShadowSocks) {
set_radioButtonUdpEnabled(false);
set_radioButtonTcpEnabled(false);
set_radioButtonTcpChecked(true);
}
set_lineEditPortText(openvpnConfig.value(config_key::port).
toString(protocols::openvpn::defaultPort));

View file

@ -0,0 +1,57 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Popup {
id: root
property string questionText
property string yesText: "yes"
property string noText: "no"
property var yesFunc
property var noFunc
anchors.centerIn: Overlay.overlay
modal: true
closePolicy: Popup.NoAutoClose
width: parent.width - 20
ColumnLayout {
width: parent.width
Text {
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
wrapMode: Text.WordWrap
font.pixelSize: 16
text: questionText
}
RowLayout {
BlueButtonType {
id: yesButton
Layout.preferredWidth: parent.width / 2
Layout.fillWidth: true
text: yesText
onClicked: {
root.enabled = false
if (yesFunc && typeof yesFunc === "function") {
yesFunc()
}
root.enabled = true
}
}
BlueButtonType {
id: noButton
Layout.preferredWidth: parent.width / 2
Layout.fillWidth: true
text: noText
onClicked: {
if (noFunc && typeof noFunc === "function") {
noFunc()
}
}
}
}
}
}

View file

@ -63,7 +63,7 @@ PageBase {
pageLoader.focus = true
}
onContainerSelected: {
onContainerSelected: function(c_index){
var containerProto = ContainerProps.defaultProtocol(c_index)
tf_port_num.text = ProtocolProps.defaultPort(containerProto)

View file

@ -1,164 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtMultimedia
import PageEnum 1.0
import QZXing 3.2
import "./"
import "../Controls"
import "../Config"
PageBase {
id: root
page: PageEnum.QrDecoder
logic: QrDecoderLogic
onDeactivated: {
console.debug("Stopping QR decoder")
loader.sourceComponent = undefined
}
BackButton {
}
Caption {
id: caption
text: qsTr("Import configuration")
}
Connections {
target: Qt.platform.os != "ios" ? QrDecoderLogic : null
function onStartDecode() {
console.debug("Starting QR decoder")
loader.sourceComponent = component
}
function onStopDecode() {
console.debug("Stopping QR decoder")
loader.sourceComponent = undefined
}
}
Loader {
id: loader
anchors.top: caption.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
}
Component {
id: component
Item {
anchors.fill: parent
Camera
{
id:camera
focus {
focusMode: CameraFocus.FocusContinuous
focusPointMode: CameraFocus.FocusPointAuto
}
}
VideoOutput
{
id: videoOutput
source: camera
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
autoOrientation: true
fillMode: VideoOutput.PreserveAspectFit
// filters: [ zxingFilter ]
Rectangle {
color: "black"
opacity: 0.5
width: videoOutput.contentRect.width * 0.15
height: videoOutput.contentRect.height
x: (videoOutput.width - videoOutput.contentRect.width)/2
anchors.verticalCenter: videoOutput.verticalCenter
}
Rectangle {
color: "black"
opacity: 0.5
width: videoOutput.contentRect.width * 0.15
height: videoOutput.contentRect.height
x: videoOutput.width/2 + videoOutput.contentRect.width/2 - videoOutput.contentRect.width * 0.15
anchors.verticalCenter: videoOutput.verticalCenter
}
Rectangle {
color: "black"
opacity: 0.5
width: videoOutput.contentRect.width * 0.7
height: videoOutput.contentRect.height * 0.15
x: (videoOutput.width - videoOutput.contentRect.width)/2 + videoOutput.contentRect.width * 0.15
y: (videoOutput.height - videoOutput.contentRect.height)/2
}
Rectangle {
color: "black"
opacity: 0.5
width: videoOutput.contentRect.width * 0.7
height: videoOutput.contentRect.height * 0.15
x: (videoOutput.width - videoOutput.contentRect.width)/2 + videoOutput.contentRect.width * 0.15
y: videoOutput.height/2 + videoOutput.contentRect.height/2 - videoOutput.contentRect.height * 0.15
}
LabelType {
width: parent.width
text: qsTr("Decoded QR chunks " + QrDecoderLogic.receivedChunksCount + "/" + QrDecoderLogic.totalChunksCount)
horizontalAlignment: Text.AlignLeft
visible: QrDecoderLogic.totalChunksCount > 0
anchors.horizontalCenter: videoOutput.horizontalCenter
y: videoOutput.height/2 + videoOutput.contentRect.height/2
}
}
QZXingFilter
{
id: zxingFilter
orientation: videoOutput.orientation
captureRect: {
// setup bindings
videoOutput.contentRect;
videoOutput.sourceRect;
return videoOutput.mapRectToSource(videoOutput.mapNormalizedRectToItem(Qt.rect(
0.15, 0.15, 0.7, 0.7 //0, 0, 1.0, 1.0
)));
}
decoder {
enabledDecoders: QZXing.DecoderFormat_QR_CODE
onTagFound: {
QrDecoderLogic.onDetectedQrCode(tag)
}
tryHarder: true
}
property int framesDecoded: 0
property real timePerFrameDecode: 0
onDecodingFinished:
{
timePerFrameDecode = (decodeTime + framesDecoded * timePerFrameDecode) / (framesDecoded + 1);
framesDecoded++;
if(succeeded)
console.log("frame finished: " + succeeded, decodeTime, timePerFrameDecode, framesDecoded);
}
}
}
}
}

View file

@ -55,7 +55,6 @@ PageBase {
tf_port_num.text = qsTr("Default")
}
else tf_port_num.text = ProtocolProps.defaultPort(containerProto)
cb_port_proto.currentIndex = ProtocolProps.defaultTransportProto(containerProto)
tf_port_num.enabled = ProtocolProps.defaultPortChangeable(containerProto)
@ -297,23 +296,24 @@ PageBase {
implicitHeight: 30
checked: default_role
MessageDialog {
id: dialogRemove
buttons: StandardButton.Yes | StandardButton.Cancel
title: "AmneziaVPN"
text: qsTr("Remove container") + " " + name_role + "?" + "\n" + qsTr("This action will erase all data of this container on the server.")
onAccepted: {
tb_c.currentIndex = -1
ServerContainersLogic.onPushButtonRemoveClicked(proxyContainersModel.mapToSource(index))
}
}
onClicked: dialogRemove.open()
onClicked: popupRemove.open()
VisibleBehavior on visible { }
}
PopupWithQuestion {
id: popupRemove
questionText: qsTr("Remove container") + " " + name_role + "?" + "\n" + qsTr("This action will erase all data of this container on the server.")
yesFunc: function() {
tb_c.currentIndex = -1
ServerContainersLogic.onPushButtonRemoveClicked(proxyContainersModel.mapToSource(index))
close()
}
noFunc: function() {
close()
}
}
ImageButtonType {
id: button_share
visible: (index === tb_c.currentIndex) && ServerContainersLogic.isManagedServer
@ -418,7 +418,7 @@ PageBase {
BlueButtonType {
id: pb_add_container
visible: container_selector.selectedIndex < 0
visible: container_selector.selectedIndex < 0 && ServerContainersLogic.isManagedServer
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
@ -430,6 +430,5 @@ PageBase {
text: qsTr("Install new service")
font.pixelSize: 16
onClicked: container_selector.visible ? container_selector.close() : container_selector.open()
}
}

View file

@ -102,24 +102,49 @@ PageBase {
ServerSettingsLogic.onPushButtonClearClientCacheClicked()
}
}
BlueButtonType {
Layout.fillWidth: true
Layout.topMargin: 10
text: ServerSettingsLogic.pushButtonClearText
visible: ServerSettingsLogic.pushButtonClearVisible
onClicked: {
ServerSettingsLogic.onPushButtonClearServer()
onClicked: {
popupClearServer.open()
}
}
PopupWithQuestion {
id: popupClearServer
questionText: "Attention! All containers will be deleted on the server. This means that configuration files, keys and certificates will be deleted. Continue?"
yesFunc: function() {
ServerSettingsLogic.onPushButtonClearServer()
close()
}
noFunc: function() {
close()
}
}
BlueButtonType {
Layout.fillWidth: true
Layout.topMargin: 10
text: qsTr("Forget this server")
onClicked: {
ServerSettingsLogic.onPushButtonForgetServer()
popupForgetServer.open()
}
}
PopupWithQuestion {
id: popupForgetServer
questionText: "Attention! This action will not remove the container on the server, it will only remove the container information from the application. Continue?"
yesFunc: function() {
ServerSettingsLogic.onPushButtonForgetServer()
close()
}
noFunc: function() {
close()
}
}
}
}

View file

@ -154,7 +154,7 @@ PageBase {
if (Qt.platform.os === "ios") {
UiLogic.goToPage(PageEnum.QrDecoderIos)
} else {
UiLogic.goToPage(PageEnum.QrDecoder)
StartPageLogic.startQrDecoder()
}
}
enabled: StartPageLogic.pushButtonConnectEnabled

View file

@ -11,10 +11,9 @@ PageProtocolBase {
protocol: ProtocolEnum.Cloak
logic: UiLogic.protocolLogic(protocol)
enabled: logic.pageEnabled
BackButton {
id: back
enabled: logic.pageEnabled
enabled: !logic.pushButtonCancelVisible
}
Caption {

View file

@ -13,7 +13,7 @@ PageProtocolBase {
BackButton {
id: back
enabled: logic.pageEnabled
enabled: !logic.pushButtonCancelVisible
}
Caption {
@ -285,8 +285,6 @@ PageProtocolBase {
}
}
}
}
BasicButtonType {
@ -338,8 +336,6 @@ PageProtocolBase {
}
}
}
}
LabelType {

View file

@ -13,7 +13,7 @@ PageProtocolBase {
BackButton {
id: back
enabled: logic.pageEnabled
enabled: !logic.pushButtonCancelVisible
}
Caption {

View file

@ -254,18 +254,6 @@ Window {
}
}
MessageDialog {
id: closePrompt
// x: (root.width - width) / 2
// y: (root.height - height) / 2
title: qsTr("Exit")
text: qsTr("Do you really want to quit?")
// standardButtons: StandardButton.Yes | StandardButton.No
// onYesClicked: {
// Qt.quit()
// }
visible: false
}
MessageDialog {
id: publicKeyWarning
title: "AmneziaVPN"