Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve navigation cpp #1061

Open
wants to merge 94 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 74 commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
02bbcd3
add focusController class
CyAn84 Sep 14, 2024
cecee37
add more key handlers
CyAn84 Sep 19, 2024
01e31b4
add focus navigation to qml
CyAn84 Sep 19, 2024
78a4caa
fixed language selector
CyAn84 Sep 19, 2024
f189f7b
add reverse focus change to FocusController
CyAn84 Sep 24, 2024
b4f4ec4
add default focus item
CyAn84 Sep 29, 2024
3c655d0
update transitions
CyAn84 Sep 29, 2024
75f189e
update pages
CyAn84 Sep 29, 2024
f3df9eb
add ListViewFocusController
CyAn84 Oct 13, 2024
89ac585
fix ListView navigation
CyAn84 Oct 17, 2024
852e90e
update CardType for using with focus navigation
CyAn84 Oct 17, 2024
0638514
remove useless key navigation
CyAn84 Oct 17, 2024
ada3f9a
remove useless slots, logs, Drawer open and close
CyAn84 Oct 19, 2024
626b9e1
fix reverse focus move on listView
CyAn84 Oct 19, 2024
2c9fa10
fix drawer radio buttons selection
CyAn84 Oct 19, 2024
9cfa4c1
fix drawer layout and focus move
CyAn84 Oct 20, 2024
c962211
fix PageSetupWizardProtocolSettings focus move
CyAn84 Oct 21, 2024
db5d289
fix back navigation on default focus item
CyAn84 Oct 21, 2024
dac45a9
fix crashes after ListView navigation
CyAn84 Oct 21, 2024
21755cb
fix protocol settings focus move
CyAn84 Oct 21, 2024
766e1c9
fix focus on users on page share
CyAn84 Oct 22, 2024
b6c59b0
clean up page share
CyAn84 Oct 22, 2024
c77e01f
fix server rename
CyAn84 Oct 24, 2024
7c3d08d
fix page share default server selection
CyAn84 Oct 24, 2024
42645a9
refactor about page for correct focus move
CyAn84 Oct 24, 2024
3d7209e
fix focus move on list views with header and-or footer
CyAn84 Oct 25, 2024
1633998
minor fixes
CyAn84 Oct 25, 2024
2e896ed
fix server list back button handler
CyAn84 Oct 26, 2024
e4d21dc
fix spawn signals on switch
CyAn84 Oct 27, 2024
88958c0
fix share details drawer
CyAn84 Oct 28, 2024
f020bdb
fix drawer open close usage
CyAn84 Oct 30, 2024
dc6c1cd
refactor listViewFocusController
CyAn84 Oct 30, 2024
5e9202f
refactor focusController to make the logic more
CyAn84 Oct 30, 2024
a92f706
fix focus on notification
CyAn84 Nov 1, 2024
45b8235
update config page for scrolling with tab
CyAn84 Nov 1, 2024
416421c
fix crash on return with esc key
CyAn84 Nov 1, 2024
ed6fc27
fix focus navigation in dynamic delegate of list view
CyAn84 Nov 1, 2024
942805c
fix focus move on qr code on share page
CyAn84 Nov 2, 2024
d956be9
refactor page logging settings for focus navigation
CyAn84 Nov 3, 2024
0620b45
update popup
CyAn84 Nov 3, 2024
940806a
Bump version
albexk Oct 29, 2024
65870ad
Add mandatory requirement for android.software.leanback.
albexk Oct 29, 2024
7df0503
Fix importing files on TVs
albexk Oct 31, 2024
a5abab8
fix: add separate method for reading files to fix file reading on And…
albexk Nov 11, 2024
341d6f5
fix(android): add CHANGE_NETWORK_STATE permission for all Android ver…
albexk Nov 3, 2024
53e766f
Fix connection check for AWG/WG
albexk Nov 1, 2024
4e1862a
chore: minor fixes (#1235)
Nethius Nov 6, 2024
d02a6df
fix: add a workaround to open files on Android TV due to lack of SAF
albexk Nov 13, 2024
51092e9
fix: change the banner format for TV
albexk Nov 18, 2024
392caca
refactor: make TvFilePicker activity more sustainable
albexk Nov 18, 2024
2987e03
fix: add the touch emulation method for Android TV
albexk Nov 24, 2024
3eeab2f
fix: null uri processing
albexk Nov 25, 2024
50bd364
Merge branch 'dev' into feature/android-tv
albexk Nov 25, 2024
c72d76a
Merge branch 'improve_navigation_cpp' into feature/android-tv
albexk Nov 25, 2024
4d2d7e4
fix: add the touch emulation method for Android TV
albexk Nov 24, 2024
09b1f32
fix: hide UI elements that use file saving
albexk Nov 25, 2024
c9205af
chore: bump version code
albexk Nov 25, 2024
f992252
Merge commit into improve_navigation_cpp
CyAn84 Dec 2, 2024
87d0011
add `ScrollBarType`
CyAn84 Dec 6, 2024
39cbe6d
update initial config page
CyAn84 Dec 9, 2024
f331c11
refactor credentials setup page to handle the focus navigation
CyAn84 Dec 9, 2024
3cc7079
add `setDelegateIndex` method to `listViewFocusController`
CyAn84 Dec 9, 2024
ea39447
fix focus behavior on new page/popup
CyAn84 Dec 9, 2024
882c288
make minor fixes and clean up
CyAn84 Dec 9, 2024
5b9ba8c
fix: get rid of the assign function call
albexk Dec 9, 2024
53f4140
Scrollbar is on if the content is larger than a screen
CyAn84 Dec 13, 2024
ef2ffce
Fix selection in language change list
CyAn84 Dec 13, 2024
d18f50a
Update select language list
CyAn84 Dec 13, 2024
b33b978
update logging settings page
CyAn84 Dec 14, 2024
149c9d1
Merge commit '367789bda28f33a11a72d61b222453c5b502299e' into improve_…
CyAn84 Dec 14, 2024
8bc31b6
fix checked item in lists
CyAn84 Dec 14, 2024
906cf7b
fix split tunneling settings
CyAn84 Dec 14, 2024
378db1a
make unchangable properties readonly
CyAn84 Dec 14, 2024
3021066
refactor SwitcherType
CyAn84 Dec 14, 2024
3a62f1f
fix hide/unhide password
CyAn84 Dec 14, 2024
10e91b0
`PageShare` readonly properties
CyAn84 Dec 15, 2024
62be292
Fix list view focus moving on `PageShare`
CyAn84 Dec 15, 2024
9620a24
remove manual focus control on `PageShare`
CyAn84 Dec 15, 2024
a57e94e
format `ListViewFocusController`
CyAn84 Dec 16, 2024
11d2097
format `FocusController`
CyAn84 Dec 16, 2024
2266702
add `focusControl` with utility functions for
CyAn84 Dec 18, 2024
7670d9d
refactor `listViewFocusController` acoording to `focusControl`
CyAn84 Dec 18, 2024
1029142
refactor `focusConroller` according to `focusControl`
CyAn84 Dec 18, 2024
14d6750
add `printSectionName` method to `listViewController`
CyAn84 Dec 18, 2024
ae31baf
remove arrow from `Close application` item
CyAn84 Dec 18, 2024
fa64c8d
fix focus movement in `ServersListView`
CyAn84 Dec 20, 2024
441a2f6
`Restore from backup` is visible only on start screen
CyAn84 Dec 20, 2024
b29b596
`I have nothing` is visible only on start screen
CyAn84 Dec 20, 2024
53aa14a
fix back button on `SelectLanguageDrawer`
CyAn84 Dec 20, 2024
c973b88
rename `focusControl` to `qmlUtils`
CyAn84 Dec 20, 2024
0cdd2ff
fix `CMakeLists.txt`
CyAn84 Dec 20, 2024
8767f48
fix `ScrollBarType`
CyAn84 Dec 22, 2024
13e680f
fix `PageSetupWizardApiServicesList`
CyAn84 Dec 22, 2024
f8f4d75
Merge commit 'b88ab8e432fbd2ab5002856cd83ef57181a6d7fa' into improve_…
CyAn84 Dec 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)

set(PROJECT AmneziaVPN)

project(${PROJECT} VERSION 4.8.2.4
project(${PROJECT} VERSION 4.8.3.0
DESCRIPTION "AmneziaVPN"
HOMEPAGE_URL "https://amnezia.org/"
)
Expand All @@ -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 2071)
set(APP_ANDROID_VERSION_CODE 2072)

if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(MZ_PLATFORM_NAME "linux")
Expand Down
3 changes: 3 additions & 0 deletions client/amnezia_application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,9 @@ void AmneziaApplication::initControllers()
m_pageController.reset(new PageController(m_serversModel, m_settings));
m_engine->rootContext()->setContextProperty("PageController", m_pageController.get());

m_focusController.reset(new FocusController(m_engine, this));
m_engine->rootContext()->setContextProperty("FocusController", m_focusController.get());

m_installController.reset(new InstallController(m_serversModel, m_containersModel, m_protocolsModel, m_clientManagementModel,
m_apiServicesModel, m_settings));
m_engine->rootContext()->setContextProperty("InstallController", m_installController.get());
Expand Down
2 changes: 2 additions & 0 deletions client/amnezia_application.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "ui/controllers/exportController.h"
#include "ui/controllers/importController.h"
#include "ui/controllers/installController.h"
#include "ui/controllers/focusController.h"
#include "ui/controllers/pageController.h"
#include "ui/controllers/settingsController.h"
#include "ui/controllers/sitesController.h"
Expand Down Expand Up @@ -124,6 +125,7 @@ class AmneziaApplication : public AMNEZIA_BASE_CLASS
#endif

QScopedPointer<ConnectionController> m_connectionController;
QScopedPointer<FocusController> m_focusController;
QScopedPointer<PageController> m_pageController;
QScopedPointer<InstallController> m_installController;
QScopedPointer<ImportController> m_importController;
Expand Down
9 changes: 8 additions & 1 deletion client/android/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<!-- for TV -->
<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.software.leanback" android:required="true" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />

<!-- The following comment will be replaced upon deployment with default features based on the dependencies
Expand Down Expand Up @@ -91,6 +91,13 @@
android:exported="false"
android:theme="@style/Translucent" />

<activity android:name=".TvFilePicker"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="false"
android:theme="@style/Translucent" />

<activity
android:name=".ImportConfigActivity"
android:excludeFromRecents="true"
Expand Down
5 changes: 0 additions & 5 deletions client/android/res/mipmap-anydpi-v26/ic_banner.xml

This file was deleted.

Binary file added client/android/res/mipmap-hdpi/ic_banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/android/res/mipmap-mdpi/ic_banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
2 changes: 2 additions & 0 deletions client/android/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@
<string name="notificationSettingsDialogTitle">Настройки уведомлений</string>
<string name="notificationSettingsDialogMessage">Для показа уведомлений необходимо включить уведомления в системных настройках</string>
<string name="openNotificationSettings">Открыть настройки уведомлений</string>

<string name="tvNoFileBrowser">Пожалуйста, установите приложение для просмотра файлов</string>
</resources>
4 changes: 0 additions & 4 deletions client/android/res/values/ic_banner_background.xml

This file was deleted.

2 changes: 2 additions & 0 deletions client/android/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@
<string name="notificationSettingsDialogTitle">Notification settings</string>
<string name="notificationSettingsDialogMessage">To show notifications, you must enable notifications in the system settings</string>
<string name="openNotificationSettings">Open notification settings</string>

<string name="tvNoFileBrowser">Please install a file management utility to browse files</string>
</resources>
206 changes: 170 additions & 36 deletions client/android/src/org/amnezia/vpn/AmneziaActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.Manifest
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.app.NotificationManager
import android.content.ActivityNotFoundException
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Intent
Expand All @@ -12,6 +13,7 @@ import android.content.Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.Uri
import android.net.VpnService
import android.os.Build
import android.os.Bundle
Expand All @@ -20,8 +22,13 @@ import android.os.IBinder
import android.os.Looper
import android.os.Message
import android.os.Messenger
import android.os.ParcelFileDescriptor
import android.os.SystemClock
import android.provider.OpenableColumns
import android.provider.Settings
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager.LayoutParams
import android.webkit.MimeTypeMap
import android.widget.Toast
Expand All @@ -30,6 +37,7 @@ import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import java.io.IOException
import kotlin.LazyThreadSafetyMode.NONE
import kotlin.coroutines.CoroutineContext
import kotlin.text.RegexOption.IGNORE_CASE
import AppListProvider
import kotlinx.coroutines.CompletableDeferred
Expand Down Expand Up @@ -71,6 +79,7 @@ class AmneziaActivity : QtActivity() {
private var isInBoundState = false
private var notificationStateReceiver: BroadcastReceiver? = null
private lateinit var vpnServiceMessenger: IpcMessenger
private var pfd: ParcelFileDescriptor? = null

private val actionResultHandlers = mutableMapOf<Int, ActivityResultHandler>()
private val permissionRequestHandlers = mutableMapOf<Int, PermissionRequestHandler>()
Expand Down Expand Up @@ -514,21 +523,25 @@ class AmneziaActivity : QtActivity() {
type = "text/*"
putExtra(Intent.EXTRA_TITLE, fileName)
}.also {
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
onSuccess = {
it?.data?.let { uri ->
Log.v(TAG, "Save file to $uri")
try {
contentResolver.openOutputStream(uri)?.use { os ->
os.bufferedWriter().use { it.write(data) }
try {
startActivityForResult(it, CREATE_FILE_ACTION_CODE, ActivityResultHandler(
onSuccess = {
it?.data?.let { uri ->
Log.v(TAG, "Save file to $uri")
try {
contentResolver.openOutputStream(uri)?.use { os ->
os.bufferedWriter().use { it.write(data) }
}
} catch (e: IOException) {
Log.e(TAG, "Failed to save file $uri: $e")
// todo: send error to Qt
}
} catch (e: IOException) {
Log.e(TAG, "Failed to save file $uri: $e")
// todo: send error to Qt
}
}
}
))
))
} catch (_: ActivityNotFoundException) {
Toast.makeText(this@AmneziaActivity, "Unsupported", Toast.LENGTH_LONG).show()
}
}
}
}
Expand All @@ -537,46 +550,115 @@ class AmneziaActivity : QtActivity() {
fun openFile(filter: String?) {
Log.v(TAG, "Open file with filter: $filter")
mainScope.launch {
val mimeTypes = if (!filter.isNullOrEmpty()) {
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
val mime = MimeTypeMap.getSingleton()
extensionRegex.findAll(filter).map {
it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
}.toSet()
} else emptySet()

Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
Log.v(TAG, "File mimyType filter: $mimeTypes")
if ("*/*" in mimeTypes) {
type = "*/*"
} else {
when (mimeTypes.size) {
1 -> type = mimeTypes.first()
val intent = if (!isOnTv()) {
val mimeTypes = if (!filter.isNullOrEmpty()) {
val extensionRegex = "\\*\\.([a-z0-9]+)".toRegex(IGNORE_CASE)
val mime = MimeTypeMap.getSingleton()
extensionRegex.findAll(filter).map {
it.groups[1]?.value?.let { mime.getMimeTypeFromExtension(it) } ?: "*/*"
}.toSet()
} else emptySet()

Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
Log.v(TAG, "File mimyType filter: $mimeTypes")
if ("*/*" in mimeTypes) {
type = "*/*"
} else {
when (mimeTypes.size) {
1 -> type = mimeTypes.first()

in 2..Int.MAX_VALUE -> {
type = "*/*"
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
}
in 2..Int.MAX_VALUE -> {
type = "*/*"
putExtra(EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
}

else -> type = "*/*"
else -> type = "*/*"
}
}
}
}.also {
startActivityForResult(it, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
} else {
Intent(this@AmneziaActivity, TvFilePicker::class.java)
}

try {
startActivityForResult(intent, OPEN_FILE_ACTION_CODE, ActivityResultHandler(
onAny = {
val uri = it?.data?.toString() ?: ""
if (isOnTv() && it?.hasExtra("activityNotFound") == true) {
showNoFileBrowserAlertDialog()
}
val uri = it?.data?.apply {
grantUriPermission(packageName, this, Intent.FLAG_GRANT_READ_URI_PERMISSION)
}?.toString() ?: ""
Log.v(TAG, "Open file: $uri")
mainScope.launch {
qtInitialized.await()
QtAndroidController.onFileOpened(uri)
}
}
))
} catch (_: ActivityNotFoundException) {
showNoFileBrowserAlertDialog()
mainScope.launch {
qtInitialized.await()
QtAndroidController.onFileOpened("")
}
}
}
}

private fun showNoFileBrowserAlertDialog() {
AlertDialog.Builder(this)
.setMessage(R.string.tvNoFileBrowser)
.setCancelable(false)
.setPositiveButton(android.R.string.ok) { _, _ ->
try {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://webstoreredirect")))
} catch (_: Throwable) {}
}
.show()
}

@Suppress("unused")
fun getFd(fileName: String): Int {
Log.v(TAG, "Get fd for $fileName")
return blockingCall {
try {
pfd = contentResolver.openFileDescriptor(Uri.parse(fileName), "r")
pfd?.fd ?: -1
} catch (e: Exception) {
Log.e(TAG, "Failed to get fd: $e")
-1
}
}
}

@Suppress("unused")
fun closeFd() {
Log.v(TAG, "Close fd")
mainScope.launch {
pfd?.close()
pfd = null
}
}

@Suppress("unused")
fun getFileName(uri: String): String {
Log.v(TAG, "Get file name for uri: $uri")
return blockingCall {
try {
contentResolver.query(Uri.parse(uri), arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { cursor ->
if (cursor.moveToFirst() && !cursor.isNull(0)) {
return@blockingCall cursor.getString(0) ?: ""
}
}
} catch (e: Exception) {
Log.e(TAG, "Failed to get file name: $e")
}
""
}
}

@Suppress("unused")
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
fun isCameraPresent(): Boolean = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
Expand Down Expand Up @@ -721,6 +803,50 @@ class AmneziaActivity : QtActivity() {
}
}

// method to workaround Qt's problem with calling the keyboard on TVs
@Suppress("unused")
fun sendTouch(x: Float, y: Float) {
Log.v(TAG, "Send touch: $x, $y")
blockingCall {
findQtWindow(window.decorView)?.let {
Log.v(TAG, "Send touch to $it")
it.dispatchTouchEvent(createEvent(x, y, SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN))
it.dispatchTouchEvent(createEvent(x, y, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP))
}
}
}

private fun findQtWindow(view: View): View? {
Log.v(TAG, "findQtWindow: process $view")
if (view::class.simpleName == "QtWindow") return view
else if (view is ViewGroup) {
for (i in 0 until view.childCount) {
val result = findQtWindow(view.getChildAt(i))
if (result != null) return result
}
return null
} else return null
}

private fun createEvent(x: Float, y: Float, eventTime: Long, action: Int): MotionEvent =
MotionEvent.obtain(
eventTime,
eventTime,
action,
1,
arrayOf(MotionEvent.PointerProperties().apply {
id = 0
toolType = MotionEvent.TOOL_TYPE_FINGER
}),
arrayOf(MotionEvent.PointerCoords().apply {
this.x = x
this.y = y
pressure = 1f
size = 1f
}),
0, 0, 1.0f, 1.0f, 0, 0, 0,0
)

// workaround for a bug in Qt that causes the mouse click event not to be handled
// also disable right-click, as it causes the application to crash
private var lastButtonState = 0
Expand Down Expand Up @@ -770,6 +896,7 @@ class AmneziaActivity : QtActivity() {
}

override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
Log.v(TAG, "dispatchTouch: $ev")
if (ev != null && ev.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
return handleMouseEvent(ev) { super.dispatchTouchEvent(it) }
}
Expand All @@ -784,6 +911,13 @@ class AmneziaActivity : QtActivity() {
/**
* Utils methods
*/
private fun <T> blockingCall(
context: CoroutineContext = Dispatchers.Main.immediate,
block: suspend () -> T
) = runBlocking {
mainScope.async(context) { block() }.await()
}

companion object {
private fun actionCodeToString(actionCode: Int): String =
when (actionCode) {
Expand Down
Loading
Loading