Compare commits

..

65 commits

Author SHA1 Message Date
Gaurav Ujjwal
698f01aacd Allow GitHub Actions to run on any push
Some checks are pending
Android CI / build (push) Waiting to run
2022-10-04 23:00:37 +05:30
Gaurav Ujjwal
09a4990433 Guard against null events passed to onScroll
It appears that on some devices, e1 can be null, which would result in a
crash. This is probably due to manufacturers messing with input handling
of AOSP.
2022-10-04 23:00:37 +05:30
Felix Yan
1fe3dedc52
Correct typos in app/CMakeLists.txt (#90) 2022-08-22 20:13:18 +05:30
Gaurav Ujjwal
ad6d081a6f Allow client-side cursor movement in view-only mode
With direct-touch off, panning the viewer requires cursor movement.
GitHub Issue: #89
2022-08-16 13:27:26 +05:30
Gaurav Ujjwal
64f06caa09 Improve two-finger-swipe vs scale detection 2022-08-15 23:17:10 +05:30
Gaurav Ujjwal
ead089fd5e Release v1.9.2 2022-08-08 20:41:00 +05:30
Gaurav Ujjwal
548e0241a4 Translations update from Hosted Weblate
* Translated using Weblate (Polish)
* Translated using Weblate (Chinese (Simplified))

Co-authored-by: WaldiS <sto@tutanota.de>
Co-authored-by: Eric <alchemillatruth@purelymail.com>
2022-08-08 20:23:04 +05:30
Gaurav Ujjwal
2ccbd0cbd8 Fix possible crash on opening the toolbar for the first time
AVNC always tried to open the left/start drawer when connecting for the
first time, to highlight it for the new users. But if a user changes the
toolbar alignment to 'End' before ever connecting to a server, AVNC will
crash when connecting for the first time, because there won't be any
'left' drawer.

Fix it by opening the toolbar directly.
2022-08-08 20:23:04 +05:30
Gaurav Ujjwal
bf73d24f9e Constrain aspect ratio of PiP window
Android requires the aspect ratio of PiP window to be within 1:2.39 range
in either orientation. If  aspect ratio of framebuffer was outside this
range, it would crash the app.

Also guarded against unlikely possibility of fbWidth/fbHeight being 0.
2022-08-02 16:50:03 +05:30
Gaurav Ujjwal
ee8ae23d5f Refactor connection initialization
Until now, connection was started at the very end of onCreate(). As a
result components like Dispatcher could not rely on VncViewModel's
profile during their initialization. Now initialization happen at
the beginning.

It also comes with a tiny bit of performance improvement.
Receiver thread can now start the connection early, while main thread
is still creating the UI.
2022-07-31 22:38:04 +05:30
Gaurav Ujjwal
a28149eb99 Fix default value for mouse back button preference 2022-07-31 12:06:20 +05:30
Gaurav Ujjwal
2abfd14d83 Remove temporary message about Hide Cursor option 2022-07-29 13:59:11 +05:30
Gaurav Ujjwal
31c90b4287 Release v1.9.1 2022-07-24 16:05:36 +05:30
Gaurav Ujjwal
4facebf48e Use immersive mode pref for full edge exclusion from System Gestures
Current approach won't work because Android enforces a 200dp vertical
limit, unless app is in sticky immersive mode.
2022-07-24 15:09:35 +05:30
Weblate (bot)
153f69d12c
Translations update from Hosted Weblate (#88)
* Translated using Weblate (German)
* Translated using Weblate (Polish)
* Translated using Weblate (Portuguese (Brazil))
* Translated using Weblate (Ukrainian)
* Translated using Weblate (Russian)

Co-authored-by: Pure Incompetence <inc@mailbox.org>
Co-authored-by: WaldiS <sto@tutanota.de>
Co-authored-by: mezysinc <mezysinc@mailon.site>
Co-authored-by: Andrij Mizyk <andmiz@proton.me>
Co-authored-by: Gaurav Ujjwal <gujjwal00@gmail.com>
2022-07-24 15:01:01 +05:30
Gaurav Ujjwal
9c3a0cc3dd Switch 'Full immersive mode' preference's default value to true
In future versions this will be merged  into 'Fullscreen' pref, so
turning it on by default should give us time to figure out any issues.
2022-07-24 14:01:42 +05:30
Gaurav Ujjwal
08b4c01d99 Exclude connection toolbar area from system gestures on API 29+ 2022-07-24 13:43:35 +05:30
Gaurav Ujjwal
22b071365e Update wolfSSL submodule 2022-07-20 20:28:26 +05:30
Gaurav Ujjwal
c671615025 OpenGL: Set texture wrap mode to GL_CLAMP_TO_EDGE
In OpenGL ES 2, support for sampling non-power-of-two textures in
fragment shaders is only guaranteed when wrap mode is GL_CLAMP_TO_EDGE.
Although most modern GPUs don't enforce this requirement, some devices
(like Tegra from Nvidia) do.

Related Github Issue: #85
2022-07-15 23:22:54 +05:30
Gaurav Ujjwal
1af619551a OpenGL: Use correct type for position attribute in Vertex Shader
For a_Position attribute, Frame.bind() only assign the x,y components,
but a_Position was declared as vec4.

While this generally does not cause any issue, its much better to
use the correct data type to avoid any possible misbehavior.
2022-07-15 23:12:02 +05:30
Gaurav Ujjwal
3d82e80beb OpenGL: Move program valition to onDrawFrame()
Program validation verifies the shaders against current OpenGL state.
So doing it in onSurfaceCreated() is not helpful, because uniforms
& attributes are not set at that stage and texture is not properly
initialized.

Now program is validated just before the draw command is issued.
2022-07-15 22:59:55 +05:30
Gaurav Ujjwal
b3bd9399f0 [Style] Cleanup 2022-06-02 21:28:59 +05:30
Gaurav Ujjwal
c0a56d1270 Release v1.9.0 2022-05-31 22:41:21 +05:30
Weblate (bot)
650668d8fa
Translations update from Hosted Weblate (#83)
Added new language using Weblate (German)

Translated using Weblate (Norwegian Bokmål)
Translated using Weblate (Ukrainian)
Translated using Weblate (Polish)
Translated using Weblate (Chinese (Simplified))
Translated using Weblate (Turkish)
Translated using Weblate (French)

Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Andrij Mizyk <andmizyk@gmail.com>
Co-authored-by: WaldiS <sto@tutanota.de>
Co-authored-by: Pure Incompetence <inc@mailbox.org>
Co-authored-by: GT-610 <myddz1005@163.com>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Nathan <bonnemainsnathan@gmail.com>
2022-05-31 22:39:38 +05:30
Gaurav Ujjwal
aa5c3e92a8 Update AndroidX Biometric library
Github Issue: #82
2022-05-26 20:19:50 +05:30
Gaurav Ujjwal
d6bf7c6189 Add preference for Natural Scrolling 2022-05-22 14:11:54 +05:30
Gaurav Ujjwal
c3292a5b90 Keep the pointer centered when Direct Touch is off
After moving the pointer, frame is moved in opposite direction to keep
the pointer centered on screen (as long as possible).
2022-05-22 14:11:31 +05:30
Gaurav Ujjwal
45b5fc4696 Disable APK splitting based on language & density
Resources in density-split APKs are not properly handled by AAB, which
causes a lot of warnings from Android.
2022-05-20 21:23:50 +05:30
Gaurav Ujjwal
e259c931a1 Add preference for screen orientation 2022-05-16 23:25:28 +05:30
Gaurav Ujjwal
890b7750b7 Update submodules 2022-05-16 22:13:35 +05:30
Gaurav Ujjwal
c4d5b7b2f8 Move custom rfbClient extensions to our own ClientEx 2022-05-16 22:08:29 +05:30
Gaurav Ujjwal
0a379a68e7 Add help for Drag gesture
The interaction of Drag gesture & Long press gesture can be confusing at first.
Now a helper message is shown for the Drag gesture.
2022-04-08 15:13:54 +05:30
Gaurav Ujjwal
41fb2e3b5f Wrap Discovery start/stop in exception handlers 2022-04-08 13:46:08 +05:30
Gaurav Ujjwal
f9f3a647de Release v1.8.1 2022-04-07 23:36:56 +05:30
Weblate (bot)
56fc2f8f3c
Translations update from Hosted Weblate (#72)
* Translated using Weblate (Persian)
* Translated using Weblate (Turkish)
* Translated using Weblate (Polish)

Co-authored-by: Eshagh Shahedany <eshagh094@gmail.com>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: WaldiS <sto@tutanota.de>
2022-04-07 23:29:58 +05:30
Gaurav Ujjwal
00bd2da0f6 Formatting + fix lint issues 2022-04-07 21:56:21 +05:30
Gaurav Ujjwal
0cce0c2ef8 Fix click position for mouse back press 2022-04-06 15:05:03 +05:30
Gaurav Ujjwal
999bdb2f72 Release v1.8.0 2022-03-31 10:02:52 +05:30
Weblate (bot)
c66d7e3668
Translations update from Hosted Weblate (#71)
Translated using Weblate (Persian)
Translated using Weblate (Ukrainian)
Translated using Weblate (Turkish)
Translated using Weblate (French)
Translated using Weblate (Polish)

Co-authored-by: Eshagh Shahedany <eshagh094@gmail.com>
Co-authored-by: Andrij Mizyk <andmizyk@gmail.com>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: Nathan <bonnemainsnathan@gmail.com>
Co-authored-by: WaldiS <sto@tutanota.de>
2022-03-30 23:57:53 +05:30
Gaurav Ujjwal
b83747b1d0 Fix tests 2022-03-30 23:34:36 +05:30
Gaurav Ujjwal
ee49c2d1b4 Refactor mouse-triggered Back-key interception 2022-03-30 23:34:36 +05:30
Gaurav Ujjwal
bfd34747e8 Update libjpeg-turbo 2022-03-27 23:34:49 +05:30
Gaurav Ujjwal
ce4e838351 Add workaround for WolSSL CMake requirement 2022-03-27 23:00:02 +05:30
Gaurav Ujjwal
a5f3faedba Misc. translation updates 2022-03-27 22:47:40 +05:30
Gaurav Ujjwal
45929278ee Remove Single-tap gesture preference
Since the implementation of relative mode, only remaining use of this
pref was to disable Single-tap gesture, which I doubt anyone uses.
2022-03-27 22:24:03 +05:30
Gaurav Ujjwal
6526cb86b2 Restore option to hide remote pointer
It is now available as a global preference.
2022-03-27 22:12:51 +05:30
wang
5780d08a9d Translation updates from Weblate
Added new language using Weblate (Polish)
Added new language using Weblate (Chinese (Simplified))

Translated using Weblate (Turkish)
Translated using Weblate (Ukrainian)
Translated using Weblate (French)
Translated using Weblate (Portuguese (Brazil))
Translated using Weblate (Norwegian Bokmål)

Co-authored-by: Andrij Mizyk <andmizyk@gmail.com>
Co-authored-by: Саша Петровић <salepetronije@gmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Nathan <bonnemainsnathan@gmail.com>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Luiz-bro <luiznneto1@gmail.com>
Co-authored-by: wang <wwj402@sina.com>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: WaldiS <sto@tutanota.de>
2022-03-27 10:49:24 +05:30
Gaurav Ujjwal
ee28bf184d Add preference for swipe sensitivity 2022-03-26 23:40:32 +05:30
Gaurav Ujjwal
c6b28ea6f1 Implement 'pan-with-cursor' for relative action mode 2022-03-26 23:19:53 +05:30
Gaurav Ujjwal
ce2736ed20 Fix incorrect cursor rendering at (0,0) position 2022-03-26 17:47:08 +05:30
Gaurav Ujjwal
36d8b66cae Use correct range for pointer position coercion 2022-03-26 17:45:51 +05:30
Gaurav Ujjwal
f0475ccdcf Remove workaround required to sync pointer position in relative mode
As client-side cursor rendering is now supported, most servers will now
send message about the real pointer position.
2022-03-26 13:38:32 +05:30
Gaurav Ujjwal
d04e19be58 Disable Fling gesture in relative action mode.
Because 1-finger swipe is used for moving the mouse, fling gesture can be
confusing/bothering as it moves the frame instead of mouse.
2022-03-26 13:29:42 +05:30
Gaurav Ujjwal
52607e2b21 Implement client-side cursor rendering
If supported by server, client-side cursor rendering gives us significant
boost in cursor responsiveness. This also unlocks many other features:

1. Most servers will now report mouse movements done unilaterally on server.
This info will help with touchpad/relative action mode.

2. We can now add the ability to follow cursor while zoomed-in (it was possible
before, but looked really ugly).

3. It serves as a workaround for cursor-invisibility in TightVNC.
2022-03-25 22:57:23 +05:30
Gaurav Ujjwal
c64140d1b2 Native refactoring: Extract miscellaneous utilities 2022-03-25 22:57:23 +05:30
Gaurav Ujjwal
e068d89eae Native refactoring: Extract client extension 2022-03-25 22:57:23 +05:30
Gaurav Ujjwal
a518be5a7a Don't set ID for ServerProfile created during Discovery
A non-zero ID is used to differentiate between saved/non-saved profiles.
(May we should have a better way for that?)

Regression from: 2b64933fcb
2022-03-24 23:37:15 +05:30
Gaurav Ujjwal
388ccc8176 Handle the initial touch-point of scroll gestures by Stylus 2022-03-07 22:17:31 +05:30
Tuan Pham
239f993082
Add option to remap back press from mouse device (#59)
Co-authored-by: Tuan Pham <tuanp.swe@gmail.com>
2022-02-24 22:00:30 +05:30
Hosted Weblate
555ea0a483 Translations update from Hosted Weblate
Translated using Weblate (Norwegian Bokmål)
Translated using Weblate (Serbian)
Translated using Weblate (Ukrainian)
Translated using Weblate (French)

Co-authored-by: Andrij Mizyk <andmizyk@gmail.com>
Co-authored-by: Саша Петровић <salepetronije@gmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Nathan <bonnemainsnathan@gmail.com>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
2022-02-21 00:14:03 +05:30
Oğuz Ersen
b29710119e Translated using Weblate (Turkish) 2022-02-20 23:41:59 +05:30
Fabriel
6293385d88
New translation: Turkish (#61) 2022-02-18 22:20:10 +05:30
Gaurav Ujjwal
8c7f3b07bc Add workaround for devices where mouse right-click is treated as back-press
GitHub issue #53
2022-02-13 21:13:14 +05:30
Gaurav Ujjwal
bbda1b8e62 Assign a different App ID to CI-built APKs
It allows the APKs to be installed alongside Release version.
2022-02-03 11:39:32 +05:30
Gaurav Ujjwal
dddad7b914 Switch to 'release' builds on GitHub Actions
Because PRs cannot access GitHub Secrets, they will continue with the debug variant.
2022-02-02 22:15:11 +05:30
113 changed files with 2362 additions and 443 deletions

View file

@ -1,10 +1,6 @@
name: Android CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
on: push
jobs:
build:
@ -23,10 +19,19 @@ jobs:
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew assembleDebug
run: ./gradlew assembleCi
- name: Sign APK
env:
CI_SIGNING_KS: ${{ secrets.CI_SIGNING_KS }}
CI_SIGNING_KS_PASSWORD: ${{ secrets.CI_SIGNING_KS_PASSWORD }}
working-directory: ./app/build/outputs/apk/ci/
run: |
echo $CI_SIGNING_KS | base64 --decode > keystore.jks
$ANDROID_HOME/build-tools/30.0.3/apksigner sign --in app-ci-unsigned.apk --out app-ci.apk --ks keystore.jks --ks-pass 'env:CI_SIGNING_KS_PASSWORD'
- name: Upload APK
uses: actions/upload-artifact@v2
with:
name: dubug-apk
path: app/build/outputs/apk/debug/*.apk
name: avnc-ci
path: app/build/outputs/apk/ci/app-ci.apk

29
.github/workflows/pr.yml vendored Normal file
View file

@ -0,0 +1,29 @@
name: Android CI
on:
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v2
with:
submodules: true
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew assembleDebug
- name: Upload APK
uses: actions/upload-artifact@v2
with:
name: avnc-debug
path: app/build/outputs/apk/debug/*.apk

View file

@ -4,24 +4,6 @@
<JavaCodeStyleSettings>
<option name="JD_P_AT_EMPTY_LINES" value="false" />
</JavaCodeStyleSettings>
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value>
<package name="java.util" alias="false" withSubpackages="false" />
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
<package name="io.ktor" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
</JetCodeStyleSettings>
<codeStyleSettings language="ObjectiveC">
<option name="RIGHT_MARGIN" value="120" />
<option name="SOFT_MARGINS" value="80,100" />

View file

@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.10)
project(NativeVNC C CXX ASM)
set(AVNC_EXTERN_DIR ${PROJECT_SOURCE_DIR}/../extern)
set(BUILD_SHARED_LIBS OFF)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared Libs" FORCE)
###############################################################################
# Utilities
@ -19,7 +19,7 @@ macro(avnc_check_submodule name)
endmacro()
# Requred to enable SIMD support on ARM
# Required to enable SIMD support on ARM
if (CMAKE_ANDROID_ARCH STREQUAL "arm64")
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} --target=aarch64-linux-android${ANDROID_NATIVE_API_LEVEL}")
elseif (CMAKE_ANDROID_ARCH STREQUAL "arm")
@ -53,7 +53,7 @@ set(AVNC_LIBSSL_SRC_DIR ${AVNC_EXTERN_DIR}/wolfssl)
set(AVNC_LIBSSL_BUILD_DIR ${CMAKE_BINARY_DIR}/wolfssl)
# CMake support in wolfSSl is still under development, so we have to
# manully set some flags to enable OpenSSL compatibility layer
# manually set some flags to enable OpenSSL compatibility layer
set(WOLFSSL_CRL yes)
set(WOLFSSL_DES3 yes)
add_definitions(-DOPENSSL_ALL -DOPENSSL_EXTRA -DHAVE_CRL -DHAVE_EX_DATA

View file

@ -11,8 +11,8 @@ android {
applicationId "com.gaurav.avnc"
minSdkVersion 21
targetSdkVersion 30
versionCode 9
versionName "1.7.0"
versionCode 14
versionName "1.9.2"
javaCompileOptions {
annotationProcessorOptions {
@ -42,6 +42,12 @@ android {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
ci {
initWith(release)
applicationIdSuffix '.ci'
versionNameSuffix ' (CI)'
}
}
buildFeatures {
@ -60,6 +66,15 @@ android {
path file('CMakeLists.txt')
}
}
bundle {
density {
enableSplit false
}
language {
enableSplit false
}
}
}
dependencies {
@ -68,7 +83,7 @@ dependencies {
implementation "androidx.fragment:fragment-ktx:1.3.4"
implementation "androidx.appcompat:appcompat:1.3.0"
implementation "androidx.dynamicanimation:dynamicanimation:1.0.0"
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha03"
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha04"
implementation "androidx.preference:preference:1.1.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
implementation "androidx.recyclerview:recyclerview:1.2.0"

View file

@ -22,7 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.gaurav.avnc.*
import com.gaurav.avnc.model.ServerProfile
import com.gaurav.avnc.ui.vnc.VncActivity
import com.gaurav.avnc.vnc.Discovery
import org.hamcrest.core.AllOf.allOf
import org.junit.Assert.assertEquals
import org.junit.Before
@ -31,14 +30,13 @@ import org.junit.Test
import org.junit.runner.RunWith
/**
* For ease of testing, we directly modify the [discovery] instance.
* TODO: Find better approach to this
* TODO: Test discovery start/stop
*/
@RunWith(AndroidJUnit4::class)
class DiscoveryListTest {
@Rule
@JvmField
val activityRule = ActivityScenarioRule(HomeActivity::class.java)
private val testProfile = ServerProfile(name = "Test Profile", host = "123.123.123.123")
private fun testProfileMatcher() = allOf(
withParent(withId(R.id.discovered_rv)),
@ -46,35 +44,31 @@ class DiscoveryListTest {
hasDescendant(withText(testProfile.host)),
)
private lateinit var discovery: Discovery
@Rule
@JvmField
val activityRule = ActivityScenarioRule(HomeActivity::class.java)
private fun addTestProfile() {
// Directly modify internal list to simulate discovery
activityRule.scenario.onActivity { it.viewModel.discovery.servers.postValue(arrayListOf(testProfile)) }
onView(testProfileMatcher()).checkWillBeDisplayed()
}
@Before
fun before() {
activityRule.scenario.onActivity {
discovery = it.viewModel.discovery
discovery.stop()
}
// Stop discovery, otherwise tests will hang due to progressbar animation
// We can't even use the stop button
activityRule.scenario.onActivity { it.viewModel.stopDiscovery() }
onView(withId(R.id.pager)).perform(swipeLeft())
onView(withId(R.id.discovered_rv)).checkWithTimeout(matches(isCompletelyDisplayed()))
onView(withContentDescription(R.string.desc_discovery_btn)).checkWillBeDisplayed()
discovery.servers.postValue(arrayListOf(testProfile))
onView(testProfileMatcher()).checkWillBeDisplayed()
}
@Test
fun launchConnection() {
Intents.init()
Intents.intending(hasComponent(VncActivity::class.qualifiedName))
.respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, null))
addTestProfile()
onView(testProfileMatcher()).doClick()
Intents.intended(hasComponent(VncActivity::class.qualifiedName))
@ -85,6 +79,7 @@ class DiscoveryListTest {
@Test
fun launchProfileSave() {
addTestProfile()
onView(allOf(withParent(testProfileMatcher()), withId(R.id.save_btn))).doClick()
onView(withText(R.string.title_add_server_profile)).inRoot(isDialog()).checkIsDisplayed()
@ -94,6 +89,7 @@ class DiscoveryListTest {
@Test
fun copyHost() {
addTestProfile()
onView(testProfileMatcher()).doLongClick()
onView(withText(R.string.title_copy_host)).doClick()
@ -102,10 +98,8 @@ class DiscoveryListTest {
}
@Test
fun emptyListTest() {
discovery.servers.postValue(arrayListOf())
onView(withContentDescription(R.string.desc_discovery_btn)).checkWillBeDisplayed()
fun noServerFound() {
activityRule.scenario.onActivity { it.viewModel.discovery.servers.postValue(arrayListOf()) }
onView(withText(R.string.tip_discovery)).checkIsDisplayed()
}
}

View file

@ -45,9 +45,10 @@ class TouchHandlerTest {
instrumentation.runOnMainSync {
val viewModel = spyk(VncViewModel(ApplicationProvider.getApplicationContext()))
// Both of these needs to be mocked
// All of these needs to be mocked
every { viewModel.pref.input.mousePassthrough } returns mousePassthrough
every { viewModel.pref.input.gesture.dragEnabled } returns dragEnabled
every { viewModel.pref.input.gesture.swipeSensitivity } returns 1f
touchHandler = TouchHandler(viewModel, mockDispatcher)
}
}

View file

@ -9,7 +9,6 @@
package com.gaurav.avnc.ui.vnc
import android.content.Context
import android.content.Intent
import android.view.inputmethod.InputMethodManager
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso.onIdle
@ -36,8 +35,7 @@ class VncActivityTest {
testServer.start()
val profile = ServerProfile(host = testServer.host, port = testServer.port)
val intent = Intent(targetContext, VncActivity::class.java)
.putExtra("com.gaurav.avnc.server_profile", profile)
val intent = createVncIntent(targetContext, profile)
ActivityScenario.launch<VncActivity>(intent).use {
onView(withId(R.id.frame_view)).checkWillBeDisplayed() // Wait for connection

View file

@ -22,15 +22,16 @@ class VncClientTest {
override fun onCredentialRequired() = UserCredential()
override fun onFramebufferUpdated() {}
override fun onFramebufferSizeChanged(width: Int, height: Int) {}
override fun onPointerMoved(x: Int, y: Int) {}
override fun onGotXCutText(text: String) {
cutText = text
}
}
lateinit var server: TestServer
lateinit var client: VncClient
lateinit var observer: TestObserver
private lateinit var server: TestServer
private lateinit var client: VncClient
private lateinit var observer: TestObserver
private val sampleText = "Smelly Cat"
private val sampleTextWithAccent = "Pokémon Fõbár"

View file

@ -9,6 +9,7 @@
package com.gaurav.avnc.vnc
import android.net.Uri
import com.gaurav.avnc.model.ServerProfile
import org.junit.Assert.assertEquals
import org.junit.Test
@ -44,6 +45,7 @@ class VncUriTest {
assertEquals("foo", uri.password)
assertEquals(2, uri.securityType)
assertEquals(true, uri.viewOnly)
assertEquals(ServerProfile.CHANNEL_TCP, uri.channelType)
}
@Test
@ -55,6 +57,7 @@ class VncUriTest {
assertEquals("foo", uri.password)
assertEquals(2, uri.securityType)
assertEquals(true, uri.viewOnly)
assertEquals(ServerProfile.CHANNEL_TCP, uri.channelType)
}
@Test

View file

@ -24,6 +24,7 @@
<activity-alias
android:name=".StartupActivity"
android:exported="true"
android:targetActivity=".ui.home.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -33,6 +34,7 @@
<activity-alias
android:name=".UriReceiverActivity"
android:exported="true"
android:targetActivity=".ui.vnc.UriReceiverActivity">
<intent-filter>

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2022 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/
#ifndef AVNC_CLIENTEX_H
#define AVNC_CLIENTEX_H
#include <jni.h>
#include "Cursor.h"
/**
* We attach some additional data to every rfbClient.
* ClientEx is used as wrapper for this data.
*/
struct ClientEx {
// Reference to managed `VncClient`
jobject managedClient;
// Although frame width & height are maintained in rfbClient, those values
// are modified before our MallocFrameBuffer callback is triggered, and
// we cannot protect them with a mutex. So we maintain the framebuffer
// size here, protected with fbMutex.
int fbRealWidth;
int fbRealHeight;
// Cursor data used for client-side cursor rendering
Cursor *cursor;
// Protects modification to framebuffer & cursor
MUTEX(mutex);
};
const int ClientExTag = 1;
ClientEx *getClientExtension(rfbClient *client) {
return (ClientEx *) rfbClientGetClientData(client, (void *) &ClientExTag);
}
void setClientExtension(rfbClient *client, ClientEx *ex) {
rfbClientSetClientData(client, (void *) &ClientExTag, ex);
}
/**
* Returns reference to managed `VncClient` associated with given rfbClient.
*/
jobject getManagedClient(rfbClient *client) {
return getClientExtension(client)->managedClient;
}
/**
* Associate given rfbClient & managed `VncClient`.
*/
void setManagedClient(rfbClient *client, jobject managedClient) {
getClientExtension(client)->managedClient = managedClient;
}
/**
* Create new ClientEx and assign it to given client.
*/
ClientEx *assignClientExtension(rfbClient *client) {
auto ex = (ClientEx *) malloc(sizeof(ClientEx));
if (ex) {
INIT_MUTEX(ex->mutex);
ex->cursor = nullptr;
setClientExtension(client, ex);
}
return ex;
}
/**
* Free all resources related to client extension.
*/
void freeClientExtension(rfbClient *client) {
auto ex = getClientExtension(client);
if (ex) {
TINI_MUTEX(ex->mutex);
freeCursor(ex->cursor);
free(ex);
setClientExtension(client, nullptr);
}
}
#endif //AVNC_CLIENTEX_H

111
app/src/main/cpp/Cursor.h Normal file
View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2022 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/
#ifndef AVNC_CURSOR_H
#define AVNC_CURSOR_H
/******************************************************************************
* Some servers (e.g TigerVNC) may not send the cursor immediately after
* connection. To provide consistent experience to users, we use a default
* cursor as fallback.
*****************************************************************************/
const uint16_t DefaultCursorWidth = 10;
const uint16_t DefaultCursorHeight = 16;
const uint16_t DefaultCursorXHot = 1;
const uint16_t DefaultCursorYHot = 1;
const uint32_t DefaultCursorBuffer[DefaultCursorWidth * DefaultCursorHeight]
= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00FFFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00FFFFFF, 0x00FFFFFF, 0, 0, 0, 0,
0, 0, 0, 0, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0, 0, 0, 0, 0, 0, 0, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF,
0x00FFFFFF, 0, 0, 0, 0, 0, 0, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0, 0, 0, 0, 0,
0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0, 0, 0, 0, 0x00FFFFFF, 0x00FFFFFF,
0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0, 0, 0, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF,
0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0, 0, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF,
0x00FFFFFF, 0x00FFFFFF, 0, 0, 0, 0, 0, 0x00FFFFFF, 0x00FFFFFF, 0, 0x00FFFFFF, 0x00FFFFFF, 0, 0, 0, 0, 0,
0x00FFFFFF, 0, 0, 0, 0x00FFFFFF, 0x00FFFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0x00FFFFFF, 0x00FFFFFF, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0x00FFFFFF, 0x00FFFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0x00FFFFFF, 0x00FFFFFF, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0};
const uint8_t DefaultCursorMask[DefaultCursorWidth * DefaultCursorHeight]
= {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0};
/******************************************************************************
* Cursor management
*****************************************************************************/
/**
* Wrapper for cursor information.
*
* rfbClient struct does not maintain all cursor related information inside it.
* Things like xHot, yHot are passed only via the cursor shape callback.
* This wrapper holds all information necessary to render the cursor.
*/
struct Cursor {
uint8_t *buffer;
uint8_t *mask;
uint8_t *scratchBuffer; //Used during rendering
uint16_t width;
uint16_t height;
uint16_t xHot;
uint16_t yHot;
};
//Only 4-byte pixels are currently supported
const uint8_t PixelBytes = 4;
/**
* Creates a new CursorData, initialized with default cursor info.
*/
Cursor *newCursor() {
auto cursor = (Cursor *) malloc(sizeof(Cursor));
if (cursor) {
cursor->buffer = (uint8_t *) DefaultCursorBuffer;
cursor->mask = (uint8_t *) DefaultCursorMask;
cursor->scratchBuffer = (uint8_t *) malloc(DefaultCursorWidth * DefaultCursorHeight * PixelBytes);
cursor->width = DefaultCursorWidth;
cursor->height = DefaultCursorHeight;
cursor->xHot = DefaultCursorXHot;
cursor->yHot = DefaultCursorYHot;
}
return cursor;
}
void freeCursorBuffers(Cursor *cursor) {
if (cursor) {
free(cursor->scratchBuffer);
if (cursor->buffer != (uint8_t *) DefaultCursorBuffer) free(cursor->buffer);
if (cursor->mask != (uint8_t *) DefaultCursorMask) free(cursor->mask);
}
}
void freeCursor(Cursor *cursor) {
freeCursorBuffers(cursor);
free(cursor);
}
void updateCursor(Cursor *cursor, uint8_t *buffer, uint8_t *mask, uint16_t width, uint16_t height,
uint16_t xHot, uint16_t yHot) {
freeCursorBuffers(cursor);
cursor->buffer = buffer;
cursor->mask = mask;
cursor->scratchBuffer = (uint8_t *) malloc(width * height * PixelBytes);
cursor->width = width;
cursor->height = height;
cursor->xHot = xHot;
cursor->yHot = yHot;
}
#endif //AVNC_CURSOR_H

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2022 Gaurav Ujjwal.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* See COPYING.txt for more details.
*/
#ifndef AVNC_UTILITY_H
#define AVNC_UTILITY_H
#include <stdarg.h>
#include <netdb.h>
#include <errno.h>
#include <android/log.h>
/******************************************************************************
* Utilities
*****************************************************************************/
/**
* Returns a native copy of the given jstring.
* Caller is responsible for releasing the memory.
*/
static char *getNativeStrCopy(JNIEnv *env, jstring jStr) {
const char *cStr = env->GetStringUTFChars(jStr, nullptr);
char *str = strdup(cStr);
env->ReleaseStringUTFChars(jStr, cStr);
return str;
}
/******************************************************************************
* Logging
*****************************************************************************/
const char *LOG_TAG = "NativeVnc";
void log_info(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
__android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
va_end(args);
}
void log_error(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
__android_log_vprint(ANDROID_LOG_ERROR, LOG_TAG, fmt, args);
va_end(args);
}
/**
* Converts given errno value to its description.
*/
static const char *errnoToStr(int e) {
// LibVNC is patched to report `getaddrinfo` errors as negative 'errno'.
// See ConnectClientToTcpAddr6WithTimeout() in sockets.c
if (e < -1000) {
return gai_strerror((-e) - 1000);
}
switch (e) {
case ENETDOWN:
case ENETRESET:
case ENETUNREACH:
case ECONNABORTED:
case EHOSTDOWN:
case EHOSTUNREACH:
case ETIMEDOUT:
case ENOMEM:
case EPROTO:
case EIO:
return strerror(e);
case ECONNREFUSED:
return "Connection refused! Server may be down or running on different port";
case ECONNRESET:
return "Connection closed by server";
case EACCES:
return "Authentication failed";
default:
// In this case we don't want to display errno description to user
// because it is more likely to be misleading (e.g. EINTR, EAGAIN).
// BUT add it to logs in case LibVNC didn't.
log_error("errnoToStr: (%d %s)", errno, strerror(errno));
return "";
}
}
#endif //AVNC_UTILITY_H

View file

@ -6,43 +6,12 @@
* See COPYING.txt for more details.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <jni.h>
#include <android/log.h>
#include <GLES2/gl2.h>
#include <rfb/rfbclient.h>
#include <errno.h>
#include <netdb.h>
/******************************************************************************
* Logging
*****************************************************************************/
static const char *TAG = "NativeVnc";
struct Logger {
static void i(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
__android_log_vprint(ANDROID_LOG_INFO, TAG, fmt, args);
va_end(args);
}
static void w(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
__android_log_vprint(ANDROID_LOG_WARN, TAG, fmt, args);
va_end(args);
}
static void e(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
__android_log_vprint(ANDROID_LOG_ERROR, TAG, fmt, args);
va_end(args);
}
};
#include "ClientEx.h"
#include "Utility.h"
/******************************************************************************
@ -54,7 +23,7 @@ struct JniContext {
jclass managedCls; //Managed `VncClient` class
jmethodID cbFramebufferUpdated; //Cached reference to managed callback
JNIEnv *getEnv() {
JNIEnv *getEnv() const {
JNIEnv *env = nullptr;
if (vm != nullptr && vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK)
@ -93,80 +62,10 @@ Java_com_gaurav_avnc_vnc_VncClient_initLibrary(JNIEnv *env, jclass clazz) {
context.cbFramebufferUpdated = env->GetMethodID(clazz, "cbFinishedFrameBufferUpdate", "()V");
//TODO: Cache more method IDs so we don't have to repeatedly search them
rfbClientLog = &Logger::i;
rfbClientErr = &Logger::e;
rfbClientLog = &log_info;
rfbClientErr = &log_error;
}
/******************************************************************************
* Utilities
*****************************************************************************/
/**
* Returns a native copy of the given jstring.
* Caller is responsible for releasing the memory.
*/
static char *getNativeStrCopy(JNIEnv *env, jstring jStr) {
const char *cStr = env->GetStringUTFChars(jStr, nullptr);
char *str = strdup(cStr);
env->ReleaseStringUTFChars(jStr, cStr);
return str;
}
/**
* Returns reference to managed `VncClient` associated with given rfbClient.
*/
static jobject getManagedClient(rfbClient *client) {
return (jobject) rfbClientGetClientData(client, context.managedCls);
}
/**
* Associate given rfbClient with a managed `VncClient`.
*/
static void setManagedClient(rfbClient *client, jobject managedClient) {
rfbClientSetClientData(client, context.managedCls, managedClient);
}
/**
* Converts given errno value to its description.
*/
static const char *errnoToStr(int e) {
// LibVNC is patched to report `getaddrinfo` errors as negative 'errno'.
// See ConnectClientToTcpAddr6WithTimeout() in sockets.c
if (e < -1000) {
return gai_strerror((-e) - 1000);
}
switch (e) {
case ENETDOWN:
case ENETRESET:
case ENETUNREACH:
case ECONNABORTED:
case EHOSTDOWN:
case EHOSTUNREACH:
case ETIMEDOUT:
case ENOMEM:
case EPROTO:
case EIO:
return strerror(e);
case ECONNREFUSED:
return "Connection refused! Server may be down or running on different port";
case ECONNRESET:
return "Connection closed by server";
case EACCES:
return "Authentication failed";
default:
// In this case we don't want to display errno description to user
// because it is more likely to be misleading (e.g. EINTR, EAGAIN).
// BUT add it to logs in case LibVNC didn't.
Logger::e("errnoToStr: (%d %s)", errno, strerror(errno));
return "";
}
}
/******************************************************************************
* rfbClient Callbacks
@ -243,10 +142,10 @@ static rfbBool onHandleCursorPos(rfbClient *client, int x, int y) {
auto env = context.getEnv();
auto cls = context.managedCls;
jmethodID mid = env->GetMethodID(cls, "cbHandleCursorPos", "(II)Z");
jboolean result = env->CallBooleanMethod(obj, mid, x, y);
jmethodID mid = env->GetMethodID(cls, "cbHandleCursorPos", "(II)V");
env->CallVoidMethod(obj, mid, x, y);
return result == JNI_TRUE ? TRUE : FALSE;
return TRUE;
}
static void onFinishedFrameBufferUpdate(rfbClient *client) {
@ -262,36 +161,38 @@ static void onFinishedFrameBufferUpdate(rfbClient *client) {
*/
static rfbBool onMallocFrameBuffer(rfbClient *client) {
auto requestedSize = (uint64_t) client->width * client->height * client->format.bitsPerPixel / 8;
const auto width = client->width;
const auto height = client->height;
const auto requestedSize = (uint64_t) width * height * client->format.bitsPerPixel / 8;
if (requestedSize >= SIZE_MAX) {
rfbClientErr("CRITICAL: cannot allocate frameBuffer, requested size is too large\n");
errno = EPROTO;
return FALSE;
}
auto allocSize = (size_t) requestedSize;
auto ex = getClientExtension(client);
LOCK(client->fbMutex);
LOCK(ex->mutex);
{
if (client->frameBuffer)
free(client->frameBuffer);
client->frameBuffer = static_cast<uint8_t *>(malloc(allocSize));
if (client->frameBuffer) {
client->fbRealWidth = client->width;
client->fbRealHeight = client->height;
ex->fbRealWidth = width;
ex->fbRealHeight = height;
memset(client->frameBuffer, 0, allocSize); //Clear any garbage
} else {
client->fbRealWidth = 0;
client->fbRealHeight = 0;
ex->fbRealWidth = 0;
ex->fbRealHeight = 0;
}
}
UNLOCK(client->fbMutex);
UNLOCK(ex->mutex);
if (client->frameBuffer == nullptr) {
errno = ENOMEM;
rfbClientErr("CRITICAL: frameBuffer allocation failed\n");
return FALSE;
}
@ -301,11 +202,28 @@ static rfbBool onMallocFrameBuffer(rfbClient *client) {
auto cls = context.managedCls;
auto mid = env->GetMethodID(cls, "cbFramebufferSizeChanged", "(II)V");
env->CallVoidMethod(obj, mid, client->width, client->height);
env->CallVoidMethod(obj, mid, width, height);
return TRUE;
}
static void onGotCursorShape(rfbClient *client, int xHot, int yHot, int width, int height, int bytesPerPixel) {
auto ex = getClientExtension(client);
LOCK(ex->mutex);
//Steel buffers from rfbClient
updateCursor(ex->cursor, client->rcSource, client->rcMask, (uint16_t) width, (uint16_t) height,
(uint16_t) xHot, (uint16_t) yHot);
client->rcSource = NULL;
client->rcMask = NULL;
UNLOCK(ex->mutex);
//Fake framebuffer update to trigger rendering
onFinishedFrameBufferUpdate(client);
}
/**
* Hooks callbacks to rfbClient.
*/
@ -317,6 +235,7 @@ static void setCallbacks(rfbClient *client) {
client->HandleCursorPos = onHandleCursorPos;
client->FinishedFrameBufferUpdate = onFinishedFrameBufferUpdate;
client->MallocFrameBuffer = onMallocFrameBuffer;
client->GotCursorShape = onGotCursorShape;
}
@ -331,6 +250,9 @@ Java_com_gaurav_avnc_vnc_VncClient_nativeClientCreate(JNIEnv *env, jobject thiz)
if (client == nullptr)
return 0;
if (!assignClientExtension(client))
return 0;
setCallbacks(client);
client->canHandleNewFBSize = TRUE;
@ -353,7 +275,10 @@ Java_com_gaurav_avnc_vnc_VncClient_nativeConfigure(JNIEnv *env, jobject thiz, jl
SetClientAuthSchemes(client, auth, 1);
}
client->appData.useRemoteCursor = use_local_cursor;
if (use_local_cursor) {
client->appData.useRemoteCursor = TRUE;
getClientExtension(client)->cursor = newCursor();
}
}
extern "C"
@ -396,6 +321,7 @@ Java_com_gaurav_avnc_vnc_VncClient_nativeCleanup(JNIEnv *env, jobject thiz,
auto managedClient = getManagedClient(client);
env->DeleteGlobalRef(managedClient);
freeClientExtension(client);
rfbClientCleanup(client);
}
@ -428,7 +354,7 @@ extern "C"
JNIEXPORT jboolean JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeSendKeyEvent(JNIEnv *env, jobject thiz, jlong client_ptr,
jlong key, jboolean is_down) {
return (jboolean) SendKeyEvent((rfbClient *) client_ptr, (uint32_t) key, is_down);
return (jboolean) SendKeyEvent((rfbClient *) client_ptr, (uint32_t) key, is_down ? TRUE : FALSE);
}
extern "C"
@ -488,20 +414,96 @@ JNIEXPORT void JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeUploadFrameTexture(JNIEnv *env, jobject thiz,
jlong client_ptr) {
auto client = (rfbClient *) client_ptr;
auto ex = getClientExtension(client);
LOCK(client->fbMutex);
LOCK(ex->mutex);
if (client->frameBuffer) {
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
client->fbRealWidth,
client->fbRealHeight,
ex->fbRealWidth,
ex->fbRealHeight,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
client->frameBuffer);
}
UNLOCK(client->fbMutex);
}
UNLOCK(ex->mutex);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_gaurav_avnc_vnc_VncClient_nativeUploadCursor(JNIEnv *env, jobject thiz, jlong client_ptr, jint px, jint py) {
auto client = (rfbClient *) client_ptr;
auto ex = getClientExtension(client);
auto cursor = ex->cursor;
if (!cursor)
return;
//Current algo for cursor rendering is slightly weird. Main issue is that
//glTexSubImage2D() does not perform any composition with target texture.
//So, we have to manually blend transparent/invalid pixels of the cursor
//with corresponding pixels from framebuffer. scratchBuffer is used for
//this composition.
LOCK(ex->mutex);
//Effective cursor position in framebuffer
int32_t fbCursorX = px - cursor->xHot;
int32_t fbCursorY = py - cursor->yHot;
//Rectangular portion of the framebuffer to be updated.
//Cursor can overflow outside the framebuffer if moved near the edges,
//but glTexSubImage2D() doesn't allow values outside target texture,
//so we need to only update the intersection of framebuffer & cursor.
int32_t left = -1, top = -1, right = -1, bottom = -1;
auto fb = (uint32_t *) client->frameBuffer;
auto buffer = (uint32_t *) cursor->buffer;
auto scratch = (uint32_t *) cursor->scratchBuffer;
auto mask = cursor->mask;
//Scratch buffer index
int32_t z = 0;
for (int32_t y = 0; y < cursor->height; ++y) {
for (int32_t x = 0; x < cursor->width; ++x) {
//Corresponding pixel in framebuffer
auto fbX = fbCursorX + x;
auto fbY = fbCursorY + y;
if (fbX >= 0 && fbX < ex->fbRealWidth && fbY >= 0 && fbY < ex->fbRealHeight) {
auto isValidPixel = mask[y * cursor->width + x];
if (isValidPixel)
scratch[z++] = buffer[y * cursor->width + x];
else
scratch[z++] = fb[fbY * ex->fbRealWidth + fbX];
if (left == -1 && top == -1) {
left = fbX;
top = fbY;
}
right = fbX;
bottom = fbY;
}
}
}
if (left >= 0 && top >= 0)
glTexSubImage2D(GL_TEXTURE_2D,
0,
left,
top,
right - left + 1,
bottom - top + 1,
GL_RGBA,
GL_UNSIGNED_BYTE,
scratch);
UNLOCK(ex->mutex);
}

View file

@ -86,8 +86,10 @@ data class ServerProfile(
/**
* Whether the cursor should be drawn by client instead of server.
* It's value is currently ignored, and hardcoded to true.
* See [com.gaurav.avnc.viewmodel.VncViewModel.configureClient]
*/
var useLocalCursor: Boolean = false,
var useLocalCursor: Boolean = true,
/**
* Compatibility mode for key events.

View file

@ -160,7 +160,9 @@ class ProfileEditorFragment : DialogFragment() {
toolbar.setNavigationOnClickListener { dismiss() }
keyImportBtn.setOnClickListener { keyFilePicker.launch(arrayOf("*/*")) }
keyCompatModeHelpBtn.setOnClickListener {
MsgDialog.show(parentFragmentManager, R.string.title_key_compat_mode, R.string.msg_key_compat_mode_help)
MsgDialog.show(parentFragmentManager,
getString(R.string.title_key_compat_mode),
getString(R.string.msg_key_compat_mode_help))
}
// Setup initial values of some CheckBox views.
@ -175,8 +177,8 @@ class ProfileEditorFragment : DialogFragment() {
// TODO Move it to proper place
val securityTypes = mapOf(
"Default Security" to 0,
"None" to 1,
getString(R.string.title_default_security) to 0,
getString(R.string.pref_gesture_action_none) to 1,
"VncAuth" to 2,
"AnonTLS" to 18,
"VeNCrypt" to 19

View file

@ -140,6 +140,11 @@ class ServerTabs(val activity: HomeActivity) {
inner class ViewHolder(val binding: ServerSavedItemBinding)
: ProfileViewHolder(viewModel, binding.root, R.menu.saved_server)
object Differ : DiffUtil.ItemCallback<ServerProfile>() {
override fun areItemsTheSame(old: ServerProfile, new: ServerProfile) = (old.ID == new.ID)
override fun areContentsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
}
}
@ -184,6 +189,14 @@ class ServerTabs(val activity: HomeActivity) {
binding.saveBtn.setOnClickListener { viewModel.onNewProfile(profile) }
}
}
/**
* Profiles generated by discovery don't have unique IDs, so we compare the whole profile.
*/
object Differ : DiffUtil.ItemCallback<ServerProfile>() {
override fun areItemsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
override fun areContentsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
}
}
@ -229,9 +242,4 @@ class ServerTabs(val activity: HomeActivity) {
Snackbar.make(rootView, R.string.msg_copied_to_clipboard, Snackbar.LENGTH_SHORT).show()
}
}
private object Differ : DiffUtil.ItemCallback<ServerProfile>() {
override fun areItemsTheSame(old: ServerProfile, new: ServerProfile) = (old.ID == new.ID)
override fun areContentsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
}
}

View file

@ -10,7 +10,12 @@ package com.gaurav.avnc.ui.prefs
import android.content.Context
import android.util.AttributeSet
import android.widget.ImageButton
import androidx.fragment.app.FragmentActivity
import androidx.preference.ListPreference
import androidx.preference.PreferenceViewHolder
import com.gaurav.avnc.R
import com.gaurav.avnc.util.MsgDialog
/**
* List preference with some extra features.
@ -27,4 +32,26 @@ class ListPreferenceEx(context: Context, attrs: AttributeSet) : ListPreference(c
return disabledStateSummary
return super.getSummary()
}
/**
* Message shown in a dialog, when help button of the preference is clicked.
* This will only work if [R.layout.help_btn] is used as widget layout.
*/
var helpMessage: CharSequence? = null
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
(holder.findViewById(R.id.help_btn) as? ImageButton)?.setOnClickListener { showHelp() }
}
private fun showHelp() {
helpMessage?.let { helpMessage ->
(context as? FragmentActivity)?.let { fragmentActivity ->
MsgDialog.show(fragmentActivity.supportFragmentManager,
fragmentActivity.getString(R.string.desc_help_btn),
helpMessage)
}
}
}
}

View file

@ -8,11 +8,13 @@
package com.gaurav.avnc.ui.prefs
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import androidx.annotation.Keep
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
@ -93,6 +95,8 @@ class PrefsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreference
}
@Keep class Input : PrefFragment(R.xml.pref_input) {
private var naturalScrollingUpdater: OnSharedPreferenceChangeListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -105,9 +109,22 @@ class PrefsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreference
}
}
findPreference<ListPreferenceEx>("gesture_swipe1")!!.apply {
disabledStateSummary = getString(R.string.pref_gesture_action_move_pointer)
val swipe1 = findPreference<ListPreferenceEx>("gesture_swipe1")!!
val swipe2 = findPreference<ListPreference>("gesture_swipe2")!!
val drag = findPreference<ListPreferenceEx>("gesture_drag")!!
swipe1.apply { disabledStateSummary = getString(R.string.pref_gesture_action_move_pointer) }
drag.apply { helpMessage = getText(R.string.msg_drag_gesture_help) }
// To reduce clutter & avoid 'UI overload', Natural scrolling pref is not visible by default.
// It becomes visible only when 'Scroll remote content' option is used.
naturalScrollingUpdater = OnSharedPreferenceChangeListener { _, _ ->
val pref = findPreference<SwitchPreference>("natural_scrolling")
pref?.isVisible = "remote-scroll" in listOf(swipe1.value, swipe2.value, drag.value)
}
naturalScrollingUpdater?.onSharedPreferenceChanged(null, null) //Initial update
swipe1.sharedPreferences.registerOnSharedPreferenceChangeListener(naturalScrollingUpdater)
}
}

View file

@ -61,7 +61,7 @@ class Dispatcher(private val activity: VncActivity) {
* Action configuration
**************************************************************************/
private val directMode = DirectMode()
private val relativeMode by lazy { RelativeMode() } //Lazy to avoid pointer sync behaviour
private val relativeMode = RelativeMode()
private val defaultMode = if (gesturePref.directTouch) directMode else relativeMode
private val tap1Action = selectPointAction(gesturePref.tap1)
@ -73,6 +73,8 @@ class Dispatcher(private val activity: VncActivity) {
private val swipe2Action = selectSwipeAction(gesturePref.swipe2)
private val dragAction = selectSwipeAction(gesturePref.drag)
private val mouseBackAction = selectPointAction(viewModel.pref.input.mouseBack)
private fun selectPointAction(actionName: String): (PointF) -> Unit {
return when (actionName) {
"left-click" -> { p -> defaultMode.doClick(PointerButton.Left, p) }
@ -111,8 +113,8 @@ class Dispatcher(private val activity: VncActivity) {
* Event receivers
**************************************************************************/
fun onGestureStart() = stopFling()
fun onGestureStop(p: PointF) = defaultMode.doButtonRelease(p)
fun onGestureStart() = defaultMode.onGestureStart()
fun onGestureStop(p: PointF) = defaultMode.onGestureStop(p)
fun onTap1(p: PointF) = tap1Action(p)
fun onTap2(p: PointF) = tap2Action(p)
@ -124,12 +126,13 @@ class Dispatcher(private val activity: VncActivity) {
fun onDrag(sp: PointF, cp: PointF, dx: Float, dy: Float) = dragAction(sp, cp, dx, dy)
fun onScale(scaleFactor: Float, fx: Float, fy: Float) = doScale(scaleFactor, fx, fy)
fun onFling(vx: Float, vy: Float) = doFling(vx, vy)
fun onFling(vx: Float, vy: Float) = defaultMode.doFling(vx, vy)
fun onMouseButtonDown(button: PointerButton, p: PointF) = directMode.doButtonDown(button, p)
fun onMouseButtonUp(button: PointerButton, p: PointF) = directMode.doButtonUp(button, p)
fun onMouseMove(p: PointF) = directMode.doMovePointer(p, 0f, 0f)
fun onMouseScroll(p: PointF, hs: Float, vs: Float) = directMode.doRemoteScrollFromMouse(p, hs, vs)
fun onMouseBack(p: PointF) = mouseBackAction(p)
fun onStylusTap(p: PointF) = directMode.doClick(PointerButton.Left, p)
fun onStylusDoubleTap(p: PointF) = directMode.doDoubleClick(PointerButton.Left, p)
@ -147,9 +150,6 @@ class Dispatcher(private val activity: VncActivity) {
private fun doScale(scaleFactor: Float, fx: Float, fy: Float) = viewModel.updateZoom(scaleFactor, fx, fy)
private fun doPan(dx: Float, dy: Float) = viewModel.panFrame(dx, dy)
private fun doFling(vx: Float, vy: Float) = viewModel.frameScroller.fling(vx, vy)
private fun stopFling() = viewModel.frameScroller.stop()
/**
* Most actions have the same implementation in both modes, only difference being
* the point where event is sent. [transformPoint] is used for this mode-specific
@ -160,10 +160,15 @@ class Dispatcher(private val activity: VncActivity) {
private var accumulatedDx = 0F
private var accumulatedDy = 0F
private val deltaPerScroll = 20F //For how much dx/dy, one scroll event will be sent
private val yScrollDirection = (if (gesturePref.naturalScrolling) 1 else -1)
abstract fun transformPoint(p: PointF): PointF?
abstract fun doMovePointer(p: PointF, dx: Float, dy: Float)
abstract fun doDrag(p: PointF, dx: Float, dy: Float)
abstract fun doFling(vx: Float, vy: Float)
open fun onGestureStart() {}
open fun onGestureStop(p: PointF) = doButtonRelease(p)
fun doButtonDown(button: PointerButton, p: PointF) {
transformPoint(p)?.let { messenger.sendPointerButtonDown(button, it) }
@ -189,7 +194,7 @@ class Dispatcher(private val activity: VncActivity) {
fun doRemoteScroll(focus: PointF, dx: Float, dy: Float) {
accumulatedDx += dx
accumulatedDy += dy
accumulatedDy += dy * yScrollDirection
//Drain horizontal change
while (abs(accumulatedDx) >= deltaPerScroll) {
@ -231,6 +236,8 @@ class Dispatcher(private val activity: VncActivity) {
override fun transformPoint(p: PointF) = viewModel.frameState.toFb(p)
override fun doMovePointer(p: PointF, dx: Float, dy: Float) = doButtonDown(PointerButton.None, p)
override fun doDrag(p: PointF, dx: Float, dy: Float) = doButtonDown(PointerButton.Left, p)
override fun doFling(vx: Float, vy: Float) = viewModel.frameScroller.fling(vx, vy)
override fun onGestureStart() = viewModel.frameScroller.stop()
}
/**
@ -239,15 +246,36 @@ class Dispatcher(private val activity: VncActivity) {
private inner class RelativeMode : AbstractMode() {
private val pointerPosition = PointF(0f, 0f)
override fun onGestureStart() {
super.onGestureStart()
//Initialize with the latest pointer position
pointerPosition.apply {
x = viewModel.client.pointerX.toFloat()
y = viewModel.client.pointerY.toFloat()
}
viewModel.client.ignorePointerMovesByServer = true
}
override fun onGestureStop(p: PointF) {
super.onGestureStop(p)
viewModel.client.ignorePointerMovesByServer = false
}
override fun transformPoint(p: PointF) = pointerPosition
override fun doMovePointer(p: PointF, dx: Float, dy: Float) {
pointerPosition.apply {
offset(dx, dy)
x = x.coerceIn(0f, viewModel.frameState.fbWidth)
y = y.coerceIn(0f, viewModel.frameState.fbHeight)
x = x.coerceIn(0f, viewModel.frameState.fbWidth - 1)
y = y.coerceIn(0f, viewModel.frameState.fbHeight - 1)
}
doButtonDown(PointerButton.None, pointerPosition)
//Try to keep the pointer centered on screen
val vp = viewModel.frameState.toVP(pointerPosition)
val centerDiffX = viewModel.frameState.vpWidth / 2 - vp.x
val centerDiffY = viewModel.frameState.vpHeight / 2 - vp.y
viewModel.panFrame(centerDiffX, centerDiffY)
}
override fun doDrag(p: PointF, dx: Float, dy: Float) {
@ -255,46 +283,6 @@ class Dispatcher(private val activity: VncActivity) {
doMovePointer(p, dx, dy)
}
/**
* As we have no way know the real pointer position (at-least for now), we move
* the remote pointer to center of the screen. This is a crude way of syncing
* [pointerPosition] with remote pointer position.
*
* We have to start somewhere, and center of the screen seems most appropriate.
* This is done immediately after connecting (and whenever framebuffer size changes),
* If we waited for a gesture before doing this, there would be a sudden jump,
* right before the gesture. Another "benefit" is, if connection is fast enough,
* user might not even notice our trickery.
*
* One caveat is, we can sync only after client initialization, but size change event
* is likely to fire during initialization. Another problem is activity restart,
* which will re-trigger our client state observer. We use [syncPending] to solve
* both of these problems.
*
* We can (hopefully) implement a better solution in the future.
*/
private var syncPending = false
init {
viewModel.fbSizeChangedEvent.observe(activity) {
syncPending = true
syncPosition()
}
viewModel.state.observe(activity) {
syncPosition()
}
}
private fun syncPosition() {
if (syncPending && viewModel.client.connected) {
pointerPosition.apply {
x = viewModel.frameState.fbWidth / 2
y = viewModel.frameState.fbHeight / 2
}
doButtonDown(PointerButton.None, pointerPosition)
syncPending = false
}
}
override fun doFling(vx: Float, vy: Float) {} //Disabled
}
}

View file

@ -194,6 +194,14 @@ class FrameState(private val minZoomScale: Float = 0.5F, private val maxZoomScal
return null
}
/**
* Converts given framebuffer point to corresponding point in viewport.
*/
fun toVP(fbPoint: PointF): PointF {
return PointF(fbPoint.x * scale + frameX, fbPoint.y * scale + frameY)
}
private fun calculateBaseScale() {
if (fbHeight == 0F || fbWidth == 0F || windowHeight == 0F)
return //Not enough info yet

View file

@ -13,6 +13,9 @@ import android.os.Build
import android.view.*
import com.gaurav.avnc.viewmodel.VncViewModel
import com.gaurav.avnc.vnc.PointerButton
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.atan2
import kotlin.math.max
/**
@ -44,7 +47,8 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
fun onHoverEvent(event: MotionEvent): Boolean {
if (event.actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
dispatcher.onMouseMove(event.point())
lastHoverPoint = event.point()
dispatcher.onMouseMove(lastHoverPoint)
return true
}
return false
@ -58,6 +62,11 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
}
}
// Used for back-press interception
private var lastHoverPoint = PointF()
fun onMouseBack() {
dispatcher.onMouseBack(lastHoverPoint)
}
/****************************************************************************************
* Mouse
@ -114,7 +123,12 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
}
inner class StylusGestureListener : GestureDetector.SimpleOnGestureListener() {
override fun onDown(e: MotionEvent?) = true
private var scrolling = false
override fun onDown(e: MotionEvent?): Boolean {
scrolling = false
return true
}
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
dispatcher.onStylusTap(e.point())
@ -133,8 +147,16 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
// Scrolling with stylus button pressed is currently used for scale gesture
if (e2.buttonState and MotionEvent.BUTTON_STYLUS_PRIMARY == 0)
if (e2.buttonState and MotionEvent.BUTTON_STYLUS_PRIMARY == 0) {
// When scrolling starts, we need to send the first event at initial touch-point.
// Otherwise, we will loose the small distance (touch-slope) required by onScroll().
if (!scrolling) {
scrolling = true
dispatcher.onStylusScroll(e1.point())
}
dispatcher.onStylusScroll(e2.point())
}
return true
}
}
@ -151,10 +173,13 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
private val gestureDetector = GestureDetector(viewModel.app, this)
private val multiFingerTapDetector = MultiFingerTapDetector()
private val dragDetector = DragDetector()
private val swipeVsScale = SwipeVsScale()
private val dragEnabled = viewModel.pref.input.gesture.dragEnabled
private val swipeSensitivity = viewModel.pref.input.gesture.swipeSensitivity
private fun handleGestureEvent(event: MotionEvent): Boolean {
swipeVsScale.onTouchEvent(event)
dragDetector.onTouchEvent(event)
multiFingerTapDetector.onTouchEvent(event)
scaleDetector.onTouchEvent(event)
@ -164,7 +189,8 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
override fun onScaleBegin(detector: ScaleGestureDetector) = true
override fun onScaleEnd(detector: ScaleGestureDetector) {}
override fun onScale(detector: ScaleGestureDetector): Boolean {
dispatcher.onScale(detector.scaleFactor, detector.focusX, detector.focusY)
if (swipeVsScale.shouldScale())
dispatcher.onScale(detector.scaleFactor, detector.focusX, detector.focusY)
return true
}
@ -194,13 +220,17 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
return true
}
override fun onScroll(e1: MotionEvent, e2: MotionEvent, dX: Float, dY: Float): Boolean {
val startPoint = e1.point()
override fun onScroll(e1: MotionEvent?, e2: MotionEvent, dX: Float, dY: Float): Boolean {
// As weird as it may be, e1 can be null on some devices
val startPoint = e1?.point() ?: return false
val currentPoint = e2.point()
val normalizedDx = -dX * swipeSensitivity
val normalizedDy = -dY * swipeSensitivity
when (e2.pointerCount) {
1 -> dispatcher.onSwipe1(startPoint, currentPoint, -dX, -dY)
2 -> dispatcher.onSwipe2(startPoint, currentPoint, -dX, -dY)
1 -> dispatcher.onSwipe1(startPoint, currentPoint, normalizedDx, normalizedDy)
2 -> if (swipeVsScale.shouldSwipe())
dispatcher.onSwipe2(startPoint, currentPoint, normalizedDx, normalizedDy)
}
multiFingerTapDetector.reset()
@ -320,4 +350,106 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
fingerCount = 0
}
}
/**
* Swipe vs Scale detector.
*
* Many two-finger gestures are detected as both swipe & scale gestures because
* [GestureDetector] & [ScaleGestureDetector] work independently. This works
* very well when two-finger swipe pref is set to 'pan' (default value).
* But when the pref is set to 'remote-scroll', this independent detection
* becomes an issue. When user tries to scale, it frequently triggers remote
* scrolling. And when user tries to scroll, it triggers scaling.
*
* This class tries to clearly differentiate between these two gestures.
* It works by tracking the fingers, and calculating the angle between two paths.
* Then [decide] between two gestures by comparing the angle to some thresholds.
*
* This class can mis-detect some gestures because fingers don't always move
* perfectly, but it does provide huge improvement over existing situation.
*/
private inner class SwipeVsScale {
private val enabled = viewModel.pref.input.gesture.swipe2 == "remote-scroll"
private var detecting = false
private var scaleDetected = false
private var swipeDetected = false
private var f1Id = 0 // Finger 1
private var f2Id = 0 // Finger 2
private val f1Start = PointF()
private val f2Start = PointF()
private val f1Current = PointF()
private val f2Current = PointF()
fun onTouchEvent(e: MotionEvent) {
if (!enabled)
return
when (e.actionMasked) {
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_CANCEL,
MotionEvent.ACTION_UP,
MotionEvent.ACTION_POINTER_UP -> {
detecting = false
scaleDetected = false
swipeDetected = false
}
MotionEvent.ACTION_POINTER_DOWN -> {
detecting = e.pointerCount == 2
if (detecting) {
f1Id = e.getPointerId(0)
f2Id = e.getPointerId(1)
f1Start.set(e.getX(f1Id), e.getY(f1Id))
f2Start.set(e.getX(f2Id), e.getY(f2Id))
f1Current.set(f1Start)
f2Current.set(f2Start)
}
}
MotionEvent.ACTION_MOVE -> {
if (detecting) {
f1Current.set(e.getX(f1Id), e.getY(f1Id))
f2Current.set(e.getX(f2Id), e.getY(f2Id))
}
}
}
}
fun shouldScale(): Boolean {
decide()
return !enabled || (detecting && scaleDetected)
}
fun shouldSwipe(): Boolean {
decide()
return !enabled || (detecting && swipeDetected)
}
/**
* Decides if gesture can be considered a swipe/scale
*/
private fun decide() {
if (!detecting)
return
val t1 = theta(f1Start, f1Current)
val t2 = theta(f2Start, f2Current)
val diff = abs(t1 - t2)
scaleDetected = diff > 45
swipeDetected = diff < 30
}
/**
* Returns the angle made by line [p1]->[p2] with the positive x-axis.
* Returned angle will be in range [0, 360]
*/
private fun theta(p1: PointF, p2: PointF): Double {
val theta = atan2(p2.y - p1.y, p2.x - p1.x)
val degree = (theta / PI) * 180
return (degree + 360) % 360 // Map [-180, 180] to [0, 360]
}
}
}

View file

@ -12,16 +12,14 @@ import android.app.Activity
import android.app.PictureInPictureParams
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.util.Rational
import android.view.Gravity
import android.view.KeyEvent
import android.view.View
import android.view.WindowManager
import android.view.*
import android.view.inputmethod.InputMethodManager
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
@ -43,10 +41,14 @@ import java.lang.ref.WeakReference
private const val PROFILE_KEY = "com.gaurav.avnc.server_profile"
fun createVncIntent(context: Context, profile: ServerProfile): Intent {
return Intent(context, VncActivity::class.java).apply {
putExtra(PROFILE_KEY, profile)
}
}
fun startVncActivity(source: Activity, profile: ServerProfile) {
val intent = Intent(source, VncActivity::class.java)
intent.putExtra(PROFILE_KEY, profile)
source.startActivity(intent)
source.startActivity(createVncIntent(source, profile))
}
fun startVncActivity(source: Activity, uri: VncUri) {
@ -61,7 +63,7 @@ fun startVncActivity(source: Activity, uri: VncUri) {
*/
class VncActivity : AppCompatActivity() {
private val profile by lazy { loadProfile() }
private lateinit var profile: ServerProfile
val viewModel by viewModels<VncViewModel>()
lateinit var binding: ActivityVncBinding
private val dispatcher by lazy { Dispatcher(this) }
@ -71,27 +73,29 @@ class VncActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
profile = loadProfile()
viewModel.initConnection(profile)
//Main UI
binding = DataBindingUtil.setContentView(this, R.layout.activity_vnc)
binding.viewModel = viewModel
binding.lifecycleOwner = this
binding.frameView.initialize(this)
viewModel.frameViewRef = WeakReference(binding.frameView)
setupLayout()
binding.frameView.initialize(this)
binding.retryConnectionBtn.setOnClickListener { retryConnection() }
//Drawers
setupDrawerLayout()
//Buttons
binding.keyboardBtn.setOnClickListener { showKeyboard(); closeDrawers() }
binding.zoomResetBtn.setOnClickListener { viewModel.resetZoom(); closeDrawers() }
binding.virtualKeysBtn.setOnClickListener { virtualKeys.show(); closeDrawers() }
binding.retryConnectionBtn.setOnClickListener { retryConnection() }
//ViewModel setup
viewModel.frameViewRef = WeakReference(binding.frameView)
//Observers
viewModel.credentialRequest.observe(this) { showCredentialDialog() }
viewModel.sshHostKeyVerifyRequest.observe(this) { showHostKeyDialog() }
viewModel.state.observe(this) { onClientStateChanged(it) }
viewModel.initConnection(profile) //Should be called after observers has been setup
}
override fun onResume() {
@ -118,14 +122,11 @@ class VncActivity : AppCompatActivity() {
private fun loadProfile(): ServerProfile {
val profile = intent.getParcelableExtra<ServerProfile>(PROFILE_KEY)
if (profile != null) {
// Make a copy to avoid modifying intent's instance,
// because we may need the original if we have to retry connection.
return profile.copy()
}
check(profile != null) { "ServerProfile is missing from VncActivity Intent" }
Log.e(javaClass.simpleName, "No connection information was passed through Intent.")
return ServerProfile()
// Make a copy to avoid modifying intent's instance,
// because we may need the original if we have to retry connection.
return profile.copy()
}
private fun retryConnection() {
@ -163,10 +164,10 @@ class VncActivity : AppCompatActivity() {
viewModel.pref.runInfo.hasConnectedSuccessfully = true
// Highlight drawer for first time users
binding.drawerLayout.open()
binding.drawerLayout.openDrawer(binding.primaryToolbar)
lifecycleScope.launchWhenCreated {
delay(1500)
binding.drawerLayout.close()
binding.drawerLayout.closeDrawer(binding.primaryToolbar)
}
}
@ -187,6 +188,8 @@ class VncActivity : AppCompatActivity() {
private fun setupLayout() {
setupOrientation()
@Suppress("DEPRECATION")
if (fullscreenMode) {
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
@ -226,6 +229,14 @@ class VncActivity : AppCompatActivity() {
}
}
private fun setupOrientation() {
requestedOrientation = when (viewModel.pref.viewer.orientation) {
"portrait" -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
"landscape" -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
else -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
}
private fun setupDrawerLayout() {
binding.drawerLayout.setScrimColor(0)
@ -238,6 +249,25 @@ class VncActivity : AppCompatActivity() {
if (viewModel.pref.experimental.swipeCloseToolbar)
Experimental.setupDrawerCloseOnScrimSwipe(binding.drawerLayout, gravityH)
//Add System Gesture exclusion rects to allow opening toolbar drawer by swiping from edge
if (Build.VERSION.SDK_INT >= 29) {
binding.primaryToolbar.addOnLayoutChangeListener { _, left, top, right, bottom, _, _, _, _ ->
val root = binding.drawerLayout
val rect = Rect(left, top, right, bottom)
if (left < 0) rect.offset(-left, 0)
if (right > root.width) rect.offset(-(right - root.width), 0)
if (immersiveMode) {
rect.top = 0
rect.bottom = root.height
}
//Set exclusion rects on root because toolbar may not be visible
root.systemGestureExclusionRects = listOf(rect)
}
}
}
@ -289,8 +319,14 @@ class VncActivity : AppCompatActivity() {
if (canEnter && Build.VERSION.SDK_INT >= 26) {
val fs = viewModel.frameState
val aspectRatio = Rational(fs.fbWidth.toInt(), fs.fbHeight.toInt())
var w = viewModel.frameState.fbWidth
var h = viewModel.frameState.fbHeight
// Android require aspect ratio to be less than 2.39
w = w.coerceIn(1f, 2.3f * h)
h = h.coerceIn(1f, 2.3f * w)
val aspectRatio = Rational(w.toInt(), h.toInt())
val param = PictureInPictureParams.Builder().setAspectRatio(aspectRatio).build()
try {
@ -306,14 +342,31 @@ class VncActivity : AppCompatActivity() {
************************************************************************************/
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
return keyHandler.onKeyEvent(event) || super.onKeyDown(keyCode, event)
return keyHandler.onKeyEvent(event) || workarounds(event) || super.onKeyDown(keyCode, event)
}
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
return keyHandler.onKeyEvent(event) || super.onKeyUp(keyCode, event)
return keyHandler.onKeyEvent(event) || workarounds(event) || super.onKeyUp(keyCode, event)
}
override fun onKeyMultiple(keyCode: Int, repeatCount: Int, event: KeyEvent): Boolean {
return keyHandler.onKeyEvent(event) || super.onKeyMultiple(keyCode, repeatCount, event)
}
private fun workarounds(keyEvent: KeyEvent): Boolean {
//It seems that some device manufacturers are hell-bent on making developers'
//life miserable. In their infinite wisdom, they decided that Android apps don't
//need Mouse right-click events. It is hardcoded to act as back-press, without
//giving apps a chance to handle it. For better or worse, they set the 'source'
//for such key events to Mouse, enabling the following workarounds.
if (keyEvent.keyCode == KeyEvent.KEYCODE_BACK &&
InputDevice.getDevice(keyEvent.deviceId).supportsSource(InputDevice.SOURCE_MOUSE) &&
viewModel.pref.input.interceptMouseBack) {
if (keyEvent.action == KeyEvent.ACTION_DOWN)
touchHandler.onMouseBack()
return true
}
return false
}
}

View file

@ -10,6 +10,7 @@ package com.gaurav.avnc.ui.vnc.gl
import android.opengl.GLES20.*
import android.util.Log
import com.gaurav.avnc.BuildConfig
/**
* Represents the GL program used for Frame rendering.
@ -34,6 +35,7 @@ class FrameProgram {
val uProjectionLocation = glGetUniformLocation(program, U_PROJECTION)
val uTexUnitLocation = glGetUniformLocation(program, U_TEXTURE_UNIT)
val textureId = createTexture()
var validated = false
fun setUniforms(projectionMatrix: FloatArray) {
@ -54,10 +56,19 @@ class FrameProgram {
glBindTexture(GL_TEXTURE_2D, texturesObjects[0])
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glBindTexture(GL_TEXTURE_2D, 0)
return texturesObjects[0]
}
fun validate() {
if (BuildConfig.DEBUG && !validated) {
ShaderCompiler.validateProgram(program)
validated = true
}
}
fun useProgram() {
glUseProgram(program)
}

View file

@ -22,6 +22,7 @@ class Renderer(val viewModel: VncViewModel) : GLSurfaceView.Renderer {
private val projectionMatrix = FloatArray(16)
private val state = viewModel.frameState
private val hideCursor = viewModel.pref.input.hideRemoteCursor
private lateinit var program: FrameProgram
private lateinit var frame: Frame
@ -86,9 +87,12 @@ class Renderer(val viewModel: VncViewModel) : GLSurfaceView.Renderer {
program.setUniforms(projectionMatrix)
viewModel.client.uploadFrameTexture()
if (!hideCursor) viewModel.client.uploadCursor()
frame.updateFbSize(state.fbWidth, state.fbHeight)
frame.bind(program)
program.validate()
frame.draw()
}
}

View file

@ -10,7 +10,6 @@ package com.gaurav.avnc.ui.vnc.gl
import android.opengl.GLES20.*
import android.util.Log
import com.gaurav.avnc.BuildConfig
object ShaderCompiler {
@ -76,9 +75,6 @@ object ShaderCompiler {
if (vertexShaderId == 0 || fragmentShaderId == 0)
return 0
val programId = linkProgram(vertexShaderId, fragmentShaderId)
if (BuildConfig.DEBUG)
validateProgram(programId)
return programId
return linkProgram(vertexShaderId, fragmentShaderId)
}
}

View file

@ -15,13 +15,13 @@ object Shaders {
//language=GLSL
const val VERTEX_SHADER = """
uniform mat4 u_Projection;
attribute vec4 a_Position;
attribute vec2 a_Position;
attribute vec2 a_TextureCoordinates;
varying vec2 v_TextureCoordinates;
void main()
{
v_TextureCoordinates = a_TextureCoordinates;
gl_Position = u_Projection * a_Position;
gl_Position = u_Projection * vec4(a_Position, 0, 1);
}"""
//language=GLSL

View file

@ -27,6 +27,7 @@ class AppPreferences(context: Context) {
}
inner class Viewer {
val orientation; get() = prefs.getString("viewer_orientation", "auto")
val fullscreen; get() = prefs.getBoolean("fullscreen_display", true)
val pipEnabled; get() = prefs.getBoolean("pip_enabled", false)
val toolbarAlignment; get() = prefs.getString("toolbar_alignment", "start")
@ -36,7 +37,7 @@ class AppPreferences(context: Context) {
inner class Gesture {
val directTouch; get() = prefs.getBoolean("gesture_direct_touch", true)
val tap1; get() = prefs.getString("gesture_single_tap", "left-click")!!
val tap1 = "left-click" //Preference UI was removed
val tap2; get() = prefs.getString("gesture_tap2", "open-keyboard")!!
val doubleTap; get() = prefs.getString("gesture_double_tap", "double-click")!!
val longPress; get() = prefs.getString("gesture_long_press", "right-click")!!
@ -44,6 +45,8 @@ class AppPreferences(context: Context) {
val swipe2; get() = prefs.getString("gesture_swipe2", "pan")!!
val drag; get() = prefs.getString("gesture_drag", "none")!!
val dragEnabled; get() = (drag != "none")
val swipeSensitivity; get() = prefs.getInt("gesture_swipe_sensitivity", 10) / 10f
val naturalScrolling; get() = prefs.getBoolean("natural_scrolling", true)
}
inner class Input {
@ -54,6 +57,9 @@ class AppPreferences(context: Context) {
val mousePassthrough; get() = prefs.getBoolean("mouse_passthrough", true)
val hideLocalCursor; get() = prefs.getBoolean("hide_local_cursor", false)
val hideRemoteCursor; get() = prefs.getBoolean("hide_remote_cursor", false)
val mouseBack; get() = prefs.getString("mouse_back", "right-click")!!
val interceptMouseBack; get() = mouseBack != "default"
val kmLanguageSwitchToSuper; get() = prefs.getBoolean("km_language_switch_to_super", false)
val kmRightAltToSuper; get() = prefs.getBoolean("km_right_alt_to_super", false)
@ -69,7 +75,7 @@ class AppPreferences(context: Context) {
inner class Experimental {
val swipeCloseToolbar; get() = prefs.getBoolean("experimental_vnc_toolbar_swipe_close", true)
val immersiveMode; get() = prefs.getBoolean("viewer_immersive_mode", false)
val immersiveMode; get() = prefs.getBoolean("viewer_immersive_mode", true)
}
/**

View file

@ -2,7 +2,6 @@ package com.gaurav.avnc.util
import android.app.Dialog
import android.os.Bundle
import androidx.annotation.StringRes
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -13,12 +12,12 @@ object MsgDialog {
/**
* Shows a dialog with given title & message,
*/
fun show(manager: FragmentManager, @StringRes titleRes: Int, @StringRes msgRes: Int) {
fun show(manager: FragmentManager, title: CharSequence, msg: CharSequence) {
val fragment = MsgDialogFragment()
val args = Bundle(2)
args.putInt("titleRes", titleRes)
args.putInt("msgRes", msgRes)
args.putCharSequence("title", title)
args.putCharSequence("msg", msg)
fragment.arguments = args
fragment.show(manager, null)
@ -27,8 +26,8 @@ object MsgDialog {
class MsgDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return MaterialAlertDialogBuilder(requireContext())
.setTitle(requireArguments().getInt("titleRes"))
.setMessage(requireArguments().getInt("msgRes"))
.setTitle(requireArguments().getCharSequence("title"))
.setMessage(requireArguments().getCharSequence("msg"))
.setPositiveButton(android.R.string.ok) { _, _ -> /* Let it dismiss */ }
.create()
}

View file

@ -66,7 +66,7 @@ open class BaseViewModel(val app: Application) : AndroidViewModel(app) {
/**
* Executes given [block] asynchronously on IO thread.
*/
protected fun asyncIO(block: () -> Unit) = asyncIO(block, {})
protected fun asyncIO(block: () -> Unit) = asyncIO(block) {}
/**
* Executes given [block] asynchronously on IO thread.

View file

@ -92,7 +92,7 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
/**
* [ServerProfile] used for current connection.
*/
var profile = ServerProfile()
var profile = ServerProfile(); private set
/**
* We have two places for connection state (both are synced):
@ -148,11 +148,6 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
*/
val frameScroller = FrameScroller(this)
/**
* Fired when size of the native framebuffer changes.
*/
val fbSizeChangedEvent = LiveEvent<FrameState>()
/**
* Used for sending events to remote server.
*/
@ -172,16 +167,14 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
/**
* Initialize VNC connection using given profile.
* [initConnection] can be called multiple times due to activity restarts.
* It may be called multiple times due to activity restarts.
*/
fun initConnection(profile: ServerProfile) {
if (state.value != State.Created)
return
this.profile = profile
state.value = State.Connecting
launchConnection()
if (state.value == State.Created) {
state.value = State.Connecting
this.profile = profile
launchConnection()
}
}
private fun launchConnection() {
@ -207,7 +200,7 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
}
private fun configureClient() {
client.configure(profile.viewOnly, profile.securityType, profile.useLocalCursor)
client.configure(profile.viewOnly, profile.securityType, true /* Hardcoded to true */)
if (profile.useRepeater)
client.setupRepeater(profile.idOnRepeater)
@ -339,7 +332,10 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
override fun onFramebufferSizeChanged(width: Int, height: Int) {
viewModelScope.launch(Dispatchers.Main) {
frameState.setFramebufferSize(width.toFloat(), height.toFloat())
fbSizeChangedEvent.fire(frameState)
}
}
override fun onPointerMoved(x: Int, y: Int) {
frameViewRef.get()?.requestRender()
}
}

View file

@ -83,7 +83,8 @@ class Discovery(private val context: Context) {
if (nsdManager == null)
nsdManager = context.getSystemService(Context.NSD_SERVICE) as NsdManager
nsdManager?.discoverServices(service, NsdManager.PROTOCOL_DNS_SD, listener)
runCatching { nsdManager?.discoverServices(service, NsdManager.PROTOCOL_DNS_SD, listener) }
.onFailure { Log.e(javaClass.simpleName, "Unable to start Discovery", it) }
}
}
@ -93,7 +94,8 @@ class Discovery(private val context: Context) {
*/
fun stop() {
if (isRunning.value == true) {
nsdManager?.stopServiceDiscovery(listener)
runCatching { nsdManager?.stopServiceDiscovery(listener) }
.onFailure { Log.e(javaClass.simpleName, "Unable to stop Discovery", it) }
}
}
@ -102,7 +104,6 @@ class Discovery(private val context: Context) {
*/
private fun addProfile(name: String, host: String, port: Int) {
val profile = ServerProfile(
ID = (name + host + port).hashCode().toLong(),
name = name,
host = host,
port = port

View file

@ -57,6 +57,7 @@ class Messenger(private val client: VncClient) {
private fun sendPointerEvent(mask: Int, p: PointF) {
val x = p.x.toInt()
val y = p.y.toInt()
client.moveClientPointer(x, y)
execute { client.sendPointerEvent(x, y, mask) }
}

View file

@ -1,7 +1,6 @@
package com.gaurav.avnc.vnc
import androidx.annotation.Keep
import com.gaurav.avnc.vnc.VncClient.Observer
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
@ -30,15 +29,13 @@ import java.nio.charset.StandardCharsets
*
* To release the resources you must call [cleanup] after you are done with
* this instance.
*
* Note: All callbacks in [Observer] are invoked on the thread which called
* [processServerMessage].
*/
class VncClient(private val observer: Observer) {
/**
* Interface for event observer.
* DO NOT throw exceptions from these methods.
* There is NO guarantee about which thread will invoke [Observer] methods.
*/
interface Observer {
fun onPasswordRequired(): String
@ -46,9 +43,9 @@ class VncClient(private val observer: Observer) {
fun onGotXCutText(text: String)
fun onFramebufferUpdated()
fun onFramebufferSizeChanged(width: Int, height: Int)
fun onPointerMoved(x: Int, y: Int)
//fun onBell()
//fun onCursorMoved(x: Int, y: Int): Boolean
}
/**
@ -81,6 +78,27 @@ class VncClient(private val observer: Observer) {
*/
var viewOnlyMode = false; private set
/**
* Latest pointer position. See [moveClientPointer].
*/
var pointerX = 0; private set
var pointerY = 0; private set
/**
* Client-side cursor rendering creates a synchronization issue.
* Suppose if pointer is moved to (50,10) by client. A PointerEvent is sent
* to the server and cursor is immediately rendered on (50,10).
* Some servers (e.g. Vino) will send back a PointerPosition event for (50, 10).
* But, by the time that event is received from server, pointer on client
* might have already moved to (60,20) (this is almost guaranteed to happen
* with touchpad/relative action mode). So the cursor will probably 'jump back'
* depending on the order of these events.
*
* This flags works around the issue by temporarily ignoring serer-side updates.
*/
@Volatile
var ignorePointerMovesByServer = false
/**
* Setup different properties for this client.
*
@ -139,6 +157,26 @@ class VncClient(private val observer: Observer) {
nativeSendPointerEvent(nativePtr, x, y, mask)
}
/**
* Updates client-side pointer position.
* No event is sent to server.
*
* Primary use-case is to update pointer position during gestures.
* This way we can immediately render the cursor on new position without
* waiting for Network IO.
*
* It also helps with servers which don't send pointer-position updates
* if pointer was moved by the client.
*
* @param x Horizontal pointer coordinate
* @param y Vertical pointer coordinate
*/
fun moveClientPointer(x: Int, y: Int) {
pointerX = x
pointerY = y
observer.onPointerMoved(x, y)
}
/**
* Sends text to remote desktop's clipboard.
@ -158,6 +196,11 @@ class VncClient(private val observer: Observer) {
*/
fun uploadFrameTexture() = nativeUploadFrameTexture(nativePtr)
/**
* Upload cursor shape into framebuffer texture.
*/
fun uploadCursor() = nativeUploadCursor(nativePtr, pointerX, pointerY)
/**
* Release all resources allocated by the client.
* DO NOT use this client after [cleanup].
@ -190,6 +233,7 @@ class VncClient(private val observer: Observer) {
private external fun nativeGetHeight(clientPtr: Long): Int
private external fun nativeIsEncrypted(clientPtr: Long): Boolean
private external fun nativeUploadFrameTexture(clientPtr: Long)
private external fun nativeUploadCursor(clientPtr: Long, px: Int, py: Int)
private external fun nativeGetLastErrorStr(): String
private external fun nativeCleanup(clientPtr: Long)
@ -215,8 +259,10 @@ class VncClient(private val observer: Observer) {
private fun cbBell() = Unit // observer.onBell()
@Keep
@Suppress("UNUSED_PARAMETER")
private fun cbHandleCursorPos(x: Int, y: Int) = true //observer.onCursorMoved(x, y)
private fun cbHandleCursorPos(x: Int, y: Int) {
if (!ignorePointerMovesByServer)
moveClientPointer(x, y)
}
/**

View file

@ -7,6 +7,7 @@
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
@ -32,10 +33,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large"
android:layout_marginBottom="@dimen/margin_normal"
android:drawableEnd="@drawable/ic_gpl"
android:drawablePadding="@dimen/padding_small"
android:text='@{@string/app_name + " is released under the GNU General Public License, version 3 or later."}'
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" />
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
app:drawableEndCompat="@drawable/ic_gpl" />
<TextView

View file

@ -144,18 +144,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/use_repeater" />
<!--Local cursor mode (Currently it is only used for 'hiding' remote cursor)-->
<CheckBox
android:id="@+id/use_local_cursor"
style="@style/FormField.CheckBox"
android:checked="@={profile.useLocalCursor}"
android:text="@string/title_hide_remote_cursor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/view_only" />
<!--Key Compatibility mode-->
<CheckBox
android:id="@+id/key_compat_mode"
@ -164,7 +152,7 @@
android:text="@string/title_key_compat_mode"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/use_local_cursor" />
app:layout_constraintTop_toBottomOf="@id/view_only" />
<ImageButton
android:id="@+id/key_compat_mode_help_btn"

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 Gaurav Ujjwal.
~
~ SPDX-License-Identifier: GPL-3.0-or-later
~
~ See COPYING.txt for more details.
-->
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/help_btn"
style="@style/ImageButton"
android:contentDescription="@string/desc_help_btn"
android:src="@drawable/ic_help"
app:tint="?colorControlNormal" />

View file

@ -45,7 +45,7 @@
android:contentDescription="Hide Virtual Keys"
android:minWidth="30dp"
android:src="@drawable/ic_clear"
android:tint="?colorError" />
app:tint="?colorError" />
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~ Meta keys ~~~~~~~~~~~~~~~~~~~~~-->

View file

@ -0,0 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="title_add_server_profile">Server hinzufügen</string>
<string name="title_copy_name">Bezeichnung kopieren</string>
<string name="title_duplicate">Duplizieren</string>
<string name="title_delete">Entfernen</string>
<string name="title_copy_host">Hostname kopieren</string>
<string name="title_advanced">Erweitert</string>
<string name="title_undo">Rückgängig machen</string>
<string name="title_settings">Einstellungen</string>
<string name="title_credentials_required">Anmelden</string>
<string name="title_remember">Speichern</string>
<string name="title_export">Exportieren</string>
<string name="title_import">Importieren</string>
<string name="title_view_only_mode">Nur anzeigen</string>
<string name="title_key_compat_mode">Legacy key events senden</string>
<string name="title_use_ssh_tunnel">Durch SSH tunneln</string>
<string name="title_password">Passwort</string>
<string name="title_use_repeater">Repeater nutzen</string>
<string name="title_private_key">Privater Schlüssel</string>
<string name="title_change">Ändern</string>
<string name="title_about">Über</string>
<string name="title_report_bug">Melde einen Fehler</string>
<string name="title_github_repo">Quelltext</string>
<string name="hint_urlbar">Verbinde dich mit einem Server</string>
<string name="hint_server_name">Bezeichnung (z.B. Arbeits-PC)</string>
<string name="hint_repeater_host">Hostname/IP-Adresse des Repeaters</string>
<string name="msg_about_app">Ein schneller und sicherer VNC-Client. Beobachten und steuern Sie jedes entfernte Gerät, auf welchem ein VNC-Server läuft.
\n
\n
\nAVNC ist eine Open-Source- und Copyleft-Software, die mit Hilfe vieler Mitwirkender erstellt wurde.</string>
<string name="msg_export_auth_missing">Dein Gerät ist nicht gesichert. Aktiviere eine Displaysperre, um die das Exportieren damit zu authentifizieren.</string>
<string name="msg_invalid_key_file">Ungültiger private key</string>
<string name="msg_invalid_vnc_uri">Ungültige VNC-URI</string>
<string name="msg_copied_to_clipboard">Kopiert</string>
<string name="msg_key_compat_mode_help">Einige Server unterstützen Unicode-Zeichen nur teilweise. Legacy key events kann die Eingabe von nicht-englischem Text ermöglichen.</string>
<string name="tip_empty_server_list">Die Serverliste ist leer.
\nDrücke \'<b>+</b>\', um einen Server hinzuzufügen, oder
\nnutze die Adresszeile oben, um direkt eine Verbindung aufzubauen.</string>
<string name="tip_urlbar">Du kannst eine IP-Adresse, einen Hostnamen oder eine \'vnc://\'-URI eingeben.</string>
<string name="tip_discovery">Discovery kann unterstützte Server
\nin deinem Netzwerk automatisch finden.</string>
<string name="desc_navigation_btn">Navigationsbereich öffnen</string>
<string name="desc_discovered_servers_tab">Entdeckte Server</string>
<string name="desc_add_new_server_btn">Server hinzufügen</string>
<string name="desc_discovery_btn">Server entdecken</string>
<string name="desc_discovery_stop_btn">Suche nach Servern beenden</string>
<string name="desc_discovery_running">Suche Server in deinem Netzwerk</string>
<string name="desc_keyboard_btn">Öffne die Bildschirmtastatur</string>
<string name="desc_virtual_keys_btn">Zeige virtuelle Tasten</string>
<string name="desc_retry_connection_btn">Erneut versuchen</string>
<string name="desc_clear_url_btn">Löschen</string>
<string name="desc_help_btn">Hilfe</string>
<string name="desc_saved_servers_list">Liste der abgespeicherten Server</string>
<string name="pref_ui_summary">Optik und Handhabung</string>
<string name="pref_viewer">Fernsteuerung</string>
<string name="pref_viewer_summary">Bild in Bild, Zoomstufen</string>
<string name="pref_input_summary">Gesten, Mauszeiger, virtuelle Tasten</string>
<string name="pref_servers">Server</string>
<string name="pref_tools">Werkzeuge</string>
<string name="pref_tools_summary">Importieren/Exportieren, Debugging</string>
<string name="pref_experimental">Experimentelles</string>
<string name="pref_experimental_summary">Weitere Optionen</string>
<string name="pref_gesture_action_none">Nichts</string>
<string name="pref_gesture_action_pan">Lokale Ansicht verschieben</string>
<string name="pref_theme">Design</string>
<string name="pref_theme_option_system">System</string>
<string name="pref_sort_server_list">Sortiere die gespeicherten Server nach ihren Namen</string>
<string name="pref_gesture">Gesten</string>
<string name="pref_swipe2">Wischen mit zwei Fingern</string>
<string name="pref_drag">Ziehen (Drag and Drop)</string>
<string name="pref_mouse_passthrough">Maus-Passthrough</string>
<string name="pref_mouse_passthrough_summary_off">Maus-Bewegungen für lokale Gesten verwenden</string>
<string name="pref_hide_local_cursor">Lokalen Mauszeiger verstecken</string>
<string name="pref_mouse_back_action_default">Standard</string>
<string name="pref_vk_show_all">Zeige alle Tasten</string>
<string name="pref_vk_open_with_keyboard">Mit der Tastatur öffnen</string>
<string name="pref_km">Tastenzuordnungen</string>
<string name="pref_km_back_to_escape">Zurück → Escape</string>
<string name="pref_km_language_switch_to_super">Sprachwechsel → Super</string>
<string name="pref_km_right_alt_to_super">Rechte Alt → Super</string>
<string name="pref_km_back_to_escape_summary">Betrifft nicht die Navigationsleiste</string>
<string name="pref_fullscreen">Vollbild</string>
<string name="pref_toolbar">Werkzeugleiste</string>
<string name="pref_toolbar_alignment">Ausrichtung</string>
<string name="pref_toolbar_alignment_option_end">Rechts</string>
<string name="pref_zoom">Zoom</string>
<string name="pref_zoom_min">Minimum</string>
<string name="pref_credential">Anmelden</string>
<string name="pref_discovery_autorun">Automatische Suche</string>
<string name="pref_discovery">Automatische Suche</string>
<string name="pref_import_export">Server ex- und importieren</string>
<string name="pref_logs">Protokolle / Logs</string>
<string name="pref_experimental_toolbar_swipe_close_summary">Während einer Fernwartung können Sie die aktuell geöffnete Symbolleiste durch Wischen schließen</string>
<string name="pref_experimental_immersive_mode">Immersiver Modus</string>
<string name="title_edit_server_profile">Server bearbeiten</string>
<string name="title_edit">Bearbeiten</string>
<string name="title_clear">Löschen</string>
<string name="title_cancel">Abbrechen</string>
<string name="title_save">Speichern</string>
<string name="title_hide_remote_cursor">Entfernten Mauszeiger ausblenden</string>
<string name="msg_server_profile_added">Server hinzugefügt</string>
<string name="msg_server_profile_deleted">Server entfernt</string>
<string name="msg_ptr_hiding_not_supported">Wird auf diesem Gerät nicht unterstützt</string>
<string name="hint_username">Benutzername</string>
<string name="hint_password">Passwort</string>
<string name="title_delete_servers_before_import">Zurzeit gespeicherte Server vor dem Importieren löschen</string>
<string name="title_continue">Fortfahren</string>
<string name="hint_host">Hostname/IP-Adresse</string>
<string name="hint_port">Port</string>
<string name="title_license">Lizenz</string>
<string name="title_open_source_libraries">Open-Source-Bibliotheken</string>
<string name="hint_key_password">Passwort des Schlüssels</string>
<string name="msg_pip_not_supported">Wird auf diesem Gerät nicht unterstützt</string>
<string name="msg_loading">Lädt…</string>
<string name="msg_imported">Importiert</string>
<string name="msg_export_auth_required">Authentifiziere dich, um die Server zu exportieren</string>
<string name="msg_exported">Exportiert</string>
<string name="pref_long_press">Tippen und gedrückt halten</string>
<string name="pref_mouse_back">Zurück-Schaltfläche</string>
<string name="desc_back_btn">Zurück</string>
<string name="desc_settings_btn">Einstellungen</string>
<string name="desc_saved_servers_tab">Gespeicherte Server</string>
<string name="desc_reset_zoom_btn">Setze die Zoom-Stufe zurück</string>
<string name="pref_ui">Benutzeroberfläche</string>
<string name="desc_save_discovered_server_btn">Speichern</string>
<string name="desc_server_url">Server-Adresse</string>
<string name="desc_discovered_list">Liste der automatisch gefundenen Server</string>
<string name="pref_input">Eingabe</string>
<string name="pref_server_summary">Login, automatische Suche</string>
<string name="pref_gesture_action_move_pointer">Mauszeiger bewegen</string>
<string name="pref_gesture_action_left_click">Linksklick</string>
<string name="pref_gesture_action_middle_click">Mittelklick</string>
<string name="pref_gesture_action_right_click">Rechtsklick</string>
<string name="pref_gesture_action_remote_scroll">entfernte Ansicht scrollen</string>
<string name="pref_gesture_action_open_keyboard">Tastatur einblenden</string>
<string name="pref_theme_option_dark">Dunkel</string>
<string name="pref_theme_option_light">Hell</string>
<string name="pref_gesture_action_double_click">Doppelklick</string>
<string name="pref_prefer_advanced_editor">Zeige sofort die erweiterten Server-Optionen an</string>
<string name="pref_two_finger_tap">Tippen mit zwei Fingern</string>
<string name="pref_swipe_sensitivity">Wisch-Empfindlichkeit</string>
<string name="pref_mouse_passthrough_summary_on">Maus-Bewegungen direkt an den Server durchreichen</string>
<string name="pref_single_tap">Einfaches Tippen</string>
<string name="pref_swipe1">Wischen mit einem Finger</string>
<string name="pref_mouse">Maus</string>
<string name="pref_double_tap">Doppeltippen</string>
<string name="pref_mouse_back_action_right">Rechtsklick</string>
<string name="pref_vk">Virtuelle Tasten</string>
<string name="pref_mouse_back_action_middle">Mittelklick</string>
<string name="pref_toolbar_alignment_option_start">Links</string>
<string name="pref_enable_pip">Bild in Bild</string>
<string name="pref_zoom_max">Maximum</string>
<string name="pref_clipboard_sync">Zwischenablage syncronisieren</string>
<string name="pref_cred_autocomplete">Auto-Vervollständigung</string>
<string name="pref_cred_autocomplete_summary">Schlage gespeicherte Zugangsdaten bei neuen Servern vor</string>
<string name="pref_keycode_map">Keycode-Übersicht</string>
<string name="pref_experimental_toolbar_swipe_close">Wischen zum Schließen der Symbolleiste</string>
<string name="pref_discovery_autorun_summary">Schon auf der Startseite automatisch nach Servern suchen</string>
<string name="pref_rediscovery_indicator">Wiederentdeckte Server hervorheben</string>
<string name="pref_debugging">Fehlersuche</string>
<string name="pref_experimental_immersive_mode_summary">Blendet im Vollbildmodus Benachrichtigungs- und Navigationsleiste aus</string>
<string name="title_unknown_ssh_host">Unbekannter SSH-Zielpunkt</string>
<string name="title_default_security">Standard-Sicherheit</string>
<string name="title_ssh_host_key_changed">Der öffentliche Schlüssel des SSH-Zielpunktes hat sich geändert</string>
<string name="hint_server_id">Server-ID</string>
<string name="pref_gesture_action_remote_drag">Drag and Drop für entfernte Inhalte</string>
<string name="pref_direct_touch">Direkte Touch-Umsetzung auf dem Server</string>
<string name="pref_direct_touch_summary_on">Setze Aktionen direkt am Ort des Tippens um</string>
<string name="pref_direct_touch_summary_off">Setze Aktionen an einem Mauszeiger um, ähnlich einem Touchpad</string>
<string name="pref_orientation">Bildschirmausrichtung</string>
<string name="pref_orientation_option_auto">Automatisch (nach Lagesensor)</string>
<string name="pref_orientation_option_portrait">Hochformat</string>
<string name="pref_orientation_option_landscape">Querformat</string>
<string name="msg_drag_gesture_help">Der Ziehgeste eine Aktion zuzuweisen, wird die Erkennung von langem Drücken ändern:
\n
\n<b>Drücken-Halten-Loslassen</b> → Lange drücken
\n<b>Drücken-Halten-Wischen</b> → Drag and Drop</string>
</resources>

View file

@ -128,7 +128,7 @@
<string name="pref_mouse_passthrough">عبور ماوس</string>
<string name="pref_mouse_passthrough_summary_off">رویدادهای ماوس برای حرکات محلّی استفاده شوند</string>
<string name="pref_mouse_passthrough_summary_on">رویدادهای ماوس به صورت مستقیم به کارساز فرستاده شوند</string>
<string name="pref_hide_local_cursor">پنهان کردن اشاره‌گر محلّی (اندروید)</string>
<string name="pref_hide_local_cursor">پنهان کردن اشاره‌گر محلّی</string>
<string name="pref_vk">کلیدهای مجازی</string>
<string name="pref_vk_show_all">نمایش همهٔ کلیدها</string>
<string name="pref_vk_open_with_keyboard">گشودن همراه صفحه‌کلید</string>
@ -166,4 +166,14 @@
<string name="pref_direct_touch">لمس مستقیم</string>
<string name="pref_direct_touch_summary_on">در جای لمس شده کنش کن</string>
<string name="pref_direct_touch_summary_off">در جای اشاره‌گر کنش کن</string>
<string name="pref_mouse_back">دکمهٔ بازگشت</string>
<string name="pref_mouse_back_action_default">پیش‌گزیده</string>
<string name="pref_mouse_back_action_right">کلیک راست</string>
<string name="pref_mouse_back_action_middle">کلیک وسط</string>
<string name="pref_discovery_autorun">اجرای خودکار</string>
<string name="pref_rediscovery_indicator">کارسازهای کشف شده را برجسته کن</string>
<string name="pref_swipe_sensitivity">حساسیت به کشیدن</string>
<string name="desc_discovery_stop_btn">اکتشاف کارسازها را متوقّف کن</string>
<string name="pref_discovery_autorun_summary">در حالی که در صفحهٔ اصلی هستید، کارسازها را کشف کنید</string>
<string name="title_default_security">امنیت پیش‌گزیده</string>
</resources>

View file

@ -136,7 +136,7 @@
<string name="title_clear">Effacer</string>
<string name="pref_theme_option_dark">Sombre</string>
<string name="pref_theme_option_light">Clair</string>
<string name="pref_hide_local_cursor">Masquer le pointeur local (Android)</string>
<string name="pref_hide_local_cursor">Masquer le pointeur local</string>
<string name="pref_vk">Touches virtuelles</string>
<string name="pref_vk_show_all">Afficher toutes les touches</string>
<string name="pref_mouse">Souris</string>
@ -152,7 +152,7 @@
<string name="pref_cred_autocomplete_summary">Suggérer les identifiants enregistrés lors de la connexion à de nouveaux serveurs</string>
<string name="pref_discovery">Découverte</string>
<string name="pref_clipboard_sync">Synchronisation du presse-papiers</string>
<string name="pref_drag">Déplacement (appui long et glissement)</string>
<string name="pref_drag">Glisser</string>
<string name="pref_km_language_switch_to_super">Changement de langue → Super</string>
<string name="pref_import_export">Importer/Exporter des serveurs</string>
<string name="pref_keycode_map">Carte des codes-clés</string>
@ -161,4 +161,19 @@
<string name="desc_discovery_stop_btn">Arrêter la découverte des serveurs</string>
<string name="pref_rediscovery_indicator">Mettre en avant les serveurs découverts de nouveau</string>
<string name="pref_discovery_autorun">Exécution automatique</string>
<string name="pref_gesture_action_pan">Cadre local panoramique</string>
<string name="pref_mouse_back">Bouton retour</string>
<string name="pref_mouse_back_action_default">Par défaut</string>
<string name="pref_mouse_back_action_middle">Clic molette</string>
<string name="pref_mouse_back_action_right">Clic droit</string>
<string name="title_default_security">Sécurité par défaut</string>
<string name="pref_swipe_sensitivity">Sensibilité du défilement</string>
<string name="msg_drag_gesture_help">L\'affectation d\'une action à un geste de glissement modifiera la détection de l\'appui long :
\n
\n<b>Appui maintenu</b> → Appui long.
\n<b>Appui-maintien-glissement</b> → Glissement</string>
<string name="pref_orientation">Orientation de l\'écran</string>
<string name="pref_orientation_option_auto">Automatique</string>
<string name="pref_orientation_option_portrait">Portrait</string>
<string name="pref_orientation_option_landscape">Paysage</string>
</resources>

View file

@ -162,4 +162,10 @@
<string name="desc_discovery_stop_btn">Stopp oppdaging av tjenere</string>
<string name="pref_discovery_autorun">Kjør automatisk</string>
<string name="pref_discovery_autorun_summary">Oppdag tjenere når hovedsiden vises</string>
<string name="pref_mouse_back">«Tilbake»-knapp</string>
<string name="pref_mouse_back_action_default">Forvalg</string>
<string name="pref_mouse_back_action_right">Høyreklikk</string>
<string name="pref_mouse_back_action_middle">Midt-klikk</string>
<string name="title_default_security">Forvalgt sikkerhet</string>
<string name="pref_swipe_sensitivity">Dragningssensitivitet</string>
</resources>

View file

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="title_cancel">Anuluj</string>
<string name="title_password">Hasło</string>
<string name="title_add_server_profile">Dodaj serwer</string>
<string name="msg_key_compat_mode_help">Niektóre serwery mają ograniczoną obsługę Unicode. Kluczowe zdarzenia starszego typu mogą pomóc w wprowadzaniu tekstu w języku innym niż angielski.</string>
<string name="title_delete">Usuń</string>
<string name="title_clear">Wyczyść</string>
<string name="title_copy_name">Nazwa kopii</string>
<string name="title_advanced">Zaawansowane</string>
<string name="title_save">Zapisz</string>
<string name="title_undo">Cofnij</string>
<string name="title_settings">Ustawienia</string>
<string name="title_view_only_mode">Tylko podgląd</string>
<string name="title_use_ssh_tunnel">Użyj tunelu SSH</string>
<string name="title_private_key">Klucz prywatny</string>
<string name="title_about">O alikacji</string>
<string name="title_license">Licencja</string>
<string name="title_open_source_libraries">Biblioteki open source</string>
<string name="title_report_bug">Zgłoś problem</string>
<string name="title_github_repo">Kod źródłowy</string>
<string name="hint_urlbar">Połącz się z serwerem</string>
<string name="hint_server_name">Nazwa (np. Komputer służbowy)</string>
<string name="hint_host">Host</string>
<string name="hint_port">Port</string>
<string name="hint_server_id">Identyfikator serwera</string>
<string name="hint_username">Nazwa użytkownika</string>
<string name="hint_password">Hasło</string>
<string name="msg_about_app">Szybki i bezpieczny klient VNC. Umożliwia zdalne wyświetlanie i kontrolowanie dowolnego urządzenia z serwerem VNC.
\n
\n
\nAVNC jest oprogramowaniem typu open source i copyleft - powstał dzięki pomocy wielu współpracowników.</string>
<string name="msg_pip_not_supported">Brak wsparcia dla tego urządzenia</string>
<string name="msg_loading">Ładowanie…</string>
<string name="msg_invalid_key_file">Nieprawidłowy plik klucza prywatnego</string>
<string name="msg_invalid_vnc_uri">Nieprawidłowy identyfikator URI VNC</string>
<string name="msg_copied_to_clipboard">Skopiowano</string>
<string name="tip_empty_server_list">Lista serwerów jest pusta.
\nKliknij przycisk \'<b>+</b>\', aby dodać serwer, lub
\nużyj górnego paska adresu, aby połączyć się bezpośrednio.</string>
<string name="tip_urlbar">Można wprowadzić adres IP, nazwę hosta lub \'vnc://\' URI.</string>
<string name="desc_navigation_btn">Otwórz szufladę nawigacyjną</string>
<string name="desc_settings_btn">Ustawienia</string>
<string name="desc_saved_servers_tab">Zapisane serwery</string>
<string name="desc_discovered_servers_tab">Wykryte serwery</string>
<string name="title_edit_server_profile">Edytuj serwer</string>
<string name="title_unknown_ssh_host">Nieznany host SSH</string>
<string name="title_ssh_host_key_changed">Zmieniono klucz hosta SSH</string>
<string name="title_delete_servers_before_import">Usuń bieżące serwery przed importem</string>
<string name="msg_ptr_hiding_not_supported">Brak wsparcia dla tego urządzenia</string>
<string name="title_continue">Kontynuuj</string>
<string name="title_default_security">Domyślne zabezpieczenie</string>
<string name="desc_reset_zoom_btn">Zresetuj powiększenie</string>
<string name="desc_retry_connection_btn">Ponów próbę</string>
<string name="desc_clear_url_btn">Wyczyść</string>
<string name="desc_help_btn">Pomoc</string>
<string name="desc_discovered_list">Lista automatycznie wykrytych serwerów</string>
<string name="desc_saved_servers_list">Lista zapisanych serwerów</string>
<string name="pref_ui">Interfejs</string>
<string name="pref_viewer_summary">Obraz w obrazie, zoom</string>
<string name="pref_input">Wejście</string>
<string name="pref_input_summary">Gesty, mysz, wirtualna klawiatura</string>
<string name="pref_servers">Serwery</string>
<string name="pref_server_summary">Logowanie, odkrywanie</string>
<string name="pref_tools">Narzędzia</string>
<string name="pref_tools_summary">Import/Eksport, debugowanie</string>
<string name="pref_experimental">Eksperymentalne</string>
<string name="pref_experimental_summary">Dodatkowe gadżety</string>
<string name="pref_gesture_action_none">Żaden</string>
<string name="pref_gesture_action_middle_click">Środkowy przycisk</string>
<string name="pref_gesture_action_left_click">Lewy przycisk</string>
<string name="pref_gesture_action_right_click">Prawy przycisk</string>
<string name="pref_gesture_action_double_click">Podwójne kliknięcie</string>
<string name="pref_gesture_action_move_pointer">Przesuń wskaźnik</string>
<string name="pref_gesture_action_pan">Przesuń ramkę lokalną</string>
<string name="pref_gesture_action_open_keyboard">Otwórz klawiaturę</string>
<string name="pref_theme">Motyw</string>
<string name="pref_theme_option_system">System</string>
<string name="pref_theme_option_dark">Ciemny</string>
<string name="pref_theme_option_light">Jasny</string>
<string name="pref_prefer_advanced_editor">Bezpośrednie otwieranie zaawansowanych opcji serwera</string>
<string name="pref_sort_server_list">Sortuj zapisane serwery według nazwy</string>
<string name="pref_gesture">Gesty</string>
<string name="pref_swipe1">Przesunięcie jednym palcem</string>
<string name="pref_swipe2">Przesunięcie dwoma palcami</string>
<string name="pref_drag">Przeciągnij</string>
<string name="pref_swipe_sensitivity">Czułość przesuwania</string>
<string name="pref_mouse_passthrough_summary_on">Wysyłanie zdarzeń myszy bezpośrednio do serwera</string>
<string name="pref_hide_local_cursor">Ukryj wskaźnik lokalny</string>
<string name="pref_mouse_back">Przycisk Wstecz</string>
<string name="pref_mouse_back_action_default">Domyślnie</string>
<string name="pref_km_back_to_escape_summary">Nie wpływa na pasek nawigacyjny</string>
<string name="pref_enable_pip">Obraz w obrazie</string>
<string name="pref_toolbar">Pasek narzędzi</string>
<string name="pref_toolbar_alignment_option_start">Start</string>
<string name="pref_zoom">Powiększenie</string>
<string name="pref_zoom_min">Minimum</string>
<string name="pref_discovery_autorun">Automatyczne uruchamianie</string>
<string name="pref_rediscovery_indicator">Wyróżnij odkryte ponownie serwery</string>
<string name="pref_debugging">Debugowanie</string>
<string name="pref_experimental_toolbar_swipe_close">Przesuń, aby zamknąć szufladę paska narzędzi</string>
<string name="pref_experimental_immersive_mode_summary">Ukrywa wszystkie paski systemowe w trybie pełnoekranowym</string>
<string name="desc_discovery_stop_btn">Zatrzymaj wykrywanie serwerów</string>
<string name="desc_virtual_keys_btn">Pokaż wirtualną klawiaturę</string>
<string name="desc_discovery_running">Wykrywanie serwerów w sieci</string>
<string name="desc_keyboard_btn">Otwórz klawiaturę</string>
<string name="desc_server_url">Adres serwera</string>
<string name="pref_ui_summary">Wygląd i działanie</string>
<string name="pref_viewer">Przeglądarka</string>
<string name="pref_mouse">Mysz</string>
<string name="pref_single_tap">Pojedyncze dotknięcie</string>
<string name="pref_double_tap">Dwukrotne stuknięcie</string>
<string name="pref_two_finger_tap">Stuknięcie dwoma palcami</string>
<string name="pref_long_press">Długie naciśnięcie</string>
<string name="pref_fullscreen">Pełny ekran</string>
<string name="pref_credential">Logowanie</string>
<string name="pref_cred_autocomplete">Autouzupełnianie</string>
<string name="pref_zoom_max">Maximum</string>
<string name="pref_clipboard_sync">Synchronizacja schowka</string>
<string name="pref_logs">Dzienniki</string>
<string name="pref_cred_autocomplete_summary">Sugeruj zapisywanie loginów przy łączeniu się z nowymi serwerami</string>
<string name="pref_experimental_toolbar_swipe_close_summary">Podczas sesji zdalnej możesz przesunąć palcem, aby zamknąć aktualnie otwarty pasek narzędzi</string>
<string name="title_edit">Edytuj</string>
<string name="title_duplicate">Duplikuj</string>
<string name="title_copy_host">Kopiuj hosta</string>
<string name="title_credentials_required">Logowanie</string>
<string name="title_remember">Zapamiętaj</string>
<string name="title_export">Eksportuj</string>
<string name="title_import">Importuj</string>
<string name="title_change">Zmień</string>
<string name="hint_key_password">Hasło klucza</string>
<string name="msg_server_profile_added">Dodano serwer</string>
<string name="msg_server_profile_deleted">Serwer usunięto</string>
<string name="msg_imported">Importowane</string>
<string name="msg_exported">Eksportowane</string>
<string name="msg_export_auth_missing">Twoje urządzenie nie jest zabezpieczone. Włącz blokadę ekranu, aby umożliwić uwierzytelnianie przed eksportem.</string>
<string name="tip_discovery">Funkcja Discovery może automatycznie znaleźć obsługiwane serwery
\ndziałające w sieci.</string>
<string name="desc_add_new_server_btn">Dodaj serwer</string>
<string name="desc_save_discovered_server_btn">Zapisz</string>
<string name="desc_back_btn">Wstecz</string>
<string name="pref_mouse_passthrough">Przekazywanie myszy</string>
<string name="pref_mouse_passthrough_summary_off">Używanie zdarzeń myszy do wykonywania gestów lokalnych</string>
<string name="pref_gesture_action_remote_scroll">Przewiń zdalną zawartość</string>
<string name="pref_mouse_back_action_middle">Kliknij środkowym przyciskiem</string>
<string name="pref_vk_show_all">Pokaż wszystkie klawisze</string>
<string name="pref_km">Mapowanie klawiszy</string>
<string name="pref_km_right_alt_to_super">Prawy Alt → Super</string>
<string name="pref_km_back_to_escape">Powrót → Escape</string>
<string name="pref_toolbar_alignment">Wyrównanie</string>
<string name="pref_toolbar_alignment_option_end">Koniec</string>
<string name="pref_discovery_autorun_summary">Odkryj serwery na stronie głównej</string>
<string name="pref_discovery">Odkrywanie</string>
<string name="pref_import_export">Importuj/Eksportuj serwery</string>
<string name="pref_keycode_map">Mapa kodów</string>
<string name="pref_direct_touch">Bezpośredni dotyk</string>
<string name="pref_direct_touch_summary_on">Wykonuj działania w punkcie kontaktu</string>
<string name="pref_mouse_back_action_right">Kliknij prawym przyciskiem</string>
<string name="pref_gesture_action_remote_drag">Przeciągnij zdalną zawartość</string>
<string name="pref_vk">Wirtualna klawiatura</string>
<string name="pref_vk_open_with_keyboard">Otwórz za pomocą klawiatury</string>
<string name="title_use_repeater">Użyj wzmacniacza</string>
<string name="title_hide_remote_cursor">Ukryj zdalny wskaźnik</string>
<string name="pref_direct_touch_summary_off">Wykonaj działania na wskaźniku</string>
<string name="pref_km_language_switch_to_super">Zmiana języka → Super</string>
<string name="pref_experimental_immersive_mode">Tryb pełnej immersji</string>
<string name="msg_drag_gesture_help">Przypisanie akcji do gestu przeciągania zmieni Wykrywanie długiego naciśnięcia:
\n
\n<b>Wciśnięcie i zwolnienie </b> → Długie naciśnięcie
\n<b>Naciśnij-przytrzymaj-przeciągnij</b> → Przeciągnij</string>
<string name="pref_orientation">Orientacja ekranu</string>
<string name="pref_orientation_option_auto">Automatycznie</string>
<string name="pref_orientation_option_portrait">Portret</string>
<string name="pref_orientation_option_landscape">Krajobraz</string>
<string name="desc_discovery_btn">Odnajduj serwery</string>
<string name="msg_export_auth_required">Uwierzytelnij się, aby eksportować serwery</string>
</resources>

View file

@ -6,11 +6,11 @@
~
~ See COPYING.txt for more details.
--><resources>
<string name="title_add_server_profile">Adicionar um servidor</string>
<string name="title_edit_server_profile">Edição do servidor</string>
<string name="title_add_server_profile">Adicionar servidor</string>
<string name="title_edit_server_profile">Editar servidor</string>
<string name="title_duplicate">Duplicar</string>
<string name="title_edit">Editar</string>
<string name="title_delete">Excluir</string>
<string name="title_delete">Apagar</string>
<string name="title_copy_host">Copiar host</string>
<string name="title_copy_name">Copiar nome</string>
<string name="title_advanced">Avançado</string>
@ -18,27 +18,27 @@
<string name="title_save">Salvar</string>
<string name="title_undo">Desfazer</string>
<string name="title_settings">Configurações</string>
<string name="title_credentials_required">Credenciais necessárias</string>
<string name="title_remember">Lembrar</string>
<string name="title_credentials_required">Autenticação</string>
<string name="title_remember">Salvar</string>
<string name="title_export">Exportar</string>
<string name="title_import">Importar</string>
<string name="title_view_only_mode">Modo somente de visualização</string>
<string name="title_hide_remote_cursor">Esconder cursor remoto</string>
<string name="title_key_compat_mode">Modo de compatibilidade do teclado</string>
<string name="title_use_ssh_tunnel">Usar túnel de SSH</string>
<string name="title_unknown_ssh_host">Host de SSH desconhecido</string>
<string name="title_ssh_host_key_changed">A chave do host foi alterada</string>
<string name="title_view_only_mode">Somente visualização</string>
<string name="title_hide_remote_cursor">Esconder ponteiro remoto</string>
<string name="title_key_compat_mode">Enviar eventos de teclado compatíveis</string>
<string name="title_use_ssh_tunnel">Usar túnel SSH</string>
<string name="title_unknown_ssh_host">Host SSH desconhecido</string>
<string name="title_ssh_host_key_changed">Chave do host de SSH mudou</string>
<string name="title_continue">Continuar</string>
<string name="title_delete_servers_before_import">Excluir os servidores atuais antes de importar</string>
<string name="title_delete_servers_before_import">Apagar os servidores atuais antes de importar</string>
<string name="title_password">Senha</string>
<string name="title_use_repeater">Usar repetidor</string>
<string name="title_private_key">Chave privada</string>
<string name="title_change">Alterar</string>
<string name="title_change">Mudar</string>
<string name="title_about">Sobre</string>
<string name="title_license">Licença</string>
<string name="title_open_source_libraries">Bibliotecas de código livre</string>
<string name="title_report_bug">Relatar um erro</string>
<string name="title_github_repo">Repositório no Github</string>
<string name="title_report_bug">Relatar um problema</string>
<string name="title_github_repo">Código fonte</string>
<string name="hint_urlbar">Conectar ao servidor</string>
<string name="hint_server_name">Nome (ex. PC do trabalho)</string>
<string name="hint_host">Host</string>
@ -48,50 +48,56 @@
<string name="hint_username">Nome de usuário</string>
<string name="hint_password">Senha</string>
<string name="hint_key_password">Senha chave</string>
<string name="msg_about_app">O AVNC é um cliente VNC de software livre para Android. Permite o controle remoto de qualquer dispositivo que executa um servidor VNC.</string>
<string name="msg_about_app">Um cliente VNC rápido e seguro. Permite visualizar e controlar remotamente qualquer dispositivo que executa um servidor VNC.
\n
\n
\nO AVNC é de código aberto e sob licença copyleft — feito com a ajuda de muitos colaboradores.</string>
<string name="msg_server_profile_added">Servidor adicionado</string>
<string name="msg_server_profile_deleted">Servidor excluído</string>
<string name="msg_pip_not_supported">O recurso de Picture-in-Picture não é suportado neste dispositivo</string>
<string name="msg_loading">Carregando …</string>
<string name="msg_imported">Importado com sucesso</string>
<string name="msg_exported">Exportado com sucesso</string>
<string name="msg_export_auth_required">É necessária a autenticação antes de exportar</string>
<string name="msg_server_profile_deleted">Servidor apagado</string>
<string name="msg_pip_not_supported">Não suportado neste dispositivo</string>
<string name="msg_loading">Carregando…</string>
<string name="msg_imported">Importado</string>
<string name="msg_exported">Exportado</string>
<string name="msg_export_auth_required">Autenticar primeiro para poder exportar servidores</string>
<string name="msg_export_auth_missing">Seu dispositivo não está seguro. Ative o bloqueio da tela para permitir a autenticação antes de exportar.</string>
<string name="msg_invalid_key_file">O arquivo não é uma chave privada válida</string>
<string name="msg_invalid_vnc_uri">O URI do VNC inválido</string>
<string name="msg_invalid_key_file">Arquivo de chave privada inválido</string>
<string name="msg_invalid_vnc_uri">A URI do VNC inválida</string>
<string name="msg_copied_to_clipboard">Copiado</string>
<string name="msg_key_compat_mode_help">Alguns servidores não tem suporte a entrada direta do Unicode. O modo de compatibilidade tenta enviar eventos chave em formato legado. \n\nTente ativar/desativar isto, se você estiver tendo problemas inserindo alguns caracteres.</string>
<string name="tip_empty_server_list">A lista de servidores está vazia.\nUse o botão \'<b>+</b>\' para adicionar um servidor, ou\nUse a barra do Url na parte superior para conectar-se diretamente a um servidor.</string>
<string name="tip_urlbar">Você pode digitar o nome de host ou endereço de IP.\n\'vnc://\' URIs também são suportados.</string>
<string name="tip_discovery">A descoberta pode encontrar automaticamente os servidores suportados\nexecutados na sua rede.</string>
<string name="msg_key_compat_mode_help">Alguns servidores têm suporte limitado ao Unicode. Os eventos de teclado legado podem ajudar a inserir texto não-inglês.</string>
<string name="tip_empty_server_list">A lista de servidores está vazia.
\nUse o botão \'<b>+</b>\' para adicionar um servidor, ou
\nuse a barra de endereço na área superior para conectar diretamente.</string>
<string name="tip_urlbar">Você pode digitar o endereço de IP, nome de host ou \'vnc://\' URI.</string>
<string name="tip_discovery">A descoberta pode automaticamente encontrar os servidores suportados
\nexecutados na sua rede.</string>
<!-- Accessibility descriptions -->
<string name="desc_navigation_btn">Abrir a aba de navegação</string>
<string name="desc_settings_btn">Abrir configurações</string>
<string name="desc_navigation_btn">Abrir aba de navegação</string>
<string name="desc_settings_btn">Configurações</string>
<string name="desc_saved_servers_tab">Servidores salvos</string>
<string name="desc_discovered_servers_tab">Servidores encontrados</string>
<string name="desc_add_new_server_btn">Adicionar um novo servidor</string>
<string name="desc_save_discovered_server_btn">Salvar este servidor</string>
<string name="desc_discovery_btn">Iniciar a descoberta de servidores</string>
<string name="desc_add_new_server_btn">Adicionar servidor</string>
<string name="desc_save_discovered_server_btn">Salvar</string>
<string name="desc_discovery_btn">Descobrir servidores</string>
<string name="desc_discovery_running">Descobrindo servidores na sua rede</string>
<string name="desc_keyboard_btn">Mostrar teclado</string>
<string name="desc_virtual_keys_btn">Mostrar teclas virtuais</string>
<string name="desc_reset_zoom_btn">Resetar o zoom para 100%</string>
<string name="desc_retry_connection_btn">Tente conectar novamente</string>
<string name="desc_reset_zoom_btn">Redefinir zoom</string>
<string name="desc_retry_connection_btn">Tente novamente</string>
<string name="desc_back_btn">Voltar</string>
<string name="desc_clear_url_btn">Remover a URL</string>
<string name="desc_server_url">Digite o endereço do servidor VNC</string>
<string name="desc_help_btn">Mostrar ajuda</string>
<string name="desc_discovered_list">Lista de servidores descobertos automaticamente</string>
<string name="desc_saved_servers_list">Lista de servidores salvos</string>
<string name="desc_clear_url_btn">Limpar</string>
<string name="desc_server_url">Endereço do servidor</string>
<string name="desc_help_btn">Ajuda</string>
<string name="desc_discovered_list">Lista dos servidores descobertos automaticamente</string>
<string name="desc_saved_servers_list">Lista dos servidores salvos</string>
<!-- Preference -->
<string name="pref_ui">Interface</string>
<string name="pref_ui_summary">Aspecto</string>
<string name="pref_viewer">Visualizador</string>
<string name="pref_viewer_summary">Picture-in-picture, zoom</string>
<string name="pref_viewer_summary">Imagem em popup, zoom</string>
<string name="pref_input">Entrada</string>
<string name="pref_input_summary">Gestos, mouse, teclas virtuais</string>
<string name="pref_servers">Servidores</string>
<string name="pref_server_summary">Descoberta, Credenciais</string>
<string name="pref_server_summary">Autenticação, descoberta</string>
<string name="pref_tools">Ferramentas</string>
<string name="pref_tools_summary">Importar/Exportar, depuração</string>
<string name="pref_experimental">Experimental</string>
@ -107,8 +113,8 @@
<string name="pref_gesture_action_remote_drag">Arrastar conteúdo remoto</string>
<string name="pref_gesture_action_open_keyboard">Mostrar teclado</string>
<string name="pref_theme">Tema</string>
<string name="pref_prefer_advanced_editor">Preferência do editor avançado</string>
<string name="pref_sort_server_list">Ordenar a lista de servidores</string>
<string name="pref_prefer_advanced_editor">Abrir diretamente opções avançadas de servidor</string>
<string name="pref_sort_server_list">Ordenar servidores salvos pelo nome</string>
<string name="pref_gesture">Gestos</string>
<string name="pref_single_tap">Toque único</string>
<string name="pref_two_finger_tap">Toque com 2 dedos</string>
@ -116,45 +122,66 @@
<string name="pref_long_press">Toque longo</string>
<string name="pref_swipe1">Deslize com 1 dedo</string>
<string name="pref_swipe2">Deslize com 2 dedos</string>
<string name="pref_drag">Arrastar (Toque longo depois deslizar)</string>
<string name="pref_drag">Arrastar</string>
<string name="pref_mouse">Mouse</string>
<string name="pref_mouse_passthrough">Passagem do mouse</string>
<string name="pref_mouse_passthrough_summary_off">Eventos do mouse são usados localmente para gestos</string>
<string name="pref_mouse_passthrough_summary_on">Eventos do mouse são enviados diretamente para servidor</string>
<string name="pref_mouse_passthrough_summary_off">Use eventos do mouse para gestos locais</string>
<string name="pref_mouse_passthrough_summary_on">Enviar eventos do mouse diretamente para servidor</string>
<string name="pref_vk">Teclas virtuais</string>
<string name="pref_vk_show_all">Mostrar todas teclas</string>
<string name="pref_vk_open_with_keyboard">Mostrar quando teclado está disponível</string>
<string name="pref_vk_open_with_keyboard">Abrir com teclado</string>
<string name="pref_km">Mapeamento de teclas</string>
<string name="pref_km_right_alt_to_super">Right Alt → Super</string>
<string name="pref_km_language_switch_to_super">Alteração de idioma → Super</string>
<string name="pref_km_back_to_escape">Voltar → Escape</string>
<string name="pref_km_back_to_escape_summary">A barra de navegação não é afetada por isto</string>
<string name="pref_fullscreen">Usar tela completa</string>
<string name="pref_enable_pip">Picture-in-picture</string>
<string name="pref_km_back_to_escape_summary">Não afeta a barra de navegação</string>
<string name="pref_fullscreen">Tela cheia</string>
<string name="pref_enable_pip">Imagem em popup</string>
<string name="pref_toolbar">Barra de ferramentas</string>
<string name="pref_toolbar_alignment">Alinhamento</string>
<string name="pref_zoom">Zoom</string>
<string name="pref_zoom_min">Zoom mínimo</string>
<string name="pref_zoom_max">Zoom máximo</string>
<string name="pref_clipboard_sync">Sincronização do Clipboard</string>
<string name="pref_credential">Credenciais</string>
<string name="pref_cred_autocomplete">Ativar preenchimento automático</string>
<string name="pref_cred_autocomplete_summary">Sugerir credenciais conhecidas ao conectar-se a um novo servidor</string>
<string name="pref_zoom_min">Mínimo</string>
<string name="pref_zoom_max">Máximo</string>
<string name="pref_clipboard_sync">Sincronização do clipboard</string>
<string name="pref_credential">Autenticação</string>
<string name="pref_cred_autocomplete">Preenchimento automático</string>
<string name="pref_cred_autocomplete_summary">Sugerir sessões salvas ao conectar para novos servidores</string>
<string name="pref_discovery">Descoberta</string>
<string name="pref_import_export">Importar/Exportar servidores</string>
<string name="pref_debugging">Depuração</string>
<string name="pref_logs">Registros</string>
<string name="pref_keycode_map">Mapa de códigos de teclado</string>
<string name="pref_experimental_toolbar_swipe_close">Deslizar para fechar a barra de ferramentas</string>
<string name="pref_experimental_toolbar_swipe_close_summary">Durante uma sessão remota, você pode deslizar para fechar a barra de ferramentas atualmente aberta</string>
<string name="pref_theme_option_system">Seguir Tema do Sistema</string>
<string name="pref_theme_option_dark">Modo Escuro</string>
<string name="pref_theme_option_light">Modo Claro</string>
<string name="pref_experimental_toolbar_swipe_close">Deslize para fechar a barra de ferramentas</string>
<string name="pref_experimental_toolbar_swipe_close_summary">Durante uma sessão remota, você pode deslizar para fechar a barra de ferramentas aberta neste momento</string>
<string name="pref_theme_option_system">Sistema</string>
<string name="pref_theme_option_dark">Escuro</string>
<string name="pref_theme_option_light">Claro</string>
<string name="pref_toolbar_alignment_option_start">Início</string>
<string name="pref_toolbar_alignment_option_end">Final</string>
<string name="pref_hide_local_cursor">Ocultar Cursor do Android</string>
<string name="pref_hide_local_cursor">Esconder ponteiro local</string>
<string name="title_clear">Limpar</string>
<string name="pref_experimental_immersive_mode">Modo totalmente imersivo</string>
<string name="pref_experimental_immersive_mode_summary">Oculta todas as barras do sistema em modo de tela cheia</string>
<string name="msg_ptr_hiding_not_supported">Ocultar cursor não é compatível com este dispositivo</string>
<string name="msg_ptr_hiding_not_supported">Não suportado neste dispositivo</string>
<string name="desc_discovery_stop_btn">Parar a descoberta de servidores</string>
<string name="pref_direct_touch_summary_off">Faça ações no ponteiro</string>
<string name="pref_mouse_back_action_default">Padrão</string>
<string name="pref_mouse_back_action_right">Clique com botão direito</string>
<string name="pref_mouse_back">Botão Voltar</string>
<string name="pref_mouse_back_action_middle">Clique com botão do meio</string>
<string name="pref_discovery_autorun">Execução automática</string>
<string name="pref_discovery_autorun_summary">Descubra servidores enquanto estiver na página inicial</string>
<string name="pref_rediscovery_indicator">Destaque servidores redescobertos</string>
<string name="pref_direct_touch_summary_on">Faça ações no ponto de contato</string>
<string name="pref_direct_touch">Toque direto</string>
<string name="title_default_security">Segurança padrão</string>
<string name="msg_drag_gesture_help">Ao atribuir ação de gesto de Arrastar você mudará Detecção de toque longo:
\n
\n<b>Toque-segure-solte</b> → Toque longo
\n<b>Toque-segure-deslize</b> → Arrastar</string>
<string name="pref_swipe_sensitivity">Sensibilidade de deslize</string>
<string name="pref_orientation">Orientação da tela</string>
<string name="pref_orientation_option_auto">Automática</string>
<string name="pref_orientation_option_portrait">Vertical</string>
<string name="pref_orientation_option_landscape">Horizontal</string>
</resources>

View file

@ -1,2 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>
<resources>
<string name="title_add_server_profile">Добавить сервер</string>
<string name="title_edit_server_profile">Изменить сервер</string>
<string name="title_duplicate">Создать копию</string>
<string name="title_edit">Изменить</string>
<string name="title_delete">Удалить</string>
<string name="title_cancel">Отменить</string>
<string name="title_save">Сохранить</string>
<string name="title_settings">Настройки</string>
<string name="title_credentials_required">Войти</string>
<string name="title_export">Экспорт</string>
<string name="title_import">Импорт</string>
<string name="title_view_only_mode">Только просмотр</string>
<string name="msg_invalid_key_file">недействительный файл приватного ключа</string>
<string name="pref_servers">Серверы</string>
<string name="pref_mouse">Мышь</string>
<string name="title_default_security">Безопасность по-умолчанию</string>
<string name="title_delete_servers_before_import">Удалите текущие серверы перед импортом</string>
<string name="title_password">Пароль</string>
<string name="title_change">Изменить</string>
<string name="title_about">О программе</string>
<string name="title_license">Лицензия</string>
<string name="title_open_source_libraries">Библиотеки с открытым исходным кодом</string>
<string name="title_github_repo">Исходный код</string>
<string name="hint_urlbar">Подключится к серверу</string>
<string name="hint_server_name">Имя (например: Рабочий ПК)</string>
<string name="hint_host">Хост</string>
<string name="hint_port">Порт</string>
<string name="hint_server_id">Идентификатор сервера</string>
<string name="msg_exported">Экспортировано</string>
<string name="msg_export_auth_missing">Ваше устройство не защищено. Включите блокировку экрана для обеспечения возможности аутентификации перед экспортом.</string>
<string name="msg_copied_to_clipboard">Скопировано</string>
<string name="desc_saved_servers_tab">Сохранённые серверы</string>
<string name="desc_discovered_servers_tab">Найденные серверы</string>
<string name="desc_discovery_btn">Найти серверы</string>
<string name="desc_discovery_running">Поиск серверов в вашей сети</string>
<string name="desc_virtual_keys_btn">Показать виртуальные клавиши</string>
<string name="desc_reset_zoom_btn">Сбросить масштабирование</string>
<string name="desc_back_btn">Назад</string>
<string name="desc_help_btn">Помощь</string>
<string name="desc_discovered_list">Список автоматически найденных серверов</string>
<string name="pref_ui_summary">Внешний вид</string>
<string name="desc_retry_connection_btn">Повторить</string>
<string name="pref_input">Ввод</string>
<string name="pref_input_summary">Жесты, мышь, виртуальные клавиши</string>
<string name="pref_server_summary">Вход, обнаружение</string>
<string name="pref_tools">Инструменты</string>
<string name="pref_tools_summary">Импорт/Экспорт, отладка</string>
<string name="pref_gesture_action_left_click">Левая кнопка мыши</string>
<string name="pref_gesture_action_middle_click">Средняя кнопка мыши</string>
<string name="pref_gesture_action_remote_scroll">Прокручивать удалённое содержимое</string>
<string name="pref_theme_option_system">Система</string>
<string name="pref_gesture">Жесты</string>
<string name="pref_double_tap">Двойное нажатие</string>
<string name="pref_sort_server_list">Сортировать сохранённые серверы по имени</string>
<string name="pref_single_tap">Одиночное нажатие</string>
<string name="pref_discovery">Обнаружение</string>
<string name="pref_discovery_autorun">Автозапуск</string>
<string name="pref_experimental_toolbar_swipe_close_summary">Во время удалённой сессии, вы можете сделать свайп для закрытия открытой панели инструментов</string>
<string name="pref_gesture_action_none">Нет</string>
<string name="pref_gesture_action_right_click">Правая кнопка мыши</string>
<string name="pref_gesture_action_double_click">Двойное нажатие</string>
<string name="pref_vk_show_all">Показать все клавиши</string>
<string name="pref_vk_open_with_keyboard">Открыть с клавиатурой</string>
<string name="pref_km_language_switch_to_super">Переключение раскладки → Super</string>
<string name="pref_toolbar_alignment_option_start">Начало</string>
<string name="pref_toolbar">Панель инструментов</string>
<string name="pref_toolbar_alignment_option_end">Конец</string>
<string name="pref_zoom">Масштабирование</string>
<string name="pref_import_export">Импортировать/Экспортировать серверы</string>
<string name="pref_debugging">Отладка</string>
<string name="title_report_bug">Сообщить об ошибке</string>
<string name="msg_pip_not_supported">Не поддерживается на этом устройстве</string>
<string name="msg_ptr_hiding_not_supported">Не поддерживается на этом устройстве</string>
<string name="msg_loading">Загрузка…</string>
<string name="msg_imported">Импортировано</string>
<string name="tip_urlbar">Вы можете ввести IP адрес, имя хоста, или \'vnc://\' URI.</string>
<string name="desc_add_new_server_btn">Добавить сервер</string>
<string name="desc_save_discovered_server_btn">Сохранить</string>
<string name="desc_discovery_stop_btn">остановить поиск серверов</string>
<string name="desc_keyboard_btn">Открыть клавиатуру</string>
<string name="desc_clear_url_btn">Очистить</string>
<string name="desc_server_url">Адрес сервера</string>
<string name="pref_viewer">Просмотр</string>
<string name="pref_viewer_summary">Картинка-в-картинке, масштабирование</string>
<string name="pref_experimental">Экспериментальные</string>
<string name="pref_experimental_summary">Экспериментальные возможности</string>
<string name="pref_gesture_action_move_pointer">Движение курсора</string>
<string name="pref_gesture_action_remote_drag">Перетаскивать удалённое содержимое</string>
<string name="pref_gesture_action_open_keyboard">Открыть клавиатуру</string>
<string name="pref_theme_option_dark">Тёмная</string>
<string name="pref_theme_option_light">Светлая</string>
<string name="pref_prefer_advanced_editor">Напрямую открывать дополнительные настройки сервера</string>
<string name="pref_long_press">Длительное нажатие</string>
<string name="pref_swipe1">Свайп одним пальцем</string>
<string name="pref_swipe2">Свайп двумя пальцами</string>
<string name="pref_drag">Перетаскивание</string>
<string name="pref_swipe_sensitivity">Чувствительность свайпа</string>
<string name="pref_hide_local_cursor">Скрыть локальный курсор</string>
<string name="pref_mouse_back">Кнопка \"назад\"</string>
<string name="pref_mouse_back_action_default">По-умолчанию</string>
<string name="pref_mouse_back_action_right">Правая кнопка мыши</string>
<string name="pref_mouse_back_action_middle">Средняя кнопка мыши</string>
<string name="pref_vk">Виртуальные клавиши</string>
<string name="pref_km_right_alt_to_super">Правый Alt → Super</string>
<string name="pref_zoom_min">Минимум</string>
<string name="pref_cred_autocomplete">Авто-завершение</string>
<string name="pref_zoom_max">Максимум</string>
<string name="pref_orientation">Ориентация экрана</string>
<string name="pref_orientation_option_auto">Автоматически</string>
<string name="pref_orientation_option_portrait">Портретная</string>
<string name="title_clear">Очистить</string>
<string name="msg_server_profile_added">Сервер добавлен</string>
<string name="msg_server_profile_deleted">Сервер удалён</string>
<string name="pref_ui">Интерфейс</string>
<string name="title_copy_host">Скопировать хост</string>
<string name="title_use_ssh_tunnel">Использовать SSH туннель</string>
<string name="title_unknown_ssh_host">Неизвестный SSH хост</string>
<string name="title_ssh_host_key_changed">Ключ хоста SSH изменён</string>
<string name="msg_about_app">Быстрый и защищённый VNC клиент. Удалённо просматривайте и управляйте любым устройством на котором запущен VNC сервер.
\n
\n
\nAVNC это программное обеспечение с открытым исходным кодом - созданное при поддержке от большого количества участников.</string>
<string name="tip_empty_server_list">Список серверов пуст.
\nНажмите \'<b>+</b>\' для добавления сервера, или
\nиспользуйте верхнюю адресную строку для прямого подключения.</string>
<string name="pref_two_finger_tap">Нажатие двумя пальцами</string>
<string name="pref_enable_pip">Картинка-в-картинке</string>
<string name="title_copy_name">Скопировать имя</string>
<string name="pref_logs">Логи</string>
<string name="title_hide_remote_cursor">Скрыть удалённый курсор</string>
<string name="title_continue">Продолжить</string>
<string name="hint_username">Имя пользователя</string>
<string name="desc_settings_btn">Настройки</string>
<string name="desc_saved_servers_list">Список сохранённых серверов</string>
<string name="hint_password">Пароль</string>
<string name="pref_theme">Тема</string>
<string name="pref_km">Назначения клавиш</string>
<string name="pref_orientation_option_landscape">Ландшафтная</string>
<string name="pref_fullscreen">Полноэкранный режим</string>
<string name="pref_clipboard_sync">Синхронизация буфера обмена</string>
<string name="pref_credential">Вход</string>
<string name="pref_experimental_immersive_mode">Режим полного присутствия</string>
<string name="pref_experimental_immersive_mode_summary">Скрывает все системные панели в полноэкранном режиме</string>
</resources>

View file

@ -158,4 +158,8 @@
<string name="pref_experimental_toolbar_swipe_close_summary">Током удаљене седнице можете превући ради затварања тренутно отворене траке прибора</string>
<string name="pref_experimental_immersive_mode">Потпуно утопљен приказ</string>
<string name="pref_experimental_immersive_mode_summary">Скрива све траке система у пуном приказу заслона</string>
<string name="pref_discovery_autorun_summary">Откривај служитеље од матичне стране</string>
<string name="desc_discovery_stop_btn">Престани да откриваш служитеље</string>
<string name="pref_discovery_autorun">Самопокрени</string>
<string name="pref_rediscovery_indicator">Истакни поново пронађене служитеље</string>
</resources>

View file

@ -0,0 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="title_edit">Düzenle</string>
<string name="title_delete">Sil</string>
<string name="title_clear">Temizle</string>
<string name="title_copy_host">Ana makineyi kopyala</string>
<string name="title_copy_name">Adı kopyala</string>
<string name="title_advanced">Gelişmiş</string>
<string name="title_cancel">İptal</string>
<string name="title_undo">Geri al</string>
<string name="title_settings">Ayarlar</string>
<string name="title_credentials_required">Oturum aç</string>
<string name="title_remember">Hatırla</string>
<string name="title_export">Dışa aktar</string>
<string name="title_import">İçe aktar</string>
<string name="title_view_only_mode">Yalnızca görüntüle</string>
<string name="title_use_ssh_tunnel">SSH tüneli kullan</string>
<string name="title_unknown_ssh_host">Bilinmeyen SSH ana makinesi</string>
<string name="title_continue">Devam et</string>
<string name="title_use_repeater">Yineleyici kullan</string>
<string name="title_private_key">Özel anahtar</string>
<string name="title_change">Değiştir</string>
<string name="title_about">Hakkında</string>
<string name="title_license">Lisans</string>
<string name="title_open_source_libraries">Açık kaynaklı kütüphaneler</string>
<string name="hint_urlbar">Bir sunucuya bağlan</string>
<string name="hint_server_name">Ad (örn. İş Bilgisayarı)</string>
<string name="hint_host">Ana makine</string>
<string name="hint_repeater_host">Yineleyici ana makine</string>
<string name="hint_password">Parola</string>
<string name="msg_about_app">Hızlı ve güvenli bir VNC istemcisi. Bir VNC sunucusu çalıştıran herhangi bir aygıtı uzaktan görüntüleyin ve denetleyin.
\n
\n
\nAVNC, katkıda bulunan birçok kişinin yardımıyla yapılmış, açık kaynaklı ve copyleft bir yazılımdır.</string>
<string name="msg_server_profile_added">Sunucu eklendi</string>
<string name="msg_server_profile_deleted">Sunucu silindi</string>
<string name="msg_pip_not_supported">Bu aygıtta desteklenmiyor</string>
<string name="msg_invalid_key_file">Geçersiz özel anahtar dosyası</string>
<string name="msg_invalid_vnc_uri">Geçersiz VNC URI\'si</string>
<string name="tip_urlbar">IP adresi, ana makine adı veya \'vnc://\' URI\'si girebilirsiniz.</string>
<string name="desc_navigation_btn">Gezinme çekmecesini aç</string>
<string name="desc_discovered_servers_tab">Keşfedilen sunucular</string>
<string name="desc_discovery_stop_btn">Sunucu keşfetmeyi durdur</string>
<string name="desc_discovery_running">Ağınızdaki sunucular keşfediliyor</string>
<string name="desc_keyboard_btn">Klavyeyi aç</string>
<string name="pref_ui">Arayüz</string>
<string name="pref_ui_summary">Görünüm</string>
<string name="pref_viewer">Görüntüleyici</string>
<string name="pref_viewer_summary">Resim içinde resim, yakınlaştırma</string>
<string name="pref_input">Giriş</string>
<string name="pref_input_summary">Hareketler, fare, sanal tuşlar</string>
<string name="pref_servers">Sunucular</string>
<string name="pref_experimental_summary">Ek güzellikler</string>
<string name="pref_gesture_action_none">Hiçbiri</string>
<string name="pref_gesture_action_left_click">Sol tıklama</string>
<string name="pref_gesture_action_double_click">Çift tıklama</string>
<string name="pref_gesture_action_move_pointer">İşaretçiyi hareket ettir</string>
<string name="pref_gesture_action_pan">Yerel çerçeveyi kaydır</string>
<string name="pref_gesture_action_open_keyboard">Klavyeyi aç</string>
<string name="pref_theme_option_dark">Koyu</string>
<string name="pref_direct_touch_summary_off">Eylemleri işaretçinin olduğu yerde yap</string>
<string name="pref_single_tap">Tek dokunma</string>
<string name="pref_swipe1">Tek parmakla kaydırma</string>
<string name="pref_drag">Sürükleme</string>
<string name="pref_mouse">Fare</string>
<string name="pref_mouse_passthrough_summary_off">Yerel hareketler için fare olaylarını kullan</string>
<string name="pref_km">Tuş eşlemeleri</string>
<string name="pref_km_right_alt_to_super">Sağ Alt → Süper</string>
<string name="pref_km_back_to_escape">Geri → Escape</string>
<string name="pref_km_back_to_escape_summary">Gezinme çubuğunu etkilemez</string>
<string name="pref_fullscreen">Tam ekran</string>
<string name="pref_enable_pip">Resim içinde resim</string>
<string name="pref_toolbar">Araç çubuğu</string>
<string name="pref_toolbar_alignment_option_end">Bitiş</string>
<string name="pref_zoom">Yakınlaştır</string>
<string name="pref_zoom_min">En az</string>
<string name="pref_zoom_max">En fazla</string>
<string name="pref_credential">Oturum aç</string>
<string name="pref_cred_autocomplete">Otomatik tamamla</string>
<string name="pref_rediscovery_indicator">Yeniden keşfedilen sunucuları vurgula</string>
<string name="pref_import_export">Sunucuları içe/dışa aktar</string>
<string name="pref_debugging">Hata ayıklama</string>
<string name="pref_logs">Günlük kayıtları</string>
<string name="pref_keycode_map">Tuş kodu eşlemesi</string>
<string name="pref_experimental_toolbar_swipe_close">Araç çubuğu çekmecesini kapatmak için kaydır</string>
<string name="pref_experimental_immersive_mode">Tam odaklanma modu</string>
<string name="pref_experimental_immersive_mode_summary">Tam ekran modunda tüm sistem çubuklarını gizler</string>
<string name="title_save">Kaydet</string>
<string name="title_key_compat_mode">Eski tuş olaylarını gönder</string>
<string name="title_hide_remote_cursor">Uzak işaretçiyi gizle</string>
<string name="title_delete_servers_before_import">İçe aktarmadan önce geçerli sunucuları sil</string>
<string name="desc_clear_url_btn">Temizle</string>
<string name="title_add_server_profile">Sunucu ekle</string>
<string name="title_github_repo">Kaynak kodu</string>
<string name="title_edit_server_profile">Sunucuyu düzenle</string>
<string name="title_ssh_host_key_changed">SSH ana makine anahtarı değişti</string>
<string name="title_password">Parola</string>
<string name="title_report_bug">Sorun bildir</string>
<string name="title_duplicate">Çoğalt</string>
<string name="msg_ptr_hiding_not_supported">Bu aygıtta desteklenmiyor</string>
<string name="hint_port">Bağlantı noktası</string>
<string name="tip_empty_server_list">Sunucu listesi boş.
\nBir sunucu eklemek için \'<b>+</b>\' simgesine tıklayın veya
\ndoğrudan bağlanmak için üst adres çubuğunu kullanın.</string>
<string name="pref_vk_show_all">Tüm tuşları göster</string>
<string name="hint_server_id">Sunucu kimliği</string>
<string name="hint_username">Kullanıcı adı</string>
<string name="msg_loading">Yükleniyor…</string>
<string name="msg_imported">İçe aktarıldı</string>
<string name="hint_key_password">Anahtar parolası</string>
<string name="msg_exported">Dışa aktarıldı</string>
<string name="msg_export_auth_missing">Aygıtınız güvenli değil. Dışa aktarmadan önce kimlik doğrulamasını etkinleştirmek için ekran kilidini etkinleştirin.</string>
<string name="msg_copied_to_clipboard">Kopyalandı</string>
<string name="msg_key_compat_mode_help">Bazı sunucular sınırlı Unicode desteğine sahiptir. Eski tuş olayları, İngilizce olmayan metinlerin girilmesine yardımcı olabilir.</string>
<string name="msg_export_auth_required">Sunucuları dışa aktarmak için kimliğinizi doğrulayın</string>
<string name="pref_tools_summary">İçe/dışa aktarma, hata ayıklama</string>
<string name="pref_theme">Tema</string>
<string name="tip_discovery">Keşif, ağınızda çalışan desteklenen sunucuları otomatik
\nolarak bulabilir.</string>
<string name="desc_settings_btn">Ayarlar</string>
<string name="desc_add_new_server_btn">Sunucu ekle</string>
<string name="desc_discovery_btn">Sunucu keşfet</string>
<string name="desc_retry_connection_btn">Yeniden dene</string>
<string name="desc_back_btn">Geri</string>
<string name="pref_experimental">Deneysel</string>
<string name="desc_saved_servers_tab">Kaydedilen sunucular</string>
<string name="desc_save_discovered_server_btn">Kaydet</string>
<string name="desc_reset_zoom_btn">Yakınlaştırmayı sıfırla</string>
<string name="pref_server_summary">Oturum açma, keşif</string>
<string name="pref_tools">Araçlar</string>
<string name="pref_gesture_action_middle_click">Orta tıklama</string>
<string name="desc_virtual_keys_btn">Sanal tuşları göster</string>
<string name="desc_server_url">Sunucu adresi</string>
<string name="desc_help_btn">Yardım</string>
<string name="desc_discovered_list">Otomatik olarak keşfedilen sunucuların listesi</string>
<string name="desc_saved_servers_list">Kaydedilen sunucuların listesi</string>
<string name="pref_gesture_action_right_click">Sağ tıklama</string>
<string name="pref_direct_touch">Doğrudan dokunma</string>
<string name="pref_toolbar_alignment_option_start">Başlangıç</string>
<string name="pref_discovery">Keşif</string>
<string name="pref_discovery_autorun_summary">Ana sayfadayken sunucuları keşfet</string>
<string name="pref_experimental_toolbar_swipe_close_summary">Uzak bir oturum sırasında, o anda açık olan araç çubuğunu kapatmak için kaydırabilirsiniz</string>
<string name="pref_theme_option_system">Sistem</string>
<string name="pref_gesture_action_remote_scroll">Uzak içeriği kaydır</string>
<string name="pref_gesture_action_remote_drag">Uzak içeriği sürükle</string>
<string name="pref_theme_option_light">Açık</string>
<string name="pref_prefer_advanced_editor">Doğrudan gelişmiş sunucu seçeneklerini aç</string>
<string name="pref_sort_server_list">Kaydedilen sunucuları ada göre sırala</string>
<string name="pref_gesture">Hareketler</string>
<string name="pref_direct_touch_summary_on">Eylemleri dokunma noktasında yap</string>
<string name="pref_vk">Sanal tuşlar</string>
<string name="pref_two_finger_tap">İki parmakla dokunma</string>
<string name="pref_double_tap">Çift dokunma</string>
<string name="pref_long_press">Uzun basma</string>
<string name="pref_swipe2">İki parmakla kaydırma</string>
<string name="pref_mouse_passthrough_summary_on">Fare olaylarını doğrudan sunucuya gönder</string>
<string name="pref_hide_local_cursor">Yerel işaretçiyi gizle</string>
<string name="pref_km_language_switch_to_super">Dil Değiştirme → Süper</string>
<string name="pref_toolbar_alignment">Hizalama</string>
<string name="pref_clipboard_sync">Panoyu eşzamanla</string>
<string name="pref_cred_autocomplete_summary">Yeni sunuculara bağlanırken kayıtlı oturum açma bilgilerini öner</string>
<string name="pref_discovery_autorun">Otomatik çalıştır</string>
<string name="pref_mouse_passthrough">Fare geçişi</string>
<string name="pref_vk_open_with_keyboard">Klavye ile aç</string>
<string name="pref_mouse_back_action_right">Sağ tıklama</string>
<string name="pref_mouse_back_action_middle">Orta tıklama</string>
<string name="pref_mouse_back">Geri düğmesi</string>
<string name="pref_mouse_back_action_default">Öntanımlı</string>
<string name="pref_swipe_sensitivity">Kaydırma hassasiyeti</string>
<string name="title_default_security">Öntanımlı güvenlik</string>
<string name="msg_drag_gesture_help">Sürükleme hareketine bir eylem atamak, Uzun basma algılamasını değiştirecektir:
\n
\n<b>Basılı tut ve bırak</b> → Uzun bas
\n<b>Basılı tut ve kaydır</b> → Sürükle</string>
<string name="pref_orientation_option_portrait">Dikey</string>
<string name="pref_orientation_option_landscape">Yatay</string>
<string name="pref_orientation">Ekran yönü</string>
<string name="pref_orientation_option_auto">Otomatik</string>
</resources>

View file

@ -59,15 +59,15 @@
<string name="pref_experimental_summary">Додаткові можливості</string>
<string name="pref_gesture_action_left_click">Ліве клацання</string>
<string name="pref_gesture_action_move_pointer">Перемістити вказівник</string>
<string name="pref_clipboard_sync">Синхронувати буфери обміну</string>
<string name="pref_clipboard_sync">Синхронізувати буфери обміну</string>
<string name="pref_gesture_action_remote_scroll">Прокрутити віддалений вміст</string>
<string name="pref_km_language_switch_to_super">Перемикання мови → Super</string>
<string name="pref_toolbar_alignment_option_start">Початок</string>
<string name="pref_theme">Тема</string>
<string name="pref_long_press">Довге натискання</string>
<string name="pref_swipe2">Ковзання двома пальцями</string>
<string name="pref_drag">Перетягування (довге натискання і ковзнути)</string>
<string name="pref_hide_local_cursor">Сховати локальний (Android) вказівник</string>
<string name="pref_drag">Перетягування</string>
<string name="pref_hide_local_cursor">Ховати локальний вказівник</string>
<string name="pref_vk">Віртуальні клавіші</string>
<string name="pref_vk_show_all">Показувати всі клавіші</string>
<string name="pref_km_back_to_escape">Назад → Escape</string>
@ -162,4 +162,18 @@
<string name="desc_discovery_stop_btn">Зупинити виявлення серверів</string>
<string name="pref_discovery_autorun">Автозапуск</string>
<string name="pref_discovery_autorun_summary">Виявляти сервери поки на домашній сторінці</string>
<string name="pref_mouse_back">Кнопка назад</string>
<string name="pref_mouse_back_action_default">Типово</string>
<string name="pref_mouse_back_action_right">Клацання правою кнопкою</string>
<string name="pref_mouse_back_action_middle">Клацання середньою кнопкою</string>
<string name="pref_swipe_sensitivity">Чутливість ковзання</string>
<string name="title_default_security">Типовий захист</string>
<string name="msg_drag_gesture_help">Присвоєння дії жесту Перетягування змінить виявлення довгого натискання:
\n
\n<b>Натиснути-утримувати-відпустити</b> → Довге натискання
\n<b>Натиснути-утримувати-потягнути</b> → Перетягування</string>
<string name="pref_orientation">Орієнтація екрана</string>
<string name="pref_orientation_option_auto">Автоматична</string>
<string name="pref_orientation_option_portrait">Портретна</string>
<string name="pref_orientation_option_landscape">Альбомна</string>
</resources>

View file

@ -0,0 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="title_add_server_profile">添加服务器</string>
<string name="title_hide_remote_cursor">隐藏远程指针</string>
<string name="title_credentials_required">登录</string>
<string name="title_edit_server_profile">编辑服务器</string>
<string name="title_duplicate">复本</string>
<string name="title_edit">编辑</string>
<string name="title_delete">删除</string>
<string name="title_copy_host">复制主机</string>
<string name="title_copy_name">复制名称</string>
<string name="title_cancel">取消</string>
<string name="title_save">保存</string>
<string name="title_undo">撤消</string>
<string name="title_remember">记忆</string>
<string name="title_import">导入</string>
<string name="title_view_only_mode">仅查看</string>
<string name="title_use_ssh_tunnel">使用 SSH 隧道</string>
<string name="title_ssh_host_key_changed">SSH 主机密钥已更改</string>
<string name="title_password">密码</string>
<string name="title_private_key">私钥</string>
<string name="title_about">关于</string>
<string name="title_license">许可证</string>
<string name="hint_urlbar">连接到服务器</string>
<string name="hint_server_name">名称(例如:Work PC)</string>
<string name="hint_host">主机</string>
<string name="hint_repeater_host">中继器主机</string>
<string name="hint_port">端口</string>
<string name="hint_server_id">服务器 ID</string>
<string name="hint_username">用户名</string>
<string name="hint_password">密码</string>
<string name="hint_key_password">密钥密码</string>
<string name="msg_server_profile_added">服务器已添加</string>
<string name="msg_server_profile_deleted">服务器已删除</string>
<string name="msg_ptr_hiding_not_supported">此设备不支持</string>
<string name="msg_imported">已导入</string>
<string name="msg_exported">已导出</string>
<string name="msg_export_auth_required">身份验证以导出服务器</string>
<string name="msg_invalid_key_file">私钥文件无效</string>
<string name="msg_invalid_vnc_uri">无效的 VNC URI</string>
<string name="msg_copied_to_clipboard">已复制</string>
<string name="tip_urlbar">你可以输入 IP 地址、主机名或“vnc://”URI。</string>
<string name="desc_navigation_btn">打开导航抽屉</string>
<string name="desc_settings_btn">设置</string>
<string name="desc_saved_servers_tab">已保存的服务器</string>
<string name="desc_discovered_servers_tab">已发现的服务器</string>
<string name="desc_add_new_server_btn">添加服务器</string>
<string name="desc_save_discovered_server_btn">保存</string>
<string name="desc_discovery_btn">发现服务器</string>
<string name="desc_discovery_stop_btn">停止发现服务器</string>
<string name="desc_discovery_running">发现网络上的服务器</string>
<string name="desc_keyboard_btn">打开键盘</string>
<string name="desc_virtual_keys_btn">显示虚拟按键</string>
<string name="desc_reset_zoom_btn">重置缩放</string>
<string name="desc_retry_connection_btn">重试</string>
<string name="desc_back_btn">返回</string>
<string name="desc_clear_url_btn">清除</string>
<string name="desc_server_url">服务器地址</string>
<string name="desc_help_btn">帮助</string>
<string name="tip_discovery">发现 可以自动查找在网络上运行的
\n受支持服务器。</string>
<string name="pref_ui">界面</string>
<string name="pref_ui_summary">界面外观</string>
<string name="pref_viewer">视图</string>
<string name="pref_servers">服务器</string>
<string name="pref_server_summary">登录、发现</string>
<string name="pref_tools_summary">导入/导出、调试</string>
<string name="pref_experimental">试验性</string>
<string name="pref_gesture_action_none">无</string>
<string name="pref_gesture_action_left_click">左键单击</string>
<string name="pref_gesture_action_middle_click">中键单击</string>
<string name="pref_gesture_action_right_click">右键单击</string>
<string name="pref_gesture_action_double_click">双击</string>
<string name="pref_gesture_action_move_pointer">移动指针</string>
<string name="pref_gesture_action_remote_scroll">滚动远程内容</string>
<string name="pref_gesture_action_remote_drag">拖动远程内容</string>
<string name="pref_theme">主题</string>
<string name="pref_theme_option_system">系统</string>
<string name="pref_theme_option_dark">暗色</string>
<string name="pref_theme_option_light">亮色</string>
<string name="pref_gesture">手势</string>
<string name="pref_direct_touch">直接触摸</string>
<string name="pref_direct_touch_summary_on">在接触点执行操作</string>
<string name="pref_direct_touch_summary_off">在指针处执行操作</string>
<string name="pref_two_finger_tap">双指敲击</string>
<string name="pref_double_tap">双次敲击</string>
<string name="pref_long_press">长按</string>
<string name="pref_swipe1">单指轻扫</string>
<string name="pref_swipe2">双指轻扫</string>
<string name="pref_drag">拖动</string>
<string name="pref_mouse">鼠标</string>
<string name="pref_mouse_passthrough">鼠标直通</string>
<string name="pref_mouse_passthrough_summary_on">将鼠标事件直接发送到服务器</string>
<string name="pref_hide_local_cursor">隐藏本地指针</string>
<string name="pref_mouse_back">返回按钮</string>
<string name="pref_mouse_back_action_default">默认</string>
<string name="pref_mouse_back_action_right">右键单击</string>
<string name="pref_vk">虚拟按键</string>
<string name="pref_vk_show_all">显示所有按键</string>
<string name="pref_km">按键映射</string>
<string name="pref_km_back_to_escape_summary">不影响导航栏</string>
<string name="pref_fullscreen">全屏</string>
<string name="pref_enable_pip">画中画</string>
<string name="pref_toolbar_alignment">对齐</string>
<string name="pref_toolbar_alignment_option_start">开始</string>
<string name="pref_toolbar_alignment_option_end">结尾</string>
<string name="pref_zoom">缩放</string>
<string name="pref_zoom_min">最小</string>
<string name="pref_clipboard_sync">剪贴板同步</string>
<string name="pref_credential">登录</string>
<string name="pref_cred_autocomplete">自动完成</string>
<string name="pref_cred_autocomplete_summary">连接到新服务器时建议保存登录信息</string>
<string name="pref_discovery">发现</string>
<string name="pref_discovery_autorun">自动运行</string>
<string name="pref_rediscovery_indicator">突出显示重新发现的服务器</string>
<string name="pref_import_export">导入/导出 服务器</string>
<string name="title_clear">清除</string>
<string name="title_use_repeater">使用中继器</string>
<string name="pref_debugging">调试</string>
<string name="pref_logs">日志</string>
<string name="pref_keycode_map">键码映射</string>
<string name="pref_experimental_toolbar_swipe_close_summary">在远程会话期间,可以轻扫以关闭当前打开的工具栏</string>
<string name="pref_experimental_immersive_mode">完全沉浸式模式</string>
<string name="pref_experimental_immersive_mode_summary">在全屏模式下隐藏所有系统栏</string>
<string name="title_advanced">高级</string>
<string name="title_settings">设置</string>
<string name="title_export">导出</string>
<string name="title_key_compat_mode">发送旧式按键事件</string>
<string name="title_unknown_ssh_host">未知 SSH 主机</string>
<string name="title_github_repo">源码</string>
<string name="msg_loading">正在加载…</string>
<string name="pref_mouse_back_action_middle">中键单击</string>
<string name="title_continue">继续</string>
<string name="title_delete_servers_before_import">导入前删除当前服务器</string>
<string name="title_change">更改</string>
<string name="title_open_source_libraries">开源库</string>
<string name="title_report_bug">报告问题</string>
<string name="msg_export_auth_missing">你的设备不安全。在导出前打开屏幕锁定以启用身份验证。</string>
<string name="msg_about_app">快速安全的 VNC 客户端。远程查看和控制运行 VNC 服务器的任何设备。
\n
\n
\nAVNC 是一个开源和非盈利版权(copyleft)软件 — 在许多贡献者的帮助下制作的。</string>
<string name="msg_pip_not_supported">此设备不支持</string>
<string name="msg_key_compat_mode_help">一些服务器对 Unicode 的支持有限。旧式按键事件可以帮助输入非英语文本。</string>
<string name="tip_empty_server_list">服务器列表为空。
\n单击“<b>+</b>”添加服务器,或
\n使用顶部的地址栏直接连接。</string>
<string name="desc_discovered_list">自动发现的服务器列表</string>
<string name="desc_saved_servers_list">已保存的服务器列表</string>
<string name="pref_viewer_summary">画中画,缩放</string>
<string name="pref_input">输入</string>
<string name="pref_input_summary">手势、鼠标、虚拟键</string>
<string name="pref_tools">工具</string>
<string name="pref_experimental_summary">额外的好东西</string>
<string name="pref_prefer_advanced_editor">直接打开高级服务器选项</string>
<string name="pref_gesture_action_pan">平移本地帧</string>
<string name="pref_gesture_action_open_keyboard">打开键盘</string>
<string name="pref_sort_server_list">按名称对已保存的服务器进行排序</string>
<string name="pref_single_tap">单次敲击</string>
<string name="pref_vk_open_with_keyboard">同时打开键盘</string>
<string name="pref_mouse_passthrough_summary_off">将鼠标事件用于本地手势</string>
<string name="pref_toolbar">工具栏</string>
<string name="pref_zoom_max">最大</string>
<string name="pref_discovery_autorun_summary">在主页上时发现服务器</string>
<string name="pref_experimental_toolbar_swipe_close">轻扫以关闭工具栏抽屉</string>
<string name="title_default_security">默认安全度</string>
<string name="pref_swipe_sensitivity">调节灵敏度</string>
<string name="pref_km_language_switch_to_super">语言切换 → Super 键</string>
<string name="pref_km_back_to_escape">退格键 → Esc 键</string>
<string name="pref_km_right_alt_to_super">右 Alt 键 → Super (开始菜单)键</string>
<string name="msg_drag_gesture_help">为拖动手势分配一个动作将改变长按检测:
\n
\n<b>按住释放</b> → 长按
\n<b>按住滑动</b> → 拖动</string>
<string name="pref_orientation">屏幕方向</string>
<string name="pref_orientation_option_auto">自动</string>
<string name="pref_orientation_option_portrait">竖屏</string>
<string name="pref_orientation_option_landscape">横屏</string>
</resources>

View file

@ -19,6 +19,16 @@
<item>light</item>
</string-array>
<string-array name="orientation_entries">
<item>@string/pref_orientation_option_auto</item>
<item>@string/pref_orientation_option_portrait</item>
<item>@string/pref_orientation_option_landscape</item>
</string-array>
<string-array name="orientation_values">
<item>auto</item>
<item>portrait</item>
<item>landscape</item>
</string-array>
<string-array name="toolbar_alignment_entries">
<item>@string/pref_toolbar_alignment_option_start</item>
@ -29,15 +39,6 @@
<item>end</item>
</string-array>
<string-array name="tap1_action_entries">
<item>@string/pref_gesture_action_none</item>
<item>@string/pref_gesture_action_left_click</item>
</string-array>
<string-array name="tap1_action_values">
<item>none</item>
<item>left-click</item>
</string-array>
<string-array name="tap2_action_entries">
<item>@string/pref_gesture_action_none</item>
<item>@string/pref_gesture_action_right_click</item>
@ -109,4 +110,15 @@
<item>pan</item>
<item>remote-scroll</item>
</string-array>
<string-array name="mouse_back_action_entries">
<item>@string/pref_mouse_back_action_default</item>
<item>@string/pref_gesture_action_middle_click</item>
<item>@string/pref_gesture_action_right_click</item>
</string-array>
<string-array name="mouse_back_action_values">
<item>default</item>
<item>middle-click</item>
<item>right-click</item>
</string-array>
</resources>

View file

@ -27,6 +27,7 @@
<string name="title_export">Export</string>
<string name="title_import">Import</string>
<string name="title_view_only_mode">View-only</string>
<string name="title_default_security">Default security</string>
<string name="title_hide_remote_cursor">Hide remote pointer</string>
<string name="title_key_compat_mode">Send legacy key events</string>
<string name="title_use_ssh_tunnel">Use SSH tunnel</string>
@ -68,6 +69,7 @@
<string name="msg_invalid_vnc_uri">Invalid VNC URI</string>
<string name="msg_copied_to_clipboard">Copied</string>
<string name="msg_key_compat_mode_help">Some servers have limited Unicode support. Legacy key events can help input non-English text.</string>
<string name="msg_drag_gesture_help">Assigning an action to Drag gesture will change Long press detection:\n\n<b>Press-hold-release</b> → Long press\n<b>Press-hold-swipe</b> → Drag</string>
<string name="tip_empty_server_list">Server list is empty.\nClick \'<b>+</b>\' to add a server, or\nuse the top address bar to connect directly.</string>
<string name="tip_urlbar">You can enter IP address, hostname, or \'vnc://\' URI.</string>
<string name="tip_discovery">Discovery can automatically find supported servers\nrunning on your network.</string>
@ -134,12 +136,17 @@
<string name="pref_long_press">Long press</string>
<string name="pref_swipe1">One-finger swipe</string>
<string name="pref_swipe2">Two-finger swipe</string>
<string name="pref_drag">Drag (long press and swipe)</string>
<string name="pref_drag">Drag</string>
<string name="pref_swipe_sensitivity">Swipe sensitivity</string>
<string name="pref_mouse">Mouse</string>
<string name="pref_mouse_passthrough">Mouse passthrough</string>
<string name="pref_mouse_passthrough_summary_off">Use mouse events for local gestures</string>
<string name="pref_mouse_passthrough_summary_on">Send mouse events directly to server</string>
<string name="pref_hide_local_cursor">Hide local (Android) pointer</string>
<string name="pref_hide_local_cursor">Hide local pointer</string>
<string name="pref_mouse_back">Back button</string>
<string name="pref_mouse_back_action_default">Default</string>
<string name="pref_mouse_back_action_right">Right click</string>
<string name="pref_mouse_back_action_middle">Middle click</string>
<string name="pref_vk">Virtual keys</string>
<string name="pref_vk_show_all">Show all keys</string>
<string name="pref_vk_open_with_keyboard">Open with keyboard</string>
@ -148,6 +155,10 @@
<string name="pref_km_language_switch_to_super">Language Switch → Super</string>
<string name="pref_km_back_to_escape">Back → Escape</string>
<string name="pref_km_back_to_escape_summary">Does not affect the navigation bar</string>
<string name="pref_orientation">Screen orientation</string>
<string name="pref_orientation_option_auto">Automatic</string>
<string name="pref_orientation_option_portrait">Portrait</string>
<string name="pref_orientation_option_landscape">Landscape</string>
<string name="pref_fullscreen">Fullscreen</string>
<string name="pref_enable_pip">Picture-in-picture</string>
<string name="pref_toolbar">Toolbar</string>

View file

@ -16,7 +16,7 @@
app:title="@string/pref_experimental_toolbar_swipe_close" />
<SwitchPreference
app:defaultValue="false"
app:defaultValue="true"
app:key="viewer_immersive_mode"
app:summary="@string/pref_experimental_immersive_mode_summary"
app:title="@string/pref_experimental_immersive_mode" />

View file

@ -6,7 +6,8 @@
~ See COPYING.txt for more details.
-->
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:title="@string/pref_input">
<PreferenceCategory
@ -20,14 +21,6 @@
app:summaryOn="@string/pref_direct_touch_summary_on"
app:title="@string/pref_direct_touch" />
<ListPreference
app:defaultValue="left-click"
app:entries="@array/tap1_action_entries"
app:entryValues="@array/tap1_action_values"
app:key="gesture_single_tap"
app:title="@string/pref_single_tap"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="double-click"
app:entries="@array/double_tap_action_entries"
@ -69,13 +62,26 @@
app:title="@string/pref_swipe2"
app:useSimpleSummaryProvider="true" />
<ListPreference
<com.gaurav.avnc.ui.prefs.ListPreferenceEx
app:defaultValue="none"
app:entries="@array/drag_action_entries"
app:entryValues="@array/drag_action_values"
app:key="gesture_drag"
app:title="@string/pref_drag"
app:useSimpleSummaryProvider="true" />
app:useSimpleSummaryProvider="true"
app:widgetLayout="@layout/help_btn" />
<SeekBarPreference
android:max="15"
app:defaultValue="10"
app:key="gesture_swipe_sensitivity"
app:min="5"
app:title="@string/pref_swipe_sensitivity" />
<SwitchPreference
android:defaultValue="true"
app:key="natural_scrolling"
app:title="Natural scrolling" />
</PreferenceCategory>
<PreferenceCategory
@ -93,6 +99,19 @@
app:defaultValue="false"
app:key="hide_local_cursor"
app:title="@string/pref_hide_local_cursor" />
<SwitchPreference
app:defaultValue="false"
app:key="hide_remote_cursor"
app:title="@string/title_hide_remote_cursor" />
<ListPreference
app:defaultValue="right-click"
app:entries="@array/mouse_back_action_entries"
app:entryValues="@array/mouse_back_action_values"
app:key="mouse_back"
app:title="@string/pref_mouse_back"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>
<PreferenceCategory

View file

@ -10,6 +10,14 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
app:title="@string/pref_viewer">
<ListPreference
android:defaultValue="auto"
android:entries="@array/orientation_entries"
android:entryValues="@array/orientation_values"
android:key="viewer_orientation"
android:title="@string/pref_orientation"
app:useSimpleSummaryProvider="true" />
<SwitchPreference
app:defaultValue="true"
app:key="fullscreen_display"

View file

@ -21,4 +21,23 @@ allprojects {
task clean(type: Delete) {
delete rootProject.buildDir
}
}
/**
* Temporary workaround to enable CMake 3.10 compatibility for WolfSSL
*/
try {
def cl = file('extern/wolfssl/CMakeLists.txt')
def Fix1Old = 'cmake_minimum_required(VERSION 3.16)'
def Fix1New = 'cmake_minimum_required(VERSION 3.10)'
def Fix2Old = 'list(JOIN HEADER_EXCLUDE "|" EXCLUDED_HEADERS_REGEX)'
def Fix2New = 'string(REPLACE ";" "|" EXCLUDED_HEADERS_REGEX "${HEADER_EXCLUDE}")'
cl.text = cl.text.replace(Fix1Old, Fix1New).replace(Fix2Old, Fix2New)
gradle.buildFinished {
cl.text = cl.text.replace(Fix1New, Fix1Old).replace(Fix2New, Fix2Old)
}
} catch (FileNotFoundException ignored) {
}

@ -1 +1 @@
Subproject commit ecf021bc0d6f435daacff7c35ccaeef0145df1b9
Subproject commit c5f269eb9665435271c05fbcaf8721fa58e9eafa

2
extern/libvncserver vendored

@ -1 +1 @@
Subproject commit e5dc3d42b913ab8c3b757de572af708b441e669c
Subproject commit ee547c7a39a0c63b24140db5dd2d21d72c525252

2
extern/wolfssl vendored

@ -1 +1 @@
Subproject commit 7e01af012157bc20c840011a018619915380f05c
Subproject commit 57aac1c50b45275c7a99eca32ad985998b292dc8

View file

@ -0,0 +1,16 @@
AVNC ist ein Open Source VNC-Client für Android. Er ermöglicht es dir, jedes Gerät fernzusteuern, auf welchem ein VNC-Server läuft.
Funktionen:
- Material Design (inkl. Dark Theme)
- Konfigurierbare Gesten
- Starke Kodierung
- virtuelle Zusatztasten (z.B. Ctrl, Alt, Tab, Esc, Pfeiltasten...)
- Bild-in-Bild-Modus
- Zuschauermodus (View-Only)
- automatische Zeroconf-Serversuche im LAN
- TLS-Unterstützung (AnonTLS, VeNCrypt)
- SSH-Tunnel (VNC getunnelt durch SSH)
- Exportieren und Importieren der abgespeicherten Server
- Unterstützung für VNC-Repeater
- Synchronisation der Zwischenablage

View file

@ -0,0 +1 @@
Schneller und sicherer VNC-Client für Android

View file

@ -0,0 +1,9 @@
* Added client-side cursor rendering for improved responsiveness
* Viewer will now follow the pointer when Direct Mode is disabled
* Intercept Back key triggered by mouse/touchpad (by Tuan Pham)
* New preference for swipe sensitivity
* New language:
Turkish (by Fabriel & Oğuz Ersen)
Polish (by WaldiS)
Chinese (Simplified) (by wang)
* Note: 'Hide remote pointer' option has moved to Settings

View file

@ -0,0 +1 @@
Fix click position of Mouse Back actions

View file

@ -0,0 +1,4 @@
* New preference for natural scrolling
* New preference for screen orientation
* Pointer is now centered on screen when Direct Touch is off
* New language: German (by Pure Incompetence)

View file

@ -0,0 +1,3 @@
* Fixed black screen issue on some Tegra GPUs
* Excluded viewer's toolbar area from system back gesture
* New language: Russian

View file

@ -0,0 +1,3 @@
* Small improvement in connection startup speed
* Fixed a crash on first ever connection when toolbar was aligned to end
* Fixed a crash when entering picture-in-picture for servers with large aspect ratio

View file

@ -0,0 +1,9 @@
* بهبود در تکمیل خودکار ورود به سامانه
* بهبود در اکتشاف کارسازها
* زبان‌های جدید:
صربستانی (توسط Саша Петровић)
اوکراینی (توسط Andrij Mizyk)
* زبان‌های به‌روز شده:
بوکمالی نروژ (توسط Allan Nordhøy)
فارسی (توسط Eshagh Shahedany)
فرانسوری (توسط J. Lavoie, Nathan)

View file

@ -0,0 +1,9 @@
* Amélioration de l'autocomplétion à la connexion
* Amélioration de la découverte des serveurs
* Nouvelles langues :
Serbe (par Саша Петровић)
Ukrainien (par Andrij Mizyk)
* Langues mises à jour :
Norvégien Bokmål (par Allan Nordhøy)
Perse (par Eshagh Shahedany)
Français (par J. Lavoie, Nathan)

View file

@ -0,0 +1,3 @@
* Tilskrudd SSH-tunnel
* Fikset port-videresenderlekkasje
* Oppdatert program-metadata

View file

@ -0,0 +1,2 @@
* Forbedret programoppstartstid
* Innstilling for sortering av tjenere

View file

@ -0,0 +1,3 @@
* Tillagt Samsung-pekeflate-støtte (av Tuan Pham)
* Tillagt pekepinne/penn-støtte
* TIllagt håndvending for to-fingertrykk

View file

@ -0,0 +1,16 @@
AVNC er en fri VNC-klient for Android. Det tillater deg å kontrollere enheter som har en VNC-tjener annensteds hen.
Funksjoner:
- Materiell design (med mørk drakt)
- Oppsettbare håndvendinger
- Stram koding
- Virtuelt tastatur
- Bilde-i-bilde -modus
- Skrivebeskyttet modus
- Zeroconf-tjeneroppdagelse
- TLS-støtte (AnonTLS, VeNCrypt)
- SSH-tunnel (VNC over SSH)
- Importer/eksporter tjenere
- VNC-gjentagelsesstøtte
- Utklippstavlesyknronisering med tjener

View file

@ -0,0 +1,9 @@
* Dodano renderowanie kursora po stronie klienta w celu poprawy responsywności
* Przeglądarka będzie teraz podążać za wskaźnikiem, gdy tryb bezpośredni jest wyłączony
* Przechwytywanie klawisza Wstecz wyzwalanego przez mysz/dotyk (autor: Tuan Pham)
* Nowe preferencje dla czułości przeciągnięcia
* Nowy język:
Turecki (autorstwa Fabriel & Oğuz Ersen)
Polski (przez WaldiS)
Chiński (uproszczony) (przez wang)
* Uwaga: opcja 'Ukryj zdalny wskaźnik' została przeniesiona do Ustawień

View file

@ -0,0 +1,3 @@
* Wzmocnić tunel SSH
* Naprawić wyciek z przekierowania portów
* Zaktualizuj metadane aplikacji

View file

@ -0,0 +1,2 @@
* Poprawiono czas uruchamiania aplikacji
* Dodano preferencję sortowania serwerów

View file

@ -0,0 +1,3 @@
* Dodano obsługę touchpada Samsunga (autor: Tuan Pham)
* Dodano obsługę rysików/penów
* Dodano gest stuknięcia dwoma palcami

View file

@ -0,0 +1,3 @@
* Umożliwić mapowanie klawiszy do klawiszy SUPER/Window (autor: Tuan Pham)
* Upewnienie się, że wirtualne meta-klawisze są zawsze zwalniane (autor: thechosenreader)
* Dodano obsługę znaków diakrytycznych (akcentów)

View file

@ -0,0 +1,3 @@
* Ulepszona obsługa skrótów klawiaturowych
* Ulepszona obsługa znaków diakrytycznych (akcentów)
* Dodano pełny tryb immersyjny (eksperymentalny).

View file

@ -0,0 +1,3 @@
* Dodano tłumaczenie na język portugalski (BR) (by inkhorn)
* Dodano preferencję ukrywania lokalnego kursora systemu Android (przez Henry'ego H)
* Poprawiono obsługę poduszki rysika na niektórych urządzeniach (autor: Henry H)

View file

@ -0,0 +1,8 @@
* Dodano tryb trackpada/touchpada
* Przechwycony klawisz Window & Alt+Tab na Samsung DeX
* Zoptymalizowany zoom dla zdalnych ekranów w trybie portretowym
* Poprawiono czułość gestu przeciągania
* Tłumaczenia: Portugalski-BR (przez João Paulo),
Norweski Bokmål (przez Allan Nordhøy),
perski (przez Eshagh Shahedany),
francuskie (przez J. Lavoie)

View file

@ -0,0 +1,9 @@
* Ulepszone autouzupełnianie logowania
* Ulepszone wykrywanie serwerów
* Nowy język:
Serbski (autorstwa Саша Петровић)
Ukraiński (przez Andrij Mizyk)
* Zaktualizowany język:
Norweski Bokmål (autor: Allan Nordhøy)
Perski (autor: Eshagh Shahedany)
Francuski (autor: J. Lavoie, Nathan)

View file

@ -0,0 +1,16 @@
AVNC to otwartoźródłowy klient VNC dla systemu Android. Umożliwia on zdalne sterowanie dowolnym urządzeniem wyposażonym w serwer VNC.
Cechy:
- Material Design (z ciemnym motywem)
- Konfigurowalne gesty
- Dokładne kodowanie
- Klawisze wirtualne
- Tryb obraz w obrazie
- Tryb tylko do oglądania
- Wykrywanie serwera Zeroconf
- Obsługa TLS (AnonTLS, VeNCrypt)
- Tunel SSH (VNC przez SSH)
- Import/Export serwerów
- Obsługa VNC Repeater
- Synchronizacja schowka z serwerem

View file

@ -0,0 +1 @@
Szybki i bezpieczny klient VNC dla systemu Android

View file

@ -0,0 +1,3 @@
* Endureça o túnel SSH
* Corrija o vazamento do encaminhador de porta
* Atualizar metadados do aplicativo

View file

@ -0,0 +1,2 @@
* Melhore o tempo de inicialização do aplicativo
* Adicionada uma preferência para classificar os servidores

View file

@ -0,0 +1,3 @@
* Adicionado suporte ao touchpad Samsung (por Tuan Pham)
* Adicionado suporte para stylus/caneta
* Adicionado gesto de toque de 2 dedos

View file

@ -0,0 +1,3 @@
* Permitir mapeamentos de teclas para a tecla SUPER/Window (por Tuan Pham)
* Certifique-se de que as meta-chaves virtuais sejam sempre liberadas (pelo leitor escolhido)
* Adicionado suporte a diacríticos (acentos)

View file

@ -0,0 +1,3 @@
* Suporte a atalhos de teclado aprimorado
* Suporte a diacríticos (acentos) aprimorado
* Adicionado modo de imersão total (experimental)

View file

@ -0,0 +1,3 @@
* Adicionada tradução para português (BR) (por inkhorn)
* Adicionada uma preferência para ocultar o cursor local do Android (por Henry H)
* Corrigido o manuseio do Stylus hover em alguns dispositivos (por Henry H)

View file

@ -0,0 +1,14 @@
AVNC é um cliente VNC de código aberto para Android. Ele permite que você controle remotamente qualquer dispositivo executando um servidor VNC.
Recursos:
- Material Design (com tema escuro)
- Gestos configuráveis
- Codificação apertada
- Chaves Virtuais
- Modo imagem em imagem
- Modo somente visualização
- Descoberta do Servidor Zeroconf
- Suporte TLS (AnonTLS, VeNCrypt)
- Túnel SSH (VNC sobre SSH)
- Servidores de importação/exportação
- Suporte a repetidor VNC
- Sincronização da área de transferência com o servidor

View file

@ -0,0 +1 @@
Cliente VNC rápido e seguro para Android

View file

@ -0,0 +1,9 @@
* Daha iyi yanıt için istemci tarafı imleç görselleştirme eklendi
* Görüntüleyici artık Doğrudan Mod devre dışı bırakıldığında işaretçiyi takip edecek
* Fare/dokunmatik yüzey tarafından tetiklenen Geri tuşunu yakala (Tuan Pham tarafından)
* Kaydırma hassasiyeti için yeni tercih
* Yeni dil:
Türkçe (Fabriel ve Oğuz Ersen tarafından)
Lehçe (WaldiS tarafından)
Çince (Basitleştirilmiş) (wang tarafından)
* Not: 'Uzak işaretçiyi gizle' seçeneği Ayarlar'a taşındı

View file

@ -0,0 +1,3 @@
* SSH tüneli daha güvenli hale getirildi
* Bağlantı noktası yönlendirici sızıntısı düzeltildi
* Uygulama üst verileri güncellendi

View file

@ -0,0 +1,2 @@
* Uygulama başlatma süresi iyileştirildi
* Sunucuları sıralamak için seçenek eklendi

View file

@ -0,0 +1,3 @@
* Samsung dokunmatik yüzey desteği eklendi (Tuan Pham tarafından)
* İğne/kalem desteği eklendi
* 2 parmakla dokunma hareketi eklendi

View file

@ -0,0 +1,3 @@
* SÜPER/Windows tuşu için tuş eşlemeleri eklendi (Tuan Pham tarafından)
* Sanal üst anahtarların her zaman serbest bırakıldığından emin olundu (thechosenreader tarafından)
* Aksanlı karakter desteği eklendi

View file

@ -0,0 +1,3 @@
* Klavye kısayolları desteği iyileştirildi
* Aksanlı karakter desteği iyileştirildi
* Tam odaklanma modu eklendi (deneysel)

View file

@ -0,0 +1,3 @@
* Portekizce (BR) çeviri eklendi (inkhorn tarafından)
* Yerel Android imlecini gizleme seçeneği eklendi (Henry H tarafından)
* Bazı aygıtlarda kalemle vurgulama davranışı düzeltildi (Henry H tarafından)

View file

@ -0,0 +1,8 @@
* İzleme paneli/dokunmatik yüzey modu eklendi
* Samsung DeX'te Windows tuşu ve Alt+Tab yakalama eklendi
* Dikey mod uzak ekranlar için yakınlaştırma iyileştirildi
* Sürükleme hareketinin hassasiyeti düzeltildi
* Çeviriler: Portekizce-BR (João Paulo tarafından),
Bokmål Norveççe (Allan Nordhøy tarafından),
Farsça (Eshagh Shahedany tarafından),
Fransızca (J. Lavoie tarafından)

View file

@ -0,0 +1,9 @@
* Oturum açma otomatik tamamlaması iyileştirildi
* Sunucu keşfi iyileştirildi
* Yeni diller:
Sırpça (Саша Петровић tarafından)
Ukraynaca (Andrij Mizyk tarafından)
* Güncellenen diller:
Bokmål Norveççe (Allan Nordhøy tarafından)
Farsça (Eshagh Shahedany tarafından)
Fransızca (J. Lavoie, Nathan tarafından)

Some files were not shown because too many files have changed in this diff Show more