diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 220fbd79..be4d068b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -297,24 +297,24 @@ jobs: env: ANDROID_BUILD_PLATFORM: android-34 - QT_VERSION: 6.6.2 + QT_VERSION: 6.7.2 QT_MODULES: 'qtremoteobjects qt5compat qtimageformats qtshadertools' PROD_AGW_PUBLIC_KEY: ${{ secrets.PROD_AGW_PUBLIC_KEY }} steps: - name: 'Install desktop Qt' - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} host: 'linux' target: 'desktop' - arch: 'gcc_64' + arch: 'linux_gcc_64' modules: ${{ env.QT_MODULES }} dir: ${{ runner.temp }} extra: '--external 7z --base ${{ env.QT_MIRROR }}' - name: 'Install android_x86_64 Qt' - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} host: 'linux' @@ -325,7 +325,7 @@ jobs: extra: '--external 7z --base ${{ env.QT_MIRROR }}' - name: 'Install android_x86 Qt' - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} host: 'linux' @@ -336,7 +336,7 @@ jobs: extra: '--external 7z --base ${{ env.QT_MIRROR }}' - name: 'Install android_armv7 Qt' - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} host: 'linux' @@ -347,7 +347,7 @@ jobs: extra: '--external 7z --base ${{ env.QT_MIRROR }}' - name: 'Install android_arm64_v8a Qt' - uses: jurplel/install-qt-action@v3 + uses: jurplel/install-qt-action@v4 with: version: ${{ env.QT_VERSION }} host: 'linux' diff --git a/client/3rd/qtkeychain b/client/3rd/qtkeychain index 74776e2a..7460df6a 160000 --- a/client/3rd/qtkeychain +++ b/client/3rd/qtkeychain @@ -1 +1 @@ -Subproject commit 74776e2a3e2d98d19943e0968901c5b5e04cc1bd +Subproject commit 7460df6a978669290de5b56c2d98b199b61c3f88 diff --git a/client/android/AndroidManifest.xml b/client/android/AndroidManifest.xml index c1c40b52..179def86 100644 --- a/client/android/AndroidManifest.xml +++ b/client/android/AndroidManifest.xml @@ -3,7 +3,6 @@ @@ -46,7 +45,7 @@ android:configChanges="uiMode|screenSize|smallestScreenSize|screenLayout|orientation|density |fontScale|layoutDirection|locale|keyboard|keyboardHidden|navigation|mcc|mnc" android:launchMode="singleInstance" - android:windowSoftInputMode="adjustResize" + android:windowSoftInputMode="stateUnchanged|adjustResize" android:exported="true"> @@ -68,9 +67,6 @@ android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --" /> - + + /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/client/android/gradlew.bat b/client/android/gradlew.bat index 93e3f59f..9d21a218 100644 --- a/client/android/gradlew.bat +++ b/client/android/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -43,11 +45,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/client/android/qt/build.gradle.kts b/client/android/qt/build.gradle.kts index 139adf4f..6b1d3fd1 100644 --- a/client/android/qt/build.gradle.kts +++ b/client/android/qt/build.gradle.kts @@ -21,5 +21,5 @@ android { } dependencies { - implementation(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar")))) + api(fileTree(mapOf("dir" to "../libs", "include" to listOf("*.jar")))) } diff --git a/client/android/res/values/styles.xml b/client/android/res/values/styles.xml index 9f4201f8..bc67beb9 100644 --- a/client/android/res/values/styles.xml +++ b/client/android/res/values/styles.xml @@ -1,6 +1,9 @@ + #FF0E0E11 diff --git a/client/android/settings.gradle.kts b/client/android/settings.gradle.kts index 5cfc8314..68426ec8 100644 --- a/client/android/settings.gradle.kts +++ b/client/android/settings.gradle.kts @@ -22,7 +22,7 @@ dependencyResolutionManagement { includeBuild("./gradle/plugins") plugins { - id("com.android.settings") version "8.2.0" + id("com.android.settings") version "8.5.2" id("settings-property-delegate") } diff --git a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt index 8a78750b..9d1c31cb 100644 --- a/client/android/src/org/amnezia/vpn/AmneziaActivity.kt +++ b/client/android/src/org/amnezia/vpn/AmneziaActivity.kt @@ -158,6 +158,10 @@ class AmneziaActivity : QtActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d(TAG, "Create Amnezia activity: $intent") + window.apply { + addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) + statusBarColor = getColor(R.color.black) + } mainScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) val proto = mainScope.async(Dispatchers.IO) { VpnStateStore.getVpnState().vpnProto @@ -610,6 +614,14 @@ class AmneziaActivity : QtActivity() { } } + @Suppress("unused") + fun setNavigationBarColor(color: Int) { + Log.v(TAG, "Change navigation bar color: ${"#%08X".format(color)}") + mainScope.launch { + window.navigationBarColor = color + } + } + @Suppress("unused") fun minimizeApp() { Log.v(TAG, "Minimize application") @@ -684,6 +696,17 @@ class AmneziaActivity : QtActivity() { .show() } + @Suppress("unused") + fun requestAuthentication() { + Log.v(TAG, "Request authentication") + mainScope.launch { + qtInitialized.await() + Intent(this@AmneziaActivity, AuthActivity::class.java).also { + startActivity(it) + } + } + } + /** * Utils methods */ diff --git a/client/android/src/org/amnezia/vpn/AuthActivity.kt b/client/android/src/org/amnezia/vpn/AuthActivity.kt new file mode 100644 index 00000000..2593315c --- /dev/null +++ b/client/android/src/org/amnezia/vpn/AuthActivity.kt @@ -0,0 +1,97 @@ +package org.amnezia.vpn + +import android.os.Build +import android.os.Bundle +import androidx.biometric.BiometricManager +import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG +import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL +import androidx.biometric.BiometricPrompt +import androidx.biometric.BiometricPrompt.AuthenticationResult +import androidx.core.content.ContextCompat +import androidx.fragment.app.FragmentActivity +import org.amnezia.vpn.qt.QtAndroidController +import org.amnezia.vpn.util.Log + +private const val TAG = "AuthActivity" + +private const val AUTHENTICATORS = BIOMETRIC_STRONG or DEVICE_CREDENTIAL + +class AuthActivity : FragmentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val biometricManager = BiometricManager.from(applicationContext) + when (biometricManager.canAuthenticate(AUTHENTICATORS)) { + BiometricManager.BIOMETRIC_SUCCESS -> { + showBiometricPrompt(biometricManager) + return + } + + BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> { + Log.w(TAG, "Unknown biometric status") + showBiometricPrompt(biometricManager) + return + } + + BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> { + Log.e(TAG, "The specified options are incompatible with the current Android " + + "version ${Build.VERSION.SDK_INT}") + } + + BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> { + Log.w(TAG, "The hardware is unavailable") + } + + BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> { + Log.w(TAG, "No biometric or device credential is enrolled") + } + + BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> { + Log.w(TAG, "There is no suitable hardware") + } + + BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> { + Log.w(TAG, "A security vulnerability has been discovered with one or " + + "more hardware sensors") + } + } + QtAndroidController.onAuthResult(true) + finish() + } + + private fun showBiometricPrompt(biometricManager: BiometricManager) { + val executor = ContextCompat.getMainExecutor(applicationContext) + val biometricPrompt = BiometricPrompt(this, executor, + object : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationSucceeded(result: AuthenticationResult) { + super.onAuthenticationSucceeded(result) + Log.d(TAG, "Authentication succeeded") + QtAndroidController.onAuthResult(true) + finish() + } + + override fun onAuthenticationFailed() { + super.onAuthenticationFailed() + Log.w(TAG, "Authentication failed") + } + + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + super.onAuthenticationError(errorCode, errString) + Log.e(TAG, "Authentication error $errorCode: $errString") + QtAndroidController.onAuthResult(false) + finish() + } + }) + + + + val promptInfo = BiometricPrompt.PromptInfo.Builder() + .setAllowedAuthenticators(AUTHENTICATORS) + .setTitle("AmneziaVPN") + .setSubtitle(biometricManager.getStrings(AUTHENTICATORS)?.promptMessage) + .build() + + biometricPrompt.authenticate(promptInfo) + } +} diff --git a/client/android/src/org/amnezia/vpn/AuthHelper.java b/client/android/src/org/amnezia/vpn/AuthHelper.java deleted file mode 100644 index 940d03c2..00000000 --- a/client/android/src/org/amnezia/vpn/AuthHelper.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.amnezia.vpn; - -import android.content.Context; -import android.app.KeyguardManager; -import android.content.Intent; -import org.qtproject.qt.android.bindings.QtActivity; - - -import static android.content.Context.KEYGUARD_SERVICE; - -public class AuthHelper extends QtActivity { - - static final String TAG = "AuthHelper"; - - public static Intent getAuthIntent(Context context) { - KeyguardManager mKeyguardManager = (KeyguardManager)context.getSystemService(KEYGUARD_SERVICE); - if (mKeyguardManager.isDeviceSecure()) { - return mKeyguardManager.createConfirmDeviceCredentialIntent(null, null); - } else { - return null; - } - } - -} diff --git a/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt b/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt index cae7ab75..9faa30d0 100644 --- a/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt +++ b/client/android/src/org/amnezia/vpn/ImportConfigActivity.kt @@ -33,10 +33,10 @@ class ImportConfigActivity : ComponentActivity() { intent?.let(::readConfig) } - override fun onNewIntent(intent: Intent?) { + override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) Log.d(TAG, "onNewIntent: $intent") - intent?.let(::readConfig) + intent.let(::readConfig) } private fun readConfig(intent: Intent) { diff --git a/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt b/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt index e382b080..4af138a2 100644 --- a/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt +++ b/client/android/src/org/amnezia/vpn/qt/QtAndroidController.kt @@ -25,5 +25,7 @@ object QtAndroidController { external fun onConfigImported(data: String) + external fun onAuthResult(result: Boolean) + external fun decodeQrCode(data: String): Boolean } \ No newline at end of file diff --git a/client/android/utils/src/main/kotlin/net/NetworkState.kt b/client/android/utils/src/main/kotlin/net/NetworkState.kt index 26d23215..b71bf393 100644 --- a/client/android/utils/src/main/kotlin/net/NetworkState.kt +++ b/client/android/utils/src/main/kotlin/net/NetworkState.kt @@ -88,7 +88,7 @@ class NetworkState( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { connectivityManager.registerBestMatchingNetworkCallback(networkRequest, networkCallback, handler) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val numberAttempts = 3 + val numberAttempts = 300 var attemptCount = 0 while(true) { try { diff --git a/client/cmake/android.cmake b/client/cmake/android.cmake index 13c357bd..c96d9ab8 100644 --- a/client/cmake/android.cmake +++ b/client/cmake/android.cmake @@ -27,7 +27,6 @@ link_directories(${CMAKE_CURRENT_SOURCE_DIR}/platforms/android) set(HEADERS ${HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.h ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.h - ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.h ${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.h ${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.h ) @@ -35,7 +34,6 @@ set(HEADERS ${HEADERS} set(SOURCES ${SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_controller.cpp ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/android_utils.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/authResultReceiver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/protocols/android_vpnprotocol.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/installedAppsImageProvider.cpp ) diff --git a/client/platforms/android/android_controller.cpp b/client/platforms/android/android_controller.cpp index c9ee3cfd..2790eb1b 100644 --- a/client/platforms/android/android_controller.cpp +++ b/client/platforms/android/android_controller.cpp @@ -98,6 +98,7 @@ bool AndroidController::initialize() {"onStatisticsUpdate", "(JJ)V", reinterpret_cast(onStatisticsUpdate)}, {"onFileOpened", "(Ljava/lang/String;)V", reinterpret_cast(onFileOpened)}, {"onConfigImported", "(Ljava/lang/String;)V", reinterpret_cast(onConfigImported)}, + {"onAuthResult", "(Z)V", reinterpret_cast(onAuthResult)}, {"decodeQrCode", "(Ljava/lang/String;)Z", reinterpret_cast(decodeQrCode)} }; @@ -210,6 +211,11 @@ void AndroidController::setScreenshotsEnabled(bool enabled) callActivityMethod("setScreenshotsEnabled", "(Z)V", enabled); } +void AndroidController::setNavigationBarColor(unsigned int color) +{ + callActivityMethod("setNavigationBarColor", "(I)V", color); +} + void AndroidController::minimizeApp() { callActivityMethod("minimizeApp", "()V"); @@ -265,6 +271,22 @@ void AndroidController::requestNotificationPermission() callActivityMethod("requestNotificationPermission", "()V"); } +bool AndroidController::requestAuthentication() +{ + QEventLoop wait; + bool result; + connect(this, &AndroidController::authenticationResult, this, + [&result, &wait](const bool &authResult){ + qDebug() << "Android authentication result:" << authResult; + result = authResult; + wait.quit(); + }, + static_cast(Qt::QueuedConnection | Qt::SingleShotConnection)); + callActivityMethod("requestAuthentication", "()V"); + wait.exec(); + return result; +} + // Moving log processing to the Android side jclass AndroidController::log; jmethodID AndroidController::logDebug; @@ -462,6 +484,14 @@ void AndroidController::onConfigImported(JNIEnv *env, jobject thiz, jstring data emit AndroidController::instance()->configImported(AndroidUtils::convertJString(env, data)); } +// static +void AndroidController::onAuthResult(JNIEnv *env, jobject thiz, jboolean result) +{ + Q_UNUSED(thiz); + + emit AndroidController::instance()->authenticationResult(result); +} + // static bool AndroidController::decodeQrCode(JNIEnv *env, jobject thiz, jstring data) { diff --git a/client/platforms/android/android_controller.h b/client/platforms/android/android_controller.h index 1041c31f..759c9c3f 100644 --- a/client/platforms/android/android_controller.h +++ b/client/platforms/android/android_controller.h @@ -41,11 +41,13 @@ public: void exportLogsFile(const QString &fileName); void clearLogs(); void setScreenshotsEnabled(bool enabled); + void setNavigationBarColor(unsigned int color); void minimizeApp(); QJsonArray getAppList(); QPixmap getAppIcon(const QString &package, QSize *size, const QSize &requestedSize); bool isNotificationPermissionGranted(); void requestNotificationPermission(); + bool requestAuthentication(); static bool initLogging(); static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message); @@ -63,6 +65,7 @@ signals: void configImported(QString config); void importConfigFromOutside(QString config); void initConnectionState(Vpn::ConnectionState state); + void authenticationResult(bool result); private: bool isWaitingStatus = true; @@ -89,6 +92,7 @@ private: static void onStatisticsUpdate(JNIEnv *env, jobject thiz, jlong rxBytes, jlong txBytes); static void onConfigImported(JNIEnv *env, jobject thiz, jstring data); static void onFileOpened(JNIEnv *env, jobject thiz, jstring uri); + static void onAuthResult(JNIEnv *env, jobject thiz, jboolean result); static bool decodeQrCode(JNIEnv *env, jobject thiz, jstring data); template diff --git a/client/platforms/android/authResultReceiver.cpp b/client/platforms/android/authResultReceiver.cpp deleted file mode 100644 index 21e838a2..00000000 --- a/client/platforms/android/authResultReceiver.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "authResultReceiver.h" - -AuthResultReceiver::AuthResultReceiver(QSharedPointer ¬ifier) : m_notifier(notifier) -{ -} - -void AuthResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data) -{ - qDebug() << "receiverRequestCode" << receiverRequestCode << "resultCode" << resultCode; - - if (resultCode == -1) { // ResultOK - emit m_notifier->authSuccessful(); - } else { - emit m_notifier->authFailed(); - } -} diff --git a/client/platforms/android/authResultReceiver.h b/client/platforms/android/authResultReceiver.h deleted file mode 100644 index 9a88dcf5..00000000 --- a/client/platforms/android/authResultReceiver.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef AUTHRESULTRECEIVER_H -#define AUTHRESULTRECEIVER_H - -#include - -#include - -class AuthResultNotifier : public QObject -{ - Q_OBJECT - -public: - AuthResultNotifier(QObject *parent = nullptr) : QObject(parent) {}; - -signals: - void authFailed(); - void authSuccessful(); -}; - -/* Auth result handler for Android */ -class AuthResultReceiver final : public QAndroidActivityResultReceiver -{ -public: - AuthResultReceiver(QSharedPointer ¬ifier); - - void handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data) override; - -private: - QSharedPointer m_notifier; -}; - -#endif // AUTHRESULTRECEIVER_H diff --git a/client/ui/controllers/exportController.cpp b/client/ui/controllers/exportController.cpp index 20c32409..2690b5b1 100644 --- a/client/ui/controllers/exportController.cpp +++ b/client/ui/controllers/exportController.cpp @@ -10,9 +10,6 @@ #include "core/controllers/vpnConfigurationController.h" #include "systemController.h" -#ifdef Q_OS_ANDROID - #include "platforms/android/android_utils.h" -#endif #include "qrcodegen.hpp" ExportController::ExportController(const QSharedPointer &serversModel, const QSharedPointer &containersModel, @@ -24,12 +21,6 @@ ExportController::ExportController(const QSharedPointer &serversMo m_clientManagementModel(clientManagementModel), m_settings(settings) { -#ifdef Q_OS_ANDROID - m_authResultNotifier.reset(new AuthResultNotifier); - m_authResultReceiver.reset(new AuthResultReceiver(m_authResultNotifier)); - connect(m_authResultNotifier.get(), &AuthResultNotifier::authFailed, this, [this]() { emit exportErrorOccurred(tr("Access error!")); }); - connect(m_authResultNotifier.get(), &AuthResultNotifier::authSuccessful, this, &ExportController::generateFullAccessConfig); -#endif } void ExportController::generateFullAccessConfig() @@ -63,26 +54,6 @@ void ExportController::generateFullAccessConfig() emit exportConfigChanged(); } -#if defined(Q_OS_ANDROID) -void ExportController::generateFullAccessConfigAndroid() -{ - /* We use builtin keyguard for ssh key export protection on Android */ - QJniObject activity = AndroidUtils::getActivity(); - auto appContext = activity.callObjectMethod("getApplicationContext", "()Landroid/content/Context;"); - if (appContext.isValid()) { - auto intent = QJniObject::callStaticObjectMethod("org/amnezia/vpn/AuthHelper", "getAuthIntent", - "(Landroid/content/Context;)Landroid/content/Intent;", appContext.object()); - if (intent.isValid()) { - if (intent.object() != nullptr) { - QtAndroidPrivate::startActivity(intent.object(), 1, m_authResultReceiver.get()); - } - } else { - generateFullAccessConfig(); - } - } -} -#endif - void ExportController::generateConnectionConfig(const QString &clientName) { clearPreviousConfig(); diff --git a/client/ui/controllers/exportController.h b/client/ui/controllers/exportController.h index 023f22cf..b031ea39 100644 --- a/client/ui/controllers/exportController.h +++ b/client/ui/controllers/exportController.h @@ -6,9 +6,6 @@ #include "ui/models/clientManagementModel.h" #include "ui/models/containers_model.h" #include "ui/models/servers_model.h" -#ifdef Q_OS_ANDROID - #include "platforms/android/authResultReceiver.h" -#endif class ExportController : public QObject { @@ -25,9 +22,6 @@ public: public slots: void generateFullAccessConfig(); -#if defined(Q_OS_ANDROID) - void generateFullAccessConfigAndroid(); -#endif void generateConnectionConfig(const QString &clientName); void generateOpenVpnConfig(const QString &clientName); void generateWireGuardConfig(const QString &clientName); @@ -74,11 +68,6 @@ private: QString m_config; QString m_nativeConfigString; QList m_qrCodes; - -#ifdef Q_OS_ANDROID - QSharedPointer m_authResultNotifier; - QSharedPointer m_authResultReceiver; -#endif }; #endif // EXPORTCONTROLLER_H diff --git a/client/ui/controllers/pageController.cpp b/client/ui/controllers/pageController.cpp index 3e5e5cc3..9daca272 100644 --- a/client/ui/controllers/pageController.cpp +++ b/client/ui/controllers/pageController.cpp @@ -10,8 +10,6 @@ #ifdef Q_OS_ANDROID #include "platforms/android/android_controller.h" - #include "platforms/android/android_utils.h" - #include #endif #if defined Q_OS_MAC #include "ui/macos_util.h" @@ -22,18 +20,8 @@ PageController::PageController(const QSharedPointer &serversModel, : QObject(parent), m_serversModel(serversModel), m_settings(settings) { #ifdef Q_OS_ANDROID - // Change color of navigation and status bar's auto initialPageNavigationBarColor = getInitialPageNavigationBarColor(); - AndroidUtils::runOnAndroidThreadSync([&initialPageNavigationBarColor]() { - QJniObject activity = AndroidUtils::getActivity(); - QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); - if (window.isValid()) { - window.callMethod("addFlags", "(I)V", 0x80000000); - window.callMethod("clearFlags", "(I)V", 0x04000000); - window.callMethod("setStatusBarColor", "(I)V", 0xFF0E0E11); - window.callMethod("setNavigationBarColor", "(I)V", initialPageNavigationBarColor); - } - }); + AndroidController::instance()->setNavigationBarColor(initialPageNavigationBarColor); #endif #if defined Q_OS_MACX @@ -115,14 +103,7 @@ unsigned int PageController::getInitialPageNavigationBarColor() void PageController::updateNavigationBarColor(const int color) { #ifdef Q_OS_ANDROID - // Change color of navigation bar - AndroidUtils::runOnAndroidThreadSync([&color]() { - QJniObject activity = AndroidUtils::getActivity(); - QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); - if (window.isValid()) { - window.callMethod("setNavigationBarColor", "(I)V", color); - } - }); + AndroidController::instance()->setNavigationBarColor(color); #endif } diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 93fd8971..911bcb16 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -285,3 +285,12 @@ QString SettingsController::getGatewayEndpoint() { return m_settings->getGatewayEndpoint(); } + +bool SettingsController::isOnTv() +{ +#ifdef Q_OS_ANDROID + return AndroidController::instance()->isOnTv(); +#else + return false; +#endif +} \ No newline at end of file diff --git a/client/ui/controllers/settingsController.h b/client/ui/controllers/settingsController.h index a18888a9..89de1bf8 100644 --- a/client/ui/controllers/settingsController.h +++ b/client/ui/controllers/settingsController.h @@ -82,6 +82,8 @@ public slots: void setGatewayEndpoint(const QString &endpoint); QString getGatewayEndpoint(); + bool isOnTv(); + signals: void primaryDnsChanged(); void secondaryDnsChanged(); diff --git a/client/ui/controllers/systemController.cpp b/client/ui/controllers/systemController.cpp index e6a9a28e..4598bff1 100644 --- a/client/ui/controllers/systemController.cpp +++ b/client/ui/controllers/systemController.cpp @@ -125,3 +125,12 @@ void SystemController::setQmlRoot(QObject *qmlRoot) { m_qmlRoot = qmlRoot; } + +bool SystemController::isAuthenticated() +{ +#ifdef Q_OS_ANDROID + return AndroidController::instance()->requestAuthentication(); +#else + return true; +#endif +} diff --git a/client/ui/controllers/systemController.h b/client/ui/controllers/systemController.h index 274df234..d2ee6f63 100644 --- a/client/ui/controllers/systemController.h +++ b/client/ui/controllers/systemController.h @@ -19,6 +19,7 @@ public slots: void setQmlRoot(QObject *qmlRoot); + bool isAuthenticated(); signals: void fileDialogClosed(const bool isAccepted); diff --git a/client/ui/qml/Components/ConnectButton.qml b/client/ui/qml/Components/ConnectButton.qml index cb706158..fa18703b 100644 --- a/client/ui/qml/Components/ConnectButton.qml +++ b/client/ui/qml/Components/ConnectButton.qml @@ -14,6 +14,7 @@ Button { property string defaultButtonColor: AmneziaStyle.color.paleGray property string progressButtonColor: AmneziaStyle.color.paleGray property string connectedButtonColor: AmneziaStyle.color.goldenApricot + property bool buttonActiveFocus: activeFocus && (Qt.platform.os !== "android" || SettingsController.isOnTv()) implicitWidth: 190 implicitHeight: 190 @@ -50,14 +51,14 @@ Button { verticalOffset: 0 radius: 10 samples: 25 - color: root.activeFocus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.goldenApricot + color: root.buttonActiveFocus ? AmneziaStyle.color.paleGray : AmneziaStyle.color.goldenApricot source: backgroundCircle } ShapePath { fillColor: AmneziaStyle.color.transparent strokeColor: AmneziaStyle.color.paleGray - strokeWidth: root.activeFocus ? 1 : 0 + strokeWidth: root.buttonActiveFocus ? 1 : 0 capStyle: ShapePath.RoundCap PathAngleArc { @@ -81,14 +82,14 @@ Button { return defaultButtonColor } } - strokeWidth: root.activeFocus ? 2 : 3 + strokeWidth: root.buttonActiveFocus ? 2 : 3 capStyle: ShapePath.RoundCap PathAngleArc { centerX: backgroundCircle.width / 2 centerY: backgroundCircle.height / 2 - radiusX: 93 - (root.activeFocus ? 2 : 0) - radiusY: 93 - (root.activeFocus ? 2 : 0) + radiusX: 93 - (root.buttonActiveFocus ? 2 : 0) + radiusY: 93 - (root.buttonActiveFocus ? 2 : 0) startAngle: 0 sweepAngle: 360 } diff --git a/client/ui/qml/Pages2/PageShareFullAccess.qml b/client/ui/qml/Pages2/PageShareFullAccess.qml index 4807c030..2a565230 100644 --- a/client/ui/qml/Pages2/PageShareFullAccess.qml +++ b/client/ui/qml/Pages2/PageShareFullAccess.qml @@ -140,22 +140,23 @@ PageType { Keys.onTabPressed: lastItemTabClicked(focusItem) clickedFunc: function() { + PageController.showBusyIndicator(true) + + if (Qt.platform.os === "android" && !SystemController.isAuthenticated()) { + PageController.showBusyIndicator(false) + ExportController.exportErrorOccurred(qsTr("Access error!")) + return + } else { + ExportController.generateFullAccessConfig() + } + shareConnectionDrawer.headerText = qsTr("Connection to ") + serverSelector.text shareConnectionDrawer.configContentHeaderText = qsTr("File with connection settings to ") + serverSelector.text shareConnectionDrawer.open() - shareConnectionDrawer.contentVisible = false - PageController.showBusyIndicator(true) - - if (Qt.platform.os === "android") { - ExportController.generateFullAccessConfigAndroid(); - } else { - ExportController.generateFullAccessConfig(); - } + shareConnectionDrawer.contentVisible = true PageController.showBusyIndicator(false) - - shareConnectionDrawer.contentVisible = true } } }