diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 64a4986d..0ce8d576 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -256,7 +256,7 @@ jobs: - name: 'Setup xcode' uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.3.1' + xcode-version: '15.4.0' - name: 'Install Qt' uses: jurplel/install-qt-action@v3 diff --git a/CMakeLists.txt b/CMakeLists.txt index b5e64e32..cb695631 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -project(${PROJECT} VERSION 4.8.2.3 +project(${PROJECT} VERSION 4.8.2.4 DESCRIPTION "AmneziaVPN" HOMEPAGE_URL "https://amnezia.org/" ) @@ -11,7 +11,7 @@ string(TIMESTAMP CURRENT_DATE "%Y-%m-%d") set(RELEASE_DATE "${CURRENT_DATE}") set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) -set(APP_ANDROID_VERSION_CODE 2069) +set(APP_ANDROID_VERSION_CODE 2071) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(MZ_PLATFORM_NAME "linux") diff --git a/README.md b/README.md index eed800f5..27a29edf 100644 --- a/README.md +++ b/README.md @@ -4,21 +4,21 @@ [![Build Status](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml/badge.svg?branch=dev)](https://github.com/amnezia-vpn/amnezia-client/actions/workflows/deploy.yml?query=branch:dev) [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/amnezia-vpn/amnezia-client) -Amnezia is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server. +[Amnezia](https://amnezia.org) is an open-source VPN client, with a key feature that enables you to deploy your own VPN server on your server. -![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png) +[![Image](https://github.com/amnezia-vpn/amnezia-client/blob/dev/metadata/img-readme/uipic4.png)](https://amnezia.org) -
+### [Website](https://amnezia.org) | [Alt website link](https://storage.googleapis.com/kldscp/amnezia.org) | [Documentation](https://docs.amnezia.org) | [Troubleshooting](https://docs.amnezia.org/troubleshooting) - - - +> [!TIP] +> If the [Amnezia website](https://amnezia.org) is blocked in your region, you can use an [Alternative website link](https://storage.googleapis.com/kldscp/amnezia.org). -[Alternative download link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org/downloads) + + [All releases](https://github.com/amnezia-vpn/amnezia-client/releases) -
+
@@ -33,7 +33,8 @@ Amnezia is an open-source VPN client, with a key feature that enables you to dep ## Links -- [https://amnezia.org](https://amnezia.org) - project website | [Alternative link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org) +- [https://amnezia.org](https://amnezia.org) - Project website | [Alternative link (mirror)](https://storage.googleapis.com/kldscp/amnezia.org) +- [https://docs.amnezia.org](https://docs.amnezia.org) - Documentation - [https://www.reddit.com/r/AmneziaVPN](https://www.reddit.com/r/AmneziaVPN) - Reddit - [https://t.me/amnezia_vpn_en](https://t.me/amnezia_vpn_en) - Telegram support channel (English) - [https://t.me/amnezia_vpn_ir](https://t.me/amnezia_vpn_ir) - Telegram support channel (Farsi) diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index 179def86..9e44e022 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -20,7 +20,7 @@ - + diff --git a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt index e93834f4..80cab96d 100644 --- a/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt +++ b/client/android/wireguard/src/main/kotlin/org/amnezia/vpn/protocol/wireguard/Wireguard.kt @@ -1,11 +1,12 @@ package org.amnezia.vpn.protocol.wireguard import android.net.VpnService.Builder -import java.io.IOException -import java.util.Locale +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel import kotlinx.coroutines.delay -import kotlinx.coroutines.withContext +import kotlinx.coroutines.launch import org.amnezia.awg.GoBackend import org.amnezia.vpn.protocol.Protocol import org.amnezia.vpn.protocol.ProtocolState.CONNECTED @@ -27,6 +28,8 @@ open class Wireguard : Protocol() { private var tunnelHandle: Int = -1 protected open val ifName: String = "amn0" + private lateinit var scope: CoroutineScope + private var statusJob: Job? = null override val statistics: Statistics get() { @@ -49,46 +52,17 @@ open class Wireguard : Protocol() { override fun internalInit() { if (!isInitialized) loadSharedLibrary(context, "wg-go") + if (this::scope.isInitialized) { + scope.cancel() + } + scope = CoroutineScope(Dispatchers.IO) } override suspend fun startVpn(config: JSONObject, vpnBuilder: Builder, protect: (Int) -> Boolean) { val wireguardConfig = parseConfig(config) - val startTime = System.currentTimeMillis() start(wireguardConfig, vpnBuilder, protect) - waitForConnection(startTime) - state.value = CONNECTED } - private suspend fun waitForConnection(startTime: Long) { - Log.d(TAG, "Waiting for connection") - withContext(Dispatchers.IO) { - val time = String.format(Locale.ROOT,"%.3f", startTime / 1000.0) - try { - delay(1000) - var log = getLogcat(time) - Log.v(TAG, "First waiting log: $log") - // check that there is a connection log, - // to avoid infinite connection - if (!log.contains("Attaching to interface")) { - Log.w(TAG, "Logs do not contain a connection log") - return@withContext - } - while (!log.contains("Received handshake response")) { - delay(1000) - log = getLogcat(time) - } - } catch (e: IOException) { - Log.e(TAG, "Failed to get logcat: $e") - } - } - } - - private fun getLogcat(time: String): String = - ProcessBuilder("logcat", "--buffer=main", "--format=raw", "*:S AmneziaWG/awg0", "-t", time) - .redirectErrorStream(true) - .start() - .inputStream.reader().readText() - protected open fun parseConfig(config: JSONObject): WireguardConfig { val configData = config.getJSONObject("wireguard_config_data") return WireguardConfig.build { @@ -178,6 +152,43 @@ open class Wireguard : Protocol() { tunnelHandle = -1 throw VpnStartException("Protect VPN interface: permission not granted or revoked") } + launchStatusJob() + } + + private fun launchStatusJob() { + Log.d(TAG, "Launch status job") + statusJob = scope.launch { + while (true) { + val lastHandshake = getLastHandshake() + Log.v(TAG, "lastHandshake=$lastHandshake") + if (lastHandshake == 0L) { + delay(1000) + continue + } + if (lastHandshake == -2L || lastHandshake > 0L) state.value = CONNECTED + else if (lastHandshake == -1L) state.value = DISCONNECTED + statusJob = null + break + } + } + } + + private fun getLastHandshake(): Long { + if (tunnelHandle == -1) { + Log.e(TAG, "Trying to get config of a non-existent tunnel") + return -1 + } + val config = GoBackend.awgGetConfig(tunnelHandle) + if (config == null) { + Log.e(TAG, "Failed to get tunnel config") + return -2 + } + val lastHandshake = config.lines().find { it.startsWith("last_handshake_time_sec=") }?.substring(24)?.toLong() + if (lastHandshake == null) { + Log.e(TAG, "Failed to get last_handshake_time_sec") + return -2 + } + return lastHandshake } override fun stopVpn() { @@ -185,6 +196,8 @@ open class Wireguard : Protocol() { Log.w(TAG, "Tunnel already down") return } + statusJob?.cancel() + statusJob = null val handleToClose = tunnelHandle tunnelHandle = -1 GoBackend.awgTurnOff(handleToClose) diff --git a/client/ui/controllers/installController.cpp b/client/ui/controllers/installController.cpp index 306e7f38..ae0804cb 100755 --- a/client/ui/controllers/installController.cpp +++ b/client/ui/controllers/installController.cpp @@ -848,7 +848,6 @@ bool InstallController::updateServiceFromApi(const int serverIndex, const QStrin newServerConfig.insert(configKey::apiConfig, newApiConfig); newServerConfig.insert(configKey::authData, authData); - newServerConfig.insert(config_key::crc, serverConfig.value(config_key::crc)); m_serversModel->editServer(newServerConfig, serverIndex); if (reloadServiceConfig) { diff --git a/client/ui/qml/Components/ShareConnectionDrawer.qml b/client/ui/qml/Components/ShareConnectionDrawer.qml index 3235ad0a..d2bf28ab 100644 --- a/client/ui/qml/Components/ShareConnectionDrawer.qml +++ b/client/ui/qml/Components/ShareConnectionDrawer.qml @@ -84,7 +84,7 @@ DrawerType2 { Layout.topMargin: 16 text: qsTr("Share") - imageSource: "qrc:/images/controls/share-2.svg" + leftImageSource: "qrc:/images/controls/share-2.svg" KeyNavigation.tab: copyConfigTextButton @@ -120,7 +120,7 @@ DrawerType2 { borderWidth: 1 text: qsTr("Copy") - imageSource: "qrc:/images/controls/copy.svg" + leftImageSource: "qrc:/images/controls/copy.svg" Keys.onReturnPressed: { copyConfigTextButton.clicked() } Keys.onEnterPressed: { copyConfigTextButton.clicked() } @@ -143,7 +143,7 @@ DrawerType2 { borderWidth: 1 text: qsTr("Copy config string") - imageSource: "qrc:/images/controls/copy.svg" + leftImageSource: "qrc:/images/controls/copy.svg" KeyNavigation.tab: showSettingsButton } diff --git a/client/ui/qml/Controls2/BasicButtonType.qml b/client/ui/qml/Controls2/BasicButtonType.qml index 5c599013..828c32bc 100644 --- a/client/ui/qml/Controls2/BasicButtonType.qml +++ b/client/ui/qml/Controls2/BasicButtonType.qml @@ -22,9 +22,10 @@ Button { property int borderWidth: 0 property int borderFocusedWidth: 1 - property string imageSource + property string leftImageSource property string rightImageSource - property string leftImageColor: textColor + property string leftImageColor + property bool changeLeftImageSize: true property bool squareLeftSide: false @@ -127,18 +128,23 @@ Button { anchors.centerIn: parent Image { - Layout.preferredHeight: 20 - Layout.preferredWidth: 20 - - source: root.imageSource - visible: root.imageSource === "" ? false : true + id: leftImage + source: root.leftImageSource + visible: root.leftImageSource === "" ? false : true layer { - enabled: true + enabled: leftImageColor !== "" ? true : false effect: ColorOverlay { color: leftImageColor } } + + Component.onCompleted: { + if (root.changeLeftImageSize) { + leftImage.Layout.preferredHeight = 20 + leftImage.Layout.preferredWidth = 20 + } + } } ButtonTextType { diff --git a/client/ui/qml/Controls2/BusyIndicatorType.qml b/client/ui/qml/Controls2/BusyIndicatorType.qml index 55af280f..480f25c1 100644 --- a/client/ui/qml/Controls2/BusyIndicatorType.qml +++ b/client/ui/qml/Controls2/BusyIndicatorType.qml @@ -14,7 +14,7 @@ Popup { visible: false Overlay.modal: Rectangle { - color: Qt.rgba(14/255, 14/255, 17/255, 0.8) + color: AmneziaStyle.color.translucentMidnightBlack } background: Rectangle { diff --git a/client/ui/qml/Controls2/CardType.qml b/client/ui/qml/Controls2/CardType.qml index 50f84dbf..f584a8fc 100644 --- a/client/ui/qml/Controls2/CardType.qml +++ b/client/ui/qml/Controls2/CardType.qml @@ -19,7 +19,7 @@ RadioButton { property string textColor: AmneziaStyle.color.midnightBlack - property string pressedBorderColor: Qt.rgba(251/255, 178/255, 106/255, 0.3) + property string pressedBorderColor: AmneziaStyle.color.softGoldenApricot property string selectedBorderColor: AmneziaStyle.color.goldenApricot property string defaultBodredColor: AmneziaStyle.color.transparent property int borderWidth: 0 diff --git a/client/ui/qml/Controls2/CardWithIconsType.qml b/client/ui/qml/Controls2/CardWithIconsType.qml index fea65116..18a29b87 100644 --- a/client/ui/qml/Controls2/CardWithIconsType.qml +++ b/client/ui/qml/Controls2/CardWithIconsType.qml @@ -145,6 +145,7 @@ Button { cursorShape: Qt.PointingHandCursor hoverEnabled: true + enabled: root.enabled onEntered: { backgroundRect.color = root.hoveredColor diff --git a/client/ui/qml/Controls2/DrawerType2.qml b/client/ui/qml/Controls2/DrawerType2.qml index 6647bc88..c4b584c1 100644 --- a/client/ui/qml/Controls2/DrawerType2.qml +++ b/client/ui/qml/Controls2/DrawerType2.qml @@ -92,7 +92,7 @@ Item { id: background anchors.fill: parent - color: root.isCollapsed ? AmneziaStyle.color.transparent : Qt.rgba(14/255, 14/255, 17/255, 0.8) + color: root.isCollapsed ? AmneziaStyle.color.transparent : AmneziaStyle.color.translucentMidnightBlack Behavior on color { PropertyAnimation { duration: 200 } diff --git a/client/ui/qml/Controls2/PopupType.qml b/client/ui/qml/Controls2/PopupType.qml index bd4aa4fb..7a6a770e 100644 --- a/client/ui/qml/Controls2/PopupType.qml +++ b/client/ui/qml/Controls2/PopupType.qml @@ -24,7 +24,7 @@ Popup { Overlay.modal: Rectangle { visible: root.closeButtonVisible - color: Qt.rgba(14/255, 14/255, 17/255, 0.8) + color: AmneziaStyle.color.translucentMidnightBlack } onOpened: { diff --git a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml index 4ec0976b..365faa94 100644 --- a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml +++ b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml @@ -183,7 +183,7 @@ Item { focusPolicy: Qt.NoFocus text: root.buttonText - imageSource: root.buttonImageSource + leftImageSource: root.buttonImageSource anchors.top: content.top anchors.bottom: content.bottom diff --git a/client/ui/qml/Controls2/TopCloseButtonType.qml b/client/ui/qml/Controls2/TopCloseButtonType.qml index 1bd7fef6..3a652da6 100644 --- a/client/ui/qml/Controls2/TopCloseButtonType.qml +++ b/client/ui/qml/Controls2/TopCloseButtonType.qml @@ -14,7 +14,7 @@ Popup { visible: false Overlay.modal: Rectangle { - color: Qt.rgba(14/255, 14/255, 17/255, 0.8) + color: AmneziaStyle.color.translucentMidnightBlack } background: Rectangle { diff --git a/client/ui/qml/Modules/Style/AmneziaStyle.qml b/client/ui/qml/Modules/Style/AmneziaStyle.qml index c0038246..1abfbe3a 100644 --- a/client/ui/qml/Modules/Style/AmneziaStyle.qml +++ b/client/ui/qml/Modules/Style/AmneziaStyle.qml @@ -22,5 +22,9 @@ QtObject { readonly property color sheerWhite: Qt.rgba(1, 1, 1, 0.12) readonly property color translucentWhite: Qt.rgba(1, 1, 1, 0.08) readonly property color barelyTranslucentWhite: Qt.rgba(1, 1, 1, 0.05) + readonly property color translucentMidnightBlack: Qt.rgba(14/255, 14/255, 17/255, 0.8) + readonly property color softGoldenApricot: Qt.rgba(251/255, 178/255, 106/255, 0.3) + readonly property color mistyGray: Qt.rgba(215/255, 216/255, 219/255, 0.8) + readonly property color cloudyGray: Qt.rgba(215/255, 216/255, 219/255, 0.65) } } diff --git a/client/ui/qml/Pages2/PageHome.qml b/client/ui/qml/Pages2/PageHome.qml index 8074337a..8422a10f 100644 --- a/client/ui/qml/Pages2/PageHome.qml +++ b/client/ui/qml/Pages2/PageHome.qml @@ -98,7 +98,6 @@ PageType { pressedColor: AmneziaStyle.color.sheerWhite disabledColor: AmneziaStyle.color.mutedGray textColor: AmneziaStyle.color.mutedGray - leftImageColor: AmneziaStyle.color.transparent borderWidth: 0 buttonTextLabel.lineHeight: 20 @@ -110,7 +109,7 @@ PageType { text: isSplitTunnelingEnabled ? qsTr("Split tunneling enabled") : qsTr("Split tunneling disabled") - imageSource: isSplitTunnelingEnabled ? "qrc:/images/controls/split-tunneling.svg" : "" + leftImageSource: isSplitTunnelingEnabled ? "qrc:/images/controls/split-tunneling.svg" : "" rightImageSource: "qrc:/images/controls/chevron-down.svg" Keys.onEnterPressed: splitTunnelingButton.clicked() @@ -166,6 +165,7 @@ PageType { anchors.left: parent.left anchors.right: parent.right + spacing: 0 Component.onCompleted: { drawer.collapsedHeight = collapsed.implicitHeight @@ -267,18 +267,39 @@ PageType { RowLayout { Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter - Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 89 : 44 + Layout.topMargin: 8 + Layout.bottomMargin: drawer.isCollapsed ? 44 : ServersModel.isDefaultServerFromApi ? 61 : 16 spacing: 0 - Image { - Layout.rightMargin: 8 - visible: source !== "" - source: ServersModel.defaultServerImagePathCollapsed - } + BasicButtonType { + enabled: (ServersModel.defaultServerImagePathCollapsed !== "") && drawer.isCollapsed + hoverEnabled: enabled + + implicitHeight: 36 + + leftPadding: 16 + rightPadding: 16 + + defaultColor: AmneziaStyle.color.transparent + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite + disabledColor: AmneziaStyle.color.transparent + textColor: AmneziaStyle.color.mutedGray + + buttonTextLabel.lineHeight: 16 + buttonTextLabel.font.pixelSize: 13 + buttonTextLabel.font.weight: 400 - LabelTextType { - id: collapsedServerMenuDescription text: drawer.isCollapsed ? ServersModel.defaultServerDescriptionCollapsed : ServersModel.defaultServerDescriptionExpanded + leftImageSource: ServersModel.defaultServerImagePathCollapsed + changeLeftImageSize: false + + rightImageSource: hoverEnabled ? "qrc:/images/controls/chevron-down.svg" : "" + + onClicked: { + ServersModel.processedIndex = ServersModel.defaultIndex + PageController.goToPage(PageEnum.PageSettingsServerInfo) + } } } } @@ -316,8 +337,8 @@ PageType { rootButtonImageColor: AmneziaStyle.color.midnightBlack rootButtonBackgroundColor: AmneziaStyle.color.paleGray - rootButtonBackgroundHoveredColor: Qt.rgba(215, 216, 219, 0.8) - rootButtonBackgroundPressedColor: Qt.rgba(215, 216, 219, 0.65) + rootButtonBackgroundHoveredColor: AmneziaStyle.color.mistyGray + rootButtonBackgroundPressedColor: AmneziaStyle.color.cloudyGray rootButtonHoveredBorderColor: AmneziaStyle.color.transparent rootButtonDefaultBorderColor: AmneziaStyle.color.transparent rootButtonTextTopMargin: 8 diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index f23e36d9..2d6c1d9b 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -132,8 +132,8 @@ PageType { implicitHeight: 32 defaultColor: "transparent" - hoveredColor: Qt.rgba(1, 1, 1, 0.08) - pressedColor: Qt.rgba(1, 1, 1, 0.12) + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite textColor: AmneziaStyle.color.vibrantRed text: qsTr("Reload API config") @@ -172,8 +172,8 @@ PageType { implicitHeight: 32 defaultColor: "transparent" - hoveredColor: Qt.rgba(1, 1, 1, 0.08) - pressedColor: Qt.rgba(1, 1, 1, 0.12) + hoveredColor: AmneziaStyle.color.translucentWhite + pressedColor: AmneziaStyle.color.sheerWhite textColor: AmneziaStyle.color.vibrantRed text: qsTr("Remove from application") diff --git a/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml index 85a50393..f726cd49 100644 --- a/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml +++ b/client/ui/qml/Pages2/PageSetupWizardApiServicesList.qml @@ -16,83 +16,82 @@ PageType { defaultActiveFocusItem: focusItem - FlickableType { - id: fl + ColumnLayout { + id: header + anchors.top: parent.top - anchors.bottom: parent.bottom - contentHeight: content.height + anchors.left: parent.left + anchors.right: parent.right - ColumnLayout { - id: content + spacing: 0 - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + Item { + id: focusItem + KeyNavigation.tab: backButton + } - spacing: 0 - - Item { - id: focusItem - KeyNavigation.tab: backButton - } - - BackButtonType { - id: backButton - Layout.topMargin: 20 + BackButtonType { + id: backButton + Layout.topMargin: 20 // KeyNavigation.tab: fileButton.rightButton - } + } - HeaderType { - Layout.fillWidth: true - Layout.topMargin: 8 - Layout.rightMargin: 16 - Layout.leftMargin: 16 - Layout.bottomMargin: 32 + HeaderType { + Layout.fillWidth: true + Layout.topMargin: 8 + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 16 - headerText: qsTr("VPN by Amnezia") - descriptionText: qsTr("Choose a VPN service that suits your needs.") - } + headerText: qsTr("VPN by Amnezia") + descriptionText: qsTr("Choose a VPN service that suits your needs.") + } + } - ListView { - id: containers - width: parent.width - height: containers.contentItem.height - spacing: 16 + ListView { + id: servicesListView + anchors.top: header.bottom + anchors.right: parent.right + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.topMargin: 16 + spacing: 0 - currentIndex: 1 - interactive: false - model: ApiServicesModel + currentIndex: 1 + clip: true + model: ApiServicesModel - delegate: Item { - implicitWidth: containers.width - implicitHeight: delegateContent.implicitHeight + ScrollBar.vertical: ScrollBar {} - ColumnLayout { - id: delegateContent + delegate: Item { + implicitWidth: servicesListView.width + implicitHeight: delegateContent.implicitHeight - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right + ColumnLayout { + id: delegateContent - CardWithIconsType { - id: card + anchors.fill: parent - Layout.fillWidth: true - Layout.rightMargin: 16 - Layout.leftMargin: 16 + CardWithIconsType { + id: card - headerText: name - bodyText: cardDescription - footerText: price + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + Layout.bottomMargin: 16 - rightImageSource: "qrc:/images/controls/chevron-right.svg" + headerText: name + bodyText: cardDescription + footerText: price - onClicked: { - if (isServiceAvailable) { - ApiServicesModel.setServiceIndex(index) - PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo) - } - } + rightImageSource: "qrc:/images/controls/chevron-right.svg" + + enabled: isServiceAvailable + + onClicked: { + if (isServiceAvailable) { + ApiServicesModel.setServiceIndex(index) + PageController.goToPage(PageEnum.PageSetupWizardApiServiceInfo) } } } diff --git a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml index 7c031997..f973c89c 100644 --- a/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml +++ b/client/ui/qml/Pages2/PageSetupWizardConfigSource.qml @@ -47,7 +47,6 @@ PageType { KeyNavigation.tab: textKey.textField } - HeaderType { property bool isVisible: SettingsController.getInstallationUuid() !== "" || PageController.isStartPageVisible() diff --git a/client/ui/qml/Pages2/PageShare.qml b/client/ui/qml/Pages2/PageShare.qml index c7dbb425..d6ce7848 100644 --- a/client/ui/qml/Pages2/PageShare.qml +++ b/client/ui/qml/Pages2/PageShare.qml @@ -573,7 +573,7 @@ PageType { visible: accessTypeSelector.currentIndex === 0 text: qsTr("Share") - imageSource: "qrc:/images/controls/share-2.svg" + leftImageSource: "qrc:/images/controls/share-2.svg" Keys.onTabPressed: lastItemTabClicked(focusItem) diff --git a/client/ui/qml/Pages2/PageShareFullAccess.qml b/client/ui/qml/Pages2/PageShareFullAccess.qml index 2a565230..404ba563 100644 --- a/client/ui/qml/Pages2/PageShareFullAccess.qml +++ b/client/ui/qml/Pages2/PageShareFullAccess.qml @@ -135,7 +135,7 @@ PageType { Layout.topMargin: 40 text: qsTr("Share") - imageSource: "qrc:/images/controls/share-2.svg" + leftImageSource: "qrc:/images/controls/share-2.svg" Keys.onTabPressed: lastItemTabClicked(focusItem) diff --git a/deploy/data/linux/post_install.sh b/deploy/data/linux/post_install.sh index b3345bac..324462d9 100755 --- a/deploy/data/linux/post_install.sh +++ b/deploy/data/linux/post_install.sh @@ -19,6 +19,11 @@ date > $LOG_FILE echo "Script started" >> $LOG_FILE sudo killall -9 $APP_NAME 2>> $LOG_FILE +if command -v steamos-readonly &> /dev/null; then + sudo steamos-readonly disable >> $LOG_FILE + echo "steamos-readonly disabled" >> $LOG_FILE +fi + if sudo systemctl is-active --quiet $APP_NAME; then sudo systemctl stop $APP_NAME >> $LOG_FILE sudo systemctl disable $APP_NAME >> $LOG_FILE @@ -42,6 +47,11 @@ sudo chmod 555 /usr/share/applications/$APP_NAME.desktop >> $LOG_FILE echo "user desktop creation loop ended" >> $LOG_FILE +if command -v steamos-readonly &> /dev/null; then + sudo steamos-readonly enable >> $LOG_FILE + echo "steamos-readonly enabled" >> $LOG_FILE +fi + date >> $LOG_FILE echo "Service status:" >> $LOG_FILE sudo systemctl status $APP_NAME >> $LOG_FILE diff --git a/deploy/data/linux/post_uninstall.sh b/deploy/data/linux/post_uninstall.sh index 5849a90e..98090d20 100755 --- a/deploy/data/linux/post_uninstall.sh +++ b/deploy/data/linux/post_uninstall.sh @@ -13,6 +13,11 @@ date >> $LOG_FILE echo "Uninstall Script started" >> $LOG_FILE sudo killall -9 $APP_NAME 2>> $LOG_FILE +if command -v steamos-readonly &> /dev/null; then + sudo steamos-readonly disable >> $LOG_FILE + echo "steamos-readonly disabled" >> $LOG_FILE +fi + ls /opt/AmneziaVPN/client/lib/* | while IFS=: read -r dir; do sudo unlink $dir >> $LOG_FILE done @@ -59,6 +64,11 @@ if test -f /usr/share/pixmaps/$APP_NAME.png; then fi +if command -v steamos-readonly &> /dev/null; then + sudo steamos-readonly enable >> $LOG_FILE + echo "steamos-readonly enabled" >> $LOG_FILE +fi + date >> $LOG_FILE echo "Service after uninstall status:" >> $LOG_FILE sudo systemctl status $APP_NAME >> $LOG_FILE diff --git a/metadata/img-readme/apl.png b/metadata/img-readme/apl.png deleted file mode 100644 index 6dedfa12..00000000 Binary files a/metadata/img-readme/apl.png and /dev/null differ diff --git a/metadata/img-readme/download-alt.svg b/metadata/img-readme/download-alt.svg new file mode 100644 index 00000000..f97c9c3d --- /dev/null +++ b/metadata/img-readme/download-alt.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/metadata/img-readme/download-website.svg b/metadata/img-readme/download-website.svg new file mode 100644 index 00000000..d0cf8375 --- /dev/null +++ b/metadata/img-readme/download-website.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/metadata/img-readme/download.png b/metadata/img-readme/download.png deleted file mode 100644 index 0e6a8850..00000000 Binary files a/metadata/img-readme/download.png and /dev/null differ diff --git a/metadata/img-readme/play.png b/metadata/img-readme/play.png deleted file mode 100644 index 2fb316c8..00000000 Binary files a/metadata/img-readme/play.png and /dev/null differ