Compare commits
No commits in common. "master" and "v1.7.0" have entirely different histories.
113 changed files with 443 additions and 2362 deletions
21
.github/workflows/main.yml
vendored
21
.github/workflows/main.yml
vendored
|
|
@ -1,6 +1,10 @@
|
|||
name: Android CI
|
||||
|
||||
on: push
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
@ -19,19 +23,10 @@ jobs:
|
|||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle
|
||||
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'
|
||||
run: ./gradlew assembleDebug
|
||||
|
||||
- name: Upload APK
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: avnc-ci
|
||||
path: app/build/outputs/apk/ci/app-ci.apk
|
||||
name: dubug-apk
|
||||
path: app/build/outputs/apk/debug/*.apk
|
||||
29
.github/workflows/pr.yml
vendored
29
.github/workflows/pr.yml
vendored
|
|
@ -1,29 +0,0 @@
|
|||
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,6 +4,24 @@
|
|||
<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" />
|
||||
|
|
|
|||
|
|
@ -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 CACHE BOOL "Build shared Libs" FORCE)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
|
||||
###############################################################################
|
||||
# Utilities
|
||||
|
|
@ -19,7 +19,7 @@ macro(avnc_check_submodule name)
|
|||
endmacro()
|
||||
|
||||
|
||||
# Required to enable SIMD support on ARM
|
||||
# Requred 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
|
||||
# manually set some flags to enable OpenSSL compatibility layer
|
||||
# manully 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 14
|
||||
versionName "1.9.2"
|
||||
versionCode 9
|
||||
versionName "1.7.0"
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
|
|
@ -42,12 +42,6 @@ android {
|
|||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
|
||||
ci {
|
||||
initWith(release)
|
||||
applicationIdSuffix '.ci'
|
||||
versionNameSuffix ' (CI)'
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
|
|
@ -66,15 +60,6 @@ android {
|
|||
path file('CMakeLists.txt')
|
||||
}
|
||||
}
|
||||
|
||||
bundle {
|
||||
density {
|
||||
enableSplit false
|
||||
}
|
||||
language {
|
||||
enableSplit false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
@ -83,7 +68,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-alpha04"
|
||||
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha03"
|
||||
implementation "androidx.preference:preference:1.1.1"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.0"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ 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
|
||||
|
|
@ -30,13 +31,14 @@ 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)),
|
||||
|
|
@ -44,31 +46,35 @@ class DiscoveryListTest {
|
|||
hasDescendant(withText(testProfile.host)),
|
||||
)
|
||||
|
||||
private fun addTestProfile() {
|
||||
// Directly modify internal list to simulate discovery
|
||||
activityRule.scenario.onActivity { it.viewModel.discovery.servers.postValue(arrayListOf(testProfile)) }
|
||||
onView(testProfileMatcher()).checkWillBeDisplayed()
|
||||
}
|
||||
private lateinit var discovery: Discovery
|
||||
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val activityRule = ActivityScenarioRule(HomeActivity::class.java)
|
||||
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
// Stop discovery, otherwise tests will hang due to progressbar animation
|
||||
// We can't even use the stop button
|
||||
activityRule.scenario.onActivity { it.viewModel.stopDiscovery() }
|
||||
activityRule.scenario.onActivity {
|
||||
discovery = it.viewModel.discovery
|
||||
discovery.stop()
|
||||
}
|
||||
|
||||
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))
|
||||
|
|
@ -79,7 +85,6 @@ 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()
|
||||
|
|
@ -89,7 +94,6 @@ class DiscoveryListTest {
|
|||
|
||||
@Test
|
||||
fun copyHost() {
|
||||
addTestProfile()
|
||||
onView(testProfileMatcher()).doLongClick()
|
||||
onView(withText(R.string.title_copy_host)).doClick()
|
||||
|
||||
|
|
@ -98,8 +102,10 @@ class DiscoveryListTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun noServerFound() {
|
||||
activityRule.scenario.onActivity { it.viewModel.discovery.servers.postValue(arrayListOf()) }
|
||||
fun emptyListTest() {
|
||||
discovery.servers.postValue(arrayListOf())
|
||||
|
||||
onView(withContentDescription(R.string.desc_discovery_btn)).checkWillBeDisplayed()
|
||||
onView(withText(R.string.tip_discovery)).checkIsDisplayed()
|
||||
}
|
||||
}
|
||||
|
|
@ -45,10 +45,9 @@ class TouchHandlerTest {
|
|||
instrumentation.runOnMainSync {
|
||||
val viewModel = spyk(VncViewModel(ApplicationProvider.getApplicationContext()))
|
||||
|
||||
// All of these needs to be mocked
|
||||
// Both 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
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
|
||||
|
|
@ -35,7 +36,8 @@ class VncActivityTest {
|
|||
testServer.start()
|
||||
|
||||
val profile = ServerProfile(host = testServer.host, port = testServer.port)
|
||||
val intent = createVncIntent(targetContext, profile)
|
||||
val intent = Intent(targetContext, VncActivity::class.java)
|
||||
.putExtra("com.gaurav.avnc.server_profile", profile)
|
||||
|
||||
ActivityScenario.launch<VncActivity>(intent).use {
|
||||
onView(withId(R.id.frame_view)).checkWillBeDisplayed() // Wait for connection
|
||||
|
|
|
|||
|
|
@ -22,16 +22,15 @@ class VncClientTest {
|
|||
override fun onCredentialRequired() = UserCredential()
|
||||
override fun onFramebufferUpdated() {}
|
||||
override fun onFramebufferSizeChanged(width: Int, height: Int) {}
|
||||
override fun onPointerMoved(x: Int, y: Int) {}
|
||||
override fun onGotXCutText(text: String) {
|
||||
cutText = text
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private lateinit var server: TestServer
|
||||
private lateinit var client: VncClient
|
||||
private lateinit var observer: TestObserver
|
||||
lateinit var server: TestServer
|
||||
lateinit var client: VncClient
|
||||
lateinit var observer: TestObserver
|
||||
|
||||
private val sampleText = "Smelly Cat"
|
||||
private val sampleTextWithAccent = "Pokémon Fõbár"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
package com.gaurav.avnc.vnc
|
||||
|
||||
import android.net.Uri
|
||||
import com.gaurav.avnc.model.ServerProfile
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
|
|
@ -45,7 +44,6 @@ class VncUriTest {
|
|||
assertEquals("foo", uri.password)
|
||||
assertEquals(2, uri.securityType)
|
||||
assertEquals(true, uri.viewOnly)
|
||||
assertEquals(ServerProfile.CHANNEL_TCP, uri.channelType)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -57,7 +55,6 @@ class VncUriTest {
|
|||
assertEquals("foo", uri.password)
|
||||
assertEquals(2, uri.securityType)
|
||||
assertEquals(true, uri.viewOnly)
|
||||
assertEquals(ServerProfile.CHANNEL_TCP, uri.channelType)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
<activity-alias
|
||||
android:name=".StartupActivity"
|
||||
android:exported="true"
|
||||
android:targetActivity=".ui.home.SplashActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
|
@ -34,7 +33,6 @@
|
|||
|
||||
<activity-alias
|
||||
android:name=".UriReceiverActivity"
|
||||
android:exported="true"
|
||||
android:targetActivity=".ui.vnc.UriReceiverActivity">
|
||||
|
||||
<intent-filter>
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* 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
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* 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,12 +6,43 @@
|
|||
* 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>
|
||||
|
||||
#include "ClientEx.h"
|
||||
#include "Utility.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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
|
|
@ -23,7 +54,7 @@ struct JniContext {
|
|||
jclass managedCls; //Managed `VncClient` class
|
||||
jmethodID cbFramebufferUpdated; //Cached reference to managed callback
|
||||
|
||||
JNIEnv *getEnv() const {
|
||||
JNIEnv *getEnv() {
|
||||
JNIEnv *env = nullptr;
|
||||
|
||||
if (vm != nullptr && vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK)
|
||||
|
|
@ -62,10 +93,80 @@ 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 = &log_info;
|
||||
rfbClientErr = &log_error;
|
||||
rfbClientLog = &Logger::i;
|
||||
rfbClientErr = &Logger::e;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* 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
|
||||
|
|
@ -142,10 +243,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)V");
|
||||
env->CallVoidMethod(obj, mid, x, y);
|
||||
jmethodID mid = env->GetMethodID(cls, "cbHandleCursorPos", "(II)Z");
|
||||
jboolean result = env->CallBooleanMethod(obj, mid, x, y);
|
||||
|
||||
return TRUE;
|
||||
return result == JNI_TRUE ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
static void onFinishedFrameBufferUpdate(rfbClient *client) {
|
||||
|
|
@ -161,38 +262,36 @@ static void onFinishedFrameBufferUpdate(rfbClient *client) {
|
|||
*/
|
||||
static rfbBool onMallocFrameBuffer(rfbClient *client) {
|
||||
|
||||
const auto width = client->width;
|
||||
const auto height = client->height;
|
||||
const auto requestedSize = (uint64_t) width * height * client->format.bitsPerPixel / 8;
|
||||
auto requestedSize = (uint64_t) client->width * client->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(ex->mutex);
|
||||
LOCK(client->fbMutex);
|
||||
{
|
||||
|
||||
if (client->frameBuffer)
|
||||
free(client->frameBuffer);
|
||||
|
||||
client->frameBuffer = static_cast<uint8_t *>(malloc(allocSize));
|
||||
|
||||
if (client->frameBuffer) {
|
||||
ex->fbRealWidth = width;
|
||||
ex->fbRealHeight = height;
|
||||
client->fbRealWidth = client->width;
|
||||
client->fbRealHeight = client->height;
|
||||
memset(client->frameBuffer, 0, allocSize); //Clear any garbage
|
||||
} else {
|
||||
ex->fbRealWidth = 0;
|
||||
ex->fbRealHeight = 0;
|
||||
client->fbRealWidth = 0;
|
||||
client->fbRealHeight = 0;
|
||||
}
|
||||
}
|
||||
UNLOCK(ex->mutex);
|
||||
UNLOCK(client->fbMutex);
|
||||
|
||||
if (client->frameBuffer == nullptr) {
|
||||
errno = ENOMEM;
|
||||
rfbClientErr("CRITICAL: frameBuffer allocation failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
|
@ -202,28 +301,11 @@ static rfbBool onMallocFrameBuffer(rfbClient *client) {
|
|||
auto cls = context.managedCls;
|
||||
|
||||
auto mid = env->GetMethodID(cls, "cbFramebufferSizeChanged", "(II)V");
|
||||
env->CallVoidMethod(obj, mid, width, height);
|
||||
env->CallVoidMethod(obj, mid, client->width, client->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.
|
||||
*/
|
||||
|
|
@ -235,7 +317,6 @@ static void setCallbacks(rfbClient *client) {
|
|||
client->HandleCursorPos = onHandleCursorPos;
|
||||
client->FinishedFrameBufferUpdate = onFinishedFrameBufferUpdate;
|
||||
client->MallocFrameBuffer = onMallocFrameBuffer;
|
||||
client->GotCursorShape = onGotCursorShape;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -250,9 +331,6 @@ 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;
|
||||
|
||||
|
|
@ -275,10 +353,7 @@ Java_com_gaurav_avnc_vnc_VncClient_nativeConfigure(JNIEnv *env, jobject thiz, jl
|
|||
SetClientAuthSchemes(client, auth, 1);
|
||||
}
|
||||
|
||||
if (use_local_cursor) {
|
||||
client->appData.useRemoteCursor = TRUE;
|
||||
getClientExtension(client)->cursor = newCursor();
|
||||
}
|
||||
client->appData.useRemoteCursor = use_local_cursor;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
|
|
@ -321,7 +396,6 @@ Java_com_gaurav_avnc_vnc_VncClient_nativeCleanup(JNIEnv *env, jobject thiz,
|
|||
auto managedClient = getManagedClient(client);
|
||||
env->DeleteGlobalRef(managedClient);
|
||||
|
||||
freeClientExtension(client);
|
||||
rfbClientCleanup(client);
|
||||
}
|
||||
|
||||
|
|
@ -354,7 +428,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 ? TRUE : FALSE);
|
||||
return (jboolean) SendKeyEvent((rfbClient *) client_ptr, (uint32_t) key, is_down);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
|
|
@ -414,96 +488,20 @@ 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(ex->mutex);
|
||||
LOCK(client->fbMutex);
|
||||
|
||||
if (client->frameBuffer) {
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA,
|
||||
ex->fbRealWidth,
|
||||
ex->fbRealHeight,
|
||||
client->fbRealWidth,
|
||||
client->fbRealHeight,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
client->frameBuffer);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
UNLOCK(client->fbMutex);
|
||||
}
|
||||
|
|
@ -86,10 +86,8 @@ 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 = true,
|
||||
var useLocalCursor: Boolean = false,
|
||||
|
||||
/**
|
||||
* Compatibility mode for key events.
|
||||
|
|
|
|||
|
|
@ -160,9 +160,7 @@ class ProfileEditorFragment : DialogFragment() {
|
|||
toolbar.setNavigationOnClickListener { dismiss() }
|
||||
keyImportBtn.setOnClickListener { keyFilePicker.launch(arrayOf("*/*")) }
|
||||
keyCompatModeHelpBtn.setOnClickListener {
|
||||
MsgDialog.show(parentFragmentManager,
|
||||
getString(R.string.title_key_compat_mode),
|
||||
getString(R.string.msg_key_compat_mode_help))
|
||||
MsgDialog.show(parentFragmentManager, R.string.title_key_compat_mode, R.string.msg_key_compat_mode_help)
|
||||
}
|
||||
|
||||
// Setup initial values of some CheckBox views.
|
||||
|
|
@ -177,8 +175,8 @@ class ProfileEditorFragment : DialogFragment() {
|
|||
|
||||
// TODO Move it to proper place
|
||||
val securityTypes = mapOf(
|
||||
getString(R.string.title_default_security) to 0,
|
||||
getString(R.string.pref_gesture_action_none) to 1,
|
||||
"Default Security" to 0,
|
||||
"None" to 1,
|
||||
"VncAuth" to 2,
|
||||
"AnonTLS" to 18,
|
||||
"VeNCrypt" to 19
|
||||
|
|
|
|||
|
|
@ -140,11 +140,6 @@ class ServerTabs(val activity: HomeActivity) {
|
|||
|
||||
inner class ViewHolder(val binding: ServerSavedItemBinding)
|
||||
: ProfileViewHolder(viewModel, binding.root, R.menu.saved_server)
|
||||
|
||||
object Differ : DiffUtil.ItemCallback<ServerProfile>() {
|
||||
override fun areItemsTheSame(old: ServerProfile, new: ServerProfile) = (old.ID == new.ID)
|
||||
override fun areContentsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -189,14 +184,6 @@ class ServerTabs(val activity: HomeActivity) {
|
|||
binding.saveBtn.setOnClickListener { viewModel.onNewProfile(profile) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Profiles generated by discovery don't have unique IDs, so we compare the whole profile.
|
||||
*/
|
||||
object Differ : DiffUtil.ItemCallback<ServerProfile>() {
|
||||
override fun areItemsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
|
||||
override fun areContentsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -242,4 +229,9 @@ class ServerTabs(val activity: HomeActivity) {
|
|||
Snackbar.make(rootView, R.string.msg_copied_to_clipboard, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private object Differ : DiffUtil.ItemCallback<ServerProfile>() {
|
||||
override fun areItemsTheSame(old: ServerProfile, new: ServerProfile) = (old.ID == new.ID)
|
||||
override fun areContentsTheSame(old: ServerProfile, new: ServerProfile) = (old == new)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,12 +10,7 @@ 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.
|
||||
|
|
@ -32,26 +27,4 @@ class ListPreferenceEx(context: Context, attrs: AttributeSet) : ListPreference(c
|
|||
return disabledStateSummary
|
||||
return super.getSummary()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Message shown in a dialog, when help button of the preference is clicked.
|
||||
* This will only work if [R.layout.help_btn] is used as widget layout.
|
||||
*/
|
||||
var helpMessage: CharSequence? = null
|
||||
|
||||
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
super.onBindViewHolder(holder)
|
||||
(holder.findViewById(R.id.help_btn) as? ImageButton)?.setOnClickListener { showHelp() }
|
||||
}
|
||||
|
||||
private fun showHelp() {
|
||||
helpMessage?.let { helpMessage ->
|
||||
(context as? FragmentActivity)?.let { fragmentActivity ->
|
||||
MsgDialog.show(fragmentActivity.supportFragmentManager,
|
||||
fragmentActivity.getString(R.string.desc_help_btn),
|
||||
helpMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,13 +8,11 @@
|
|||
|
||||
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
|
||||
|
|
@ -95,8 +93,6 @@ 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)
|
||||
|
||||
|
|
@ -109,22 +105,9 @@ class PrefsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreference
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
findPreference<ListPreferenceEx>("gesture_swipe1")!!.apply {
|
||||
disabledStateSummary = getString(R.string.pref_gesture_action_move_pointer)
|
||||
}
|
||||
naturalScrollingUpdater?.onSharedPreferenceChanged(null, null) //Initial update
|
||||
swipe1.sharedPreferences.registerOnSharedPreferenceChangeListener(naturalScrollingUpdater)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class Dispatcher(private val activity: VncActivity) {
|
|||
* Action configuration
|
||||
**************************************************************************/
|
||||
private val directMode = DirectMode()
|
||||
private val relativeMode = RelativeMode()
|
||||
private val relativeMode by lazy { RelativeMode() } //Lazy to avoid pointer sync behaviour
|
||||
private val defaultMode = if (gesturePref.directTouch) directMode else relativeMode
|
||||
|
||||
private val tap1Action = selectPointAction(gesturePref.tap1)
|
||||
|
|
@ -73,8 +73,6 @@ class Dispatcher(private val activity: VncActivity) {
|
|||
private val swipe2Action = selectSwipeAction(gesturePref.swipe2)
|
||||
private val dragAction = selectSwipeAction(gesturePref.drag)
|
||||
|
||||
private val mouseBackAction = selectPointAction(viewModel.pref.input.mouseBack)
|
||||
|
||||
private fun selectPointAction(actionName: String): (PointF) -> Unit {
|
||||
return when (actionName) {
|
||||
"left-click" -> { p -> defaultMode.doClick(PointerButton.Left, p) }
|
||||
|
|
@ -113,8 +111,8 @@ class Dispatcher(private val activity: VncActivity) {
|
|||
* Event receivers
|
||||
**************************************************************************/
|
||||
|
||||
fun onGestureStart() = defaultMode.onGestureStart()
|
||||
fun onGestureStop(p: PointF) = defaultMode.onGestureStop(p)
|
||||
fun onGestureStart() = stopFling()
|
||||
fun onGestureStop(p: PointF) = defaultMode.doButtonRelease(p)
|
||||
|
||||
fun onTap1(p: PointF) = tap1Action(p)
|
||||
fun onTap2(p: PointF) = tap2Action(p)
|
||||
|
|
@ -126,13 +124,12 @@ 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) = defaultMode.doFling(vx, vy)
|
||||
fun onFling(vx: Float, vy: Float) = doFling(vx, vy)
|
||||
|
||||
fun onMouseButtonDown(button: PointerButton, p: PointF) = directMode.doButtonDown(button, p)
|
||||
fun onMouseButtonUp(button: PointerButton, p: PointF) = directMode.doButtonUp(button, p)
|
||||
fun onMouseMove(p: PointF) = directMode.doMovePointer(p, 0f, 0f)
|
||||
fun onMouseScroll(p: PointF, hs: Float, vs: Float) = directMode.doRemoteScrollFromMouse(p, hs, vs)
|
||||
fun onMouseBack(p: PointF) = mouseBackAction(p)
|
||||
|
||||
fun onStylusTap(p: PointF) = directMode.doClick(PointerButton.Left, p)
|
||||
fun onStylusDoubleTap(p: PointF) = directMode.doDoubleClick(PointerButton.Left, p)
|
||||
|
|
@ -150,6 +147,9 @@ class Dispatcher(private val activity: VncActivity) {
|
|||
private fun doScale(scaleFactor: Float, fx: Float, fy: Float) = viewModel.updateZoom(scaleFactor, fx, fy)
|
||||
private fun doPan(dx: Float, dy: Float) = viewModel.panFrame(dx, dy)
|
||||
|
||||
private fun doFling(vx: Float, vy: Float) = viewModel.frameScroller.fling(vx, vy)
|
||||
private fun stopFling() = viewModel.frameScroller.stop()
|
||||
|
||||
/**
|
||||
* Most actions have the same implementation in both modes, only difference being
|
||||
* the point where event is sent. [transformPoint] is used for this mode-specific
|
||||
|
|
@ -160,15 +160,10 @@ class Dispatcher(private val activity: VncActivity) {
|
|||
private var accumulatedDx = 0F
|
||||
private var accumulatedDy = 0F
|
||||
private val deltaPerScroll = 20F //For how much dx/dy, one scroll event will be sent
|
||||
private val yScrollDirection = (if (gesturePref.naturalScrolling) 1 else -1)
|
||||
|
||||
abstract fun transformPoint(p: PointF): PointF?
|
||||
abstract fun doMovePointer(p: PointF, dx: Float, dy: Float)
|
||||
abstract fun doDrag(p: PointF, dx: Float, dy: Float)
|
||||
abstract fun doFling(vx: Float, vy: Float)
|
||||
|
||||
open fun onGestureStart() {}
|
||||
open fun onGestureStop(p: PointF) = doButtonRelease(p)
|
||||
|
||||
fun doButtonDown(button: PointerButton, p: PointF) {
|
||||
transformPoint(p)?.let { messenger.sendPointerButtonDown(button, it) }
|
||||
|
|
@ -194,7 +189,7 @@ class Dispatcher(private val activity: VncActivity) {
|
|||
|
||||
fun doRemoteScroll(focus: PointF, dx: Float, dy: Float) {
|
||||
accumulatedDx += dx
|
||||
accumulatedDy += dy * yScrollDirection
|
||||
accumulatedDy += dy
|
||||
|
||||
//Drain horizontal change
|
||||
while (abs(accumulatedDx) >= deltaPerScroll) {
|
||||
|
|
@ -236,8 +231,6 @@ class Dispatcher(private val activity: VncActivity) {
|
|||
override fun transformPoint(p: PointF) = viewModel.frameState.toFb(p)
|
||||
override fun doMovePointer(p: PointF, dx: Float, dy: Float) = doButtonDown(PointerButton.None, p)
|
||||
override fun doDrag(p: PointF, dx: Float, dy: Float) = doButtonDown(PointerButton.Left, p)
|
||||
override fun doFling(vx: Float, vy: Float) = viewModel.frameScroller.fling(vx, vy)
|
||||
override fun onGestureStart() = viewModel.frameScroller.stop()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -246,36 +239,15 @@ class Dispatcher(private val activity: VncActivity) {
|
|||
private inner class RelativeMode : AbstractMode() {
|
||||
private val pointerPosition = PointF(0f, 0f)
|
||||
|
||||
override fun onGestureStart() {
|
||||
super.onGestureStart()
|
||||
//Initialize with the latest pointer position
|
||||
pointerPosition.apply {
|
||||
x = viewModel.client.pointerX.toFloat()
|
||||
y = viewModel.client.pointerY.toFloat()
|
||||
}
|
||||
viewModel.client.ignorePointerMovesByServer = true
|
||||
}
|
||||
|
||||
override fun onGestureStop(p: PointF) {
|
||||
super.onGestureStop(p)
|
||||
viewModel.client.ignorePointerMovesByServer = false
|
||||
}
|
||||
|
||||
override fun transformPoint(p: PointF) = pointerPosition
|
||||
|
||||
override fun doMovePointer(p: PointF, dx: Float, dy: Float) {
|
||||
pointerPosition.apply {
|
||||
offset(dx, dy)
|
||||
x = x.coerceIn(0f, viewModel.frameState.fbWidth - 1)
|
||||
y = y.coerceIn(0f, viewModel.frameState.fbHeight - 1)
|
||||
x = x.coerceIn(0f, viewModel.frameState.fbWidth)
|
||||
y = y.coerceIn(0f, viewModel.frameState.fbHeight)
|
||||
}
|
||||
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) {
|
||||
|
|
@ -283,6 +255,46 @@ class Dispatcher(private val activity: VncActivity) {
|
|||
doMovePointer(p, dx, dy)
|
||||
}
|
||||
|
||||
override fun doFling(vx: Float, vy: Float) {} //Disabled
|
||||
/**
|
||||
* As we have no way know the real pointer position (at-least for now), we move
|
||||
* the remote pointer to center of the screen. This is a crude way of syncing
|
||||
* [pointerPosition] with remote pointer position.
|
||||
*
|
||||
* We have to start somewhere, and center of the screen seems most appropriate.
|
||||
* This is done immediately after connecting (and whenever framebuffer size changes),
|
||||
* If we waited for a gesture before doing this, there would be a sudden jump,
|
||||
* right before the gesture. Another "benefit" is, if connection is fast enough,
|
||||
* user might not even notice our trickery.
|
||||
*
|
||||
* One caveat is, we can sync only after client initialization, but size change event
|
||||
* is likely to fire during initialization. Another problem is activity restart,
|
||||
* which will re-trigger our client state observer. We use [syncPending] to solve
|
||||
* both of these problems.
|
||||
*
|
||||
* We can (hopefully) implement a better solution in the future.
|
||||
*/
|
||||
|
||||
private var syncPending = false
|
||||
|
||||
init {
|
||||
viewModel.fbSizeChangedEvent.observe(activity) {
|
||||
syncPending = true
|
||||
syncPosition()
|
||||
}
|
||||
viewModel.state.observe(activity) {
|
||||
syncPosition()
|
||||
}
|
||||
}
|
||||
|
||||
private fun syncPosition() {
|
||||
if (syncPending && viewModel.client.connected) {
|
||||
pointerPosition.apply {
|
||||
x = viewModel.frameState.fbWidth / 2
|
||||
y = viewModel.frameState.fbHeight / 2
|
||||
}
|
||||
doButtonDown(PointerButton.None, pointerPosition)
|
||||
syncPending = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -194,14 +194,6 @@ 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
|
||||
|
|
|
|||
|
|
@ -13,9 +13,6 @@ 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
|
||||
|
||||
/**
|
||||
|
|
@ -47,8 +44,7 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
|
|||
|
||||
fun onHoverEvent(event: MotionEvent): Boolean {
|
||||
if (event.actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
|
||||
lastHoverPoint = event.point()
|
||||
dispatcher.onMouseMove(lastHoverPoint)
|
||||
dispatcher.onMouseMove(event.point())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
@ -62,11 +58,6 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
|
|||
}
|
||||
}
|
||||
|
||||
// Used for back-press interception
|
||||
private var lastHoverPoint = PointF()
|
||||
fun onMouseBack() {
|
||||
dispatcher.onMouseBack(lastHoverPoint)
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Mouse
|
||||
|
|
@ -123,12 +114,7 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
|
|||
}
|
||||
|
||||
inner class StylusGestureListener : GestureDetector.SimpleOnGestureListener() {
|
||||
private var scrolling = false
|
||||
|
||||
override fun onDown(e: MotionEvent?): Boolean {
|
||||
scrolling = false
|
||||
return true
|
||||
}
|
||||
override fun onDown(e: MotionEvent?) = true
|
||||
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
dispatcher.onStylusTap(e.point())
|
||||
|
|
@ -147,16 +133,8 @@ 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) {
|
||||
|
||||
// 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())
|
||||
}
|
||||
if (e2.buttonState and MotionEvent.BUTTON_STYLUS_PRIMARY == 0)
|
||||
dispatcher.onStylusScroll(e2.point())
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -173,13 +151,10 @@ 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)
|
||||
|
|
@ -189,8 +164,7 @@ 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 {
|
||||
if (swipeVsScale.shouldScale())
|
||||
dispatcher.onScale(detector.scaleFactor, detector.focusX, detector.focusY)
|
||||
dispatcher.onScale(detector.scaleFactor, detector.focusX, detector.focusY)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -220,17 +194,13 @@ class TouchHandler(private val viewModel: VncViewModel, private val dispatcher:
|
|||
return true
|
||||
}
|
||||
|
||||
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
|
||||
override fun onScroll(e1: MotionEvent, e2: MotionEvent, dX: Float, dY: Float): Boolean {
|
||||
val startPoint = e1.point()
|
||||
val currentPoint = e2.point()
|
||||
val normalizedDx = -dX * swipeSensitivity
|
||||
val normalizedDy = -dY * swipeSensitivity
|
||||
|
||||
when (e2.pointerCount) {
|
||||
1 -> dispatcher.onSwipe1(startPoint, currentPoint, normalizedDx, normalizedDy)
|
||||
2 -> if (swipeVsScale.shouldSwipe())
|
||||
dispatcher.onSwipe2(startPoint, currentPoint, normalizedDx, normalizedDy)
|
||||
1 -> dispatcher.onSwipe1(startPoint, currentPoint, -dX, -dY)
|
||||
2 -> dispatcher.onSwipe2(startPoint, currentPoint, -dX, -dY)
|
||||
}
|
||||
|
||||
multiFingerTapDetector.reset()
|
||||
|
|
@ -350,106 +320,4 @@ 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,14 +12,16 @@ 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.*
|
||||
import android.view.Gravity
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
|
@ -41,14 +43,10 @@ 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) {
|
||||
source.startActivity(createVncIntent(source, profile))
|
||||
val intent = Intent(source, VncActivity::class.java)
|
||||
intent.putExtra(PROFILE_KEY, profile)
|
||||
source.startActivity(intent)
|
||||
}
|
||||
|
||||
fun startVncActivity(source: Activity, uri: VncUri) {
|
||||
|
|
@ -63,7 +61,7 @@ fun startVncActivity(source: Activity, uri: VncUri) {
|
|||
*/
|
||||
class VncActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var profile: ServerProfile
|
||||
private val profile by lazy { loadProfile() }
|
||||
val viewModel by viewModels<VncViewModel>()
|
||||
lateinit var binding: ActivityVncBinding
|
||||
private val dispatcher by lazy { Dispatcher(this) }
|
||||
|
|
@ -73,29 +71,27 @@ 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()
|
||||
setupDrawerLayout()
|
||||
|
||||
//Buttons
|
||||
binding.frameView.initialize(this)
|
||||
binding.retryConnectionBtn.setOnClickListener { retryConnection() }
|
||||
|
||||
//Drawers
|
||||
setupDrawerLayout()
|
||||
binding.keyboardBtn.setOnClickListener { showKeyboard(); closeDrawers() }
|
||||
binding.zoomResetBtn.setOnClickListener { viewModel.resetZoom(); closeDrawers() }
|
||||
binding.virtualKeysBtn.setOnClickListener { virtualKeys.show(); closeDrawers() }
|
||||
binding.retryConnectionBtn.setOnClickListener { retryConnection() }
|
||||
|
||||
//Observers
|
||||
//ViewModel setup
|
||||
viewModel.frameViewRef = WeakReference(binding.frameView)
|
||||
viewModel.credentialRequest.observe(this) { showCredentialDialog() }
|
||||
viewModel.sshHostKeyVerifyRequest.observe(this) { showHostKeyDialog() }
|
||||
viewModel.state.observe(this) { onClientStateChanged(it) }
|
||||
viewModel.initConnection(profile) //Should be called after observers has been setup
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
@ -122,11 +118,14 @@ class VncActivity : AppCompatActivity() {
|
|||
|
||||
private fun loadProfile(): ServerProfile {
|
||||
val profile = intent.getParcelableExtra<ServerProfile>(PROFILE_KEY)
|
||||
check(profile != null) { "ServerProfile is missing from VncActivity Intent" }
|
||||
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()
|
||||
}
|
||||
|
||||
// Make a copy to avoid modifying intent's instance,
|
||||
// because we may need the original if we have to retry connection.
|
||||
return profile.copy()
|
||||
Log.e(javaClass.simpleName, "No connection information was passed through Intent.")
|
||||
return ServerProfile()
|
||||
}
|
||||
|
||||
private fun retryConnection() {
|
||||
|
|
@ -164,10 +163,10 @@ class VncActivity : AppCompatActivity() {
|
|||
viewModel.pref.runInfo.hasConnectedSuccessfully = true
|
||||
|
||||
// Highlight drawer for first time users
|
||||
binding.drawerLayout.openDrawer(binding.primaryToolbar)
|
||||
binding.drawerLayout.open()
|
||||
lifecycleScope.launchWhenCreated {
|
||||
delay(1500)
|
||||
binding.drawerLayout.closeDrawer(binding.primaryToolbar)
|
||||
binding.drawerLayout.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -188,8 +187,6 @@ class VncActivity : AppCompatActivity() {
|
|||
|
||||
private fun setupLayout() {
|
||||
|
||||
setupOrientation()
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
if (fullscreenMode) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
|
|
@ -229,14 +226,6 @@ 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)
|
||||
|
||||
|
|
@ -249,25 +238,6 @@ 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -319,14 +289,8 @@ class VncActivity : AppCompatActivity() {
|
|||
|
||||
if (canEnter && Build.VERSION.SDK_INT >= 26) {
|
||||
|
||||
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 fs = viewModel.frameState
|
||||
val aspectRatio = Rational(fs.fbWidth.toInt(), fs.fbHeight.toInt())
|
||||
val param = PictureInPictureParams.Builder().setAspectRatio(aspectRatio).build()
|
||||
|
||||
try {
|
||||
|
|
@ -342,31 +306,14 @@ class VncActivity : AppCompatActivity() {
|
|||
************************************************************************************/
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||
return keyHandler.onKeyEvent(event) || workarounds(event) || super.onKeyDown(keyCode, event)
|
||||
return keyHandler.onKeyEvent(event) || super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
|
||||
return keyHandler.onKeyEvent(event) || workarounds(event) || super.onKeyUp(keyCode, event)
|
||||
return keyHandler.onKeyEvent(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,7 +10,6 @@ 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.
|
||||
|
|
@ -35,7 +34,6 @@ 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) {
|
||||
|
|
@ -56,19 +54,10 @@ 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ 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
|
||||
|
||||
|
|
@ -87,12 +86,9 @@ 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,6 +10,7 @@ package com.gaurav.avnc.ui.vnc.gl
|
|||
|
||||
import android.opengl.GLES20.*
|
||||
import android.util.Log
|
||||
import com.gaurav.avnc.BuildConfig
|
||||
|
||||
object ShaderCompiler {
|
||||
|
||||
|
|
@ -75,6 +76,9 @@ object ShaderCompiler {
|
|||
if (vertexShaderId == 0 || fragmentShaderId == 0)
|
||||
return 0
|
||||
|
||||
return linkProgram(vertexShaderId, fragmentShaderId)
|
||||
val programId = linkProgram(vertexShaderId, fragmentShaderId)
|
||||
if (BuildConfig.DEBUG)
|
||||
validateProgram(programId)
|
||||
return programId
|
||||
}
|
||||
}
|
||||
|
|
@ -15,13 +15,13 @@ object Shaders {
|
|||
//language=GLSL
|
||||
const val VERTEX_SHADER = """
|
||||
uniform mat4 u_Projection;
|
||||
attribute vec2 a_Position;
|
||||
attribute vec4 a_Position;
|
||||
attribute vec2 a_TextureCoordinates;
|
||||
varying vec2 v_TextureCoordinates;
|
||||
void main()
|
||||
{
|
||||
v_TextureCoordinates = a_TextureCoordinates;
|
||||
gl_Position = u_Projection * vec4(a_Position, 0, 1);
|
||||
gl_Position = u_Projection * a_Position;
|
||||
}"""
|
||||
|
||||
//language=GLSL
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ 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,7 @@ class AppPreferences(context: Context) {
|
|||
|
||||
inner class Gesture {
|
||||
val directTouch; get() = prefs.getBoolean("gesture_direct_touch", true)
|
||||
val tap1 = "left-click" //Preference UI was removed
|
||||
val tap1; get() = prefs.getString("gesture_single_tap", "left-click")!!
|
||||
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,8 +44,6 @@ 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 {
|
||||
|
|
@ -57,9 +54,6 @@ 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)
|
||||
|
|
@ -75,7 +69,7 @@ class AppPreferences(context: Context) {
|
|||
|
||||
inner class Experimental {
|
||||
val swipeCloseToolbar; get() = prefs.getBoolean("experimental_vnc_toolbar_swipe_close", true)
|
||||
val immersiveMode; get() = prefs.getBoolean("viewer_immersive_mode", true)
|
||||
val immersiveMode; get() = prefs.getBoolean("viewer_immersive_mode", false)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ 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
|
||||
|
|
@ -12,12 +13,12 @@ object MsgDialog {
|
|||
/**
|
||||
* Shows a dialog with given title & message,
|
||||
*/
|
||||
fun show(manager: FragmentManager, title: CharSequence, msg: CharSequence) {
|
||||
fun show(manager: FragmentManager, @StringRes titleRes: Int, @StringRes msgRes: Int) {
|
||||
val fragment = MsgDialogFragment()
|
||||
val args = Bundle(2)
|
||||
|
||||
args.putCharSequence("title", title)
|
||||
args.putCharSequence("msg", msg)
|
||||
args.putInt("titleRes", titleRes)
|
||||
args.putInt("msgRes", msgRes)
|
||||
fragment.arguments = args
|
||||
|
||||
fragment.show(manager, null)
|
||||
|
|
@ -26,8 +27,8 @@ object MsgDialog {
|
|||
class MsgDialogFragment : DialogFragment() {
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(requireArguments().getCharSequence("title"))
|
||||
.setMessage(requireArguments().getCharSequence("msg"))
|
||||
.setTitle(requireArguments().getInt("titleRes"))
|
||||
.setMessage(requireArguments().getInt("msgRes"))
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> /* Let it dismiss */ }
|
||||
.create()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
|
|||
/**
|
||||
* [ServerProfile] used for current connection.
|
||||
*/
|
||||
var profile = ServerProfile(); private set
|
||||
var profile = ServerProfile()
|
||||
|
||||
/**
|
||||
* We have two places for connection state (both are synced):
|
||||
|
|
@ -148,6 +148,11 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
|
|||
*/
|
||||
val frameScroller = FrameScroller(this)
|
||||
|
||||
/**
|
||||
* Fired when size of the native framebuffer changes.
|
||||
*/
|
||||
val fbSizeChangedEvent = LiveEvent<FrameState>()
|
||||
|
||||
/**
|
||||
* Used for sending events to remote server.
|
||||
*/
|
||||
|
|
@ -167,14 +172,16 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
|
|||
|
||||
/**
|
||||
* Initialize VNC connection using given profile.
|
||||
* It may be called multiple times due to activity restarts.
|
||||
* [initConnection] can be called multiple times due to activity restarts.
|
||||
*/
|
||||
fun initConnection(profile: ServerProfile) {
|
||||
if (state.value == State.Created) {
|
||||
state.value = State.Connecting
|
||||
this.profile = profile
|
||||
launchConnection()
|
||||
}
|
||||
if (state.value != State.Created)
|
||||
return
|
||||
|
||||
this.profile = profile
|
||||
state.value = State.Connecting
|
||||
|
||||
launchConnection()
|
||||
}
|
||||
|
||||
private fun launchConnection() {
|
||||
|
|
@ -200,7 +207,7 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
|
|||
}
|
||||
|
||||
private fun configureClient() {
|
||||
client.configure(profile.viewOnly, profile.securityType, true /* Hardcoded to true */)
|
||||
client.configure(profile.viewOnly, profile.securityType, profile.useLocalCursor)
|
||||
|
||||
if (profile.useRepeater)
|
||||
client.setupRepeater(profile.idOnRepeater)
|
||||
|
|
@ -332,10 +339,7 @@ class VncViewModel(app: Application) : BaseViewModel(app), VncClient.Observer {
|
|||
override fun onFramebufferSizeChanged(width: Int, height: Int) {
|
||||
viewModelScope.launch(Dispatchers.Main) {
|
||||
frameState.setFramebufferSize(width.toFloat(), height.toFloat())
|
||||
fbSizeChangedEvent.fire(frameState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPointerMoved(x: Int, y: Int) {
|
||||
frameViewRef.get()?.requestRender()
|
||||
}
|
||||
}
|
||||
|
|
@ -83,8 +83,7 @@ class Discovery(private val context: Context) {
|
|||
if (nsdManager == null)
|
||||
nsdManager = context.getSystemService(Context.NSD_SERVICE) as NsdManager
|
||||
|
||||
runCatching { nsdManager?.discoverServices(service, NsdManager.PROTOCOL_DNS_SD, listener) }
|
||||
.onFailure { Log.e(javaClass.simpleName, "Unable to start Discovery", it) }
|
||||
nsdManager?.discoverServices(service, NsdManager.PROTOCOL_DNS_SD, listener)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,8 +93,7 @@ class Discovery(private val context: Context) {
|
|||
*/
|
||||
fun stop() {
|
||||
if (isRunning.value == true) {
|
||||
runCatching { nsdManager?.stopServiceDiscovery(listener) }
|
||||
.onFailure { Log.e(javaClass.simpleName, "Unable to stop Discovery", it) }
|
||||
nsdManager?.stopServiceDiscovery(listener)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +102,7 @@ class Discovery(private val context: Context) {
|
|||
*/
|
||||
private fun addProfile(name: String, host: String, port: Int) {
|
||||
val profile = ServerProfile(
|
||||
ID = (name + host + port).hashCode().toLong(),
|
||||
name = name,
|
||||
host = host,
|
||||
port = port
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ 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) }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +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,13 +30,15 @@ import java.nio.charset.StandardCharsets
|
|||
*
|
||||
* To release the resources you must call [cleanup] after you are done with
|
||||
* this instance.
|
||||
*
|
||||
* Note: All callbacks in [Observer] are invoked on the thread which called
|
||||
* [processServerMessage].
|
||||
*/
|
||||
class VncClient(private val observer: Observer) {
|
||||
|
||||
/**
|
||||
* Interface for event observer.
|
||||
* DO NOT throw exceptions from these methods.
|
||||
* There is NO guarantee about which thread will invoke [Observer] methods.
|
||||
*/
|
||||
interface Observer {
|
||||
fun onPasswordRequired(): String
|
||||
|
|
@ -43,9 +46,9 @@ class VncClient(private val observer: Observer) {
|
|||
fun onGotXCutText(text: String)
|
||||
fun onFramebufferUpdated()
|
||||
fun onFramebufferSizeChanged(width: Int, height: Int)
|
||||
fun onPointerMoved(x: Int, y: Int)
|
||||
|
||||
//fun onBell()
|
||||
//fun onCursorMoved(x: Int, y: Int): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -78,27 +81,6 @@ 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.
|
||||
*
|
||||
|
|
@ -157,26 +139,6 @@ 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.
|
||||
|
|
@ -196,11 +158,6 @@ class VncClient(private val observer: Observer) {
|
|||
*/
|
||||
fun uploadFrameTexture() = nativeUploadFrameTexture(nativePtr)
|
||||
|
||||
/**
|
||||
* Upload cursor shape into framebuffer texture.
|
||||
*/
|
||||
fun uploadCursor() = nativeUploadCursor(nativePtr, pointerX, pointerY)
|
||||
|
||||
/**
|
||||
* Release all resources allocated by the client.
|
||||
* DO NOT use this client after [cleanup].
|
||||
|
|
@ -233,7 +190,6 @@ 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)
|
||||
|
||||
|
|
@ -259,10 +215,8 @@ class VncClient(private val observer: Observer) {
|
|||
private fun cbBell() = Unit // observer.onBell()
|
||||
|
||||
@Keep
|
||||
private fun cbHandleCursorPos(x: Int, y: Int) {
|
||||
if (!ignorePointerMovesByServer)
|
||||
moveClientPointer(x, y)
|
||||
}
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun cbHandleCursorPos(x: Int, y: Int) = true //observer.onCursorMoved(x, y)
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
-->
|
||||
|
||||
<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
|
||||
|
|
@ -33,10 +32,10 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_large"
|
||||
android:layout_marginBottom="@dimen/margin_normal"
|
||||
android:drawableEnd="@drawable/ic_gpl"
|
||||
android:drawablePadding="@dimen/padding_small"
|
||||
android:text='@{@string/app_name + " is released under the GNU General Public License, version 3 or later."}'
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:drawableEndCompat="@drawable/ic_gpl" />
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" />
|
||||
|
||||
|
||||
<TextView
|
||||
|
|
|
|||
|
|
@ -144,6 +144,18 @@
|
|||
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"
|
||||
|
|
@ -152,7 +164,7 @@
|
|||
android:text="@string/title_key_compat_mode"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/view_only" />
|
||||
app:layout_constraintTop_toBottomOf="@id/use_local_cursor" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/key_compat_mode_help_btn"
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
<?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" />
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
android:contentDescription="Hide Virtual Keys"
|
||||
android:minWidth="30dp"
|
||||
android:src="@drawable/ic_clear"
|
||||
app:tint="?colorError" />
|
||||
android:tint="?colorError" />
|
||||
|
||||
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~ Meta keys ~~~~~~~~~~~~~~~~~~~~~-->
|
||||
|
||||
|
|
|
|||
|
|
@ -1,179 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -128,7 +128,7 @@
|
|||
<string name="pref_mouse_passthrough">عبور ماوس</string>
|
||||
<string name="pref_mouse_passthrough_summary_off">رویدادهای ماوس برای حرکات محلّی استفاده شوند</string>
|
||||
<string name="pref_mouse_passthrough_summary_on">رویدادهای ماوس به صورت مستقیم به کارساز فرستاده شوند</string>
|
||||
<string name="pref_hide_local_cursor">پنهان کردن اشارهگر محلّی</string>
|
||||
<string name="pref_hide_local_cursor">پنهان کردن اشارهگر محلّی (اندروید)</string>
|
||||
<string name="pref_vk">کلیدهای مجازی</string>
|
||||
<string name="pref_vk_show_all">نمایش همهٔ کلیدها</string>
|
||||
<string name="pref_vk_open_with_keyboard">گشودن همراه صفحهکلید</string>
|
||||
|
|
@ -166,14 +166,4 @@
|
|||
<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>
|
||||
|
|
@ -136,7 +136,7 @@
|
|||
<string name="title_clear">Effacer</string>
|
||||
<string name="pref_theme_option_dark">Sombre</string>
|
||||
<string name="pref_theme_option_light">Clair</string>
|
||||
<string name="pref_hide_local_cursor">Masquer le pointeur local</string>
|
||||
<string name="pref_hide_local_cursor">Masquer le pointeur local (Android)</string>
|
||||
<string name="pref_vk">Touches virtuelles</string>
|
||||
<string name="pref_vk_show_all">Afficher toutes les touches</string>
|
||||
<string name="pref_mouse">Souris</string>
|
||||
|
|
@ -152,7 +152,7 @@
|
|||
<string name="pref_cred_autocomplete_summary">Suggérer les identifiants enregistrés lors de la connexion à de nouveaux serveurs</string>
|
||||
<string name="pref_discovery">Découverte</string>
|
||||
<string name="pref_clipboard_sync">Synchronisation du presse-papiers</string>
|
||||
<string name="pref_drag">Glisser</string>
|
||||
<string name="pref_drag">Déplacement (appui long et glissement)</string>
|
||||
<string name="pref_km_language_switch_to_super">Changement de langue → Super</string>
|
||||
<string name="pref_import_export">Importer/Exporter des serveurs</string>
|
||||
<string name="pref_keycode_map">Carte des codes-clés</string>
|
||||
|
|
@ -161,19 +161,4 @@
|
|||
<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>
|
||||
|
|
@ -162,10 +162,4 @@
|
|||
<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>
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -6,11 +6,11 @@
|
|||
~
|
||||
~ See COPYING.txt for more details.
|
||||
--><resources>
|
||||
<string name="title_add_server_profile">Adicionar servidor</string>
|
||||
<string name="title_edit_server_profile">Editar servidor</string>
|
||||
<string name="title_add_server_profile">Adicionar um servidor</string>
|
||||
<string name="title_edit_server_profile">Edição do servidor</string>
|
||||
<string name="title_duplicate">Duplicar</string>
|
||||
<string name="title_edit">Editar</string>
|
||||
<string name="title_delete">Apagar</string>
|
||||
<string name="title_delete">Excluir</string>
|
||||
<string name="title_copy_host">Copiar host</string>
|
||||
<string name="title_copy_name">Copiar nome</string>
|
||||
<string name="title_advanced">Avançado</string>
|
||||
|
|
@ -18,27 +18,27 @@
|
|||
<string name="title_save">Salvar</string>
|
||||
<string name="title_undo">Desfazer</string>
|
||||
<string name="title_settings">Configurações</string>
|
||||
<string name="title_credentials_required">Autenticação</string>
|
||||
<string name="title_remember">Salvar</string>
|
||||
<string name="title_credentials_required">Credenciais necessárias</string>
|
||||
<string name="title_remember">Lembrar</string>
|
||||
<string name="title_export">Exportar</string>
|
||||
<string name="title_import">Importar</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_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_continue">Continuar</string>
|
||||
<string name="title_delete_servers_before_import">Apagar os servidores atuais antes de importar</string>
|
||||
<string name="title_delete_servers_before_import">Excluir 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">Mudar</string>
|
||||
<string name="title_change">Alterar</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 problema</string>
|
||||
<string name="title_github_repo">Código fonte</string>
|
||||
<string name="title_report_bug">Relatar um erro</string>
|
||||
<string name="title_github_repo">Repositório no Github</string>
|
||||
<string name="hint_urlbar">Conectar ao servidor</string>
|
||||
<string name="hint_server_name">Nome (ex. PC do trabalho)</string>
|
||||
<string name="hint_host">Host</string>
|
||||
|
|
@ -48,56 +48,50 @@
|
|||
<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">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_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 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_server_profile_deleted">Servidor excluído</string>
|
||||
<string name="msg_pip_not_supported">O recurso de Picture-in-Picture não é suportado neste dispositivo</string>
|
||||
<string name="msg_loading">Carregando …</string>
|
||||
<string name="msg_imported">Importado com sucesso</string>
|
||||
<string name="msg_exported">Exportado com sucesso</string>
|
||||
<string name="msg_export_auth_required">É necessária a autenticação antes de exportar</string>
|
||||
<string name="msg_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">Arquivo de chave privada inválido</string>
|
||||
<string name="msg_invalid_vnc_uri">A URI do VNC inválida</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 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>
|
||||
<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>
|
||||
<!-- Accessibility descriptions -->
|
||||
<string name="desc_navigation_btn">Abrir aba de navegação</string>
|
||||
<string name="desc_settings_btn">Configurações</string>
|
||||
<string name="desc_navigation_btn">Abrir a aba de navegação</string>
|
||||
<string name="desc_settings_btn">Abrir 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 servidor</string>
|
||||
<string name="desc_save_discovered_server_btn">Salvar</string>
|
||||
<string name="desc_discovery_btn">Descobrir servidores</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_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">Redefinir zoom</string>
|
||||
<string name="desc_retry_connection_btn">Tente novamente</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_back_btn">Voltar</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>
|
||||
<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>
|
||||
<!-- 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 em popup, zoom</string>
|
||||
<string name="pref_viewer_summary">Picture-in-picture, zoom</string>
|
||||
<string name="pref_input">Entrada</string>
|
||||
<string name="pref_input_summary">Gestos, mouse, teclas virtuais</string>
|
||||
<string name="pref_servers">Servidores</string>
|
||||
<string name="pref_server_summary">Autenticação, descoberta</string>
|
||||
<string name="pref_server_summary">Descoberta, Credenciais</string>
|
||||
<string name="pref_tools">Ferramentas</string>
|
||||
<string name="pref_tools_summary">Importar/Exportar, depuração</string>
|
||||
<string name="pref_experimental">Experimental</string>
|
||||
|
|
@ -113,8 +107,8 @@
|
|||
<string name="pref_gesture_action_remote_drag">Arrastar conteúdo remoto</string>
|
||||
<string name="pref_gesture_action_open_keyboard">Mostrar teclado</string>
|
||||
<string name="pref_theme">Tema</string>
|
||||
<string name="pref_prefer_advanced_editor">Abrir diretamente opções avançadas de servidor</string>
|
||||
<string name="pref_sort_server_list">Ordenar servidores salvos pelo nome</string>
|
||||
<string name="pref_prefer_advanced_editor">Preferência do editor avançado</string>
|
||||
<string name="pref_sort_server_list">Ordenar a lista de servidores</string>
|
||||
<string name="pref_gesture">Gestos</string>
|
||||
<string name="pref_single_tap">Toque único</string>
|
||||
<string name="pref_two_finger_tap">Toque com 2 dedos</string>
|
||||
|
|
@ -122,66 +116,45 @@
|
|||
<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</string>
|
||||
<string name="pref_drag">Arrastar (Toque longo depois deslizar)</string>
|
||||
<string name="pref_mouse">Mouse</string>
|
||||
<string name="pref_mouse_passthrough">Passagem do mouse</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_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_vk">Teclas virtuais</string>
|
||||
<string name="pref_vk_show_all">Mostrar todas teclas</string>
|
||||
<string name="pref_vk_open_with_keyboard">Abrir com teclado</string>
|
||||
<string name="pref_vk_open_with_keyboard">Mostrar quando teclado está disponível</string>
|
||||
<string name="pref_km">Mapeamento de teclas</string>
|
||||
<string name="pref_km_right_alt_to_super">Right Alt → Super</string>
|
||||
<string name="pref_km_language_switch_to_super">Alteração de idioma → Super</string>
|
||||
<string name="pref_km_back_to_escape">Voltar → Escape</string>
|
||||
<string name="pref_km_back_to_escape_summary">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_km_back_to_escape_summary">A barra de navegação não é afetada por isto</string>
|
||||
<string name="pref_fullscreen">Usar tela completa</string>
|
||||
<string name="pref_enable_pip">Picture-in-picture</string>
|
||||
<string name="pref_toolbar">Barra de ferramentas</string>
|
||||
<string name="pref_toolbar_alignment">Alinhamento</string>
|
||||
<string name="pref_zoom">Zoom</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_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_discovery">Descoberta</string>
|
||||
<string name="pref_import_export">Importar/Exportar servidores</string>
|
||||
<string name="pref_debugging">Depuração</string>
|
||||
<string name="pref_logs">Registros</string>
|
||||
<string name="pref_keycode_map">Mapa de códigos de teclado</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close">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_experimental_toolbar_swipe_close">Deslizar para fechar a barra de ferramentas</string>
|
||||
<string name="pref_experimental_toolbar_swipe_close_summary">Durante uma sessão remota, você pode deslizar para fechar a barra de ferramentas atualmente aberta</string>
|
||||
<string name="pref_theme_option_system">Seguir Tema do Sistema</string>
|
||||
<string name="pref_theme_option_dark">Modo Escuro</string>
|
||||
<string name="pref_theme_option_light">Modo Claro</string>
|
||||
<string name="pref_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="pref_hide_local_cursor">Ocultar Cursor do Android</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>
|
||||
<string name="msg_ptr_hiding_not_supported">Ocultar cursor não é compatível com este dispositivo</string>
|
||||
</resources>
|
||||
|
|
@ -1,146 +1,2 @@
|
|||
<?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>
|
||||
<resources></resources>
|
||||
|
|
@ -158,8 +158,4 @@
|
|||
<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>
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -59,15 +59,15 @@
|
|||
<string name="pref_experimental_summary">Додаткові можливості</string>
|
||||
<string name="pref_gesture_action_left_click">Ліве клацання</string>
|
||||
<string name="pref_gesture_action_move_pointer">Перемістити вказівник</string>
|
||||
<string name="pref_clipboard_sync">Синхронізувати буфери обміну</string>
|
||||
<string name="pref_clipboard_sync">Синхронувати буфери обміну</string>
|
||||
<string name="pref_gesture_action_remote_scroll">Прокрутити віддалений вміст</string>
|
||||
<string name="pref_km_language_switch_to_super">Перемикання мови → Super</string>
|
||||
<string name="pref_toolbar_alignment_option_start">Початок</string>
|
||||
<string name="pref_theme">Тема</string>
|
||||
<string name="pref_long_press">Довге натискання</string>
|
||||
<string name="pref_swipe2">Ковзання двома пальцями</string>
|
||||
<string name="pref_drag">Перетягування</string>
|
||||
<string name="pref_hide_local_cursor">Ховати локальний вказівник</string>
|
||||
<string name="pref_drag">Перетягування (довге натискання і ковзнути)</string>
|
||||
<string name="pref_hide_local_cursor">Сховати локальний (Android) вказівник</string>
|
||||
<string name="pref_vk">Віртуальні клавіші</string>
|
||||
<string name="pref_vk_show_all">Показувати всі клавіші</string>
|
||||
<string name="pref_km_back_to_escape">Назад → Escape</string>
|
||||
|
|
@ -162,18 +162,4 @@
|
|||
<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>
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -19,16 +19,6 @@
|
|||
<item>light</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="orientation_entries">
|
||||
<item>@string/pref_orientation_option_auto</item>
|
||||
<item>@string/pref_orientation_option_portrait</item>
|
||||
<item>@string/pref_orientation_option_landscape</item>
|
||||
</string-array>
|
||||
<string-array name="orientation_values">
|
||||
<item>auto</item>
|
||||
<item>portrait</item>
|
||||
<item>landscape</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="toolbar_alignment_entries">
|
||||
<item>@string/pref_toolbar_alignment_option_start</item>
|
||||
|
|
@ -39,6 +29,15 @@
|
|||
<item>end</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="tap1_action_entries">
|
||||
<item>@string/pref_gesture_action_none</item>
|
||||
<item>@string/pref_gesture_action_left_click</item>
|
||||
</string-array>
|
||||
<string-array name="tap1_action_values">
|
||||
<item>none</item>
|
||||
<item>left-click</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="tap2_action_entries">
|
||||
<item>@string/pref_gesture_action_none</item>
|
||||
<item>@string/pref_gesture_action_right_click</item>
|
||||
|
|
@ -110,15 +109,4 @@
|
|||
<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>
|
||||
|
|
@ -27,7 +27,6 @@
|
|||
<string name="title_export">Export</string>
|
||||
<string name="title_import">Import</string>
|
||||
<string name="title_view_only_mode">View-only</string>
|
||||
<string name="title_default_security">Default security</string>
|
||||
<string name="title_hide_remote_cursor">Hide remote pointer</string>
|
||||
<string name="title_key_compat_mode">Send legacy key events</string>
|
||||
<string name="title_use_ssh_tunnel">Use SSH tunnel</string>
|
||||
|
|
@ -69,7 +68,6 @@
|
|||
<string name="msg_invalid_vnc_uri">Invalid VNC URI</string>
|
||||
<string name="msg_copied_to_clipboard">Copied</string>
|
||||
<string name="msg_key_compat_mode_help">Some servers have limited Unicode support. Legacy key events can help input non-English text.</string>
|
||||
<string name="msg_drag_gesture_help">Assigning an action to Drag gesture will change Long press detection:\n\n<b>Press-hold-release</b> → Long press\n<b>Press-hold-swipe</b> → Drag</string>
|
||||
<string name="tip_empty_server_list">Server list is empty.\nClick \'<b>+</b>\' to add a server, or\nuse the top address bar to connect directly.</string>
|
||||
<string name="tip_urlbar">You can enter IP address, hostname, or \'vnc://\' URI.</string>
|
||||
<string name="tip_discovery">Discovery can automatically find supported servers\nrunning on your network.</string>
|
||||
|
|
@ -136,17 +134,12 @@
|
|||
<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_drag">Drag (long press and swipe)</string>
|
||||
<string name="pref_mouse">Mouse</string>
|
||||
<string name="pref_mouse_passthrough">Mouse passthrough</string>
|
||||
<string name="pref_mouse_passthrough_summary_off">Use mouse events for local gestures</string>
|
||||
<string name="pref_mouse_passthrough_summary_on">Send mouse events directly to server</string>
|
||||
<string name="pref_hide_local_cursor">Hide local 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_hide_local_cursor">Hide local (Android) pointer</string>
|
||||
<string name="pref_vk">Virtual keys</string>
|
||||
<string name="pref_vk_show_all">Show all keys</string>
|
||||
<string name="pref_vk_open_with_keyboard">Open with keyboard</string>
|
||||
|
|
@ -155,10 +148,6 @@
|
|||
<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>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
app:title="@string/pref_experimental_toolbar_swipe_close" />
|
||||
|
||||
<SwitchPreference
|
||||
app:defaultValue="true"
|
||||
app:defaultValue="false"
|
||||
app:key="viewer_immersive_mode"
|
||||
app:summary="@string/pref_experimental_immersive_mode_summary"
|
||||
app:title="@string/pref_experimental_immersive_mode" />
|
||||
|
|
|
|||
|
|
@ -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_input">
|
||||
|
||||
<PreferenceCategory
|
||||
|
|
@ -21,6 +20,14 @@
|
|||
app:summaryOn="@string/pref_direct_touch_summary_on"
|
||||
app:title="@string/pref_direct_touch" />
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue="left-click"
|
||||
app:entries="@array/tap1_action_entries"
|
||||
app:entryValues="@array/tap1_action_values"
|
||||
app:key="gesture_single_tap"
|
||||
app:title="@string/pref_single_tap"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue="double-click"
|
||||
app:entries="@array/double_tap_action_entries"
|
||||
|
|
@ -62,26 +69,13 @@
|
|||
app:title="@string/pref_swipe2"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<com.gaurav.avnc.ui.prefs.ListPreferenceEx
|
||||
<ListPreference
|
||||
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:widgetLayout="@layout/help_btn" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:max="15"
|
||||
app:defaultValue="10"
|
||||
app:key="gesture_swipe_sensitivity"
|
||||
app:min="5"
|
||||
app:title="@string/pref_swipe_sensitivity" />
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
app:key="natural_scrolling"
|
||||
app:title="Natural scrolling" />
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
|
|
@ -99,19 +93,6 @@
|
|||
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
|
||||
|
|
|
|||
|
|
@ -10,14 +10,6 @@
|
|||
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"
|
||||
|
|
|
|||
21
build.gradle
21
build.gradle
|
|
@ -21,23 +21,4 @@ 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 c5f269eb9665435271c05fbcaf8721fa58e9eafa
|
||||
Subproject commit ecf021bc0d6f435daacff7c35ccaeef0145df1b9
|
||||
2
extern/libvncserver
vendored
2
extern/libvncserver
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit ee547c7a39a0c63b24140db5dd2d21d72c525252
|
||||
Subproject commit e5dc3d42b913ab8c3b757de572af708b441e669c
|
||||
2
extern/wolfssl
vendored
2
extern/wolfssl
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 57aac1c50b45275c7a99eca32ad985998b292dc8
|
||||
Subproject commit 7e01af012157bc20c840011a018619915380f05c
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
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 +0,0 @@
|
|||
Schneller und sicherer VNC-Client für Android
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
* 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 +0,0 @@
|
|||
Fix click position of Mouse Back actions
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
* 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)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Fixed black screen issue on some Tegra GPUs
|
||||
* Excluded viewer's toolbar area from system back gesture
|
||||
* New language: Russian
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* 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
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
* بهبود در تکمیل خودکار ورود به سامانه
|
||||
* بهبود در اکتشاف کارسازها
|
||||
* زبانهای جدید:
|
||||
صربستانی (توسط Саша Петровић)
|
||||
اوکراینی (توسط Andrij Mizyk)
|
||||
* زبانهای بهروز شده:
|
||||
بوکمالی نروژ (توسط Allan Nordhøy)
|
||||
فارسی (توسط Eshagh Shahedany)
|
||||
فرانسوری (توسط J. Lavoie, Nathan)
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
* Amélioration de l'autocomplétion à la connexion
|
||||
* Amélioration de la découverte des serveurs
|
||||
* Nouvelles langues :
|
||||
Serbe (par Саша Петровић)
|
||||
Ukrainien (par Andrij Mizyk)
|
||||
* Langues mises à jour :
|
||||
Norvégien Bokmål (par Allan Nordhøy)
|
||||
Perse (par Eshagh Shahedany)
|
||||
Français (par J. Lavoie, Nathan)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Tilskrudd SSH-tunnel
|
||||
* Fikset port-videresenderlekkasje
|
||||
* Oppdatert program-metadata
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
* Forbedret programoppstartstid
|
||||
* Innstilling for sortering av tjenere
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Tillagt Samsung-pekeflate-støtte (av Tuan Pham)
|
||||
* Tillagt pekepinne/penn-støtte
|
||||
* TIllagt håndvending for to-fingertrykk
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
AVNC er en fri VNC-klient for Android. Det tillater deg å kontrollere enheter som har en VNC-tjener annensteds hen.
|
||||
|
||||
Funksjoner:
|
||||
|
||||
- Materiell design (med mørk drakt)
|
||||
- Oppsettbare håndvendinger
|
||||
- Stram koding
|
||||
- Virtuelt tastatur
|
||||
- Bilde-i-bilde -modus
|
||||
- Skrivebeskyttet modus
|
||||
- Zeroconf-tjeneroppdagelse
|
||||
- TLS-støtte (AnonTLS, VeNCrypt)
|
||||
- SSH-tunnel (VNC over SSH)
|
||||
- Importer/eksporter tjenere
|
||||
- VNC-gjentagelsesstøtte
|
||||
- Utklippstavlesyknronisering med tjener
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
* Dodano renderowanie kursora po stronie klienta w celu poprawy responsywności
|
||||
* Przeglądarka będzie teraz podążać za wskaźnikiem, gdy tryb bezpośredni jest wyłączony
|
||||
* Przechwytywanie klawisza Wstecz wyzwalanego przez mysz/dotyk (autor: Tuan Pham)
|
||||
* Nowe preferencje dla czułości przeciągnięcia
|
||||
* Nowy język:
|
||||
Turecki (autorstwa Fabriel & Oğuz Ersen)
|
||||
Polski (przez WaldiS)
|
||||
Chiński (uproszczony) (przez wang)
|
||||
* Uwaga: opcja 'Ukryj zdalny wskaźnik' została przeniesiona do Ustawień
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Wzmocnić tunel SSH
|
||||
* Naprawić wyciek z przekierowania portów
|
||||
* Zaktualizuj metadane aplikacji
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
* Poprawiono czas uruchamiania aplikacji
|
||||
* Dodano preferencję sortowania serwerów
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Dodano obsługę touchpada Samsunga (autor: Tuan Pham)
|
||||
* Dodano obsługę rysików/penów
|
||||
* Dodano gest stuknięcia dwoma palcami
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Umożliwić mapowanie klawiszy do klawiszy SUPER/Window (autor: Tuan Pham)
|
||||
* Upewnienie się, że wirtualne meta-klawisze są zawsze zwalniane (autor: thechosenreader)
|
||||
* Dodano obsługę znaków diakrytycznych (akcentów)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Ulepszona obsługa skrótów klawiaturowych
|
||||
* Ulepszona obsługa znaków diakrytycznych (akcentów)
|
||||
* Dodano pełny tryb immersyjny (eksperymentalny).
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Dodano tłumaczenie na język portugalski (BR) (by inkhorn)
|
||||
* Dodano preferencję ukrywania lokalnego kursora systemu Android (przez Henry'ego H)
|
||||
* Poprawiono obsługę poduszki rysika na niektórych urządzeniach (autor: Henry H)
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
* Dodano tryb trackpada/touchpada
|
||||
* Przechwycony klawisz Window & Alt+Tab na Samsung DeX
|
||||
* Zoptymalizowany zoom dla zdalnych ekranów w trybie portretowym
|
||||
* Poprawiono czułość gestu przeciągania
|
||||
* Tłumaczenia: Portugalski-BR (przez João Paulo),
|
||||
Norweski Bokmål (przez Allan Nordhøy),
|
||||
perski (przez Eshagh Shahedany),
|
||||
francuskie (przez J. Lavoie)
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
* Ulepszone autouzupełnianie logowania
|
||||
* Ulepszone wykrywanie serwerów
|
||||
* Nowy język:
|
||||
Serbski (autorstwa Саша Петровић)
|
||||
Ukraiński (przez Andrij Mizyk)
|
||||
* Zaktualizowany język:
|
||||
Norweski Bokmål (autor: Allan Nordhøy)
|
||||
Perski (autor: Eshagh Shahedany)
|
||||
Francuski (autor: J. Lavoie, Nathan)
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
AVNC to otwartoźródłowy klient VNC dla systemu Android. Umożliwia on zdalne sterowanie dowolnym urządzeniem wyposażonym w serwer VNC.
|
||||
|
||||
Cechy:
|
||||
|
||||
- Material Design (z ciemnym motywem)
|
||||
- Konfigurowalne gesty
|
||||
- Dokładne kodowanie
|
||||
- Klawisze wirtualne
|
||||
- Tryb obraz w obrazie
|
||||
- Tryb tylko do oglądania
|
||||
- Wykrywanie serwera Zeroconf
|
||||
- Obsługa TLS (AnonTLS, VeNCrypt)
|
||||
- Tunel SSH (VNC przez SSH)
|
||||
- Import/Export serwerów
|
||||
- Obsługa VNC Repeater
|
||||
- Synchronizacja schowka z serwerem
|
||||
|
|
@ -1 +0,0 @@
|
|||
Szybki i bezpieczny klient VNC dla systemu Android
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Endureça o túnel SSH
|
||||
* Corrija o vazamento do encaminhador de porta
|
||||
* Atualizar metadados do aplicativo
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
* Melhore o tempo de inicialização do aplicativo
|
||||
* Adicionada uma preferência para classificar os servidores
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Adicionado suporte ao touchpad Samsung (por Tuan Pham)
|
||||
* Adicionado suporte para stylus/caneta
|
||||
* Adicionado gesto de toque de 2 dedos
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Permitir mapeamentos de teclas para a tecla SUPER/Window (por Tuan Pham)
|
||||
* Certifique-se de que as meta-chaves virtuais sejam sempre liberadas (pelo leitor escolhido)
|
||||
* Adicionado suporte a diacríticos (acentos)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Suporte a atalhos de teclado aprimorado
|
||||
* Suporte a diacríticos (acentos) aprimorado
|
||||
* Adicionado modo de imersão total (experimental)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Adicionada tradução para português (BR) (por inkhorn)
|
||||
* Adicionada uma preferência para ocultar o cursor local do Android (por Henry H)
|
||||
* Corrigido o manuseio do Stylus hover em alguns dispositivos (por Henry H)
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
AVNC é um cliente VNC de código aberto para Android. Ele permite que você controle remotamente qualquer dispositivo executando um servidor VNC.
|
||||
Recursos:
|
||||
- Material Design (com tema escuro)
|
||||
- Gestos configuráveis
|
||||
- Codificação apertada
|
||||
- Chaves Virtuais
|
||||
- Modo imagem em imagem
|
||||
- Modo somente visualização
|
||||
- Descoberta do Servidor Zeroconf
|
||||
- Suporte TLS (AnonTLS, VeNCrypt)
|
||||
- Túnel SSH (VNC sobre SSH)
|
||||
- Servidores de importação/exportação
|
||||
- Suporte a repetidor VNC
|
||||
- Sincronização da área de transferência com o servidor
|
||||
|
|
@ -1 +0,0 @@
|
|||
Cliente VNC rápido e seguro para Android
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
* Daha iyi yanıt için istemci tarafı imleç görselleştirme eklendi
|
||||
* Görüntüleyici artık Doğrudan Mod devre dışı bırakıldığında işaretçiyi takip edecek
|
||||
* Fare/dokunmatik yüzey tarafından tetiklenen Geri tuşunu yakala (Tuan Pham tarafından)
|
||||
* Kaydırma hassasiyeti için yeni tercih
|
||||
* Yeni dil:
|
||||
Türkçe (Fabriel ve Oğuz Ersen tarafından)
|
||||
Lehçe (WaldiS tarafından)
|
||||
Çince (Basitleştirilmiş) (wang tarafından)
|
||||
* Not: 'Uzak işaretçiyi gizle' seçeneği Ayarlar'a taşındı
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* SSH tüneli daha güvenli hale getirildi
|
||||
* Bağlantı noktası yönlendirici sızıntısı düzeltildi
|
||||
* Uygulama üst verileri güncellendi
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
* Uygulama başlatma süresi iyileştirildi
|
||||
* Sunucuları sıralamak için seçenek eklendi
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Samsung dokunmatik yüzey desteği eklendi (Tuan Pham tarafından)
|
||||
* İğne/kalem desteği eklendi
|
||||
* 2 parmakla dokunma hareketi eklendi
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* SÜPER/Windows tuşu için tuş eşlemeleri eklendi (Tuan Pham tarafından)
|
||||
* Sanal üst anahtarların her zaman serbest bırakıldığından emin olundu (thechosenreader tarafından)
|
||||
* Aksanlı karakter desteği eklendi
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Klavye kısayolları desteği iyileştirildi
|
||||
* Aksanlı karakter desteği iyileştirildi
|
||||
* Tam odaklanma modu eklendi (deneysel)
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
* Portekizce (BR) çeviri eklendi (inkhorn tarafından)
|
||||
* Yerel Android imlecini gizleme seçeneği eklendi (Henry H tarafından)
|
||||
* Bazı aygıtlarda kalemle vurgulama davranışı düzeltildi (Henry H tarafından)
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
* İzleme paneli/dokunmatik yüzey modu eklendi
|
||||
* Samsung DeX'te Windows tuşu ve Alt+Tab yakalama eklendi
|
||||
* Dikey mod uzak ekranlar için yakınlaştırma iyileştirildi
|
||||
* Sürükleme hareketinin hassasiyeti düzeltildi
|
||||
* Çeviriler: Portekizce-BR (João Paulo tarafından),
|
||||
Bokmål Norveççe (Allan Nordhøy tarafından),
|
||||
Farsça (Eshagh Shahedany tarafından),
|
||||
Fransızca (J. Lavoie tarafından)
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
* Oturum açma otomatik tamamlaması iyileştirildi
|
||||
* Sunucu keşfi iyileştirildi
|
||||
* Yeni diller:
|
||||
Sırpça (Саша Петровић tarafından)
|
||||
Ukraynaca (Andrij Mizyk tarafından)
|
||||
* Güncellenen diller:
|
||||
Bokmål Norveççe (Allan Nordhøy tarafından)
|
||||
Farsça (Eshagh Shahedany tarafından)
|
||||
Fransızca (J. Lavoie, Nathan tarafından)
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue