Compare commits
104 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
698f01aacd | ||
|
|
09a4990433 | ||
|
|
1fe3dedc52 | ||
|
|
ad6d081a6f | ||
|
|
64f06caa09 | ||
|
|
ead089fd5e | ||
|
|
548e0241a4 | ||
|
|
2ccbd0cbd8 | ||
|
|
bf73d24f9e | ||
|
|
ee8ae23d5f | ||
|
|
a28149eb99 | ||
|
|
2abfd14d83 | ||
|
|
31c90b4287 | ||
|
|
4facebf48e | ||
|
|
153f69d12c | ||
|
|
9c3a0cc3dd | ||
|
|
08b4c01d99 | ||
|
|
22b071365e | ||
|
|
c671615025 | ||
|
|
1af619551a | ||
|
|
3d82e80beb | ||
|
|
b3bd9399f0 | ||
|
|
c0a56d1270 | ||
|
|
650668d8fa | ||
|
|
aa5c3e92a8 | ||
|
|
d6bf7c6189 | ||
|
|
c3292a5b90 | ||
|
|
45b5fc4696 | ||
|
|
e259c931a1 | ||
|
|
890b7750b7 | ||
|
|
c4d5b7b2f8 | ||
|
|
0a379a68e7 | ||
|
|
41fb2e3b5f | ||
|
|
f9f3a647de | ||
|
|
56fc2f8f3c | ||
|
|
00bd2da0f6 | ||
|
|
0cce0c2ef8 | ||
|
|
999bdb2f72 | ||
|
|
c66d7e3668 | ||
|
|
b83747b1d0 | ||
|
|
ee49c2d1b4 | ||
|
|
bfd34747e8 | ||
|
|
ce4e838351 | ||
|
|
a5f3faedba | ||
|
|
45929278ee | ||
|
|
6526cb86b2 | ||
|
|
5780d08a9d | ||
|
|
ee28bf184d | ||
|
|
c6b28ea6f1 | ||
|
|
ce2736ed20 | ||
|
|
36d8b66cae | ||
|
|
f0475ccdcf | ||
|
|
d04e19be58 | ||
|
|
52607e2b21 | ||
|
|
c64140d1b2 | ||
|
|
e068d89eae | ||
|
|
a518be5a7a | ||
|
|
388ccc8176 | ||
|
|
239f993082 | ||
|
|
555ea0a483 | ||
|
|
b29710119e | ||
|
|
6293385d88 | ||
|
|
8c7f3b07bc | ||
|
|
bbda1b8e62 | ||
|
|
dddad7b914 | ||
|
|
921601c05b | ||
|
|
d180fafc5e | ||
|
|
5bc110479b | ||
|
|
e3e4e86305 | ||
|
|
bfc0607870 | ||
|
|
3e04963715 | ||
|
|
26d303ebbd | ||
|
|
781263fc5f | ||
|
|
2b64933fcb | ||
|
|
2b6441080f | ||
|
|
db275ce209 | ||
|
|
8bcae288e0 | ||
|
|
ef107b1fd3 | ||
|
|
c29b8431f3 | ||
|
|
8b85396e5d | ||
|
|
6c0299446c | ||
|
|
f60f4f9772 | ||
|
|
823a633edc | ||
|
|
049714f9ea | ||
|
|
08117b45f9 | ||
|
|
7fd428b6d5 | ||
|
|
1d11afb829 | ||
|
|
9af1e88969 | ||
|
|
fd2314f004 | ||
|
|
4382b6b46d | ||
|
|
c94559f620 | ||
|
|
9b8d41fc9d | ||
|
|
cd285ca597 | ||
|
|
1976d4d89a | ||
|
|
8bca42afcf | ||
|
|
c5b4b2b720 | ||
|
|
c1879004ff | ||
|
|
8915809875 | ||
|
|
bbd1c294d9 | ||
|
|
7b0ecb269b | ||
|
|
91a3fab1bd | ||
|
|
a403fe9ec7 | ||
|
|
f855fce915 | ||
|
|
841c0f40e4 |
172 changed files with 4127 additions and 1100 deletions
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
liberapay: gujjwal00
|
||||
custom: ["https://paypal.me/gujjwal00"]
|
||||
21
.github/workflows/main.yml
vendored
21
.github/workflows/main.yml
vendored
|
|
@ -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
29
.github/workflows/pr.yml
vendored
Normal 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
|
||||
18
.idea/codeStyles/Project.xml
generated
18
.idea/codeStyles/Project.xml
generated
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -59,3 +59,7 @@ Now you can import the project in Android Studio, or build it directly from term
|
|||
|
||||
Read [Architecture.kt](app/src/main/java/com/gaurav/avnc/Architecture.kt) (preferably in
|
||||
Android Studio) to know more about the code.
|
||||
|
||||
|
||||
### Translation
|
||||
You can translate AVNC on [Weblate](https://hosted.weblate.org/engage/avnc/).
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ android {
|
|||
applicationId "com.gaurav.avnc"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 7
|
||||
versionName "1.5.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"
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -43,8 +43,8 @@ class KeyHandlerTest {
|
|||
dispatchedKeyDowns = arrayListOf()
|
||||
dispatchedKeyUps = arrayListOf()
|
||||
mockDispatcher = mockk()
|
||||
every { mockDispatcher.onXKeySym(any(), true) } answers { dispatchedKeyDowns.add(firstArg()) }
|
||||
every { mockDispatcher.onXKeySym(any(), false) } answers { dispatchedKeyUps.add(firstArg()) }
|
||||
every { mockDispatcher.onXKeySym(any(), true) } answers { dispatchedKeyDowns.add(firstArg()); true }
|
||||
every { mockDispatcher.onXKeySym(any(), false) } answers { dispatchedKeyUps.add(firstArg()); true }
|
||||
|
||||
keyHandler = KeyHandler(mockDispatcher, true, prefs)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -197,7 +198,7 @@ class TouchHandlerTest {
|
|||
sendDown()
|
||||
sendUp()
|
||||
verifyOrder {
|
||||
mockDispatcher.onGestureStart(testPoint)
|
||||
mockDispatcher.onGestureStart()
|
||||
mockDispatcher.onGestureStop(testPoint)
|
||||
}
|
||||
}
|
||||
|
|
@ -207,7 +208,7 @@ class TouchHandlerTest {
|
|||
sendDown()
|
||||
sendEvent(createEvent(MotionEvent.ACTION_CANCEL, testPoint))
|
||||
verifyOrder {
|
||||
mockDispatcher.onGestureStart(testPoint)
|
||||
mockDispatcher.onGestureStart()
|
||||
mockDispatcher.onGestureStop(testPoint)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ package com.gaurav.avnc.vnc
|
|||
|
||||
import com.gaurav.avnc.TestServer
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
|
|
@ -23,16 +22,16 @@ class VncClientTest {
|
|||
override fun onCredentialRequired() = UserCredential()
|
||||
override fun onFramebufferUpdated() {}
|
||||
override fun onFramebufferSizeChanged(width: Int, height: Int) {}
|
||||
override fun onClientStateChanged(newState: VncClient.State) {}
|
||||
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"
|
||||
|
|
@ -47,8 +46,8 @@ class VncClientTest {
|
|||
|
||||
private fun connect() {
|
||||
server.start()
|
||||
assertTrue(client.connect(server.host, server.port))
|
||||
assertTrue(client.processServerMessage(10000))
|
||||
client.connect(server.host, server.port)
|
||||
client.processServerMessage(10000)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
87
app/src/main/cpp/ClientEx.h
Normal file
87
app/src/main/cpp/ClientEx.h
Normal 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
111
app/src/main/cpp/Cursor.h
Normal 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
|
||||
94
app/src/main/cpp/Utility.h
Normal file
94
app/src/main/cpp/Utility.h
Normal 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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
19
app/src/main/java/com/gaurav/avnc/model/LoginInfo.kt
Normal file
19
app/src/main/java/com/gaurav/avnc/model/LoginInfo.kt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Gaurav Ujjwal.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* See COPYING.txt for more details.
|
||||
*/
|
||||
|
||||
package com.gaurav.avnc.model
|
||||
|
||||
/**
|
||||
* Simple model used by login auto-completion.
|
||||
*/
|
||||
data class LoginInfo(
|
||||
val name: String, //Profile name
|
||||
val host: String,
|
||||
val username: String,
|
||||
val password: String,
|
||||
)
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ package com.gaurav.avnc.model.db
|
|||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
import com.gaurav.avnc.model.LoginInfo
|
||||
import com.gaurav.avnc.model.ServerProfile
|
||||
import com.gaurav.avnc.vnc.UserCredential
|
||||
|
||||
@Dao
|
||||
interface ServerProfileDao {
|
||||
|
|
@ -26,8 +26,8 @@ interface ServerProfileDao {
|
|||
@Query("SELECT * FROM profiles")
|
||||
fun getList(): List<ServerProfile>
|
||||
|
||||
@Query("SELECT username, password FROM profiles")
|
||||
fun getCredentials(): LiveData<List<UserCredential>>
|
||||
@Query("SELECT name, host, username, password FROM profiles")
|
||||
fun getCredentials(): LiveData<List<LoginInfo>>
|
||||
|
||||
@Insert
|
||||
fun insert(profile: ServerProfile): Long
|
||||
|
|
|
|||
|
|
@ -59,6 +59,17 @@ class HomeActivity : AppCompatActivity() {
|
|||
viewModel.discovery.servers.observe(this) { updateDiscoveryBadge(it) }
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
viewModel.autoStartDiscovery()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
if (!isChangingConfigurations)
|
||||
viewModel.autoStopDiscovery()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle drawer item selection.
|
||||
*/
|
||||
|
|
|
|||
73
app/src/main/java/com/gaurav/avnc/ui/home/IndicatorView.kt
Normal file
73
app/src/main/java/com/gaurav/avnc/ui/home/IndicatorView.kt
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Gaurav Ujjwal.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* See COPYING.txt for more details.
|
||||
*/
|
||||
|
||||
package com.gaurav.avnc.ui.home
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.gaurav.avnc.model.ServerProfile
|
||||
|
||||
/**
|
||||
* [IndicatorView] highlights rediscovered servers. Main benefit is to immediately
|
||||
* identify reachable servers, without checking Discovery tab.
|
||||
*
|
||||
* Don't know about other users, but I really like this feature.
|
||||
* It complements discovery-autorun very well.
|
||||
*/
|
||||
class IndicatorView(context: Context, attrs: AttributeSet? = null) : View(context, attrs) {
|
||||
|
||||
private object Controller {
|
||||
private val animatedViews = ArrayList<IndicatorView>()
|
||||
private val animator = ValueAnimator.ofFloat(0F, 1F).apply {
|
||||
duration = 800
|
||||
repeatCount = ValueAnimator.INFINITE
|
||||
repeatMode = ValueAnimator.REVERSE
|
||||
addUpdateListener { animatedViews.forEach { view -> view.alpha = animatedValue as Float } }
|
||||
}
|
||||
|
||||
fun add(view: IndicatorView) {
|
||||
animatedViews.add(view)
|
||||
if (!animator.isStarted) animator.start()
|
||||
}
|
||||
|
||||
fun remove(view: IndicatorView) {
|
||||
animatedViews.remove(view)
|
||||
if (animatedViews.isEmpty() && animator.isStarted) animator.end()
|
||||
}
|
||||
}
|
||||
|
||||
private var profile: ServerProfile? = null
|
||||
private var indicatedProfiles: LiveData<List<ServerProfile>>? = null
|
||||
private val observer = Observer<List<ServerProfile>> { isVisible = it?.contains(profile) == true }
|
||||
|
||||
fun setup(profile: ServerProfile, indicatedProfiles: LiveData<List<ServerProfile>>) {
|
||||
this.profile = profile
|
||||
this.indicatedProfiles = indicatedProfiles
|
||||
indicatedProfiles.removeObserver(observer)
|
||||
indicatedProfiles.observeForever(observer)
|
||||
}
|
||||
|
||||
override fun onVisibilityChanged(changedView: View, visibility: Int) {
|
||||
super.onVisibilityChanged(changedView, visibility)
|
||||
if (changedView == this) {
|
||||
if (visibility == VISIBLE) Controller.add(this)
|
||||
else Controller.remove(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
Controller.remove(this)
|
||||
indicatedProfiles?.removeObserver(observer)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ package com.gaurav.avnc.ui.home
|
|||
|
||||
import android.view.*
|
||||
import androidx.core.view.forEach
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
|
|
@ -24,14 +22,10 @@ import com.gaurav.avnc.databinding.ServerSavedBinding
|
|||
import com.gaurav.avnc.databinding.ServerSavedItemBinding
|
||||
import com.gaurav.avnc.model.ServerProfile
|
||||
import com.gaurav.avnc.ui.home.ServerTabs.PagerAdapter.ViewHolder
|
||||
import com.gaurav.avnc.util.Experimental
|
||||
import com.gaurav.avnc.viewmodel.HomeViewModel
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* This class creates and manages tabs in [HomeActivity].
|
||||
|
|
@ -121,11 +115,6 @@ class ServerTabs(val activity: HomeActivity) {
|
|||
binding.serversRv.setHasFixedSize(true)
|
||||
|
||||
activity.viewModel.serverProfiles.observe(activity) { adapter.submitList(it) }
|
||||
|
||||
if (activity.viewModel.pref.experimental.indicator) {
|
||||
Experimental.Indicator().setup(binding, adapter)
|
||||
}
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
|
|
@ -136,9 +125,6 @@ class ServerTabs(val activity: HomeActivity) {
|
|||
class SavedServerAdapter(val viewModel: HomeViewModel)
|
||||
: ListAdapter<ServerProfile, SavedServerAdapter.ViewHolder>(Differ) {
|
||||
|
||||
//Experimental feature
|
||||
var bindListener: ((ViewHolder, ServerProfile) -> Unit)? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val binding = ServerSavedItemBinding.inflate(inflater, parent, false)
|
||||
|
|
@ -149,7 +135,7 @@ class ServerTabs(val activity: HomeActivity) {
|
|||
val profile = getItem(position)
|
||||
holder.profile = profile
|
||||
holder.binding.viewModel = profile
|
||||
bindListener?.invoke(holder, profile)
|
||||
holder.binding.indicator.setup(profile, viewModel.rediscoveredProfiles)
|
||||
}
|
||||
|
||||
inner class ViewHolder(val binding: ServerSavedItemBinding)
|
||||
|
|
@ -177,27 +163,10 @@ class ServerTabs(val activity: HomeActivity) {
|
|||
binding.discoveredRv.setHasFixedSize(true)
|
||||
|
||||
activity.viewModel.discovery.servers.observe(activity) { adapter.submitList(it) }
|
||||
launchDiscoveryRestarter()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private fun launchDiscoveryRestarter() {
|
||||
activity.lifecycleScope.launch(Dispatchers.Main) {
|
||||
val viewModel = activity.viewModel
|
||||
|
||||
while (true) {
|
||||
delay(viewModel.pref.server.discoveryTimeout + viewModel.pref.server.discoveryRestartDelay)
|
||||
|
||||
if (activity.lifecycle.currentState == Lifecycle.State.RESUMED &&
|
||||
viewModel.pref.server.discoveryAutoStart &&
|
||||
viewModel.pref.server.discoveryRestart) {
|
||||
viewModel.startDiscovery()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DiscoveredServerAdapter(val viewModel: HomeViewModel)
|
||||
: ListAdapter<ServerProfile, DiscoveredServerAdapter.ViewHolder>(Differ) {
|
||||
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class LogsFragment : InfoFragment() {
|
|||
*/
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
menu.add("Clear").setOnMenuItemClickListener {
|
||||
menu.add(getString(R.string.title_clear)).setOnMenuItemClickListener {
|
||||
Debugging.clearLogs()
|
||||
Snackbar.make(requireView(), "Cleared!", Snackbar.LENGTH_SHORT).show()
|
||||
parentFragmentManager.popBackStack()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Gaurav Ujjwal.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* See COPYING.txt for more details.
|
||||
*/
|
||||
|
||||
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.
|
||||
*/
|
||||
class ListPreferenceEx(context: Context, attrs: AttributeSet) : ListPreference(context, attrs) {
|
||||
|
||||
/**
|
||||
* Summary used when preference is disabled.
|
||||
*/
|
||||
var disabledStateSummary: CharSequence? = null
|
||||
|
||||
override fun getSummary(): CharSequence? {
|
||||
if (!isEnabled && disabledStateSummary != null)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -104,6 +108,23 @@ class PrefsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreference
|
|||
summary = getString(R.string.msg_ptr_hiding_not_supported)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,19 +9,16 @@
|
|||
package com.gaurav.avnc.ui.vnc
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.util.ArrayMap
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.gaurav.avnc.R
|
||||
import com.gaurav.avnc.databinding.FragmentCredentialBinding
|
||||
import com.gaurav.avnc.model.LoginInfo
|
||||
import com.gaurav.avnc.viewmodel.VncViewModel
|
||||
import com.gaurav.avnc.vnc.UserCredential
|
||||
import com.gaurav.avnc.vnc.VncClient
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
|
||||
|
|
@ -53,9 +50,10 @@ class CredentialFragment : DialogFragment() {
|
|||
.setTitle(R.string.title_credentials_required)
|
||||
.setView(binding.root)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val cred = UserCredential(binding.username.text.toString(),
|
||||
binding.password.text.toString())
|
||||
val username = binding.username.text.toString()
|
||||
val password = getRealPassword(binding.password.text.toString())
|
||||
|
||||
val cred = UserCredential(username, password)
|
||||
viewModel.credentialRequest.offerResponse(cred)
|
||||
|
||||
if (binding.remember.isChecked)
|
||||
|
|
@ -80,8 +78,8 @@ class CredentialFragment : DialogFragment() {
|
|||
*/
|
||||
private fun scheduleCredentialSave(viewModel: VncViewModel, cred: UserCredential) {
|
||||
with(viewModel) {
|
||||
clientState.observeForever {
|
||||
if (it == VncClient.State.Connected) {
|
||||
state.observeForever {
|
||||
if (it == VncViewModel.State.Connected) {
|
||||
profile.username = cred.username
|
||||
profile.password = cred.password
|
||||
saveProfile()
|
||||
|
|
@ -106,11 +104,11 @@ class CredentialFragment : DialogFragment() {
|
|||
}
|
||||
|
||||
viewModel.knownCredentials.observe(this) { list ->
|
||||
val usernames = list.map { it.username }.filter { it.isNotBlank() }.distinct()
|
||||
val passwords = list.map { it.password }.filter { it.isNotBlank() }.distinct()
|
||||
val usernames = list.map { it.username }.filter { it.isNotEmpty() }.distinct()
|
||||
val passwords = preparePasswordSuggestions(list)
|
||||
|
||||
val usernameAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, usernames)
|
||||
val passwordAdapter = PasswordAdapter(requireContext(), android.R.layout.simple_list_item_1, passwords)
|
||||
val passwordAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, passwords)
|
||||
|
||||
binding.username.setAdapter(usernameAdapter)
|
||||
binding.password.setAdapter(passwordAdapter)
|
||||
|
|
@ -118,21 +116,20 @@ class CredentialFragment : DialogFragment() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Specialized adapter for password completion. We don't want to show plaintext password
|
||||
* in completion list, so we partially obfuscate it.
|
||||
*
|
||||
* @param layout Must point to a resource with [TextView] as root
|
||||
* Instead of showing plaintext passwords, we show server name & host in suggestion
|
||||
* list. When user taps OK, we convert the suggestion back to real password.
|
||||
*/
|
||||
private class PasswordAdapter(context: Context, layout: Int, passwords: List<String>)
|
||||
: ArrayAdapter<String>(context, layout, passwords) {
|
||||
private val passwordMap = ArrayMap<String, String>()
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val view = super.getView(position, convertView, parent) as TextView
|
||||
private fun preparePasswordSuggestions(list: List<LoginInfo>): List<String> {
|
||||
list.filter { it.password.isNotEmpty() }
|
||||
.map { Pair("from: ${it.name} [${it.host}]", it.password) }
|
||||
.distinct()
|
||||
.toMap(passwordMap)
|
||||
.removeAll(passwordMap.values) //Guard against (very unlikely) clash with real password
|
||||
|
||||
val obfuscated = "**********" + view.text.takeLast(2)
|
||||
view.text = obfuscated
|
||||
|
||||
return view
|
||||
}
|
||||
return passwordMap.keys.toList()
|
||||
}
|
||||
|
||||
private fun getRealPassword(typedPassword: String) = passwordMap[typedPassword] ?: typedPassword
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
package com.gaurav.avnc.ui.vnc
|
||||
|
||||
import android.graphics.PointF
|
||||
import com.gaurav.avnc.ui.vnc.Dispatcher.SwipeAction
|
||||
import com.gaurav.avnc.viewmodel.VncViewModel
|
||||
import com.gaurav.avnc.vnc.Messenger
|
||||
import com.gaurav.avnc.vnc.PointerButton
|
||||
|
|
@ -53,62 +54,67 @@ class Dispatcher(private val activity: VncActivity) {
|
|||
|
||||
private val viewModel = activity.viewModel
|
||||
private val messenger = viewModel.messenger
|
||||
private val prefs = viewModel.pref
|
||||
private val gesturePref = viewModel.pref.input.gesture
|
||||
|
||||
//Used for remote scrolling
|
||||
private var accumulatedDx = 0F
|
||||
private var accumulatedDy = 0F
|
||||
private val deltaPerScroll = 20F //For how much dx/dy, one scroll event will be sent
|
||||
|
||||
/**************************************************************************
|
||||
* Action configuration
|
||||
**************************************************************************/
|
||||
private val directMode = DirectMode()
|
||||
private val relativeMode = RelativeMode()
|
||||
private val defaultMode = if (gesturePref.directTouch) directMode else relativeMode
|
||||
|
||||
val swipe1Action by lazy { selectSwipeAction(prefs.input.gesture.swipe1) }
|
||||
val swipe2Action by lazy { selectSwipeAction(prefs.input.gesture.swipe2) }
|
||||
val dragAction by lazy { selectSwipeAction(prefs.input.gesture.drag) }
|
||||
private val tap1Action = selectPointAction(gesturePref.tap1)
|
||||
private val tap2Action = selectPointAction(gesturePref.tap2)
|
||||
private val doubleTapAction = selectPointAction(gesturePref.doubleTap)
|
||||
private val longPressAction = selectPointAction(gesturePref.longPress)
|
||||
|
||||
val tap1Action by lazy { selectPointerAction(prefs.input.gesture.tap1) }
|
||||
val tap2Action by lazy { selectPointerAction(prefs.input.gesture.tap2) }
|
||||
val doubleTapAction by lazy { selectPointerAction(prefs.input.gesture.doubleTap) }
|
||||
val longPressAction by lazy { selectPointerAction(prefs.input.gesture.longPress) }
|
||||
private val swipe1Action = selectSwipeAction(if (gesturePref.directTouch) gesturePref.swipe1 else "move-pointer")
|
||||
private val swipe2Action = selectSwipeAction(gesturePref.swipe2)
|
||||
private val dragAction = selectSwipeAction(gesturePref.drag)
|
||||
|
||||
private fun selectPointerAction(actionName: String): (PointF) -> Unit {
|
||||
private val mouseBackAction = selectPointAction(viewModel.pref.input.mouseBack)
|
||||
|
||||
private fun selectPointAction(actionName: String): (PointF) -> Unit {
|
||||
return when (actionName) {
|
||||
"left-click" -> { p -> doClick(PointerButton.Left, p) }
|
||||
"double-click" -> { p -> doDoubleClick(PointerButton.Left, p) }
|
||||
"middle-click" -> { p -> doClick(PointerButton.Middle, p) }
|
||||
"right-click" -> { p -> doClick(PointerButton.Right, p) }
|
||||
"move-pointer" -> { p -> doMovePointer(p) }
|
||||
"left-click" -> { p -> defaultMode.doClick(PointerButton.Left, p) }
|
||||
"double-click" -> { p -> defaultMode.doDoubleClick(PointerButton.Left, p) }
|
||||
"middle-click" -> { p -> defaultMode.doClick(PointerButton.Middle, p) }
|
||||
"right-click" -> { p -> defaultMode.doClick(PointerButton.Right, p) }
|
||||
"open-keyboard" -> { _ -> doOpenKeyboard() }
|
||||
else -> { _ -> } //Nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a lambda which takes 4 arguments:
|
||||
*
|
||||
* sp - Start point of the gesture
|
||||
* cp - Current point of the gesture
|
||||
* dx - Change along x-axis since last event
|
||||
* dy - Change along y-axis since last event
|
||||
*/
|
||||
private fun selectSwipeAction(actionName: String): (PointF, PointF, Float, Float) -> Unit {
|
||||
private fun selectSwipeAction(actionName: String): SwipeAction {
|
||||
return when (actionName) {
|
||||
"pan" -> { _, _, dx, dy -> doPan(dx, dy) }
|
||||
"remote-scroll" -> { sp, _, dx, dy -> doRemoteScroll(sp, dx, dy) }
|
||||
"remote-drag" -> { _, cp, _, _ -> doDrag(cp) }
|
||||
else -> { _, _, _, _ -> } //Nothing
|
||||
"pan" -> SwipeAction { _, _, dx, dy -> doPan(dx, dy) }
|
||||
"move-pointer" -> SwipeAction { _, cp, dx, dy -> defaultMode.doMovePointer(cp, dx, dy) }
|
||||
"remote-scroll" -> SwipeAction { sp, _, dx, dy -> defaultMode.doRemoteScroll(sp, dx, dy) }
|
||||
"remote-drag" -> SwipeAction { _, cp, dx, dy -> defaultMode.doDrag(cp, dx, dy) }
|
||||
else -> SwipeAction { _, _, _, _ -> } //Nothing
|
||||
}
|
||||
}
|
||||
|
||||
//Instead of using generic lambda, like point actions, we are using a functional
|
||||
//interface with SAM conversion to avoid boxing/unboxing overhead for dx & dy.
|
||||
private fun interface SwipeAction {
|
||||
/**
|
||||
* [sp] Start point of the gesture
|
||||
* [cp] Current point of the gesture
|
||||
* [dx] Change along x-axis since last event
|
||||
* [dy] Change along y-axis since last event
|
||||
*/
|
||||
operator fun invoke(sp: PointF, cp: PointF, dx: Float, dy: Float)
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* Event receivers
|
||||
**************************************************************************/
|
||||
|
||||
fun onGestureStart(p: PointF) = stopFling()
|
||||
fun onGestureStop(p: PointF) = doPointerButtonRelease(p)
|
||||
fun onGestureStart() = defaultMode.onGestureStart()
|
||||
fun onGestureStop(p: PointF) = defaultMode.onGestureStop(p)
|
||||
|
||||
fun onTap1(p: PointF) = tap1Action(p)
|
||||
fun onTap2(p: PointF) = tap2Action(p)
|
||||
|
|
@ -120,102 +126,163 @@ 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) = doPointerButtonDown(button, p)
|
||||
fun onMouseButtonUp(button: PointerButton, p: PointF) = doPointerButtonUp(button, p)
|
||||
fun onMouseMove(p: PointF) = doMovePointer(p)
|
||||
fun onMouseScroll(p: PointF, hs: Float, vs: Float) = doRemoteScroll(p, hs * deltaPerScroll, vs * deltaPerScroll)
|
||||
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) = doClick(PointerButton.Left, p)
|
||||
fun onStylusDoubleTap(p: PointF) = doDoubleClick(PointerButton.Left, p)
|
||||
fun onStylusLongPress(p: PointF) = doClick(PointerButton.Right, p)
|
||||
fun onStylusScroll(p: PointF) = doPointerButtonDown(PointerButton.Left, p)
|
||||
fun onStylusTap(p: PointF) = directMode.doClick(PointerButton.Left, p)
|
||||
fun onStylusDoubleTap(p: PointF) = directMode.doDoubleClick(PointerButton.Left, p)
|
||||
fun onStylusLongPress(p: PointF) = directMode.doClick(PointerButton.Right, p)
|
||||
fun onStylusScroll(p: PointF) = directMode.doButtonDown(PointerButton.Left, p)
|
||||
|
||||
fun onXKeySym(keySym: Int, isDown: Boolean) = messenger.sendKey(keySym, isDown)
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* Available actions
|
||||
**************************************************************************/
|
||||
|
||||
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()
|
||||
|
||||
private fun doRemoteScroll(focus: PointF, dx: Float, dy: Float) {
|
||||
accumulatedDx += dx
|
||||
accumulatedDy += dy
|
||||
|
||||
//Drain horizontal shift
|
||||
while (abs(accumulatedDx) >= deltaPerScroll) {
|
||||
if (accumulatedDx > 0) {
|
||||
doClick(PointerButton.WheelLeft, focus)
|
||||
accumulatedDx -= deltaPerScroll
|
||||
} else {
|
||||
doClick(PointerButton.WheelRight, focus)
|
||||
accumulatedDx += deltaPerScroll
|
||||
}
|
||||
}
|
||||
|
||||
//Drain vertical shift
|
||||
while (abs(accumulatedDy) >= deltaPerScroll) {
|
||||
if (accumulatedDy > 0) {
|
||||
doClick(PointerButton.WheelUp, focus)
|
||||
accumulatedDy -= deltaPerScroll
|
||||
} else {
|
||||
doClick(PointerButton.WheelDown, focus)
|
||||
accumulatedDy += deltaPerScroll
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun doPointerButtonUp(button: PointerButton, p: PointF) = inFbCoordinates(p) {
|
||||
messenger.sendPointerButtonUp(button, it)
|
||||
}
|
||||
|
||||
private fun doPointerButtonDown(button: PointerButton, p: PointF) = inFbCoordinates(p) {
|
||||
messenger.sendPointerButtonDown(button, it)
|
||||
}
|
||||
|
||||
private fun doPointerButtonRelease(p: PointF) = inFbCoordinates(p) {
|
||||
messenger.sendPointerButtonRelease(it)
|
||||
}
|
||||
|
||||
private fun doClick(button: PointerButton, p: PointF) = inFbCoordinates(p) {
|
||||
messenger.sendClick(button, it)
|
||||
}
|
||||
|
||||
private fun doDoubleClick(button: PointerButton, p: PointF) {
|
||||
doClick(button, p)
|
||||
doClick(button, p)
|
||||
}
|
||||
|
||||
private fun doMovePointer(p: PointF) = doPointerButtonDown(PointerButton.None, p)
|
||||
private fun doDrag(p: PointF) = doPointerButtonDown(PointerButton.Left, p)
|
||||
|
||||
private fun doOpenKeyboard() = activity.showKeyboard()
|
||||
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)
|
||||
|
||||
/**
|
||||
* Positions of input events received from Android are in viewport coordinates.
|
||||
* We need to convert them into corresponding position in framebuffer.
|
||||
* Also, some positions in viewport might not correspond to a valid framebuffer
|
||||
* position (e.g. if zoom is less than 100%).
|
||||
*
|
||||
* This small utility method converts the position [p] into framebuffer
|
||||
* coordinates and invokes [block] with it IF it is valid.
|
||||
* 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
|
||||
* point selection.
|
||||
*/
|
||||
private inline fun inFbCoordinates(p: PointF, block: (PointF) -> Unit) {
|
||||
val fbp = viewModel.frameState.toFb(p)
|
||||
if (fbp != null) {
|
||||
block(fbp)
|
||||
private abstract inner class AbstractMode {
|
||||
//Used for remote scrolling
|
||||
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) }
|
||||
}
|
||||
|
||||
fun doButtonUp(button: PointerButton, p: PointF) {
|
||||
transformPoint(p)?.let { messenger.sendPointerButtonUp(button, it) }
|
||||
}
|
||||
|
||||
fun doButtonRelease(p: PointF) {
|
||||
transformPoint(p)?.let { messenger.sendPointerButtonRelease(it) }
|
||||
}
|
||||
|
||||
fun doClick(button: PointerButton, p: PointF) {
|
||||
doButtonDown(button, p)
|
||||
doButtonUp(button, p)
|
||||
}
|
||||
|
||||
fun doDoubleClick(button: PointerButton, p: PointF) {
|
||||
doClick(button, p)
|
||||
doClick(button, p)
|
||||
}
|
||||
|
||||
fun doRemoteScroll(focus: PointF, dx: Float, dy: Float) {
|
||||
accumulatedDx += dx
|
||||
accumulatedDy += dy * yScrollDirection
|
||||
|
||||
//Drain horizontal change
|
||||
while (abs(accumulatedDx) >= deltaPerScroll) {
|
||||
if (accumulatedDx > 0) {
|
||||
doClick(PointerButton.WheelLeft, focus)
|
||||
accumulatedDx -= deltaPerScroll
|
||||
} else {
|
||||
doClick(PointerButton.WheelRight, focus)
|
||||
accumulatedDx += deltaPerScroll
|
||||
}
|
||||
}
|
||||
|
||||
//Drain vertical change
|
||||
while (abs(accumulatedDy) >= deltaPerScroll) {
|
||||
if (accumulatedDy > 0) {
|
||||
doClick(PointerButton.WheelUp, focus)
|
||||
accumulatedDy -= deltaPerScroll
|
||||
} else {
|
||||
doClick(PointerButton.WheelDown, focus)
|
||||
accumulatedDy += deltaPerScroll
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [hs] Movement of horizontal scroll wheel
|
||||
* [vs] Movement of vertical scroll wheel
|
||||
*/
|
||||
fun doRemoteScrollFromMouse(p: PointF, hs: Float, vs: Float) {
|
||||
doRemoteScroll(p, hs * deltaPerScroll, vs * deltaPerScroll)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions happen at touch-point, which is simply transformed from
|
||||
* viewport coordinates into corresponding position in framebuffer.
|
||||
*/
|
||||
private inner class DirectMode : AbstractMode() {
|
||||
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()
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions happen at [pointerPosition], which is updated by [doMovePointer].
|
||||
*/
|
||||
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 - 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) {
|
||||
doButtonDown(PointerButton.Left, p)
|
||||
doMovePointer(p, dx, dy)
|
||||
}
|
||||
|
||||
override fun doFling(vx: Float, vy: Float) {} //Disabled
|
||||
}
|
||||
}
|
||||
|
|
@ -61,22 +61,29 @@ import kotlin.math.min
|
|||
* Scaling
|
||||
* =======
|
||||
*
|
||||
* Scaling controls the 'size' of rendered frame. Scaling is done in two (conceptual) steps:
|
||||
* Scaling controls the 'size' of rendered frame. It involves multiple factors, like window size,
|
||||
* framebuffer size, user choice etc. To achieve best experience, we split scaling in two parts.
|
||||
* One automatic, and one user controlled.
|
||||
*
|
||||
* 1. Base Scale [baseScale] :
|
||||
* Motivation behind base scale is to start with the most optimal frame size. It is automatically
|
||||
* calculated (and updated) using window size & framebuffer size. When orientation of local
|
||||
* device is such that longer edge of the window is aligned with longer edge of the frame,
|
||||
* base scale will satisfy following constraints (see [calculateBaseScale]):
|
||||
*
|
||||
* This is used to manage the difference between framebuffer size & window size.
|
||||
* At this scale frame will be completely visible inside window (in landscape mode),
|
||||
* and at least one side of frame will match the corresponding side of window.
|
||||
* - Frame is completely visible
|
||||
* - Frame's aspect ratio is maintained
|
||||
* - Maximum window space is utilized
|
||||
*
|
||||
* 2. Zoom Scale [zoomScale] :
|
||||
* This is the user controlled part. It always starts at 1 (100% zoom), and only updated in response
|
||||
* to pinch gestures. Conceptually, it works 'on top of' the base scale.
|
||||
*
|
||||
* This value represents the 'zoom level' requested by user (through pinch gestures).
|
||||
* It works 'on top of' base scale.
|
||||
*
|
||||
* Effective scale [scale] is calculated as the product of these two values.
|
||||
* Effective scale [scale] is calculated as the product of these two parts, so:
|
||||
|
||||
* FrameSize = (FramebufferSize * BaseScale) * ZoomScale
|
||||
*
|
||||
* So: FrameSize = (FramebufferSize * BaseScale) * ZoomScale
|
||||
*
|
||||
* Thread safety
|
||||
* =============
|
||||
|
|
@ -187,16 +194,22 @@ 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
|
||||
|
||||
//Base scale is calculated as-if we are in 'Landscape' mode.
|
||||
//So larger window side is treated as width and smaller as height
|
||||
val wRatio = max(windowWidth, windowHeight) / fbWidth
|
||||
val hRatio = min(windowWidth, windowHeight) / fbHeight
|
||||
val s1 = max(windowWidth, windowHeight) / max(fbWidth, fbHeight)
|
||||
val s2 = min(windowWidth, windowHeight) / min(fbWidth, fbHeight)
|
||||
|
||||
baseScale = min(wRatio, hRatio)
|
||||
baseScale = min(s1, s2)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -211,8 +211,7 @@ class KeyHandler(private val dispatcher: Dispatcher, private val compatMode: Boo
|
|||
if (keySym == 0)
|
||||
return false
|
||||
|
||||
dispatcher.onXKeySym(keySym, isDown)
|
||||
return true
|
||||
return dispatcher.onXKeySym(keySym, isDown)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -52,12 +56,17 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
|
|||
|
||||
private fun handleGestureStartStop(event: MotionEvent) {
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> dispatcher.onGestureStart(event.point())
|
||||
MotionEvent.ACTION_DOWN -> dispatcher.onGestureStart()
|
||||
MotionEvent.ACTION_UP,
|
||||
MotionEvent.ACTION_CANCEL -> dispatcher.onGestureStop(event.point())
|
||||
}
|
||||
}
|
||||
|
||||
// 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,8 +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)
|
||||
|
|
@ -162,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
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +209,7 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
|
|||
override fun onLongPress(e: MotionEvent) {
|
||||
viewModel.frameViewRef.get()?.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
|
||||
if (dragDetector.dragEnabled)
|
||||
if (dragEnabled)
|
||||
dragDetector.onLongPress(e)
|
||||
else
|
||||
dispatcher.onLongPress(e.point())
|
||||
|
|
@ -192,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()
|
||||
|
|
@ -211,54 +243,61 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
|
|||
*
|
||||
* Detection:
|
||||
*
|
||||
* 1. Wait for the long-press before doing anything.
|
||||
* 1. Wait for the long-press from [gestureDetector] before doing anything.
|
||||
* 2. Once long-press is detected, start looking at incoming events.
|
||||
* 3. If [MotionEvent.ACTION_UP] is received next, send long-press to dispatcher.
|
||||
* 4. If [MotionEvent.ACTION_MOVE] is received instead, start dragging.
|
||||
* 5. If [MotionEvent.ACTION_UP] is received WHILE dragging, stop dragging.
|
||||
* 6. In another finger goes down, or gesture is canceled, stop dragging.
|
||||
* 3. If [MotionEvent.ACTION_UP] is received next, send long-press.
|
||||
* 4. If scroll event is received from [scrollDetector], start dragging.
|
||||
* 5. Reset, if gesture is finished, or another finger goes down.
|
||||
*
|
||||
* Instead of using our own [GestureDetector], we rely on [gestureDetector]
|
||||
* for long-press detection.
|
||||
* But, if long-press detection is enabled for a [GestureDetector] object, it will not report
|
||||
* scroll events after long-press. So, we rely on [gestureDetector] for long-press detection,
|
||||
* and use a separate object [scrollDetector] for scroll events.
|
||||
*
|
||||
* Left mouse button pressed during dragging is automatically released
|
||||
* by [Dispatcher.onGestureStop].
|
||||
*/
|
||||
private inner class DragDetector {
|
||||
val dragEnabled = viewModel.pref.input.gesture.dragEnabled
|
||||
private var longPressDetected = false
|
||||
private var isDragging = false
|
||||
private var startPoint = PointF()
|
||||
private var lastPoint = PointF()
|
||||
private val scrollDetector = GestureDetector(viewModel.app, ScrollListener()).apply {
|
||||
setIsLongpressEnabled(false)
|
||||
}
|
||||
|
||||
fun onLongPress(e: MotionEvent): Boolean {
|
||||
private inner class ScrollListener : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onScroll(e1: MotionEvent, e2: MotionEvent, dx: Float, dy: Float): Boolean {
|
||||
if (longPressDetected) {
|
||||
if (!isDragging) {
|
||||
isDragging = true
|
||||
|
||||
//Send first drag event at startPoint, otherwise drag gesture
|
||||
//will start off by (at-least) touch-slope used by GestureDetector
|
||||
dispatcher.onDrag(startPoint, startPoint, 0f, 0f)
|
||||
}
|
||||
|
||||
dispatcher.onDrag(startPoint, e2.point(), -dx, -dy)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
fun onLongPress(e: MotionEvent) {
|
||||
longPressDetected = true
|
||||
startPoint = e.point()
|
||||
lastPoint = startPoint
|
||||
return true
|
||||
}
|
||||
|
||||
fun onTouchEvent(event: MotionEvent) {
|
||||
if (!dragEnabled || !longPressDetected)
|
||||
if (!dragEnabled)
|
||||
return
|
||||
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
isDragging = true
|
||||
scrollDetector.onTouchEvent(event)
|
||||
|
||||
val cp = event.point()
|
||||
dispatcher.onDrag(startPoint, cp, cp.x - lastPoint.x, cp.y - lastPoint.y)
|
||||
lastPoint = cp
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
if (!isDragging)
|
||||
dispatcher.onLongPress(event.point())
|
||||
|
||||
longPressDetected = false
|
||||
isDragging = false
|
||||
}
|
||||
val action = event.actionMasked
|
||||
if (action == MotionEvent.ACTION_UP && longPressDetected && !isDragging)
|
||||
dispatcher.onLongPress(event.point())
|
||||
|
||||
when (action) {
|
||||
MotionEvent.ACTION_UP,
|
||||
MotionEvent.ACTION_POINTER_DOWN,
|
||||
MotionEvent.ACTION_CANCEL -> {
|
||||
longPressDetected = false
|
||||
|
|
@ -311,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]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -33,8 +31,8 @@ import com.gaurav.avnc.R
|
|||
import com.gaurav.avnc.databinding.ActivityVncBinding
|
||||
import com.gaurav.avnc.model.ServerProfile
|
||||
import com.gaurav.avnc.util.Experimental
|
||||
import com.gaurav.avnc.util.SamsungDex
|
||||
import com.gaurav.avnc.viewmodel.VncViewModel
|
||||
import com.gaurav.avnc.vnc.VncClient
|
||||
import com.gaurav.avnc.vnc.VncUri
|
||||
import kotlinx.coroutines.delay
|
||||
import java.lang.ref.WeakReference
|
||||
|
|
@ -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.clientState.observe(this) { onClientStateChanged(it) }
|
||||
viewModel.initConnection(profile) //Should be called after observers has been setup
|
||||
viewModel.state.observe(this) { onClientStateChanged(it) }
|
||||
}
|
||||
|
||||
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() {
|
||||
|
|
@ -156,19 +157,23 @@ class VncActivity : AppCompatActivity() {
|
|||
|
||||
private fun closeDrawers() = binding.drawerLayout.closeDrawers()
|
||||
|
||||
private fun onClientStateChanged(newState: VncClient.State) {
|
||||
if (newState == VncClient.State.Connected) {
|
||||
private fun onClientStateChanged(newState: VncViewModel.State) {
|
||||
if (newState == VncViewModel.State.Connected) {
|
||||
|
||||
if (!viewModel.pref.runInfo.hasConnectedSuccessfully) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
SamsungDex.setMetaKeyCapture(this, true)
|
||||
} else {
|
||||
SamsungDex.setMetaKeyCapture(this, false)
|
||||
}
|
||||
|
||||
updateSystemUiVisibility()
|
||||
|
|
@ -183,6 +188,8 @@ class VncActivity : AppCompatActivity() {
|
|||
|
||||
private fun setupLayout() {
|
||||
|
||||
setupOrientation()
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
if (fullscreenMode) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
|
|
@ -222,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)
|
||||
|
||||
|
|
@ -234,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -249,7 +283,7 @@ class VncActivity : AppCompatActivity() {
|
|||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
|
||||
window.decorView.apply {
|
||||
if (viewModel.clientState.value == VncClient.State.Connected)
|
||||
if (viewModel.client.connected)
|
||||
systemUiVisibility = systemUiVisibility or flags
|
||||
else
|
||||
systemUiVisibility = systemUiVisibility and flags.inv()
|
||||
|
|
@ -281,12 +315,18 @@ class VncActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun enterPiPMode() {
|
||||
val canEnter = viewModel.pref.viewer.pipEnabled && viewModel.client.state == VncClient.State.Connected
|
||||
val canEnter = viewModel.pref.viewer.pipEnabled && viewModel.client.connected
|
||||
|
||||
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 {
|
||||
|
|
@ -302,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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import android.opengl.GLES20.*
|
|||
import android.opengl.GLSurfaceView
|
||||
import android.opengl.Matrix
|
||||
import com.gaurav.avnc.viewmodel.VncViewModel
|
||||
import com.gaurav.avnc.vnc.VncClient
|
||||
import javax.microedition.khronos.egl.EGLConfig
|
||||
import javax.microedition.khronos.opengles.GL10
|
||||
|
||||
|
|
@ -23,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
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ class Renderer(val viewModel: VncViewModel) : GLSurfaceView.Renderer {
|
|||
override fun onDrawFrame(gl: GL10?) {
|
||||
glClear(GL_COLOR_BUFFER_BIT)
|
||||
|
||||
if (viewModel.client.state != VncClient.State.Connected)
|
||||
if (!viewModel.client.connected)
|
||||
return
|
||||
|
||||
if (state.vpWidth == 0f || state.vpHeight == 0f)
|
||||
|
|
@ -87,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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ import androidx.preference.PreferenceManager
|
|||
|
||||
/**
|
||||
* Utility class for accessing app preferences
|
||||
*
|
||||
* - All timeouts are in milliseconds.
|
||||
*/
|
||||
class AppPreferences(context: Context) {
|
||||
|
||||
|
|
@ -29,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")
|
||||
|
|
@ -37,7 +36,8 @@ class AppPreferences(context: Context) {
|
|||
}
|
||||
|
||||
inner class Gesture {
|
||||
val tap1; get() = prefs.getString("gesture_single_tap", "left-click")!!
|
||||
val directTouch; get() = prefs.getBoolean("gesture_direct_touch", true)
|
||||
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")!!
|
||||
|
|
@ -45,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 {
|
||||
|
|
@ -55,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)
|
||||
|
|
@ -62,19 +67,15 @@ class AppPreferences(context: Context) {
|
|||
}
|
||||
|
||||
inner class Server {
|
||||
val discoveryTimeout; get() = prefs.getInt("discovery_timeout", 10) * 1000L
|
||||
val discoveryAutoStart; get() = prefs.getBoolean("discovery_auto_start", true)
|
||||
val discoveryRestart; get() = prefs.getBoolean("discovery_restart", false)
|
||||
val discoveryRestartDelay; get() = prefs.getInt("discovery_restart_delay", 5) * 60 * 1000L
|
||||
|
||||
val credAutocomplete; get() = prefs.getBoolean("cred_autocomplete", true)
|
||||
val clipboardSync; get() = prefs.getBoolean("clipboard_sync", true)
|
||||
val discoveryAutorun; get() = prefs.getBoolean("discovery_autorun", true)
|
||||
val rediscoveryIndicator = LivePref("rediscovery_indicator", true)
|
||||
}
|
||||
|
||||
inner class Experimental {
|
||||
val indicator; get() = prefs.getBoolean("experimental_indicator", false)
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -109,9 +110,14 @@ class AppPreferences(context: Context) {
|
|||
updateValue()
|
||||
}
|
||||
|
||||
init {
|
||||
updateValue() //Initial load
|
||||
prefs.registerOnSharedPreferenceChangeListener(prefChangeListener)
|
||||
private var initialized = false
|
||||
|
||||
override fun onActive() {
|
||||
if (!initialized) {
|
||||
initialized = true
|
||||
updateValue()
|
||||
prefs.registerOnSharedPreferenceChangeListener(prefChangeListener)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateValue() {
|
||||
|
|
|
|||
|
|
@ -8,148 +8,18 @@
|
|||
|
||||
package com.gaurav.avnc.util
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.util.Log
|
||||
import android.view.GestureDetector
|
||||
import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.annotation.Keep
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.OnLifecycleEvent
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gaurav.avnc.databinding.ServerSavedBinding
|
||||
import com.gaurav.avnc.databinding.ServerSavedItemBinding
|
||||
import com.gaurav.avnc.model.ServerProfile
|
||||
import com.gaurav.avnc.ui.home.ServerTabs.SavedServerAdapter
|
||||
|
||||
/**
|
||||
* All the experimental stuff is dumped here.
|
||||
*/
|
||||
object Experimental {
|
||||
|
||||
/**
|
||||
* This feature allows us to show indicator (small pulsating circle) for already
|
||||
* saved profiles if we find the same server in Discovery. Main benefit is to
|
||||
* immediately know about reachable servers.
|
||||
*
|
||||
* Why Experimental:
|
||||
* - For this to be most useful the user needs to run Discovery frequently.
|
||||
* Otherwise, indicators will become stale.
|
||||
* - Because of Discovery & animation we will consume slightly more battery.
|
||||
*
|
||||
* One other approach for this implementation is to have a field in [ServerProfile],
|
||||
* update that field in [com.gaurav.avnc.viewmodel.HomeViewModel] and let Data Binding
|
||||
* automatically render the indicator. But that approach has some disadvantages:
|
||||
*
|
||||
* - It complicates code in other classes too much.
|
||||
* - Because each Indicator view has an independent animator, they pulse out of sync.
|
||||
* - I simply don't like that approach.
|
||||
*/
|
||||
class Indicator {
|
||||
|
||||
/**
|
||||
* Local reference to discovered servers.
|
||||
*/
|
||||
private var discoveredServers: List<ServerProfile>? = null
|
||||
|
||||
/**
|
||||
* Maps each [ServerSavedItemBinding.indicator] view to the server profile being
|
||||
* rendered by its container.
|
||||
*/
|
||||
private val profileMap = HashMap<View, ServerProfile>(5)
|
||||
|
||||
/**
|
||||
* Maps each [ServerSavedItemBinding.indicator] view to its availability state.
|
||||
* If state is true, the view will be animated otherwise it will be hidden.
|
||||
*/
|
||||
private val animatorMap = HashMap<View, Boolean>(5)
|
||||
|
||||
/**
|
||||
* Animator for 'alpha' property of indicator views.
|
||||
*/
|
||||
private val animator = ValueAnimator.ofFloat(0F, 1F).apply {
|
||||
duration = 1000
|
||||
repeatCount = ValueAnimator.INFINITE
|
||||
repeatMode = ValueAnimator.REVERSE
|
||||
addUpdateListener { onAnimationUpdate() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Indicator support.
|
||||
*
|
||||
* Note: Viewmodel & lifecycle must have been assigned to [binding].
|
||||
*/
|
||||
fun setup(binding: ServerSavedBinding, adapter: SavedServerAdapter) {
|
||||
Log.i(javaClass.simpleName, "Setting up Experimental Indicator")
|
||||
|
||||
// There are 4 event sources which we are interested in:
|
||||
|
||||
// 1 - Discovery: Need to know which items should show indicator.
|
||||
binding.viewModel!!.discovery.servers.observe(binding.lifecycleOwner!!) {
|
||||
discoveredServers = it
|
||||
|
||||
for ((v, p) in profileMap)
|
||||
animatorMap[v] = isServerDiscovered(p)
|
||||
|
||||
updateAnimator()
|
||||
}
|
||||
|
||||
// 2 - RV Binding: We need to know which RV Item View is rendering which profile.
|
||||
adapter.bindListener = { holder, profile ->
|
||||
val view = holder.binding.indicator
|
||||
profileMap[view] = profile
|
||||
animatorMap[view] = isServerDiscovered(profile)
|
||||
}
|
||||
|
||||
// 3 - RV Views: We need to clear Views which are no longer used by RecyclerView.
|
||||
binding.serversRv.addOnChildAttachStateChangeListener(object : RecyclerView.OnChildAttachStateChangeListener {
|
||||
override fun onChildViewAttachedToWindow(view: View) {}
|
||||
override fun onChildViewDetachedFromWindow(view: View) {
|
||||
profileMap.remove(view)
|
||||
animatorMap.remove(view)
|
||||
}
|
||||
})
|
||||
|
||||
// 4 - Activity Lifecycle: We don't want to run animation if our activity is not in front.
|
||||
binding.lifecycleOwner!!.lifecycle.addObserver(object : LifecycleObserver {
|
||||
@Keep
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_START)
|
||||
fun onStart() = updateAnimator()
|
||||
|
||||
@Keep
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
|
||||
fun onStop() = animator.end()
|
||||
})
|
||||
}
|
||||
|
||||
private fun isServerDiscovered(p: ServerProfile): Boolean {
|
||||
return discoveredServers?.find { p.host == it.host && p.port == it.port } != null
|
||||
}
|
||||
|
||||
private fun updateAnimator() {
|
||||
if (discoveredServers?.isNotEmpty() == true) {
|
||||
if (!animator.isStarted)
|
||||
animator.start()
|
||||
} else
|
||||
animator.end()
|
||||
}
|
||||
|
||||
private fun onAnimationUpdate() {
|
||||
for ((v, animate) in animatorMap) {
|
||||
if (animate) {
|
||||
v.alpha = animator.animatedValue as Float
|
||||
v.visibility = View.VISIBLE
|
||||
} else {
|
||||
v.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Normally, drawers in [DrawerLayout] are closed by two gestures:
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
53
app/src/main/java/com/gaurav/avnc/util/SamsungDex.kt
Normal file
53
app/src/main/java/com/gaurav/avnc/util/SamsungDex.kt
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Gaurav Ujjwal.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* See COPYING.txt for more details.
|
||||
*/
|
||||
|
||||
package com.gaurav.avnc.util
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
|
||||
/**
|
||||
* Utilities related to Samsung DeX
|
||||
*/
|
||||
object SamsungDex {
|
||||
|
||||
/**
|
||||
* Returns true, if DeX mode is enabled.
|
||||
*/
|
||||
private fun isInDexMode(context: Context) = runCatching {
|
||||
val config = context.resources.configuration
|
||||
val configClass = config.javaClass
|
||||
|
||||
val flag = configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass)
|
||||
val value = configClass.getField("semDesktopModeEnabled").getInt(config)
|
||||
|
||||
value == flag
|
||||
}.getOrDefault(false)
|
||||
|
||||
|
||||
/**
|
||||
* Enables/disables meta-key event capturing.
|
||||
*/
|
||||
fun setMetaKeyCapture(activity: Activity, isEnabled: Boolean) {
|
||||
if (!isInDexMode(activity))
|
||||
return
|
||||
|
||||
runCatching {
|
||||
val managerClass = Class.forName("com.samsung.android.view.SemWindowManager")
|
||||
val instanceMethod = managerClass.getMethod("getInstance")
|
||||
val manager = instanceMethod.invoke(null)
|
||||
|
||||
val requestMethod = managerClass.getDeclaredMethod("requestMetaKeyEvent",
|
||||
ComponentName::class.java,
|
||||
Boolean::class.java)
|
||||
requestMethod.invoke(manager, activity.componentName, isEnabled)
|
||||
}.onFailure { Log.d("DeX Support", "Meta key capture error", it) }
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -9,12 +9,11 @@
|
|||
package com.gaurav.avnc.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.gaurav.avnc.model.ServerProfile
|
||||
import com.gaurav.avnc.vnc.Discovery
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class HomeViewModel(app: Application) : BaseViewModel(app) {
|
||||
|
||||
|
|
@ -57,27 +56,37 @@ class HomeViewModel(app: Application) : BaseViewModel(app) {
|
|||
*/
|
||||
val profileDeletedEvent = LiveEvent<ServerProfile>()
|
||||
|
||||
|
||||
init {
|
||||
if (pref.server.discoveryAutoStart)
|
||||
startDiscovery()
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts new connection to given profile.
|
||||
*/
|
||||
fun startConnection(profile: ServerProfile) = newConnectionEvent.fire(profile)
|
||||
|
||||
/**
|
||||
* Starts discovery service.
|
||||
* It will be automatically stopped after timeout.
|
||||
*/
|
||||
fun startDiscovery() {
|
||||
discovery.start()
|
||||
/**************************************************************************
|
||||
* Server Discovery
|
||||
*
|
||||
* To save battery, Discovery is stopped when HomeActivity is in background.
|
||||
**************************************************************************/
|
||||
private var autoStopped = false
|
||||
|
||||
viewModelScope.launch {
|
||||
runCatching { delay(pref.server.discoveryTimeout) }
|
||||
discovery.stop()
|
||||
fun startDiscovery() {
|
||||
autoStopped = false
|
||||
discovery.start()
|
||||
}
|
||||
|
||||
fun stopDiscovery() {
|
||||
autoStopped = false
|
||||
discovery.stop()
|
||||
}
|
||||
|
||||
fun autoStartDiscovery() {
|
||||
if (pref.server.discoveryAutorun || autoStopped)
|
||||
startDiscovery()
|
||||
}
|
||||
|
||||
fun autoStopDiscovery() {
|
||||
if (discovery.isRunning.value == true) {
|
||||
stopDiscovery()
|
||||
autoStopped = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,4 +125,29 @@ class HomeViewModel(app: Application) : BaseViewModel(app) {
|
|||
fun deleteProfile(profile: ServerProfile) = asyncIO({ serverProfileDao.delete(profile) }, {
|
||||
profileDeletedEvent.fire(profile)
|
||||
})
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* Rediscovery Indicator
|
||||
*
|
||||
* [rediscoveredProfiles] is the intersection of saved & discovered servers.
|
||||
*
|
||||
* To detect reachable server in [serverProfiles], we could directly 'ping'
|
||||
* them, but that has its own issues.
|
||||
**************************************************************************/
|
||||
val rediscoveredProfiles by lazy {
|
||||
Transformations.switchMap(pref.server.rediscoveryIndicator) {
|
||||
if (it) prepareRediscoveredProfiles()
|
||||
else MutableLiveData(null)
|
||||
}!!
|
||||
}
|
||||
|
||||
private fun prepareRediscoveredProfiles() = with(MediatorLiveData<List<ServerProfile>>()) {
|
||||
val filter = { saved: List<ServerProfile>?, discovered: List<ServerProfile>? ->
|
||||
saved?.filter { s -> discovered?.find { s.host == it.host && s.port == it.port } != null }
|
||||
}
|
||||
addSource(serverProfiles) { value = filter(it, discovery.servers.value) }
|
||||
addSource(discovery.servers) { value = filter(serverProfiles.value, it) }
|
||||
this
|
||||
}
|
||||
}
|
||||
|
|
@ -65,23 +65,47 @@ import java.lang.ref.WeakReference
|
|||
*/
|
||||
class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
|
||||
|
||||
/**
|
||||
* Connection lifecycle:
|
||||
*
|
||||
* Created
|
||||
* |
|
||||
* v
|
||||
* Connecting ----------+
|
||||
* | |
|
||||
* v |
|
||||
* Connected |
|
||||
* | |
|
||||
* v |
|
||||
* Disconnected <-------+
|
||||
*
|
||||
*/
|
||||
enum class State {
|
||||
Created,
|
||||
Connecting,
|
||||
Connected,
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
val client = VncClient(this)
|
||||
|
||||
/**
|
||||
* Client state is exposed as live data here to allow dynamic data binding
|
||||
* from layouts.
|
||||
*/
|
||||
val clientState = MutableLiveData(client.state)
|
||||
|
||||
/**
|
||||
* Reason for [clientState] being [VncClient.State.Disconnected].
|
||||
*/
|
||||
val disconnectReason = MutableLiveData("")
|
||||
|
||||
/**
|
||||
* [ServerProfile] used for current connection.
|
||||
*/
|
||||
var profile = ServerProfile()
|
||||
var profile = ServerProfile(); private set
|
||||
|
||||
/**
|
||||
* We have two places for connection state (both are synced):
|
||||
*
|
||||
* [VncClient.connected] - Simple boolean state, used most of the time
|
||||
* [state] - More granular, used by observers & data binding
|
||||
*/
|
||||
val state = MutableLiveData(State.Created)
|
||||
|
||||
/**
|
||||
* Reason for disconnecting.
|
||||
*/
|
||||
val disconnectReason = MutableLiveData("")
|
||||
|
||||
/**
|
||||
* Fired when [VncClient] has asked for credential. It is used to
|
||||
|
|
@ -141,23 +165,16 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
|
|||
* Connection management
|
||||
**************************************************************************/
|
||||
|
||||
/**
|
||||
* Because [initConnection] can be called multiple times due to activity restarts,
|
||||
* initialization inside it is performed only once.
|
||||
*/
|
||||
private var initialized = false
|
||||
|
||||
/**
|
||||
* Initialize VNC connection using given profile.
|
||||
* It may be called multiple times due to activity restarts.
|
||||
*/
|
||||
fun initConnection(profile: ServerProfile) {
|
||||
if (initialized)
|
||||
return
|
||||
|
||||
initialized = true
|
||||
this.profile = profile
|
||||
|
||||
launchConnection()
|
||||
if (state.value == State.Created) {
|
||||
state.value = State.Connecting
|
||||
this.profile = profile
|
||||
launchConnection()
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchConnection() {
|
||||
|
|
@ -170,12 +187,12 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
|
|||
processMessages()
|
||||
|
||||
}.onFailure {
|
||||
client.disconnect()
|
||||
if (it is IOException)
|
||||
disconnectReason.postValue(it.message)
|
||||
if (it is IOException) disconnectReason.postValue(it.message)
|
||||
Log.e("ReceiverCoroutine", "Connection failed", it)
|
||||
}
|
||||
|
||||
state.postValue(State.Disconnected)
|
||||
|
||||
//Wait until activity is finished and viewmodel is cleaned up.
|
||||
runCatching { awaitCancellation() }
|
||||
cleanup()
|
||||
|
|
@ -183,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)
|
||||
|
|
@ -203,16 +220,13 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
|
|||
else -> throw IOException("Unknown Channel: ${profile.channelType}")
|
||||
}
|
||||
|
||||
if (client.state != VncClient.State.Connected)
|
||||
throw IOException(client.getLastErrorStr())
|
||||
state.postValue(State.Connected)
|
||||
sendClipboardText() //Initial sync
|
||||
}
|
||||
|
||||
private fun processMessages() {
|
||||
while (viewModelScope.isActive && client.processServerMessage()) {
|
||||
//Message Loop
|
||||
}
|
||||
|
||||
disconnectReason.postValue(client.getLastErrorStr())
|
||||
while (viewModelScope.isActive)
|
||||
client.processServerMessage()
|
||||
}
|
||||
|
||||
private fun cleanup() {
|
||||
|
|
@ -315,16 +329,13 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
|
|||
receiveClipboardText(text)
|
||||
}
|
||||
|
||||
override fun onClientStateChanged(newState: VncClient.State) {
|
||||
clientState.postValue(newState)
|
||||
|
||||
if (newState == VncClient.State.Connected)
|
||||
sendClipboardText() //Initial sync
|
||||
}
|
||||
|
||||
override fun onFramebufferSizeChanged(width: Int, height: Int) {
|
||||
viewModelScope.launch(Dispatchers.Main) {
|
||||
frameState.setFramebufferSize(width.toFloat(), height.toFloat())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPointerMoved(x: Int, y: Int) {
|
||||
frameViewRef.get()?.requestRender()
|
||||
}
|
||||
}
|
||||
|
|
@ -76,14 +76,15 @@ class Discovery(private val context: Context) {
|
|||
}
|
||||
|
||||
isRunning.value = true
|
||||
servers.value = ArrayList() //Forget known servers
|
||||
if (servers.value?.size != 0) servers.value = ArrayList() //Forget known servers
|
||||
|
||||
// Construction of NSD manager is done on a background thread because it appears to be quite heavy.
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -101,11 +103,11 @@ class Discovery(private val context: Context) {
|
|||
* Adds a new profile with given details to list.
|
||||
*/
|
||||
private fun addProfile(name: String, host: String, port: Int) {
|
||||
val profile = ServerProfile().apply {
|
||||
this.name = name
|
||||
this.host = host
|
||||
this.port = port
|
||||
}
|
||||
val profile = ServerProfile(
|
||||
name = name,
|
||||
host = host,
|
||||
port = port
|
||||
)
|
||||
|
||||
runBlocking(Dispatchers.Main) {
|
||||
val currentList = servers.value!!
|
||||
|
|
@ -168,7 +170,6 @@ class Discovery(private val context: Context) {
|
|||
*/
|
||||
private inner class ResolveListener : NsdManager.ResolveListener {
|
||||
override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
|
||||
Log.d(javaClass.simpleName, "Resolved service: ${serviceInfo.serviceName.take(3)}*******")
|
||||
addProfile(serviceInfo.serviceName, serviceInfo.host.hostAddress, serviceInfo.port)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
|
||||
|
|
@ -77,13 +78,12 @@ class Messenger(private val client: VncClient) {
|
|||
}
|
||||
}
|
||||
|
||||
fun sendClick(button: PointerButton, p: PointF) {
|
||||
sendPointerButtonDown(button, p)
|
||||
sendPointerButtonUp(button, p)
|
||||
}
|
||||
fun sendKey(keySym: Int, isDown: Boolean): Boolean {
|
||||
if (!client.connected)
|
||||
return false
|
||||
|
||||
fun sendKey(keySym: Int, isDown: Boolean) {
|
||||
execute { client.sendKeyEvent(keySym, isDown) }
|
||||
return true
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
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
|
||||
|
||||
|
|
@ -29,39 +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) {
|
||||
|
||||
/**
|
||||
* [VncClient] Lifecycle:
|
||||
*
|
||||
* Created ---------------+
|
||||
* | |
|
||||
* v |
|
||||
* Connecting ----------+ |
|
||||
* | | |
|
||||
* v | |
|
||||
* Connected | |
|
||||
* | | |
|
||||
* v | |
|
||||
* Disconnected <-------+ |
|
||||
* | |
|
||||
* v |
|
||||
* Destroyed <-------------+
|
||||
*/
|
||||
enum class State {
|
||||
Created,
|
||||
Connecting,
|
||||
Connected,
|
||||
Disconnected,
|
||||
Destroyed
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -69,10 +43,9 @@ class VncClient(private val observer: Observer) {
|
|||
fun onGotXCutText(text: String)
|
||||
fun onFramebufferUpdated()
|
||||
fun onFramebufferSizeChanged(width: Int, height: Int)
|
||||
fun onClientStateChanged(newState: State)
|
||||
fun onPointerMoved(x: Int, y: Int)
|
||||
|
||||
//fun onBell()
|
||||
//fun onCursorMoved(x: Int, y: Int): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -86,14 +59,9 @@ class VncClient(private val observer: Observer) {
|
|||
throw RuntimeException("Could not create native rfbClient!")
|
||||
}
|
||||
|
||||
/**
|
||||
* Current client state.
|
||||
*/
|
||||
var state: State = State.Created
|
||||
private set(value) {
|
||||
field = value
|
||||
observer.onClientStateChanged(value)
|
||||
}
|
||||
@Volatile
|
||||
var connected = false
|
||||
private set
|
||||
|
||||
/**
|
||||
* Name of remote desktop
|
||||
|
|
@ -110,16 +78,33 @@ 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.
|
||||
* Only valid in [State.Connected] state.
|
||||
*
|
||||
* @param securityType RFB security type to use.
|
||||
*/
|
||||
fun configure(viewOnly: Boolean, securityType: Int, useLocalCursor: Boolean) {
|
||||
if (state != State.Created)
|
||||
return
|
||||
|
||||
viewOnlyMode = viewOnly
|
||||
nativeConfigure(nativePtr, securityType, useLocalCursor)
|
||||
}
|
||||
|
|
@ -130,42 +115,25 @@ class VncClient(private val observer: Observer) {
|
|||
|
||||
/**
|
||||
* Initializes VNC connection.
|
||||
*
|
||||
* @return true if initialization was successful
|
||||
*/
|
||||
fun connect(host: String, port: Int): Boolean {
|
||||
if (state != State.Created)
|
||||
return false
|
||||
|
||||
state = State.Connecting
|
||||
|
||||
if (nativeInit(nativePtr, host, port)) {
|
||||
state = State.Connected
|
||||
return true
|
||||
} else {
|
||||
state = State.Disconnected
|
||||
return false
|
||||
}
|
||||
fun connect(host: String, port: Int) {
|
||||
connected = nativeInit(nativePtr, host, port)
|
||||
if (!connected) throw IOException(nativeGetLastErrorStr())
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for incoming server message, parses it and then invokes appropriate
|
||||
* callbacks.
|
||||
* Waits for incoming server message, parses it and then invokes appropriate callbacks.
|
||||
*
|
||||
* @param uSecTimeout Timeout in microseconds.
|
||||
* @return true if message was successfully handled or no message was received within timeout,
|
||||
* false otherwise.
|
||||
*/
|
||||
fun processServerMessage(uSecTimeout: Int = 1000000): Boolean {
|
||||
if (state != State.Connected)
|
||||
return false
|
||||
fun processServerMessage(uSecTimeout: Int = 1000000) {
|
||||
if (!connected)
|
||||
return
|
||||
|
||||
if (!nativeProcessServerMessage(nativePtr, uSecTimeout)) {
|
||||
disconnect() //Reason: Msg processing will only fail on irrecoverable errors
|
||||
return false
|
||||
connected = false
|
||||
throw IOException(nativeGetLastErrorStr())
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -189,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.
|
||||
|
|
@ -209,30 +197,16 @@ class VncClient(private val observer: Observer) {
|
|||
fun uploadFrameTexture() = nativeUploadFrameTexture(nativePtr)
|
||||
|
||||
/**
|
||||
* Marks this client as 'disconnected'.
|
||||
*
|
||||
* This doesn't actually release the resources from server but
|
||||
* simply updates the state. [cleanup] is used for that.
|
||||
* Upload cursor shape into framebuffer texture.
|
||||
*/
|
||||
fun disconnect() {
|
||||
state = State.Disconnected
|
||||
}
|
||||
fun uploadCursor() = nativeUploadCursor(nativePtr, pointerX, pointerY)
|
||||
|
||||
/**
|
||||
* Returns a string describing last error.
|
||||
* Only valid in disconnected state.
|
||||
*/
|
||||
fun getLastErrorStr() = nativeGetLastErrorStr()
|
||||
|
||||
/**
|
||||
* Releases all resource (native & managed) currently held.
|
||||
* After cleanup, this client MUST NOT be used anymore.
|
||||
* Release all resources allocated by the client.
|
||||
* DO NOT use this client after [cleanup].
|
||||
*/
|
||||
fun cleanup() {
|
||||
if (state == State.Destroyed)
|
||||
return
|
||||
|
||||
state = State.Destroyed
|
||||
connected = false
|
||||
nativeCleanup(nativePtr)
|
||||
}
|
||||
|
||||
|
|
@ -241,7 +215,7 @@ class VncClient(private val observer: Observer) {
|
|||
* events to remote server.
|
||||
*/
|
||||
private inline fun executeSend(block: () -> Unit) {
|
||||
if (state == State.Connected && !viewOnlyMode)
|
||||
if (connected && !viewOnlyMode)
|
||||
block()
|
||||
}
|
||||
|
||||
|
|
@ -259,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)
|
||||
|
||||
|
|
@ -284,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)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -155,8 +155,8 @@ object XKeySymAndroid {
|
|||
XKeySym.XK_Control_R, // KEYCODE_CTRL_RIGHT = 114
|
||||
XKeySym.XK_Caps_Lock, // KEYCODE_CAPS_LOCK = 115
|
||||
XKeySym.XK_Scroll_Lock, // KEYCODE_SCROLL_LOCK = 116
|
||||
XKeySym.XK_Meta_L, // KEYCODE_META_LEFT = 117
|
||||
XKeySym.XK_Meta_R, // KEYCODE_META_RIGHT = 118
|
||||
XKeySym.XK_Super_L, // KEYCODE_META_LEFT = 117
|
||||
XKeySym.XK_Super_R, // KEYCODE_META_RIGHT = 118
|
||||
0, // KEYCODE_FUNCTION = 119
|
||||
XKeySym.XK_Sys_Req, // KEYCODE_SYSRQ = 120
|
||||
XKeySym.XK_Break, // KEYCODE_BREAK = 121
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
<data>
|
||||
|
||||
<import type="com.gaurav.avnc.vnc.VncClient.State" />
|
||||
<import type="com.gaurav.avnc.viewmodel.VncViewModel.State" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:isVisible="@{viewModel.clientState == State.Connected}">
|
||||
app:isVisible="@{viewModel.state == State.Connected}">
|
||||
|
||||
|
||||
<!--FrameView-->
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?colorSurface"
|
||||
app:isVisible="@{viewModel.clientState != State.Connected}">
|
||||
app:isVisible="@{viewModel.state != State.Connected}">
|
||||
|
||||
<ProgressBar
|
||||
style="@style/Widget.AppCompat.ProgressBar"
|
||||
|
|
@ -107,7 +107,7 @@
|
|||
android:layout_height="60dp"
|
||||
android:indeterminate="true"
|
||||
android:padding="@dimen/padding_small"
|
||||
app:isVisible="@{viewModel.clientState != State.Disconnected}"
|
||||
app:isVisible="@{viewModel.state == State.Connecting}"
|
||||
app:layout_constraintBottom_toTopOf="@id/client_state"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
|
@ -117,7 +117,7 @@
|
|||
android:layout_height="60dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/ic_warning"
|
||||
app:isVisible="@{viewModel.clientState == State.Disconnected}"
|
||||
app:isVisible="@{viewModel.state == State.Disconnected}"
|
||||
app:layout_constraintBottom_toTopOf="@id/client_state"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
@ -128,8 +128,8 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/margin_normal"
|
||||
android:text="@{viewModel.clientState.toString()}"
|
||||
app:isVisible="@{viewModel.clientState != State.Created}"
|
||||
android:text="@{viewModel.state.toString()}"
|
||||
app:isVisible="@{viewModel.state != State.Created}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
@ -144,7 +144,7 @@
|
|||
android:gravity="center"
|
||||
android:text='@{ "( " + viewModel.disconnectReason + " )"}'
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
|
||||
app:isVisible="@{viewModel.clientState == State.Disconnected && !viewModel.disconnectReason.empty}"
|
||||
app:isVisible="@{viewModel.state == State.Disconnected && !viewModel.disconnectReason.empty}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/client_state"
|
||||
|
|
@ -156,7 +156,7 @@
|
|||
android:background="@drawable/bg_circular_button"
|
||||
android:contentDescription="@string/desc_retry_connection_btn"
|
||||
android:src="@drawable/ic_refresh"
|
||||
app:isVisible="@{viewModel.clientState == State.Disconnected}"
|
||||
app:isVisible="@{viewModel.state == State.Disconnected}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -25,17 +26,17 @@
|
|||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Copyright (c) 2020-2021 Gaurav Ujjwal" />
|
||||
android:text="Copyleft (c) 2020-2021 Gaurav Ujjwal" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
15
app/src/main/res/layout/help_btn.xml
Normal file
15
app/src/main/res/layout/help_btn.xml
Normal 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" />
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
style="@style/TipView"
|
||||
android:text="@string/tip_discovery"
|
||||
android:visibility="gone"
|
||||
app:isVisible="@{!viewModel.discovery.isRunning && viewModel.discovery.servers.empty}"
|
||||
app:isVisible="@{viewModel.discovery.servers.empty}"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
|
|
@ -57,5 +57,15 @@
|
|||
android:contentDescription="@string/desc_discovery_running"
|
||||
android:indeterminate="true"
|
||||
app:isVisible="@{viewModel.discovery.isRunning}" />
|
||||
|
||||
<ImageButton
|
||||
style="@style/ImageButton"
|
||||
android:layout_gravity="end|bottom"
|
||||
android:layout_margin="@dimen/margin_normal"
|
||||
android:contentDescription="@string/desc_discovery_stop_btn"
|
||||
android:onClick="@{()-> viewModel.stopDiscovery()}"
|
||||
android:src="@drawable/ic_clear"
|
||||
app:isVisible="@{viewModel.discovery.isRunning}"
|
||||
app:tint="?colorSecondary" />
|
||||
</FrameLayout>
|
||||
</layout>
|
||||
|
|
@ -26,8 +26,7 @@
|
|||
layout="@layout/server_item"
|
||||
app:profile="@{viewModel}" />
|
||||
|
||||
<!--Experimental Feature-->
|
||||
<View
|
||||
<com.gaurav.avnc.ui.home.IndicatorView
|
||||
android:id="@+id/indicator"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
|
|
|
|||
|
|
@ -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 ~~~~~~~~~~~~~~~~~~~~~-->
|
||||
|
||||
|
|
|
|||
179
app/src/main/res/values-de/strings.xml
Normal file
179
app/src/main/res/values-de/strings.xml
Normal 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>
|
||||
179
app/src/main/res/values-fa/strings.xml
Normal file
179
app/src/main/res/values-fa/strings.xml
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2021 eshagh shahidani.
|
||||
~
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
~
|
||||
~ See COPYING.txt for more details.
|
||||
--><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_copy_host">رونویسی میزبان</string>
|
||||
<string name="title_copy_name">رونویسی نام</string>
|
||||
<string name="title_advanced">پیشرفته</string>
|
||||
<string name="title_cancel">لغو</string>
|
||||
<string name="title_save">ذخیره</string>
|
||||
<string name="title_undo">برگردان</string>
|
||||
<string name="title_settings">تنظیمات</string>
|
||||
<string name="title_credentials_required">ورود</string>
|
||||
<string name="title_remember">به یاد داشتن</string>
|
||||
<string name="title_export">برونریزی</string>
|
||||
<string name="title_import">درونریزی</string>
|
||||
<string name="title_view_only_mode">فقط مشاهده</string>
|
||||
<string name="title_hide_remote_cursor">پنهان کردن اشارهگر دوردست</string>
|
||||
<string name="title_key_compat_mode">فرستادن رویدادهای کلید قدیمی</string>
|
||||
<string name="title_use_ssh_tunnel">استفاده از تونل پوستهٔ امن</string>
|
||||
<string name="title_unknown_ssh_host">میزبان ناشناختهٔ پوستهٔ امن</string>
|
||||
<string name="title_ssh_host_key_changed">کلید میزبان پوستهٔ امن تغییر کرد</string>
|
||||
<string name="title_continue">ادامه</string>
|
||||
<string name="title_delete_servers_before_import">حذف کارسازهای کنونی پیش از درونریزی</string>
|
||||
<string name="title_password">گذرواژه</string>
|
||||
<string name="title_use_repeater">استفاده از تکرارکننده</string>
|
||||
<string name="title_private_key">کلید خصوصی</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_report_bug">گزارش یک اشکال</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_repeater_host">میزبان تکرارکننده</string>
|
||||
<string name="hint_port">درگاه</string>
|
||||
<string name="hint_server_id">آیدی کارساز</string>
|
||||
<string name="hint_username">نام کاربری</string>
|
||||
<string name="hint_password">گذرواژه</string>
|
||||
<string name="hint_key_password">گذرواژهٔ کلید</string>
|
||||
<string name="msg_about_app">یک کارخواه سریع و ایمن VNC. به صورت دوردست هر افزارهای که یک کارساز VNC را اجرا کند را ببینید و کنترل کنید.
|
||||
\n
|
||||
\n
|
||||
\nبرنامهٔ AVNC یک نرمافزار متنباز و کپیلفت است — که با کمک همیاری مشارکتکنندگان ساخته شده است.</string>
|
||||
<string name="msg_server_profile_added">کارساز اضافه شد</string>
|
||||
<string name="msg_server_profile_deleted">کارساز حذف شد</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="msg_exported">برونریزی شد</string>
|
||||
<string name="msg_export_auth_required">احراز هویت برای برونریزی کارسازها</string>
|
||||
<string name="msg_export_auth_missing">افزارهٔ شما ایمن نیست. پیش از برونریزی قفل صفحهتان را برای بکار انداختن احراز هویت، روشن کنید.</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="msg_key_compat_mode_help">برخی از کارسازها پشتیبانی یونیکد محدودی دارند. رویدادهای کلید قدیمی میتواند به وارد کردن متن غیر انگلیسی کمک کند.</string>
|
||||
<string name="tip_empty_server_list">فهرست کارسازها خالی است.
|
||||
\nبرای افزودن کارساز بر روی «<b>+</b>» کلیک کنید، یا
|
||||
\nاز نوار نشانی بالا برای اتّصال مستقیم استفاده کنید.</string>
|
||||
<!-- در رشته زیر از نویسهٔ LRM استفاده شده است -->
|
||||
<string name="tip_urlbar">شما میتوانید نشانی آیپی، نام میزبان یا نشانی «vnc://» را وارد کنید.</string>
|
||||
<string name="tip_discovery">اکتشاف میتواند به صورت خودکار کارسازهای پشتیبانیشده\nکه در شبکهٔ شما در حال اجرا هستند، را بیابد.</string>
|
||||
<!-- Accessibility descriptions -->
|
||||
<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_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="desc_discovered_list">فهرست کارسازهای به صورت خودکار کشف شده</string>
|
||||
<string name="desc_saved_servers_list">فهرست کارسازهای ذخیره شده</string>
|
||||
<!-- Preference -->
|
||||
<string name="pref_ui">واسط</string>
|
||||
<string name="pref_ui_summary">دیدن و احساس</string>
|
||||
<string name="pref_viewer">نمایشگر</string>
|
||||
<string name="pref_viewer_summary">تصویر در تصویر، بزرگنمایی</string>
|
||||
<string name="pref_input">ورودی</string>
|
||||
<string name="pref_input_summary">حرکات، ماوس، کلیدهای مجازی</string>
|
||||
<string name="pref_servers">کارسازها</string>
|
||||
<string name="pref_server_summary">ورود، اکتشاف</string>
|
||||
<string name="pref_tools">ابزار</string>
|
||||
<string name="pref_tools_summary">درونریزی/برونریزی، اشکال زدایی</string>
|
||||
<string name="pref_experimental">آزمایشی</string>
|
||||
<string name="pref_experimental_summary">چیزهای اضافی</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_pan">حرکت دادن قاب محلّی</string>
|
||||
<string name="pref_gesture_action_remote_scroll">لغزش محتوای دوردست</string>
|
||||
<string name="pref_gesture_action_remote_drag">کشیدن محتوای دوردست</string>
|
||||
<string name="pref_gesture_action_open_keyboard">گشودن صفحهکلید</string>
|
||||
<string name="pref_theme">زمینه</string>
|
||||
<string name="pref_prefer_advanced_editor">گشودن مستقیم گزینههای پیشرفتهٔ کارساز</string>
|
||||
<string name="pref_sort_server_list">مرتبسازی کارسازهای ذخیره شده بر اساس نام</string>
|
||||
<string name="pref_gesture">حرکات</string>
|
||||
<string name="pref_single_tap">یک ضربه</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_off">رویدادهای ماوس برای حرکات محلّی استفاده شوند</string>
|
||||
<string name="pref_mouse_passthrough_summary_on">رویدادهای ماوس به صورت مستقیم به کارساز فرستاده شوند</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>
|
||||
<string name="pref_km">نقشهکشی کلیدها</string>
|
||||
<string name="pref_km_right_alt_to_super">دگرساز راست ← سوپر</string>
|
||||
<string name="pref_km_language_switch_to_super">تعویض زبان ← سوپر</string>
|
||||
<string name="pref_km_back_to_escape">بازگشت ← گریز</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">نوار ابزار</string>
|
||||
<string name="pref_toolbar_alignment">تراز</string>
|
||||
<string name="pref_zoom">بزرگنمایی</string>
|
||||
<string name="pref_zoom_min">کمینه</string>
|
||||
<string name="pref_zoom_max">بیشینه</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_import_export">درونریزی/برونریزی کارسازها</string>
|
||||
<string name="pref_debugging">اشکال زدایی</string>
|
||||
<string name="pref_logs">گزارشات</string>
|
||||
<string name="pref_keycode_map">نقشهٔ کد کلید</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close">برای بستن کشوی نوار ابزار، انگشت خود را بکشید</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_clear">پاککردن</string>
|
||||
<string name="pref_theme_option_light">روشن</string>
|
||||
<string name="pref_toolbar_alignment_option_end">پایان</string>
|
||||
<string name="pref_theme_option_system">سامانه</string>
|
||||
<string name="pref_theme_option_dark">تاریک</string>
|
||||
<string name="pref_toolbar_alignment_option_start">شروع</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_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>
|
||||
179
app/src/main/res/values-fr/strings.xml
Normal file
179
app/src/main/res/values-fr/strings.xml
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="title_add_server_profile">Ajouter un serveur</string>
|
||||
<string name="title_edit_server_profile">Modifier le serveur</string>
|
||||
<string name="title_delete">Supprimer</string>
|
||||
<string name="title_copy_host">Copier l\'hôte</string>
|
||||
<string name="title_copy_name">Copier le nom</string>
|
||||
<string name="title_advanced">Avancé</string>
|
||||
<string name="title_cancel">Annuler</string>
|
||||
<string name="title_save">Enregistrer</string>
|
||||
<string name="title_undo">Annuler</string>
|
||||
<string name="title_settings">Paramètres</string>
|
||||
<string name="title_credentials_required">Se connecter</string>
|
||||
<string name="title_export">Exporter</string>
|
||||
<string name="title_import">Importer</string>
|
||||
<string name="title_view_only_mode">Visualisation uniquement</string>
|
||||
<string name="title_remember">Mémoriser</string>
|
||||
<string name="title_hide_remote_cursor">Masquer le curseur distant</string>
|
||||
<string name="title_key_compat_mode">Envoyer des événements claviers rétrocompatibles</string>
|
||||
<string name="title_unknown_ssh_host">Hôte SSH inconnu</string>
|
||||
<string name="title_ssh_host_key_changed">La clé de l\'hôte SSH a été modifiée</string>
|
||||
<string name="title_continue">Continuer</string>
|
||||
<string name="title_delete_servers_before_import">Supprimer les serveurs courants avant l\'importation</string>
|
||||
<string name="title_password">Mot de passe</string>
|
||||
<string name="title_use_repeater">Utiliser un répéteur</string>
|
||||
<string name="title_github_repo">Code source</string>
|
||||
<string name="hint_server_name">Nom (par ex. PC Travail)</string>
|
||||
<string name="hint_host">Hôte</string>
|
||||
<string name="hint_repeater_host">Hôte du répéteur</string>
|
||||
<string name="hint_port">Port</string>
|
||||
<string name="hint_username">Nom d\'utilisateur</string>
|
||||
<string name="hint_password">Mot de passe</string>
|
||||
<string name="hint_key_password">Mot de passe clé</string>
|
||||
<string name="msg_about_app">Un client pour VNC rapide et sécurisé. Visualisez et contrôlez à distance tout appareil exécutant un serveur VNC.
|
||||
\n
|
||||
\n
|
||||
\nAVNC est un logiciel open source et copyleft - réalisé avec l\'aide de nombreux contributeurs.</string>
|
||||
<string name="msg_server_profile_added">Serveur ajouté</string>
|
||||
<string name="msg_loading">Chargement…</string>
|
||||
<string name="msg_imported">Importé</string>
|
||||
<string name="msg_exported">Exporté</string>
|
||||
<string name="msg_export_auth_required">Authentifiez-vous pour exporter des serveurs</string>
|
||||
<string name="msg_export_auth_missing">Votre appareil n\'est pas sécurisé. Activez le verrouillage de l\'écran pour permettre l\'authentification avant l\'exportation.</string>
|
||||
<string name="msg_invalid_key_file">Fichier de clé privée invalide</string>
|
||||
<string name="msg_invalid_vnc_uri">URI VNC invalide</string>
|
||||
<string name="msg_copied_to_clipboard">Copié</string>
|
||||
<string name="desc_navigation_btn">Ouvrir le menu de navigation</string>
|
||||
<string name="desc_settings_btn">Paramètres</string>
|
||||
<string name="desc_saved_servers_tab">Serveurs enregistrés</string>
|
||||
<string name="desc_discovered_servers_tab">Serveurs découverts</string>
|
||||
<string name="desc_add_new_server_btn">Ajouter un serveur</string>
|
||||
<string name="desc_save_discovered_server_btn">Enregistrer</string>
|
||||
<string name="desc_discovery_btn">Découvrir des serveurs</string>
|
||||
<string name="desc_discovery_running">Découverte des serveurs sur votre réseau</string>
|
||||
<string name="desc_keyboard_btn">Ouvrir le clavier</string>
|
||||
<string name="desc_virtual_keys_btn">Afficher les touches virtuelles</string>
|
||||
<string name="desc_reset_zoom_btn">Réinitialiser le niveau de zoom</string>
|
||||
<string name="desc_retry_connection_btn">Réessayer</string>
|
||||
<string name="title_edit">Modifier</string>
|
||||
<string name="title_duplicate">Dupliquer</string>
|
||||
<string name="msg_ptr_hiding_not_supported">Non pris en charge sur cet appareil</string>
|
||||
<string name="desc_clear_url_btn">Effacer</string>
|
||||
<string name="pref_gesture_action_none">Aucun</string>
|
||||
<string name="pref_gesture_action_right_click">Clic droit</string>
|
||||
<string name="title_open_source_libraries">Bibliothèques open source</string>
|
||||
<string name="pref_tools_summary">Importation/Exportation, déboggage</string>
|
||||
<string name="pref_gesture_action_double_click">Double clic</string>
|
||||
<string name="hint_urlbar">Se connecter à un serveur</string>
|
||||
<string name="pref_input_summary">Gestes, souris, clés virtuelles</string>
|
||||
<string name="pref_experimental">Expérimental</string>
|
||||
<string name="title_use_ssh_tunnel">Utiliser un tunnel SSH</string>
|
||||
<string name="title_about">À propos</string>
|
||||
<string name="title_private_key">Clé privée</string>
|
||||
<string name="title_change">Modifier</string>
|
||||
<string name="title_license">Licence</string>
|
||||
<string name="title_report_bug">Signaler un problème</string>
|
||||
<string name="desc_back_btn">Retour</string>
|
||||
<string name="pref_viewer">Visionneuse</string>
|
||||
<string name="hint_server_id">Identifiant du serveur</string>
|
||||
<string name="msg_pip_not_supported">Non pris en charge sur cet appareil</string>
|
||||
<string name="msg_server_profile_deleted">Serveur supprimé</string>
|
||||
<string name="msg_key_compat_mode_help">Certains serveurs ont une prise en charge limitée du format Unicode. Les événements de saisie rétrocompatibles peuvent aider à saisir du texte non anglais.</string>
|
||||
<string name="desc_server_url">Adresse du serveur</string>
|
||||
<string name="desc_help_btn">Aide</string>
|
||||
<string name="desc_discovered_list">Liste des serveurs découverts automatiquement</string>
|
||||
<string name="desc_saved_servers_list">Liste des serveurs enregistrés</string>
|
||||
<string name="pref_ui">Interface</string>
|
||||
<string name="pref_ui_summary">Apparence et ressenti</string>
|
||||
<string name="pref_viewer_summary">Picture-in-picture, Zoom</string>
|
||||
<string name="pref_server_summary">Connexion, découverte</string>
|
||||
<string name="pref_input">Entrée</string>
|
||||
<string name="pref_servers">Serveurs</string>
|
||||
<string name="pref_experimental_summary">Bonus</string>
|
||||
<string name="pref_tools">Outils</string>
|
||||
<string name="pref_gesture_action_left_click">Clic gauche</string>
|
||||
<string name="pref_gesture_action_middle_click">Clic du milieu</string>
|
||||
<string name="pref_theme">Thème</string>
|
||||
<string name="tip_empty_server_list">La liste des serveurs est vide.
|
||||
\nCliquez sur « <b>+</b> » ; pour ajouter un serveur, ou
|
||||
\nUtilisez la barre d\'adresse supérieure pour vous connecter directement.</string>
|
||||
<string name="pref_experimental_immersive_mode">Mode immersif complet</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close">Faîtes glisser pour fermer le menu de la barre d\'outils</string>
|
||||
<string name="pref_experimental_immersive_mode_summary">Masquer toutes les barres du système en mode plein écran</string>
|
||||
<string name="pref_theme_option_system">Système</string>
|
||||
<string name="pref_swipe2">Glissement à deux doigts</string>
|
||||
<string name="pref_double_tap">Appui double</string>
|
||||
<string name="pref_long_press">Appui long</string>
|
||||
<string name="pref_swipe1">Glissement à un doigt</string>
|
||||
<string name="pref_fullscreen">Plein écran</string>
|
||||
<string name="pref_km_back_to_escape">Retour → Échap</string>
|
||||
<string name="pref_km_back_to_escape_summary">N\'affecte pas la barre de navigation</string>
|
||||
<string name="pref_toolbar_alignment_option_start">Début</string>
|
||||
<string name="pref_toolbar_alignment">Alignement</string>
|
||||
<string name="pref_toolbar_alignment_option_end">Fin</string>
|
||||
<string name="pref_zoom">Zoom</string>
|
||||
<string name="tip_urlbar">Vous pouvez saisir une adresse IP, un nom d\'hôte ou une URI « vnc:// ».</string>
|
||||
<string name="tip_discovery">Le mode découverte peut trouver automatiquement les serveurs pris en charge
|
||||
\ns\'exécutant sur votre réseau.</string>
|
||||
<string name="pref_sort_server_list">Trier les serveurs enregistrés par nom</string>
|
||||
<string name="pref_gesture">Gestes</string>
|
||||
<string name="pref_single_tap">Appui simple</string>
|
||||
<string name="pref_two_finger_tap">Appui à deux doigts</string>
|
||||
<string name="pref_gesture_action_move_pointer">Déplacer le curseur</string>
|
||||
<string name="pref_gesture_action_remote_scroll">Faire défiler le contenu distant</string>
|
||||
<string name="pref_gesture_action_remote_drag">Faire glisser le contenu distant</string>
|
||||
<string name="pref_gesture_action_open_keyboard">Ouvrir le clavier</string>
|
||||
<string name="pref_prefer_advanced_editor">Ouvrir directement les options avancées du serveur</string>
|
||||
<string name="pref_logs">Journaux</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close_summary">Au cours d\'une session distante, vous pouvez faire glisser pour fermer la barre d\'outils ouverte</string>
|
||||
<string name="pref_enable_pip">Picture-in-picture</string>
|
||||
<string name="pref_toolbar">Barre d\'outils</string>
|
||||
<string name="pref_direct_touch_summary_on">Effectuer les actions au point de contact</string>
|
||||
<string name="pref_direct_touch">Pointage direct</string>
|
||||
<string name="pref_direct_touch_summary_off">Effectuer les actions au niveau du pointeur</string>
|
||||
<string name="pref_debugging">Débogage</string>
|
||||
<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</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>
|
||||
<string name="pref_mouse_passthrough_summary_off">Utiliser les événements de la souris pour les gestes locaux</string>
|
||||
<string name="pref_mouse_passthrough_summary_on">Envoyer les événements de la souris directement au serveur</string>
|
||||
<string name="pref_vk_open_with_keyboard">Ouvrir avec le clavier</string>
|
||||
<string name="pref_km">Association des touches</string>
|
||||
<string name="pref_km_right_alt_to_super">Alt Droite → Super</string>
|
||||
<string name="pref_zoom_min">Minimum</string>
|
||||
<string name="pref_zoom_max">Maximum</string>
|
||||
<string name="pref_credential">Connexion</string>
|
||||
<string name="pref_cred_autocomplete">Autocomplétion</string>
|
||||
<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">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>
|
||||
<string name="pref_mouse_passthrough">Passage de la souris</string>
|
||||
<string name="pref_discovery_autorun_summary">Découverte des serveurs sur la page d\'accueil</string>
|
||||
<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>
|
||||
171
app/src/main/res/values-nb-rNO/strings.xml
Normal file
171
app/src/main/res/values-nb-rNO/strings.xml
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="desc_help_btn">Hjelp</string>
|
||||
<string name="title_ssh_host_key_changed">SSH-vertsnøkkelen endret seg</string>
|
||||
<string name="title_add_server_profile">Legg til tjener</string>
|
||||
<string name="title_edit">Rediger</string>
|
||||
<string name="title_settings">Innstillinger</string>
|
||||
<string name="title_edit_server_profile">Rediger tjener</string>
|
||||
<string name="title_delete">Slett</string>
|
||||
<string name="title_copy_host">Kopier vert</string>
|
||||
<string name="title_advanced">Avansert</string>
|
||||
<string name="title_cancel">Avbryt</string>
|
||||
<string name="title_credentials_required">Innlogging</string>
|
||||
<string name="title_remember">Husk</string>
|
||||
<string name="title_duplicate">Dupliser</string>
|
||||
<string name="title_view_only_mode">Skrivebeskyttet</string>
|
||||
<string name="title_use_ssh_tunnel">Bruk SSH-tunnel</string>
|
||||
<string name="title_unknown_ssh_host">Ukjent SSH-vert</string>
|
||||
<string name="title_password">Passord</string>
|
||||
<string name="title_private_key">Privat nøkkel</string>
|
||||
<string name="title_key_compat_mode">Kompabilitetsmodus for tastatur</string>
|
||||
<string name="title_delete_servers_before_import">Slett nåværende tjenere før import</string>
|
||||
<string name="title_github_repo">Kildekode</string>
|
||||
<string name="hint_urlbar">Koble til en tjener</string>
|
||||
<string name="hint_server_name">Navn (f.eks. arbeids-PC)</string>
|
||||
<string name="hint_host">Vert</string>
|
||||
<string name="hint_port">Port</string>
|
||||
<string name="hint_server_id">Tjener-ID</string>
|
||||
<string name="hint_username">Brukernavn</string>
|
||||
<string name="msg_server_profile_deleted">Tjener slettet</string>
|
||||
<string name="msg_server_profile_added">Tjener lagt til</string>
|
||||
<string name="msg_loading">Laster inn …</string>
|
||||
<string name="msg_imported">Importert</string>
|
||||
<string name="title_open_source_libraries">Frie bibliotek</string>
|
||||
<string name="msg_ptr_hiding_not_supported">Ikke støttet på denne enheten</string>
|
||||
<string name="msg_invalid_key_file">Ugyldig privat nøkkelfil</string>
|
||||
<string name="desc_navigation_btn">Åpne navigasjonsskuff</string>
|
||||
<string name="desc_settings_btn">Innstillinger</string>
|
||||
<string name="desc_saved_servers_tab">Lagrede tjenere</string>
|
||||
<string name="desc_discovered_servers_tab">Oppdagede tjenere</string>
|
||||
<string name="desc_save_discovered_server_btn">Lagre</string>
|
||||
<string name="msg_export_auth_required">Identitetsbekreftelse for eksport av tjenere</string>
|
||||
<string name="tip_urlbar">Du kan skrive inn vertsnavn, IP-adresse, eller «vnc://«-URI.</string>
|
||||
<string name="desc_discovery_btn">Oppdag tjenere</string>
|
||||
<string name="desc_keyboard_btn">Åpne tastatur</string>
|
||||
<string name="desc_discovery_running">Oppdaging av tjenere på nettverket ditt</string>
|
||||
<string name="desc_back_btn">Tilbake</string>
|
||||
<string name="desc_saved_servers_list">Liste over lagrede søk</string>
|
||||
<string name="pref_ui">Grensesnitt</string>
|
||||
<string name="pref_servers">Tjenere</string>
|
||||
<string name="pref_tools">Verktøy</string>
|
||||
<string name="pref_experimental">Eksperimentelt</string>
|
||||
<string name="desc_clear_url_btn">Tøm</string>
|
||||
<string name="desc_server_url">Tjeneradresse</string>
|
||||
<string name="pref_ui_summary">Utseende og oppførsel</string>
|
||||
<string name="pref_viewer_summary">Bilde-i-bilde, forstørrelse</string>
|
||||
<string name="pref_gesture_action_middle_click">Midtklikk</string>
|
||||
<string name="pref_gesture_action_right_click">Høyreklikk</string>
|
||||
<string name="pref_gesture_action_move_pointer">Flytt peker</string>
|
||||
<string name="pref_theme">Drakt</string>
|
||||
<string name="pref_gesture_action_open_keyboard">Åpne tastatur</string>
|
||||
<string name="pref_prefer_advanced_editor">Foretrekk avansert tekstbehandler</string>
|
||||
<string name="pref_sort_server_list">Sorter lagrede tjenere etter navn</string>
|
||||
<string name="pref_gesture">Håndvendinger</string>
|
||||
<string name="pref_single_tap">Enkelttrykk</string>
|
||||
<string name="pref_double_tap">Dobbelttrykk</string>
|
||||
<string name="pref_long_press">Lang-trykk</string>
|
||||
<string name="pref_mouse">Mus</string>
|
||||
<string name="pref_km">Tastetilknytninger</string>
|
||||
<string name="pref_fullscreen">Fullskjermsvisning</string>
|
||||
<string name="pref_zoom">Forstørrelse</string>
|
||||
<string name="pref_zoom_min">Min</string>
|
||||
<string name="pref_zoom_max">Maks</string>
|
||||
<string name="pref_clipboard_sync">Utklippstavlesynkronisering</string>
|
||||
<string name="pref_credential">Innlogging</string>
|
||||
<string name="pref_discovery">Oppdagelse</string>
|
||||
<string name="title_import">Importer</string>
|
||||
<string name="pref_experimental_immersive_mode">Helt oppslukende modus</string>
|
||||
<string name="pref_experimental_immersive_mode_summary">Skjuler alle systemfelter i fullskjermsmodus</string>
|
||||
<string name="desc_add_new_server_btn">Legg til tjener</string>
|
||||
<string name="title_copy_name">Kopier navn</string>
|
||||
<string name="title_export">Eksporter</string>
|
||||
<string name="hint_password">Passord</string>
|
||||
<string name="title_undo">Angre</string>
|
||||
<string name="title_change">Endre</string>
|
||||
<string name="title_save">Lagre</string>
|
||||
<string name="title_about">Om</string>
|
||||
<string name="title_license">Lisens</string>
|
||||
<string name="title_continue">Fortsett</string>
|
||||
<string name="title_report_bug">Innrapporter en feil</string>
|
||||
<string name="msg_exported">Eksportert</string>
|
||||
<string name="msg_copied_to_clipboard">Kopiert</string>
|
||||
<string name="desc_virtual_keys_btn">Vis virtuelle taster</string>
|
||||
<string name="pref_gesture_action_left_click">Venstreklikk</string>
|
||||
<string name="pref_experimental_summary">Ekstra godsaker</string>
|
||||
<string name="pref_gesture_action_none">Ingen</string>
|
||||
<string name="pref_gesture_action_double_click">Dobbeltklikk</string>
|
||||
<string name="pref_vk_show_all">Vis alle taster</string>
|
||||
<string name="msg_invalid_vnc_uri">Ugyldig VNC-URI</string>
|
||||
<string name="desc_reset_zoom_btn">Tilbakestill forstørrelse</string>
|
||||
<string name="pref_toolbar">Verktøylinje</string>
|
||||
<string name="msg_pip_not_supported">Ikke støttet på denne enheten</string>
|
||||
<string name="pref_input_summary">Håndvendinger, mus, virtuelle taster</string>
|
||||
<string name="pref_vk_open_with_keyboard">Åpne med tastatur</string>
|
||||
<string name="pref_toolbar_alignment">Justering</string>
|
||||
<string name="pref_enable_pip">Bilde-i-bilde</string>
|
||||
<string name="pref_drag">Dra (lang-trykk og så sveip)</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close">Dra for å lukke verktøysskuff</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close_summary">Under fjernøkter kan du dra for å lukke verktøylinjen</string>
|
||||
<string name="title_clear">Tøm</string>
|
||||
<string name="title_hide_remote_cursor">Skjul fjernpeker</string>
|
||||
<string name="desc_retry_connection_btn">Prøv igjen</string>
|
||||
<string name="pref_theme_option_system">System</string>
|
||||
<string name="pref_theme_option_dark">Mørk</string>
|
||||
<string name="pref_theme_option_light">Lys</string>
|
||||
<string name="pref_two_finger_tap">Tofingerstrykk</string>
|
||||
<string name="pref_swipe1">Énfingerstrykk</string>
|
||||
<string name="pref_swipe2">Tofingerssveip</string>
|
||||
<string name="pref_mouse_passthrough">Mus-videresending</string>
|
||||
<string name="pref_gesture_action_pan">Panorer lokal ramme</string>
|
||||
<string name="pref_gesture_action_remote_scroll">Rull fjerninnhold</string>
|
||||
<string name="pref_gesture_action_remote_drag">Dra fjerninnhold</string>
|
||||
<string name="pref_mouse_passthrough_summary_off">Bruk musehendelser lokalt for håndvendinger</string>
|
||||
<string name="pref_mouse_passthrough_summary_on">Send musehendelser direkte til tjeneren</string>
|
||||
<string name="pref_hide_local_cursor">Skjul lokal (Android-) peker</string>
|
||||
<string name="pref_vk">Virtuelle taster</string>
|
||||
<string name="pref_km_right_alt_to_super">Høyre Alt → Super</string>
|
||||
<string name="pref_km_language_switch_to_super">Bytt språk → Super</string>
|
||||
<string name="pref_km_back_to_escape_summary">Har ingen innvirkning på navigasjonsfeltet</string>
|
||||
<string name="pref_toolbar_alignment_option_start">Start</string>
|
||||
<string name="pref_cred_autocomplete_summary">Foreslå kjente innlogginger ved tilkobling til nye tjenere</string>
|
||||
<string name="pref_import_export">Importer/eksporter tjenere</string>
|
||||
<string name="pref_debugging">Avlusing</string>
|
||||
<string name="pref_logs">Logger</string>
|
||||
<string name="msg_about_app">En rask og sikker VNC-klient. Koble til alle enheter som har en VNC-tjener.
|
||||
\n
|
||||
\n
|
||||
\nAVNC er gemenhetslig fri programvare — laget av mange bidragsytere.</string>
|
||||
<string name="desc_discovered_list">Liste over oppdagede tjenere</string>
|
||||
<string name="pref_viewer">Viser</string>
|
||||
<string name="hint_repeater_host">Gjentaker-vert</string>
|
||||
<string name="pref_tools_summary">Import/eksport, avlusing</string>
|
||||
<string name="pref_server_summary">Innlogging, oppdagelse</string>
|
||||
<string name="pref_toolbar_alignment_option_end">Slutt</string>
|
||||
<string name="pref_cred_autocomplete">Auto-fullføring</string>
|
||||
<string name="pref_keycode_map">Tastaturtilknytninger</string>
|
||||
<string name="hint_key_password">Nøkkelpassord</string>
|
||||
<string name="msg_key_compat_mode_help">Foreldet modus for tjenere som ikke støtter inntasting av alle tegn.</string>
|
||||
<string name="pref_input">Inndata</string>
|
||||
<string name="tip_empty_server_list">Tjenerlisten er tom.
|
||||
\nKlikk på «<b>+</b>»-knappen for å legge til en tjener,
|
||||
\neller feltet ovenfor for å koble til direkte.</string>
|
||||
<string name="pref_km_back_to_escape">Tilbake → Esc</string>
|
||||
<string name="title_use_repeater">Bruk gjentaker</string>
|
||||
<string name="tip_discovery">Oppdagelse kan automatisk finne støttede tjenere
|
||||
\npå nettverket ditt.</string>
|
||||
<string name="msg_export_auth_missing">Skru på skjermlås for å kreve identitetsbekreftelse før eksport.</string>
|
||||
<string name="pref_direct_touch">Direkte-trykk</string>
|
||||
<string name="pref_direct_touch_summary_on">Utfør handlinger ved berøring</string>
|
||||
<string name="pref_direct_touch_summary_off">Utfør handlinger ved peker</string>
|
||||
<string name="pref_rediscovery_indicator">Framhev tjenere som oppdages igjen</string>
|
||||
<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>
|
||||
177
app/src/main/res/values-pl/strings.xml
Normal file
177
app/src/main/res/values-pl/strings.xml
Normal 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>
|
||||
|
|
@ -1,17 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2020 Gaurav Ujjwal.
|
||||
~
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
~
|
||||
~ 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>
|
||||
--><resources>
|
||||
<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>
|
||||
|
|
@ -19,28 +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>
|
||||
|
|
@ -50,58 +48,60 @@
|
|||
<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_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 Imagem-na-imagem 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_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 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_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_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 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">Imagem-na-imagem, 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_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_tools_summary">Importar/Exportar, depuração</string>
|
||||
<string name="pref_experimental">Experimental</string>
|
||||
<string name="pref_experimental_summary">Mais recursos!</string>
|
||||
|
||||
<string name="pref_experimental_summary">Mais recursos</string>
|
||||
<string name="pref_gesture_action_none">Nenhum</string>
|
||||
<string name="pref_gesture_action_left_click">Clique esquerdo</string>
|
||||
<string name="pref_gesture_action_middle_click">Clique do meio</string>
|
||||
|
|
@ -112,56 +112,76 @@
|
|||
<string name="pref_gesture_action_remote_scroll">Rolagem de conteúdo remoto</string>
|
||||
<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_prefer_advanced_editor_summary">Ative isto para abrir diretamente o Modo avançado para adicionar/editar servidores</string>
|
||||
<string name="pref_sort_server_list">Ordenar a lista de servidores</string>
|
||||
<string name="pref_sort_server_list_summary">Servidores são ordenados pelo nome</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 (experimental)</string>
|
||||
<string name="pref_two_finger_tap">Toque com 2 dedos</string>
|
||||
<string name="pref_double_tap">Toque duplo</string>
|
||||
<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 para Super</string>
|
||||
<string name="pref_km_language_switch_to_super">Alteração de idioma para Super</string>
|
||||
<string name="pref_km_back_to_escape">Voltar para 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">Ativar Imagem-na-imagem</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">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_discovery_timeout">Intervalo (em segundos)</string>
|
||||
<string name="pref_discovery_auto_start">Iniciar automaticamente</string>
|
||||
<string name="pref_discovery_auto_start_summary">Ao abrir app iniciar a Descoberta</string>
|
||||
<string name="pref_discovery_restart">Reiniciar</string>
|
||||
<string name="pref_discovery_restart_summary">Reiniciar a Descoberta depois de parar</string>
|
||||
<string name="pref_discovery_restart_delay">Resetar adiamento (minutos)</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_experimental_indicator">Indicador de descoberta para servidores salvos</string>
|
||||
<string name="pref_experimental_indicator_summary">Destacar os servidores já salvos que também foram encontrados pela Descoberta.\nRequer reinício do app.</string>
|
||||
</resources>
|
||||
<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">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">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>
|
||||
146
app/src/main/res/values-ru/strings.xml
Normal file
146
app/src/main/res/values-ru/strings.xml
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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>
|
||||
165
app/src/main/res/values-sr/strings.xml
Normal file
165
app/src/main/res/values-sr/strings.xml
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="hint_repeater_host">Понови домаћина</string>
|
||||
<string name="hint_server_name">Име (нпр. Радни рачунар)</string>
|
||||
<string name="msg_about_app">Брзи и безбедни корисник ВНЦ-а. Удаљени преглед и управљање било каквим уређајима који опслужују ВНЦ.
|
||||
\n
|
||||
\n
|
||||
\nАВНЦ је отвореног извора и слободног (копилефт) умножавања — направљен уз помоћ многих доприносиоца.</string>
|
||||
<string name="msg_invalid_vnc_uri">Није исправна адреса ВНЦ-а</string>
|
||||
<string name="desc_reset_zoom_btn">Поврати увећање на подразумевано</string>
|
||||
<string name="desc_add_new_server_btn">Додај служитеља</string>
|
||||
<string name="desc_discovery_running">Откривам служитеље на овој мрежи</string>
|
||||
<string name="pref_gesture_action_move_pointer">Померај показивач</string>
|
||||
<string name="desc_saved_servers_list">Списак сачуваних служитеља</string>
|
||||
<string name="desc_discovered_list">Списак самостално откривених служитеља</string>
|
||||
<string name="pref_tools">Прибор</string>
|
||||
<string name="pref_ui_summary">Изглед и дојам</string>
|
||||
<string name="pref_viewer_summary">Слика у слици, увећање</string>
|
||||
<string name="pref_servers">Служитељи</string>
|
||||
<string name="pref_experimental">Опитно</string>
|
||||
<string name="pref_server_summary">Пријава</string>
|
||||
<string name="pref_tools_summary">Увоз/извоз, исправке грешака</string>
|
||||
<string name="pref_experimental_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_mouse_passthrough">Пролаз мишем</string>
|
||||
<string name="pref_direct_touch">Непосредан додир</string>
|
||||
<string name="pref_direct_touch_summary_off">Извршавај радње на показивачу</string>
|
||||
<string name="pref_long_press">Дуги притисак</string>
|
||||
<string name="pref_vk_open_with_keyboard">Отвори тастатуром</string>
|
||||
<string name="pref_direct_touch_summary_on">Извршавај радње на тачци додира</string>
|
||||
<string name="pref_mouse">Миш</string>
|
||||
<string name="pref_theme">Тема</string>
|
||||
<string name="pref_theme_option_light">Светла</string>
|
||||
<string name="pref_prefer_advanced_editor">Отвори непосредно напредне могућности служитеља</string>
|
||||
<string name="pref_two_finger_tap">Двопрсти тап</string>
|
||||
<string name="pref_double_tap">Двоструки тап</string>
|
||||
<string name="pref_swipe1">Прелаз једним прстом</string>
|
||||
<string name="pref_vk">Тастатура заслона</string>
|
||||
<string name="pref_drag">Превуци (дуги притисак и прелаз)</string>
|
||||
<string name="pref_km">Распоред дугмади</string>
|
||||
<string name="pref_fullscreen">Пуни приказ</string>
|
||||
<string name="pref_hide_local_cursor">Сакриј местни (Андроидов) показивач</string>
|
||||
<string name="pref_km_right_alt_to_super">Десни алт → супер</string>
|
||||
<string name="pref_km_language_switch_to_super">Промена језика → супер</string>
|
||||
<string name="pref_km_back_to_escape">Назад → бег</string>
|
||||
<string name="pref_km_back_to_escape_summary">Не утиче на траку навођења</string>
|
||||
<string name="pref_credential">Пријава</string>
|
||||
<string name="desc_discovered_servers_tab">Откривени служитељи</string>
|
||||
<string name="desc_save_discovered_server_btn">Сачувај</string>
|
||||
<string name="desc_discovery_btn">Откриј служитеље</string>
|
||||
<string name="desc_keyboard_btn">Отвори тастатуру</string>
|
||||
<string name="desc_virtual_keys_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="pref_ui">Сучеље</string>
|
||||
<string name="pref_viewer">Прегледач</string>
|
||||
<string name="pref_input">Унос</string>
|
||||
<string name="pref_input_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_gesture_action_remote_drag">Вуци удаљени садржај</string>
|
||||
<string name="pref_gesture_action_open_keyboard">Отвори тастатуру</string>
|
||||
<string name="pref_theme_option_system">Систем</string>
|
||||
<string name="pref_theme_option_dark">Тамна</string>
|
||||
<string name="pref_sort_server_list">Распореди сачуване служитеље по имену</string>
|
||||
<string name="pref_gesture">Потези</string>
|
||||
<string name="pref_single_tap">Један тап</string>
|
||||
<string name="pref_swipe2">Прелаз двома прстима</string>
|
||||
<string name="pref_mouse_passthrough_summary_on">Пошаљи догађаје миша непосредно служитељу</string>
|
||||
<string name="pref_toolbar_alignment_option_end">Крај</string>
|
||||
<string name="pref_zoom">Увећање</string>
|
||||
<string name="pref_zoom_min">Најмање</string>
|
||||
<string name="pref_zoom_max">Највише</string>
|
||||
<string name="pref_clipboard_sync">Услкађивање складишта исечака</string>
|
||||
<string name="pref_cred_autocomplete">Самопопуна</string>
|
||||
<string name="pref_cred_autocomplete_summary">Предлаже сачуване пријаве при повезивању са новим служитељем</string>
|
||||
<string name="pref_discovery">Откривање</string>
|
||||
<string name="pref_import_export">Увези/извези служитеље</string>
|
||||
<string name="pref_debugging">Отклон грешака</string>
|
||||
<string name="pref_logs">Дневници</string>
|
||||
<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_clear">Очисти</string>
|
||||
<string name="title_copy_host">Умножи домаћина</string>
|
||||
<string name="title_copy_name">Умножи име</string>
|
||||
<string name="title_advanced">Напредно</string>
|
||||
<string name="title_cancel">Откажи</string>
|
||||
<string name="title_save">Сачувај</string>
|
||||
<string name="title_undo">Опозови</string>
|
||||
<string name="title_settings">Поставке</string>
|
||||
<string name="title_credentials_required">Пријава</string>
|
||||
<string name="title_remember">Запамти</string>
|
||||
<string name="title_export">Извези</string>
|
||||
<string name="title_import">Увези</string>
|
||||
<string name="title_view_only_mode">Само за читање</string>
|
||||
<string name="title_hide_remote_cursor">Сакриј удаљеног показивача</string>
|
||||
<string name="title_key_compat_mode">Пошаљи старинске догађаје дугмади</string>
|
||||
<string name="title_unknown_ssh_host">Непознати домаћин ССХ-а</string>
|
||||
<string name="title_use_ssh_tunnel">Користи тунел ССХ-а</string>
|
||||
<string name="title_ssh_host_key_changed">Кључ домаћина ССХ-а је промењен</string>
|
||||
<string name="title_continue">Настави</string>
|
||||
<string name="title_delete_servers_before_import">Избриши тренутне служитеље пре увоза</string>
|
||||
<string name="title_password">Лозинка</string>
|
||||
<string name="title_use_repeater">Користи понављање</string>
|
||||
<string name="title_private_key">Лични кључ</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_report_bug">Пријави проблем</string>
|
||||
<string name="title_github_repo">Изворни код</string>
|
||||
<string name="hint_urlbar">Повежи се са служитељем</string>
|
||||
<string name="hint_host">Домаћин</string>
|
||||
<string name="hint_port">Прикључник</string>
|
||||
<string name="hint_server_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_pip_not_supported">Нема подршке на овом уређају</string>
|
||||
<string name="msg_ptr_hiding_not_supported">Није подржано на овом уређају</string>
|
||||
<string name="msg_loading">Учитавам…</string>
|
||||
<string name="msg_imported">Увезено</string>
|
||||
<string name="msg_exported">Извезено</string>
|
||||
<string name="msg_export_auth_required">Пријавите се ради извоза служитеља</string>
|
||||
<string name="msg_export_auth_missing">Ваш уређај није обезбеђен. Укључите закључавање заслона ради омогућавања пријаве пре извоза.</string>
|
||||
<string name="msg_invalid_key_file">Датотека личног кључа није исправна</string>
|
||||
<string name="msg_copied_to_clipboard">Умножено</string>
|
||||
<string name="msg_key_compat_mode_help">Неки служитељи имају ограничену подршку уникода. Застарели догађаји дугмади могу помоћи за неенглеско писмо.</string>
|
||||
<string name="tip_empty_server_list">Списак служитеља је празан.
|
||||
\nКликните на „<b>+</b>“ ради додавања служитеља, или
|
||||
\nупотребите горњу траку уноса ради непосредног повезивања.</string>
|
||||
<string name="tip_urlbar">Можете унети адресу ИП-а, име домаћина, или \'vnc://\' URI.</string>
|
||||
<string name="tip_discovery">Откривање може самостално да пронађе служитеље
|
||||
\nкоји раде на Вашој мрежи.</string>
|
||||
<string name="desc_navigation_btn">Отвори крмило навођења</string>
|
||||
<string name="desc_settings_btn">Поставке</string>
|
||||
<string name="desc_saved_servers_tab">Сачувани служитељи</string>
|
||||
<string name="pref_mouse_passthrough_summary_off">Користи догађаје миша за местне потезе</string>
|
||||
<string name="pref_vk_show_all">Приказуј сву дугмад</string>
|
||||
<string name="pref_enable_pip">Слика у слици</string>
|
||||
<string name="pref_toolbar">Трака прибора</string>
|
||||
<string name="pref_toolbar_alignment">Поравнање</string>
|
||||
<string name="pref_toolbar_alignment_option_start">Почетак</string>
|
||||
<string name="pref_gesture_action_pan">Премести положај оквира</string>
|
||||
<string name="pref_keycode_map">Мапа распореда дугмади</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close">Превуци за затварање траке прибора</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="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>
|
||||
8
app/src/main/res/values-ta/strings.xml
Normal file
8
app/src/main/res/values-ta/strings.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<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_add_server_profile">சேவையகத்தைச் சேர்க்கவும்</string>
|
||||
</resources>
|
||||
179
app/src/main/res/values-tr/strings.xml
Normal file
179
app/src/main/res/values-tr/strings.xml
Normal 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>
|
||||
179
app/src/main/res/values-uk/strings.xml
Normal file
179
app/src/main/res/values-uk/strings.xml
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pref_gesture_action_remote_drag">Перетягнути віддалений вміст</string>
|
||||
<string name="pref_gesture_action_open_keyboard">Відкрити клавіатуру</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_sort_server_list">Сортувати збережені сервери за назвою</string>
|
||||
<string name="pref_gesture">Жести</string>
|
||||
<string name="pref_prefer_advanced_editor">Відразу відкривати розширені параметри сервера</string>
|
||||
<string name="pref_direct_touch_summary_off">Робити дії з вказівником</string>
|
||||
<string name="pref_direct_touch">Дотик напряму</string>
|
||||
<string name="pref_single_tap">Один дотик</string>
|
||||
<string name="pref_swipe1">Ковзання одним пальцем</string>
|
||||
<string name="pref_mouse">Мишка</string>
|
||||
<string name="pref_fullscreen">На повний екран</string>
|
||||
<string name="pref_toolbar">Панель засобів</string>
|
||||
<string name="pref_zoom">Збільшення</string>
|
||||
<string name="pref_discovery">Виявлення</string>
|
||||
<string name="pref_debugging">Зневадження</string>
|
||||
<string name="pref_logs">Журнал</string>
|
||||
<string name="title_add_server_profile">Додати сервер</string>
|
||||
<string name="title_edit">Змінити</string>
|
||||
<string name="title_advanced">Розширено</string>
|
||||
<string name="title_use_ssh_tunnel">Викор. тунель SSH</string>
|
||||
<string name="hint_password">Пароль</string>
|
||||
<string name="title_delete_servers_before_import">Видалити поточні сервери перед імпортуванням</string>
|
||||
<string name="title_open_source_libraries">Бібліотеки з відкритим кодом</string>
|
||||
<string name="title_license">Ліцензія</string>
|
||||
<string name="hint_urlbar">Підʼєднатися до сервера</string>
|
||||
<string name="hint_server_id">ІД сервера</string>
|
||||
<string name="msg_pip_not_supported">Не підтримується на цьому пристрої</string>
|
||||
<string name="msg_about_app">Швидкий і безпечний VNC-клієнт. Переглядайте і керуйте віддалено будь-яким пристроєм, що працює на VNC-сервері.
|
||||
\n
|
||||
\n
|
||||
\nAVNC це copyleft-ПЗ з відкритим кодом — зроблене з допомогою багатьох співрозробників.</string>
|
||||
<string name="msg_invalid_key_file">Неправильний файл приватного ключа</string>
|
||||
<string name="msg_invalid_vnc_uri">Неправильна URI-адреса VNC</string>
|
||||
<string name="desc_save_discovered_server_btn">Зберегти</string>
|
||||
<string name="pref_input_summary">Жести, мишка, віртуальні клавіші</string>
|
||||
<string name="pref_km_right_alt_to_super">Правий Alt → Super</string>
|
||||
<string name="pref_toolbar_alignment">Вирівнювання</string>
|
||||
<string name="tip_empty_server_list">Список серверів порожній.
|
||||
\nНатисніть \'<b>+</b>\', щоб додати сервер, або
|
||||
\nвикористовуйте верхній рядок адреси, щоб зʼєднатися напряму.</string>
|
||||
<string name="desc_navigation_btn">Відкрити шторку переходів</string>
|
||||
<string name="desc_clear_url_btn">Очистити</string>
|
||||
<string name="pref_ui">Інтерфейс</string>
|
||||
<string name="pref_viewer_summary">Картинка-в-картинці, збільшення</string>
|
||||
<string name="desc_saved_servers_tab">Збережені сервери</string>
|
||||
<string name="desc_add_new_server_btn">Додати сервер</string>
|
||||
<string name="desc_keyboard_btn">Відкрити клавіатуру</string>
|
||||
<string name="desc_server_url">Адреса сервера</string>
|
||||
<string name="desc_help_btn">Довідка</string>
|
||||
<string name="pref_viewer">Переглядач</string>
|
||||
<string name="pref_tools">Засоби</string>
|
||||
<string name="pref_gesture_action_double_click">Подвійне клацання</string>
|
||||
<string name="pref_gesture_action_pan">Посунути локальний фрейм</string>
|
||||
<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_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">Ховати локальний вказівник</string>
|
||||
<string name="pref_vk">Віртуальні клавіші</string>
|
||||
<string name="pref_vk_show_all">Показувати всі клавіші</string>
|
||||
<string name="pref_km_back_to_escape">Назад → Escape</string>
|
||||
<string name="pref_km_back_to_escape_summary">Не впливає на панель пересування</string>
|
||||
<string name="pref_zoom_max">Найбільше</string>
|
||||
<string name="pref_cred_autocomplete">Автодоповнення</string>
|
||||
<string name="pref_enable_pip">Картинка-в-картинці</string>
|
||||
<string name="pref_toolbar_alignment_option_end">Кінець</string>
|
||||
<string name="pref_zoom_min">Найменше</string>
|
||||
<string name="pref_credential">Вхід</string>
|
||||
<string name="pref_cred_autocomplete_summary">Пропонувати збережені дані входу під час зʼєднання з новими серверами</string>
|
||||
<string name="pref_experimental_immersive_mode_summary">Ховає всі системні панелі в повноекранному режимі</string>
|
||||
<string name="pref_import_export">Імпорт/експорт серверів</string>
|
||||
<string name="pref_keycode_map">Мапа кодів клавіш</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close">Ковзнути, щоб закрити шторку панелі засобів</string>
|
||||
<string name="title_delete">Видалити</string>
|
||||
<string name="title_edit_server_profile">Змінити сервер</string>
|
||||
<string name="title_duplicate">Дублювати</string>
|
||||
<string name="title_clear">Очистити</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_settings">Налаштування</string>
|
||||
<string name="title_export">Експортувати</string>
|
||||
<string name="title_import">Імпортувати</string>
|
||||
<string name="title_view_only_mode">Лише перегляд</string>
|
||||
<string name="title_hide_remote_cursor">Ховати віддалений вказівник</string>
|
||||
<string name="title_unknown_ssh_host">Невідомий SSH-хост</string>
|
||||
<string name="title_ssh_host_key_changed">Ключ SSH-хоста змінено</string>
|
||||
<string name="title_continue">Продовжити</string>
|
||||
<string name="title_password">Пароль</string>
|
||||
<string name="title_use_repeater">Викор. повторювач</string>
|
||||
<string name="title_private_key">Приватний ключ</string>
|
||||
<string name="title_change">Змінити</string>
|
||||
<string name="title_about">Про застосунок</string>
|
||||
<string name="title_undo">Відмінити</string>
|
||||
<string name="title_credentials_required">Вхід</string>
|
||||
<string name="title_key_compat_mode">Надсилати успадковані події клавіш</string>
|
||||
<string name="title_report_bug">Звіт про ваду</string>
|
||||
<string name="title_github_repo">Вихідний код</string>
|
||||
<string name="hint_server_name">Назва (напр., Робочий ПК)</string>
|
||||
<string name="hint_host">Хост</string>
|
||||
<string name="hint_repeater_host">Хост повторювача</string>
|
||||
<string name="hint_port">Порт</string>
|
||||
<string name="hint_username">Імʼя користувача</string>
|
||||
<string name="msg_server_profile_added">Сервер додано</string>
|
||||
<string name="hint_key_password">Пароль ключа</string>
|
||||
<string name="msg_server_profile_deleted">Сервер видалено</string>
|
||||
<string name="msg_ptr_hiding_not_supported">Не підтримується на цьому пристрої</string>
|
||||
<string name="msg_loading">Завантаження…</string>
|
||||
<string name="msg_imported">Імпортовано</string>
|
||||
<string name="msg_exported">Експортовано</string>
|
||||
<string name="msg_export_auth_required">Автентифікування для експортування серверів</string>
|
||||
<string name="msg_copied_to_clipboard">Скопійовано</string>
|
||||
<string name="tip_urlbar">Ви можете ввести IP-адресу, назву хоста або URI-адресу \'vnc://\'.</string>
|
||||
<string name="desc_settings_btn">Налаштування</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_back_btn">Назад</string>
|
||||
<string name="desc_reset_zoom_btn">Скинути збільшення</string>
|
||||
<string name="desc_discovered_list">Перелік автоматично виявлених серверів</string>
|
||||
<string name="desc_saved_servers_list">Перелік збережених серверів</string>
|
||||
<string name="pref_ui_summary">Смак і дотик</string>
|
||||
<string name="pref_input">Введення</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_middle_click">Середнє клацання</string>
|
||||
<string name="pref_gesture_action_right_click">Праве клацання</string>
|
||||
<string name="pref_two_finger_tap">Торкання двома пальцями</string>
|
||||
<string name="title_remember">Памʼятати</string>
|
||||
<string name="msg_export_auth_missing">Ваш пристрій не захищено. Увімкніть блокування екрана, щоб дозволити автентифікування перед експортуванням.</string>
|
||||
<string name="msg_key_compat_mode_help">Деякі сервери мають обмежену підтримку Unicode. Успадковані події клавіш допоможуть вводити не-англійський текст.</string>
|
||||
<string name="desc_retry_connection_btn">Повторити</string>
|
||||
<string name="tip_discovery">Виявлення може автоматично знаходити підтримувані сервери,
|
||||
\nщо працюють у Вашій мережі.</string>
|
||||
<string name="pref_direct_touch_summary_on">Робити дії в точці доторкання</string>
|
||||
<string name="pref_double_tap">Подвійний дотик</string>
|
||||
<string name="pref_mouse_passthrough">Перехід миші</string>
|
||||
<string name="pref_mouse_passthrough_summary_off">Використовувати події миші для локальних жестів</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close_summary">Під час віддаленого сеансу Ви можете ковзнути, щоб закрити відкриту панель засобів</string>
|
||||
<string name="pref_mouse_passthrough_summary_on">Надсилати події миші відразу на сервер</string>
|
||||
<string name="pref_vk_open_with_keyboard">Відкривати з клавіатурою</string>
|
||||
<string name="pref_km">Мапування клавіш</string>
|
||||
<string name="pref_experimental_immersive_mode">Повний імерсивний режим</string>
|
||||
<string name="pref_rediscovery_indicator">Виділяти попередньо виявлені сервери</string>
|
||||
<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>
|
||||
179
app/src/main/res/values-zh-rCN/strings.xml
Normal file
179
app/src/main/res/values-zh-rCN/strings.xml
Normal 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>
|
||||
|
|
@ -9,9 +9,9 @@
|
|||
<resources>
|
||||
|
||||
<string-array name="theme_entries">
|
||||
<item>Follow System Theme</item>
|
||||
<item>Dark</item>
|
||||
<item>Light</item>
|
||||
<item>@string/pref_theme_option_system</item>
|
||||
<item>@string/pref_theme_option_dark</item>
|
||||
<item>@string/pref_theme_option_light</item>
|
||||
</string-array>
|
||||
<string-array name="theme_values">
|
||||
<item>system</item>
|
||||
|
|
@ -19,27 +19,26 @@
|
|||
<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>Start</item>
|
||||
<item>End</item>
|
||||
<item>@string/pref_toolbar_alignment_option_start</item>
|
||||
<item>@string/pref_toolbar_alignment_option_end</item>
|
||||
</string-array>
|
||||
<string-array name="toolbar_alignment_values">
|
||||
<item>start</item>
|
||||
<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>
|
||||
<item>@string/pref_gesture_action_move_pointer</item>
|
||||
</string-array>
|
||||
<string-array name="tap1_action_values">
|
||||
<item>none</item>
|
||||
<item>left-click</item>
|
||||
<item>move-pointer</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="tap2_action_entries">
|
||||
<item>@string/pref_gesture_action_none</item>
|
||||
<item>@string/pref_gesture_action_right_click</item>
|
||||
|
|
@ -56,14 +55,12 @@
|
|||
<item>@string/pref_gesture_action_double_click</item>
|
||||
<item>@string/pref_gesture_action_middle_click</item>
|
||||
<item>@string/pref_gesture_action_right_click</item>
|
||||
<item>@string/pref_gesture_action_move_pointer</item>
|
||||
</string-array>
|
||||
<string-array name="double_tap_action_values">
|
||||
<item>none</item>
|
||||
<item>double-click</item>
|
||||
<item>middle-click</item>
|
||||
<item>right-click</item>
|
||||
<item>move-pointer</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="long_press_action_entries">
|
||||
|
|
@ -71,14 +68,12 @@
|
|||
<item>@string/pref_gesture_action_double_click</item>
|
||||
<item>@string/pref_gesture_action_middle_click</item>
|
||||
<item>@string/pref_gesture_action_right_click</item>
|
||||
<item>@string/pref_gesture_action_move_pointer</item>
|
||||
</string-array>
|
||||
<string-array name="long_press_action_values">
|
||||
<item>none</item>
|
||||
<item>double-click</item>
|
||||
<item>middle-click</item>
|
||||
<item>right-click</item>
|
||||
<item>move-pointer</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="swipe1_action_entries">
|
||||
|
|
@ -115,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>
|
||||
|
|
@ -9,85 +9,89 @@
|
|||
<resources>
|
||||
<string name="app_name" translatable="false">AVNC</string>
|
||||
|
||||
<string name="title_add_server_profile">Add Server</string>
|
||||
<string name="title_edit_server_profile">Edit Server</string>
|
||||
<string name="title_add_server_profile">Add server</string>
|
||||
<string name="title_edit_server_profile">Edit server</string>
|
||||
<string name="title_duplicate">Duplicate</string>
|
||||
<string name="title_edit">Edit</string>
|
||||
<string name="title_delete">Delete</string>
|
||||
<string name="title_copy_host">Copy Host</string>
|
||||
<string name="title_copy_name">Copy Name</string>
|
||||
<string name="title_clear">Clear</string>
|
||||
<string name="title_copy_host">Copy host</string>
|
||||
<string name="title_copy_name">Copy name</string>
|
||||
<string name="title_advanced">Advanced</string>
|
||||
<string name="title_cancel">Cancel</string>
|
||||
<string name="title_save">Save</string>
|
||||
<string name="title_undo">Undo</string>
|
||||
<string name="title_settings">Settings</string>
|
||||
<string name="title_credentials_required">Credentials required</string>
|
||||
<string name="title_credentials_required">Login</string>
|
||||
<string name="title_remember">Remember</string>
|
||||
<string name="title_export">Export</string>
|
||||
<string name="title_import">Import</string>
|
||||
<string name="title_view_only_mode">View-only Mode</string>
|
||||
<string name="title_hide_remote_cursor">Hide Remote Cursor</string>
|
||||
<string name="title_key_compat_mode">Keyboard Compatibility Mode</string>
|
||||
<string name="title_use_ssh_tunnel">Use SSH Tunnel</string>
|
||||
<string name="title_unknown_ssh_host">Unknown SSH Host</string>
|
||||
<string name="title_ssh_host_key_changed">Host Key changed!</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>
|
||||
<string name="title_unknown_ssh_host">Unknown SSH host</string>
|
||||
<string name="title_ssh_host_key_changed">SSH host key changed</string>
|
||||
<string name="title_continue">Continue</string>
|
||||
<string name="title_delete_servers_before_import">Delete current Servers before Import</string>
|
||||
<string name="title_delete_servers_before_import">Delete current servers before import</string>
|
||||
<string name="title_password">Password</string>
|
||||
<string name="title_use_repeater">Use Repeater</string>
|
||||
<string name="title_private_key">Private Key</string>
|
||||
<string name="title_use_repeater">Use repeater</string>
|
||||
<string name="title_private_key">Private key</string>
|
||||
<string name="title_change">Change</string>
|
||||
<string name="title_about">About</string>
|
||||
<string name="title_license">Licence</string>
|
||||
<string name="title_open_source_libraries">Open Source Libraries</string>
|
||||
<string name="title_report_bug">Report a Bug</string>
|
||||
<string name="title_github_repo">Github Repo</string>
|
||||
<string name="title_open_source_libraries">Open source libraries</string>
|
||||
<string name="title_report_bug">Report an issue</string>
|
||||
<string name="title_github_repo">Source code</string>
|
||||
|
||||
<string name="hint_urlbar">Connect to a Server</string>
|
||||
<string name="hint_server_name">Name (e.g. Work PC)</string>
|
||||
<string name="hint_host">Host</string>
|
||||
<string name="hint_repeater_host">Repeater Host</string>
|
||||
<string name="hint_repeater_host">Repeater host</string>
|
||||
<string name="hint_port">Port</string>
|
||||
<string name="hint_server_id">Server ID</string>
|
||||
<string name="hint_username">Username</string>
|
||||
<string name="hint_password">Password</string>
|
||||
<string name="hint_key_password">Key Password</string>
|
||||
<string name="hint_key_password">Key password</string>
|
||||
|
||||
<string name="msg_about_app">AVNC is an open-source VNC client for Android. It allows you to remotely control any device running a VNC server.</string>
|
||||
<string name="msg_server_profile_added">Server added.</string>
|
||||
<string name="msg_server_profile_deleted">Server deleted.</string>
|
||||
<string name="msg_pip_not_supported">Picture-in-Picture is not supported on this device</string>
|
||||
<string name="msg_ptr_hiding_not_supported">Hiding cursor is not supported on this device</string>
|
||||
<string name="msg_loading">Loading …</string>
|
||||
<string name="msg_imported">Imported Successfully</string>
|
||||
<string name="msg_exported">Exported Successfully</string>
|
||||
<string name="msg_export_auth_required">Authentication is required before Export.</string>
|
||||
<string name="msg_export_auth_missing">Your device is not secured. Turn on screen lock to enable authentication before Export.</string>
|
||||
<string name="msg_invalid_key_file">File is not a valid private key!</string>
|
||||
<string name="msg_about_app">A fast and secure VNC client. Remotely view and control any device running a VNC server.\n\n\nAVNC is an open source and copyleft software — made with help from many contributors.</string>
|
||||
<string name="msg_server_profile_added">Server added</string>
|
||||
<string name="msg_server_profile_deleted">Server deleted</string>
|
||||
<string name="msg_pip_not_supported">Not supported on this device</string>
|
||||
<string name="msg_ptr_hiding_not_supported">Not supported on this device</string>
|
||||
<string name="msg_loading">Loading…</string>
|
||||
<string name="msg_imported">Imported</string>
|
||||
<string name="msg_exported">Exported</string>
|
||||
<string name="msg_export_auth_required">Authenticate to export servers</string>
|
||||
<string name="msg_export_auth_missing">Your device is not secured. Turn on screen lock to enable authentication before export.</string>
|
||||
<string name="msg_invalid_key_file">Invalid private key file</string>
|
||||
<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 don\'t support direct Unicode input. Compatibility Mode tries to send key events in legacy format. \n\nTry enabling/disabling this if you are having trouble entering some characters.</string>
|
||||
<string name="tip_empty_server_list">Server list is empty.\nUse the \'<b>+</b>\' button to add a Server, or\nUse the top Urlbar to directly connect to a server.</string>
|
||||
<string name="tip_urlbar">You can enter hostname or IP address.\n\'vnc://\' URIs are also supported.</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>
|
||||
|
||||
<!-- Accessibility descriptions -->
|
||||
<string name="desc_navigation_btn">Open Navigation Drawer</string>
|
||||
<string name="desc_settings_btn">Open Settings</string>
|
||||
<string name="desc_saved_servers_tab">Saved Servers</string>
|
||||
<string name="desc_discovered_servers_tab">Discovered Servers</string>
|
||||
<string name="desc_add_new_server_btn">Add new Server</string>
|
||||
<string name="desc_save_discovered_server_btn">Save this Server</string>
|
||||
<string name="desc_discovery_btn">Start Server Discovery</string>
|
||||
<string name="desc_discovery_running">Discovering Servers on you network</string>
|
||||
<string name="desc_keyboard_btn">Open Keyboard</string>
|
||||
<string name="desc_virtual_keys_btn">Show Virtual Keys</string>
|
||||
<string name="desc_reset_zoom_btn">Reset Zoom to 100%</string>
|
||||
<string name="desc_retry_connection_btn">Retry Connection</string>
|
||||
<string name="desc_back_btn">Go Back</string>
|
||||
<string name="desc_clear_url_btn">Clear URL</string>
|
||||
<string name="desc_server_url">Enter address of VNC server</string>
|
||||
<string name="desc_help_btn">Show Help</string>
|
||||
<string name="desc_navigation_btn">Open navigation drawer</string>
|
||||
<string name="desc_settings_btn">Settings</string>
|
||||
<string name="desc_saved_servers_tab">Saved servers</string>
|
||||
<string name="desc_discovered_servers_tab">Discovered servers</string>
|
||||
<string name="desc_add_new_server_btn">Add server</string>
|
||||
<string name="desc_save_discovered_server_btn">Save</string>
|
||||
<string name="desc_discovery_btn">Discover servers</string>
|
||||
<string name="desc_discovery_stop_btn">Stop discovering servers</string>
|
||||
<string name="desc_discovery_running">Discovering servers on you network</string>
|
||||
<string name="desc_keyboard_btn">Open keyboard</string>
|
||||
<string name="desc_virtual_keys_btn">Show virtual keys</string>
|
||||
<string name="desc_reset_zoom_btn">Reset zoom</string>
|
||||
<string name="desc_retry_connection_btn">Retry</string>
|
||||
<string name="desc_back_btn">Back</string>
|
||||
<string name="desc_clear_url_btn">Clear</string>
|
||||
<string name="desc_server_url">Server address</string>
|
||||
<string name="desc_help_btn">Help</string>
|
||||
<string name="desc_discovered_list">List of automatically discovered servers</string>
|
||||
<string name="desc_saved_servers_list">List of saved Servers</string>
|
||||
|
||||
|
|
@ -95,79 +99,89 @@
|
|||
<string name="pref_ui">Interface</string>
|
||||
<string name="pref_ui_summary">Look and feel</string>
|
||||
<string name="pref_viewer">Viewer</string>
|
||||
<string name="pref_viewer_summary">Picture-in-Picture, Zoom</string>
|
||||
<string name="pref_viewer_summary">Picture-in-picture, zoom</string>
|
||||
<string name="pref_input">Input</string>
|
||||
<string name="pref_input_summary">Gestures, Mouse, Virtual keys</string>
|
||||
<string name="pref_input_summary">Gestures, mouse, virtual keys</string>
|
||||
<string name="pref_servers">Servers</string>
|
||||
<string name="pref_server_summary">Discovery, Credentials</string>
|
||||
<string name="pref_server_summary">Login, discovery</string>
|
||||
<string name="pref_tools">Tools</string>
|
||||
<string name="pref_tools_summary">Import/Export, Debugging</string>
|
||||
<string name="pref_tools_summary">Import/Export, debugging</string>
|
||||
<string name="pref_experimental">Experimental</string>
|
||||
<string name="pref_experimental_summary">Extra goodies!</string>
|
||||
<string name="pref_experimental_summary">Extra goodies</string>
|
||||
|
||||
<string name="pref_gesture_action_none">None</string>
|
||||
<string name="pref_gesture_action_left_click">Left Click</string>
|
||||
<string name="pref_gesture_action_middle_click">Middle Click</string>
|
||||
<string name="pref_gesture_action_right_click">Right Click</string>
|
||||
<string name="pref_gesture_action_double_click">Double Click</string>
|
||||
<string name="pref_gesture_action_move_pointer">Move Pointer</string>
|
||||
<string name="pref_gesture_action_pan">Pan Local Frame</string>
|
||||
<string name="pref_gesture_action_remote_scroll">Scroll Remote Content</string>
|
||||
<string name="pref_gesture_action_remote_drag">Drag Remote Content</string>
|
||||
<string name="pref_gesture_action_open_keyboard">Open Keyboard</string>
|
||||
<string name="pref_gesture_action_left_click">Left-click</string>
|
||||
<string name="pref_gesture_action_middle_click">Middle-click</string>
|
||||
<string name="pref_gesture_action_right_click">Right-click</string>
|
||||
<string name="pref_gesture_action_double_click">Double-click</string>
|
||||
<string name="pref_gesture_action_move_pointer">Move pointer</string>
|
||||
<string name="pref_gesture_action_pan">Pan local frame</string>
|
||||
<string name="pref_gesture_action_remote_scroll">Scroll remote content</string>
|
||||
<string name="pref_gesture_action_remote_drag">Drag remote content</string>
|
||||
<string name="pref_gesture_action_open_keyboard">Open keyboard</string>
|
||||
|
||||
<string name="pref_theme">Theme</string>
|
||||
<string name="pref_prefer_advanced_editor">Prefer Advanced Editor</string>
|
||||
<string name="pref_prefer_advanced_editor_summary">Enable this to directly open Advanced mode to add/edit Servers</string>
|
||||
<string name="pref_sort_server_list">Sort Server List</string>
|
||||
<string name="pref_sort_server_list_summary">Servers are sorted by their name</string>
|
||||
<string name="pref_theme_option_system">System</string>
|
||||
<string name="pref_theme_option_dark">Dark</string>
|
||||
<string name="pref_theme_option_light">Light</string>
|
||||
<string name="pref_prefer_advanced_editor">Directly open advanced server options</string>
|
||||
<string name="pref_sort_server_list">Sort saved servers by name</string>
|
||||
<string name="pref_gesture">Gestures</string>
|
||||
<string name="pref_single_tap">Single Tap</string>
|
||||
<string name="pref_two_finger_tap">2 Finger Tap (Experimental)</string>
|
||||
<string name="pref_double_tap">Double Tap</string>
|
||||
<string name="pref_long_press">Long Press</string>
|
||||
<string name="pref_swipe1">1 Finger Swipe</string>
|
||||
<string name="pref_swipe2">2 Finger Swipe</string>
|
||||
<string name="pref_drag">Drag (Long Press then Swipe)</string>
|
||||
<string name="pref_direct_touch">Direct touch</string>
|
||||
<string name="pref_direct_touch_summary_on">Do actions at touch-point</string>
|
||||
<string name="pref_direct_touch_summary_off">Do actions at pointer</string>
|
||||
<string name="pref_single_tap">Single tap</string>
|
||||
<string name="pref_two_finger_tap">Two-finger tap</string>
|
||||
<string name="pref_double_tap">Double tap</string>
|
||||
<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</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">Mouse events are used locally for gestures</string>
|
||||
<string name="pref_mouse_passthrough_summary_on">Mouse events are sent directly to Server</string>
|
||||
<string name="pref_hide_local_cursor">Hide Local (Android) Cursor</string>
|
||||
<string name="pref_vk">Virtual Keys</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 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">Show when Keyboard is open</string>
|
||||
<string name="pref_km">Key Mappings</string>
|
||||
<string name="pref_km_right_alt_to_super">Right Alt to Super</string>
|
||||
<string name="pref_km_language_switch_to_super">Language Switch to Super</string>
|
||||
<string name="pref_km_back_to_escape">Back to Escape</string>
|
||||
<string name="pref_km_back_to_escape_summary">Navigation Bar is not affected by this</string>
|
||||
<string name="pref_fullscreen">Use Fullscreen</string>
|
||||
<string name="pref_enable_pip">Enable Picture-in-Picture</string>
|
||||
<string name="pref_vk_open_with_keyboard">Open with keyboard</string>
|
||||
<string name="pref_km">Key mappings</string>
|
||||
<string name="pref_km_right_alt_to_super">Right Alt → Super</string>
|
||||
<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>
|
||||
<string name="pref_toolbar_alignment">Alignment</string>
|
||||
<string name="pref_toolbar_alignment_option_start">Start</string>
|
||||
<string name="pref_toolbar_alignment_option_end">End</string>
|
||||
<string name="pref_zoom">Zoom</string>
|
||||
<string name="pref_zoom_min">Minimum Zoom</string>
|
||||
<string name="pref_zoom_max">Maximum Zoom</string>
|
||||
<string name="pref_clipboard_sync">Clipboard Sync</string>
|
||||
<string name="pref_credential">Credentials</string>
|
||||
<string name="pref_cred_autocomplete">Enable Autocomplete</string>
|
||||
<string name="pref_cred_autocomplete_summary">Suggests known credentials when connecting to a new Server</string>
|
||||
<string name="pref_zoom_min">Minimum</string>
|
||||
<string name="pref_zoom_max">Maximum</string>
|
||||
<string name="pref_clipboard_sync">Clipboard sync</string>
|
||||
<string name="pref_credential">Login</string>
|
||||
<string name="pref_cred_autocomplete">Auto-completion</string>
|
||||
<string name="pref_cred_autocomplete_summary">Suggest saved logins when connecting to new servers</string>
|
||||
<string name="pref_discovery">Discovery</string>
|
||||
<string name="pref_discovery_timeout">Timeout (Seconds)</string>
|
||||
<string name="pref_discovery_auto_start">Auto Start</string>
|
||||
<string name="pref_discovery_auto_start_summary">Start Discovery when App starts</string>
|
||||
<string name="pref_discovery_restart">Restart</string>
|
||||
<string name="pref_discovery_restart_summary">Restart Discovery after it stops</string>
|
||||
<string name="pref_discovery_restart_delay">Restart Delay (Minutes)</string>
|
||||
<string name="pref_import_export">Import/Export Servers</string>
|
||||
<string name="pref_discovery_autorun">Autorun</string>
|
||||
<string name="pref_discovery_autorun_summary">Discover servers while on homepage</string>
|
||||
<string name="pref_rediscovery_indicator">Highlight rediscovered servers</string>
|
||||
<string name="pref_import_export">Import/Export servers</string>
|
||||
<string name="pref_debugging">Debugging</string>
|
||||
<string name="pref_logs">Logs</string>
|
||||
<string name="pref_keycode_map">Keycode Map</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close">Swipe to close Toolbar Drawer</string>
|
||||
<string name="pref_keycode_map">Keycode map</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close">Swipe to close toolbar drawer</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close_summary">During a remote session, you can swipe to close currently open Toolbar</string>
|
||||
<string name="pref_experimental_indicator">Discovery indicator for saved Servers</string>
|
||||
<string name="pref_experimental_indicator_summary">Highlights already saved servers which are also found by Discovery.\nRequires App restart.</string>
|
||||
<string name="pref_experimental_immersive_mode">Full immersive mode</string>
|
||||
<string name="pref_experimental_immersive_mode_summary">Hides all system bars in fullscreen mode</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -19,14 +19,12 @@
|
|||
|
||||
<SwitchPreference
|
||||
app:defaultValue="false"
|
||||
app:key="prefer_advanced_editor"
|
||||
app:summary="@string/pref_prefer_advanced_editor_summary"
|
||||
app:title="@string/pref_prefer_advanced_editor" />
|
||||
app:key="sort_server_list"
|
||||
app:title="@string/pref_sort_server_list" />
|
||||
|
||||
<SwitchPreference
|
||||
app:defaultValue="false"
|
||||
app:key="sort_server_list"
|
||||
app:summary="@string/pref_sort_server_list_summary"
|
||||
app:title="@string/pref_sort_server_list" />
|
||||
app:key="prefer_advanced_editor"
|
||||
app:title="@string/pref_prefer_advanced_editor" />
|
||||
|
||||
</PreferenceScreen>
|
||||
|
|
@ -9,12 +9,6 @@
|
|||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
app:title="@string/pref_experimental">
|
||||
|
||||
<SwitchPreference
|
||||
app:defaultValue="false"
|
||||
app:key="experimental_indicator"
|
||||
app:summary="@string/pref_experimental_indicator_summary"
|
||||
app:title="@string/pref_experimental_indicator" />
|
||||
|
||||
<SwitchPreference
|
||||
app:defaultValue="true"
|
||||
app:key="experimental_vnc_toolbar_swipe_close"
|
||||
|
|
@ -22,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" />
|
||||
|
|
|
|||
|
|
@ -6,20 +6,20 @@
|
|||
~ 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
|
||||
app:icon="@drawable/ic_gesture"
|
||||
app:title="@string/pref_gesture">
|
||||
|
||||
<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" />
|
||||
<SwitchPreference
|
||||
app:defaultValue="true"
|
||||
app:key="gesture_direct_touch"
|
||||
app:summaryOff="@string/pref_direct_touch_summary_off"
|
||||
app:summaryOn="@string/pref_direct_touch_summary_on"
|
||||
app:title="@string/pref_direct_touch" />
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue="double-click"
|
||||
|
|
@ -38,7 +38,16 @@
|
|||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue="open-keyboard"
|
||||
app:entries="@array/tap2_action_entries"
|
||||
app:entryValues="@array/tap2_action_values"
|
||||
app:key="gesture_tap2"
|
||||
app:title="@string/pref_two_finger_tap"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<com.gaurav.avnc.ui.prefs.ListPreferenceEx
|
||||
app:defaultValue="pan"
|
||||
app:dependency="gesture_direct_touch"
|
||||
app:entries="@array/swipe1_action_entries"
|
||||
app:entryValues="@array/swipe1_action_values"
|
||||
app:key="gesture_swipe1"
|
||||
|
|
@ -53,21 +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" />
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue="open-keyboard"
|
||||
app:entries="@array/tap2_action_entries"
|
||||
app:entryValues="@array/tap2_action_values"
|
||||
app:key="gesture_tap2"
|
||||
app:title="@string/pref_two_finger_tap"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
<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
|
||||
|
|
@ -85,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
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
~ See COPYING.txt for more details.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
app:title="@string/pref_servers">
|
||||
|
||||
|
||||
|
|
@ -32,38 +31,16 @@
|
|||
app:icon="@drawable/ic_search"
|
||||
app:title="@string/pref_discovery">
|
||||
|
||||
<SeekBarPreference
|
||||
android:max="90"
|
||||
app:defaultValue="10"
|
||||
app:key="discovery_timeout"
|
||||
app:min="5"
|
||||
app:showSeekBarValue="true"
|
||||
app:title="@string/pref_discovery_timeout"
|
||||
app:updatesContinuously="true" />
|
||||
<SwitchPreference
|
||||
app:defaultValue="true"
|
||||
app:key="discovery_autorun"
|
||||
app:summary="@string/pref_discovery_autorun_summary"
|
||||
app:title="@string/pref_discovery_autorun" />
|
||||
|
||||
<SwitchPreference
|
||||
app:defaultValue="true"
|
||||
app:key="discovery_auto_start"
|
||||
app:summary="@string/pref_discovery_auto_start_summary"
|
||||
app:title="@string/pref_discovery_auto_start" />
|
||||
|
||||
<SwitchPreference
|
||||
android:dependency="discovery_auto_start"
|
||||
app:defaultValue="false"
|
||||
app:key="discovery_restart"
|
||||
app:summary="@string/pref_discovery_restart_summary"
|
||||
app:title="@string/pref_discovery_restart" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:dependency="discovery_restart"
|
||||
android:max="30"
|
||||
app:defaultValue="5"
|
||||
app:key="discovery_restart_delay"
|
||||
app:min="1"
|
||||
app:showSeekBarValue="true"
|
||||
app:title="@string/pref_discovery_restart_delay"
|
||||
app:updatesContinuously="true" />
|
||||
|
||||
app:key="rediscovery_indicator"
|
||||
app:title="@string/pref_rediscovery_indicator" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -86,6 +86,30 @@ class FrameStateTest {
|
|||
assertEquals(2F, state.baseScale) //base scale should also increase
|
||||
}
|
||||
|
||||
@Test
|
||||
fun baseScaleTest7() {
|
||||
val state = FrameState()
|
||||
state.setWindowSize(50f, 100f)
|
||||
state.setFramebufferSize(50f, 100f) //Remote screen in portrait mode
|
||||
assertEquals(1F, state.baseScale)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun baseScaleTest8() {
|
||||
val state = FrameState()
|
||||
state.setWindowSize(50f, 100f)
|
||||
state.setFramebufferSize(25f, 50f) //Remote screen in portrait mode, but half size
|
||||
assertEquals(2F, state.baseScale)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun baseScaleTest9() {
|
||||
val state = FrameState()
|
||||
state.setWindowSize(50f, 100f)
|
||||
state.setFramebufferSize(100f, 200f) //Remote screen in portrait mode, but double size
|
||||
assertEquals(.5F, state.baseScale)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun positionCoerceTest1() {
|
||||
val state = FrameState()
|
||||
|
|
|
|||
21
build.gradle
21
build.gradle
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
|||
2
extern/libjpeg-turbo
vendored
2
extern/libjpeg-turbo
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit ecf021bc0d6f435daacff7c35ccaeef0145df1b9
|
||||
Subproject commit c5f269eb9665435271c05fbcaf8721fa58e9eafa
|
||||
2
extern/libvncserver
vendored
2
extern/libvncserver
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit e5dc3d42b913ab8c3b757de572af708b441e669c
|
||||
Subproject commit ee547c7a39a0c63b24140db5dd2d21d72c525252
|
||||
2
extern/wolfssl
vendored
2
extern/wolfssl
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 7e01af012157bc20c840011a018619915380f05c
|
||||
Subproject commit 57aac1c50b45275c7a99eca32ad985998b292dc8
|
||||
16
metadata/de/full_description.txt
Normal file
16
metadata/de/full_description.txt
Normal 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
|
||||
1
metadata/de/short_description.txt
Normal file
1
metadata/de/short_description.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
Schneller und sicherer VNC-Client für Android
|
||||
9
metadata/en-US/changelogs/10.txt
Normal file
9
metadata/en-US/changelogs/10.txt
Normal 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
|
||||
1
metadata/en-US/changelogs/11.txt
Normal file
1
metadata/en-US/changelogs/11.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix click position of Mouse Back actions
|
||||
4
metadata/en-US/changelogs/12.txt
Normal file
4
metadata/en-US/changelogs/12.txt
Normal 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)
|
||||
3
metadata/en-US/changelogs/13.txt
Normal file
3
metadata/en-US/changelogs/13.txt
Normal 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
|
||||
3
metadata/en-US/changelogs/14.txt
Normal file
3
metadata/en-US/changelogs/14.txt
Normal 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
|
||||
8
metadata/en-US/changelogs/8.txt
Normal file
8
metadata/en-US/changelogs/8.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
* Added trackpad/touchpad mode
|
||||
* Captured Window key & Alt+Tab on Samsung DeX
|
||||
* Optimized zoom for portrait-mode remote screens
|
||||
* Fixed sensitivity of Drag gesture
|
||||
* Translations: Portuguese-BR (by João Paulo),
|
||||
Norwegian Bokmål (by Allan Nordhøy),
|
||||
Persian (by Eshagh Shahedany),
|
||||
French (by J. Lavoie)
|
||||
9
metadata/en-US/changelogs/9.txt
Normal file
9
metadata/en-US/changelogs/9.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
* Improved login auto-completion
|
||||
* Improved server discovery
|
||||
* New language:
|
||||
Serbian (by Саша Петровић)
|
||||
Ukrainian (by Andrij Mizyk)
|
||||
* Updated language:
|
||||
Norwegian Bokmål (by Allan Nordhøy)
|
||||
Persian (by Eshagh Shahedany)
|
||||
French (by J. Lavoie, Nathan)
|
||||
3
metadata/fa_IR/changelogs/2.txt
Normal file
3
metadata/fa_IR/changelogs/2.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
* تونل SSH محکمتر
|
||||
* رفع نشت هدایتگر درگاه
|
||||
* بهروز رسانی فرادادهٔ کاره
|
||||
2
metadata/fa_IR/changelogs/3.txt
Normal file
2
metadata/fa_IR/changelogs/3.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
* بهبود زمان راهاندازی برنامه
|
||||
* افزودن یک اولویت برای مرتبسازی کارسازها
|
||||
3
metadata/fa_IR/changelogs/4.txt
Normal file
3
metadata/fa_IR/changelogs/4.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
* افزودن پشتیبانی از پد لمسی سامسونگ (توسط Tuan Pham)
|
||||
* افزودن پشتیبانی از stylus/pen
|
||||
* افزودن حرکت ضربهٔ دو-انگشتی
|
||||
3
metadata/fa_IR/changelogs/5.txt
Normal file
3
metadata/fa_IR/changelogs/5.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
* Allow key mappings to SUPER/Window key (by Tuan Pham)
|
||||
* Ensure virtual meta-keys are always released (by thechosenreader)
|
||||
* Added diacritics (accent) support
|
||||
3
metadata/fa_IR/changelogs/6.txt
Normal file
3
metadata/fa_IR/changelogs/6.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
* Improved keyboard shortcuts support
|
||||
* Improved diacritic (accent) support
|
||||
* Added full immersive mode (experimental)
|
||||
3
metadata/fa_IR/changelogs/7.txt
Normal file
3
metadata/fa_IR/changelogs/7.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
* افزودن ترجمهٔ پرتغالی (برزیل) (توسط inkhorn)
|
||||
* افزودن یک ترجیح برای پنهان کردن اشارهگر محلّی اندروید (توسط Henry H)
|
||||
* برطرف کردن اداره کردن شناور قلم در برخی افزارها (by Henry H)
|
||||
8
metadata/fa_IR/changelogs/8.txt
Normal file
8
metadata/fa_IR/changelogs/8.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
* افزودن حالت تاچپد/ترکپک
|
||||
* ضبط کردن کلید ویندوز و دگرساز+جهش در Samsung DeX
|
||||
* بهینهسازی بزرگنمایی برای صفحهنمایشهای عمودی دوردست
|
||||
* برطرف کردن حساسیت حرکت کشیدن
|
||||
* ترجمه: پرتغالی برزیل (توسط João Paulo)،
|
||||
بوکملی نروژ (توسط Allan Nordhøy)،
|
||||
فارسی (توسط Eshagh Shahedany)،
|
||||
فرانسوی (توسط J. Lavoie)
|
||||
9
metadata/fa_IR/changelogs/9.txt
Normal file
9
metadata/fa_IR/changelogs/9.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
* بهبود در تکمیل خودکار ورود به سامانه
|
||||
* بهبود در اکتشاف کارسازها
|
||||
* زبانهای جدید:
|
||||
صربستانی (توسط Саша Петровић)
|
||||
اوکراینی (توسط Andrij Mizyk)
|
||||
* زبانهای بهروز شده:
|
||||
بوکمالی نروژ (توسط Allan Nordhøy)
|
||||
فارسی (توسط Eshagh Shahedany)
|
||||
فرانسوری (توسط J. Lavoie, Nathan)
|
||||
16
metadata/fa_IR/full_description.txt
Normal file
16
metadata/fa_IR/full_description.txt
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
برنامهٔ AVNC یک کارخواه متنباز VNC برای اندروید است. این به شما اجازهٔ کنترل از راه دور هر افزارهای که یک کارساز VNC را اجرا کند، را میدهد.
|
||||
|
||||
قابلیتها:
|
||||
|
||||
- طراحی متریال (به همراه زمینهٔ تاریک)
|
||||
- حرکات قابل پیکربندی
|
||||
- رمزگذاری محکم
|
||||
- کلیدهای مجازی
|
||||
- حالت تصویر در تصویر
|
||||
- حالت فقط مشاهده
|
||||
- اکتشاف کارساز بدون پیکربندی
|
||||
- پشتیبانی از TLS (AnonTLS, VeNCrypt)
|
||||
- تونل SSH (VNC بر روی SSH)
|
||||
- درونریزی/برونریزی کارسازها
|
||||
- پشتیبانی از تکرارکنندهٔ VNC
|
||||
- همگامسازی تختهگیره با کارساز
|
||||
1
metadata/fa_IR/short_description.txt
Normal file
1
metadata/fa_IR/short_description.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
یک کارخواه VNC سریع و ایمن برای اندروید
|
||||
3
metadata/fr/changelogs/2.txt
Normal file
3
metadata/fr/changelogs/2.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
* Renforcement du tunnel SSH
|
||||
* Correction de la fuite du port de redirection
|
||||
* Mise à jour des métadonnées de l'application
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue