mirror of
https://github.com/amnezia-vpn/amneziawg-android.git
synced 2026-07-03 14:05:13 +02:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b5ceac9f7 | |||
| 9fe0019ec1 | |||
| 84654a0302 | |||
| cd43444d1f | |||
| b7028896c7 | |||
| c1e86acb3c | |||
| 7d31bd2be9 | |||
| 2e573a66a4 | |||
| 8d128cf2e9 | |||
| d5ffa08480 | |||
| 43ce69bef4 | |||
| 44a0f53e58 | |||
| d74b988f75 | |||
| 10e910186e | |||
| 017f420d42 | |||
| bc186fe6ad | |||
| 09b40cdec7 | |||
| d2721f2d7d | |||
| 3095e19e13 | |||
| c547d033c3 | |||
| 4d4764eefb | |||
| d44a83faaa | |||
| 2337fe37be | |||
| c5b71cb484 | |||
| 480c95d4d6 | |||
| 8a45e965eb | |||
| c9717693d0 | |||
| 0fa3fe3d43 | |||
| ed090f7ecb | |||
| f916f96761 | |||
| 79e766c4e1 | |||
| 183273dcf5 | |||
| b3bb7c694b | |||
| 456a74db05 |
@@ -12,6 +12,8 @@ $ cd wireguard-android
|
||||
$ ./gradlew assembleRelease
|
||||
```
|
||||
|
||||
macOS users may need [flock(1)](https://github.com/discoteq/flock).
|
||||
|
||||
## Embedding
|
||||
|
||||
The tunnel library is [on JCenter](https://bintray.com/wireguard/wireguard-android/wireguard-android/_latestVersion), alongside [extensive class library documentation](https://javadoc.io/doc/com.wireguard.android/tunnel).
|
||||
@@ -28,3 +30,7 @@ compileOptions {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
```
|
||||
|
||||
## Translating
|
||||
|
||||
Please help us translate the app into several languages on [our translation platform](https://crowdin.com/project/WireGuard).
|
||||
|
||||
+11
-9
@@ -1,3 +1,4 @@
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
@@ -7,27 +8,28 @@ allprojects {
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
agpVersion = '3.6.2'
|
||||
annotationsVersion = '1.1.0'
|
||||
appcompatVersion = '1.1.0'
|
||||
bintrayPluginVersion = '1.8.4'
|
||||
biometricVersion = '1.0.1'
|
||||
cardviewVersion = '1.0.0'
|
||||
collectionVersion = '1.1.0'
|
||||
coreKtxVersion = '1.2.0'
|
||||
coroutinesVersion = '1.3.5'
|
||||
constraintLayoutVersion = '1.1.3'
|
||||
coordinatorLayoutVersion = '1.1.0'
|
||||
agpVersion = '3.6.1'
|
||||
fragmentVersion = '1.2.3'
|
||||
materialComponentsVersion = '1.1.0'
|
||||
coreKtxVersion = '1.2.0'
|
||||
coroutinesVersion = '1.3.5'
|
||||
eddsaVersion = '0.3.0'
|
||||
fragmentVersion = '1.2.4'
|
||||
jsr305Version = '3.0.2'
|
||||
junitVersion = '4.13'
|
||||
kotlinVersion = '1.3.71'
|
||||
materialComponentsVersion = '1.1.0'
|
||||
mavenPluginVersion = '2.1'
|
||||
preferenceVersion = '1.1.0'
|
||||
streamsupportVersion = '1.7.2'
|
||||
threetenabpVersion = '1.2.3'
|
||||
zxingEmbeddedVersion = '3.6.0'
|
||||
eddsaVersion = '0.3.0'
|
||||
bintrayPluginVersion = '1.8.4'
|
||||
mavenPluginVersion = '2.1'
|
||||
junitVersion = '4.13'
|
||||
|
||||
groupName = 'com.wireguard.android'
|
||||
}
|
||||
|
||||
@@ -17,3 +17,17 @@ org.gradle.jvmargs=-Xmx1536m
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
# R8 Full mode
|
||||
android.enableR8.fullMode=true
|
||||
|
||||
# https://jakewharton.com/increased-accuracy-of-aapt2-keep-rules/
|
||||
android.useMinimalKeepRules=true
|
||||
|
||||
# Enable rudimentary R class namespacing where each library only contains
|
||||
# references to the resources it declares instead of declarations plus all
|
||||
# transitive dependency references.
|
||||
android.namespacedRClass=true
|
||||
|
||||
# Suppress warnings for some features that aren't yet stabilized
|
||||
android.suppressUnsupportedOptionWarnings=android.enableR8.fullMode,android.useMinimalKeepRules,android.namespacedRClass,android.suppressUnsupportedOptionWarnings
|
||||
|
||||
@@ -26,6 +26,22 @@ android {
|
||||
events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_PACKAGE_NAME=${groupName}", "-DGRADLE_USER_HOME=${project.gradle.gradleUserHomeDir}"
|
||||
}
|
||||
}
|
||||
}
|
||||
debug {
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_PACKAGE_NAME=${groupName}.debug", "-DGRADLE_USER_HOME=${project.gradle.gradleUserHomeDir}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.wireguard.android.tunnel">
|
||||
|
||||
<application>
|
||||
<service
|
||||
<service
|
||||
android:name="com.wireguard.android.backend.GoBackend$VpnService"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||
<intent-filter>
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.wireguard.util.NonNullForAll;
|
||||
public final class BackendException extends Exception {
|
||||
private final Object[] format;
|
||||
private final Reason reason;
|
||||
|
||||
public BackendException(final Reason reason, final Object... format) {
|
||||
this.reason = reason;
|
||||
this.format = format;
|
||||
|
||||
@@ -35,13 +35,14 @@ import androidx.collection.ArraySet;
|
||||
|
||||
@NonNullForAll
|
||||
public final class GoBackend implements Backend {
|
||||
private static final String TAG = "WireGuard/" + GoBackend.class.getSimpleName();
|
||||
private static final String TAG = "WireGuard/GoBackend";
|
||||
@Nullable private static AlwaysOnCallback alwaysOnCallback;
|
||||
private static GhettoCompletableFuture<VpnService> vpnService = new GhettoCompletableFuture<>();
|
||||
private final Context context;
|
||||
@Nullable private Config currentConfig;
|
||||
@Nullable private Tunnel currentTunnel;
|
||||
private int currentTunnelHandle = -1;
|
||||
|
||||
public GoBackend(final Context context) {
|
||||
SharedLibraryLoader.loadSharedLibrary(context, "wg-go");
|
||||
this.context = context;
|
||||
|
||||
@@ -41,7 +41,7 @@ import java9.util.stream.Stream;
|
||||
|
||||
@NonNullForAll
|
||||
public final class WgQuickBackend implements Backend {
|
||||
private static final String TAG = "WireGuard/" + WgQuickBackend.class.getSimpleName();
|
||||
private static final String TAG = "WireGuard/WgQuickBackend";
|
||||
private final File localTemporaryDir;
|
||||
private final RootShell rootShell;
|
||||
private final Map<Tunnel, Config> runningConfigs = new HashMap<>();
|
||||
|
||||
@@ -29,7 +29,7 @@ import androidx.annotation.Nullable;
|
||||
@NonNullForAll
|
||||
public class RootShell {
|
||||
private static final String SU = "su";
|
||||
private static final String TAG = "WireGuard/" + RootShell.class.getSimpleName();
|
||||
private static final String TAG = "WireGuard/RootShell";
|
||||
|
||||
private final File localBinaryDir;
|
||||
private final File localTemporaryDir;
|
||||
@@ -189,6 +189,7 @@ public class RootShell {
|
||||
public static class RootShellException extends Exception {
|
||||
private final Object[] format;
|
||||
private final Reason reason;
|
||||
|
||||
public RootShellException(final Reason reason, final Object... format) {
|
||||
this.reason = reason;
|
||||
this.format = format;
|
||||
|
||||
@@ -27,7 +27,7 @@ import androidx.annotation.RestrictTo.Scope;
|
||||
@NonNullForAll
|
||||
@RestrictTo(Scope.LIBRARY_GROUP)
|
||||
public final class SharedLibraryLoader {
|
||||
private static final String TAG = "WireGuard/" + SharedLibraryLoader.class.getSimpleName();
|
||||
private static final String TAG = "WireGuard/SharedLibraryLoader";
|
||||
|
||||
private SharedLibraryLoader() {
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public final class ToolsInstaller {
|
||||
new File("/system/bin"),
|
||||
};
|
||||
@Nullable private static final File INSTALL_DIR = getInstallDir();
|
||||
private static final String TAG = "WireGuard/" + ToolsInstaller.class.getSimpleName();
|
||||
private static final String TAG = "WireGuard/ToolsInstaller";
|
||||
|
||||
private final Context context;
|
||||
private final File localBinaryDir;
|
||||
|
||||
@@ -18,9 +18,11 @@ import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class BadConfigExceptionTest {
|
||||
private static final Map<String, InputStream> CONFIG_MAP = new HashMap<>();
|
||||
private static final String[] CONFIG_NAMES = {
|
||||
"invalid-key",
|
||||
"invalid-number",
|
||||
@@ -31,14 +33,6 @@ public class BadConfigExceptionTest {
|
||||
"unknown-attribute",
|
||||
"unknown-section"
|
||||
};
|
||||
private static final Map<String, InputStream> CONFIG_MAP = new HashMap<>();
|
||||
|
||||
@BeforeClass
|
||||
public static void readConfigs() {
|
||||
for (final String config: CONFIG_NAMES) {
|
||||
CONFIG_MAP.put(config, BadConfigExceptionTest.class.getClassLoader().getResourceAsStream(config + ".conf"));
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void closeStreams() {
|
||||
@@ -50,6 +44,13 @@ public class BadConfigExceptionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void readConfigs() {
|
||||
for (final String config : CONFIG_NAMES) {
|
||||
CONFIG_MAP.put(config, BadConfigExceptionTest.class.getClassLoader().getResourceAsStream(config + ".conf"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void throws_correctly_with_INVALID_KEY_reason() {
|
||||
try {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package com.wireguard.config;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -15,8 +14,20 @@ import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class ConfigTest {
|
||||
|
||||
@Test(expected = BadConfigException.class)
|
||||
public void invalid_config_throws() throws IOException, BadConfigException {
|
||||
try (final InputStream is = Objects.requireNonNull(getClass().getClassLoader()).getResourceAsStream("broken.conf")) {
|
||||
Config.parse(is);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valid_config_parses_correctly() throws IOException, ParseException {
|
||||
Config config = null;
|
||||
@@ -35,11 +46,4 @@ public class ConfigTest {
|
||||
assertEquals("Test config's allowed IPs are 0.0.0.0/0 and ::0/0", config.getPeers().get(0).getAllowedIps(), expectedAllowedIps);
|
||||
assertEquals("Test config has one DNS server", 1, config.getInterface().getDnsServers().size());
|
||||
}
|
||||
|
||||
@Test(expected = BadConfigException.class)
|
||||
public void invalid_config_throws() throws IOException, BadConfigException {
|
||||
try (final InputStream is = Objects.requireNonNull(getClass().getClassLoader()).getResourceAsStream("broken.conf")) {
|
||||
Config.parse(is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ add_custom_target(libwg-go.so WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib
|
||||
ANDROID_LLVM_TRIPLE=${ANDROID_LLVM_TRIPLE}
|
||||
ANDROID_SYSROOT=${ANDROID_SYSROOT}
|
||||
ANDROID_PACKAGE_NAME=${ANDROID_PACKAGE_NAME}
|
||||
GRADLE_USER_HOME=${GRADLE_USER_HOME}
|
||||
CFLAGS=${CMAKE_C_FLAGS}\ -Wno-unused-command-line-argument
|
||||
LDFLAGS=${CMAKE_SHARED_LINKER_FLAGS}\ -fuse-ld=gold
|
||||
DESTDIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
|
||||
@@ -14,22 +14,38 @@ NDK_GO_ARCH_MAP_mips64 := mips64x
|
||||
|
||||
CLANG_FLAGS := --target=$(ANDROID_LLVM_TRIPLE) --gcc-toolchain=$(ANDROID_TOOLCHAIN_ROOT) --sysroot=$(ANDROID_SYSROOT)
|
||||
export CGO_CFLAGS := $(CLANG_FLAGS) $(CFLAGS)
|
||||
export CGO_LDFLAGS := $(CLANG_FLAGS) $(LDFLAGS)
|
||||
export CGO_LDFLAGS := $(CLANG_FLAGS) $(LDFLAGS) -Wl,-soname=libwg-go.so
|
||||
export CC := $(ANDROID_C_COMPILER)
|
||||
export GOARCH := $(NDK_GO_ARCH_MAP_$(ANDROID_ARCH_NAME))
|
||||
export GOOS := android
|
||||
export CGO_ENABLED := 1
|
||||
|
||||
DESIRED_GO_VERSION := 1.14.1
|
||||
GO_VERSION := 1.14.1
|
||||
GO_PLATFORM := $(shell uname -s | tr '[:upper:]' '[:lower:]')-$(NDK_GO_ARCH_MAP_$(shell uname -m))
|
||||
GO_TARBALL := go$(GO_VERSION).$(GO_PLATFORM).tar.gz
|
||||
GO_HASH_darwin-amd64 := 6632f9d53fd95632e431e8c34295349cca3f0a124e3a28b760ae5c42b32816e3
|
||||
GO_HASH_linux-amd64 := 2f49eb17ce8b48c680cdb166ffd7389702c0dec6effa090c324804a5cac8a7f8
|
||||
|
||||
default: $(DESTDIR)/libwg-go.so
|
||||
|
||||
$(BUILDDIR)/go-$(DESIRED_GO_VERSION)/.prepared:
|
||||
$(GRADLE_USER_HOME)/caches/golang/$(GO_TARBALL):
|
||||
mkdir -p "$(dir $@)"
|
||||
curl "https://dl.google.com/go/go$(DESIRED_GO_VERSION).$(shell uname -s | tr '[:upper:]' '[:lower:]')-$(NDK_GO_ARCH_MAP_$(shell uname -m)).tar.gz" | tar -C "$(dir $@)" --strip-components=1 -xzf -
|
||||
patch -p1 -f -N -r- -d "$(dir $@)" < goruntime-boottime-over-monotonic.diff
|
||||
touch "$@"
|
||||
flock "$@.lock" -c ' \
|
||||
[ -f "$@" ] && exit 0; \
|
||||
curl -o "$@.tmp" "https://dl.google.com/go/$(GO_TARBALL)" && \
|
||||
echo "$(GO_HASH_$(GO_PLATFORM)) $@.tmp" | sha256sum -c && \
|
||||
mv "$@.tmp" "$@"'
|
||||
|
||||
$(DESTDIR)/libwg-go.so: export PATH := $(BUILDDIR)/go-$(DESIRED_GO_VERSION)/bin/:$(PATH)
|
||||
$(DESTDIR)/libwg-go.so: $(BUILDDIR)/go-$(DESIRED_GO_VERSION)/.prepared go.mod
|
||||
$(BUILDDIR)/go-$(GO_VERSION)/.prepared: $(GRADLE_USER_HOME)/caches/golang/$(GO_TARBALL)
|
||||
mkdir -p "$(dir $@)"
|
||||
flock "$@.lock" -c ' \
|
||||
[ -f "$@" ] && exit 0; \
|
||||
tar -C "$(dir $@)" --strip-components=1 -xzf "$^" && \
|
||||
patch -p1 -f -N -r- -d "$(dir $@)" < goruntime-boottime-over-monotonic.diff && \
|
||||
touch "$@"'
|
||||
|
||||
$(DESTDIR)/libwg-go.so: export PATH := $(BUILDDIR)/go-$(GO_VERSION)/bin/:$(PATH)
|
||||
$(DESTDIR)/libwg-go.so: $(BUILDDIR)/go-$(GO_VERSION)/.prepared go.mod
|
||||
go build -tags linux -ldflags="-X golang.zx2c4.com/wireguard/ipc.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard" -v -trimpath -o "$@" -buildmode c-shared
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
Submodule tunnel/tools/wireguard-tools updated: 828ffc88cd...a8063adc8a
+5
-12
@@ -51,24 +51,17 @@ android {
|
||||
buildTypes {
|
||||
release {
|
||||
if (keystorePropertiesFile.exists()) signingConfig signingConfigs.release
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_PACKAGE_NAME=${android.defaultConfig.applicationId}"
|
||||
}
|
||||
}
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
|
||||
proguardFiles "proguard-android-optimize.txt", "proguard-rules.pro"
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
versionNameSuffix "-debug"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_PACKAGE_NAME=${android.defaultConfig.applicationId}${applicationIdSuffix}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lintOptions {
|
||||
disable('LongLogTag')
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -78,6 +71,7 @@ dependencies {
|
||||
implementation "androidx.cardview:cardview:$cardviewVersion"
|
||||
implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:$coordinatorLayoutVersion"
|
||||
implementation "androidx.biometric:biometric:$biometricVersion"
|
||||
implementation "androidx.core:core-ktx:$coreKtxVersion"
|
||||
implementation "androidx.databinding:databinding-runtime:$agpVersion"
|
||||
implementation "androidx.fragment:fragment:$fragmentVersion"
|
||||
@@ -88,7 +82,6 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
|
||||
implementation "net.sourceforge.streamsupport:android-retrofuture:$streamsupportVersion"
|
||||
implementation "net.sourceforge.streamsupport:android-retrostreams:$streamsupportVersion"
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
-allowaccessmodification
|
||||
-dontpreverify
|
||||
-dontusemixedcaseclassnames
|
||||
-dontskipnonpubliclibraryclasses
|
||||
-verbose
|
||||
|
||||
-keepattributes *Annotation*
|
||||
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
-keepclassmembers class * implements android.os.Parcelable {
|
||||
public static final ** CREATOR;
|
||||
}
|
||||
|
||||
-keep class androidx.annotation.Keep
|
||||
|
||||
-keep @androidx.annotation.Keep class * {*;}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@androidx.annotation.Keep <methods>;
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@androidx.annotation.Keep <fields>;
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@androidx.annotation.Keep <init>(...);
|
||||
}
|
||||
Vendored
+11
@@ -1,3 +1,14 @@
|
||||
# Squelch all warnings, they're harmless but ProGuard
|
||||
# escalates them as errors.
|
||||
-dontwarn sun.misc.Unsafe
|
||||
|
||||
# Retain some information to keep stacktraces usable
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# Fragment 1.2.4 allows Fragment classes to be obfuscated but
|
||||
# databinding references in XML seem to not be rewritten to
|
||||
# match, so we preserve the names as 1.2.3 did.
|
||||
-if public class ** extends androidx.fragment.app.Fragment
|
||||
-keep public class <1> {
|
||||
public <init>();
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class Application : android.app.Application(), OnSharedPreferenceChangeListener
|
||||
|
||||
companion object {
|
||||
val USER_AGENT = String.format(Locale.ENGLISH, "WireGuard/%s (Android %d; %s; %s; %s %s; %s)", BuildConfig.VERSION_NAME, Build.VERSION.SDK_INT, if (Build.SUPPORTED_ABIS.isNotEmpty()) Build.SUPPORTED_ABIS[0] else "unknown ABI", Build.BOARD, Build.MANUFACTURER, Build.MODEL, Build.FINGERPRINT)
|
||||
private val TAG = "WireGuard/${Application::class.simpleName}"
|
||||
private const val TAG = "WireGuard/Application"
|
||||
private lateinit var weakSelf: WeakReference<Application>
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@@ -29,6 +29,6 @@ class BootShutdownReceiver : BroadcastReceiver() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "WireGuard/${BootShutdownReceiver::class.simpleName}"
|
||||
private const val TAG = "WireGuard/BootShutdownReceiver"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +153,6 @@ class QuickTileService : TileService() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "WireGuard/${QuickTileService::class.simpleName}"
|
||||
private const val TAG = "WireGuard/QuickTileService"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,7 +346,10 @@ class LogViewerActivity : AppCompatActivity() {
|
||||
if (mode != "r") return null
|
||||
val log = logForUri(uri) ?: return null
|
||||
return openPipeHelper(uri, "text/plain", null, log) { output, _, _, _, l ->
|
||||
FileOutputStream(output.fileDescriptor).write(l!!)
|
||||
try {
|
||||
FileOutputStream(output.fileDescriptor).write(l!!)
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,6 @@ class TunnelToggleActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "WireGuard/${TunnelToggleActivity::class.simpleName}"
|
||||
private const val TAG = "WireGuard/TunnelToggleActivity"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,6 @@ class FileConfigStore(private val context: Context) : ConfigStore {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "WireGuard/${FileConfigStore::class.simpleName}"
|
||||
private const val TAG = "WireGuard/FileConfigStore"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ package com.wireguard.android.databinding
|
||||
|
||||
import android.text.InputFilter
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.databinding.BindingAdapter
|
||||
@@ -13,6 +15,7 @@ import androidx.databinding.DataBindingUtil
|
||||
import androidx.databinding.ObservableList
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.databinding.adapters.ListenerUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.wireguard.android.BR
|
||||
@@ -41,10 +44,10 @@ object BindingAdapters {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("items", "layout")
|
||||
@BindingAdapter("items", "layout", "fragment")
|
||||
fun <E> setItems(view: LinearLayout,
|
||||
oldList: ObservableList<E>?, oldLayoutId: Int,
|
||||
newList: ObservableList<E>?, newLayoutId: Int) {
|
||||
oldList: ObservableList<E>?, oldLayoutId: Int, @Suppress("UNUSED_PARAMETER") oldFragment: Fragment?,
|
||||
newList: ObservableList<E>?, newLayoutId: Int, newFragment: Fragment?) {
|
||||
if (oldList === newList && oldLayoutId == newLayoutId)
|
||||
return
|
||||
var listener: ItemChangeListener<E>? = ListenerUtil.getListener(view, R.id.item_change_listener)
|
||||
@@ -59,7 +62,7 @@ object BindingAdapters {
|
||||
if (newList == null || newLayoutId == 0)
|
||||
return
|
||||
if (listener == null) {
|
||||
listener = ItemChangeListener(view, newLayoutId)
|
||||
listener = ItemChangeListener(view, newLayoutId, newFragment)
|
||||
ListenerUtil.trackListener(view, listener, R.id.item_change_listener)
|
||||
}
|
||||
// Either the list changed, or this is an entirely new listener because the layout changed.
|
||||
@@ -123,10 +126,17 @@ object BindingAdapters {
|
||||
view.setOnBeforeCheckedChangeListener(listener)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("onFocusChange")
|
||||
fun setOnFocusChange(view: EditText,
|
||||
listener: View.OnFocusChangeListener?) {
|
||||
view.setOnFocusChangeListener(listener)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("android:text")
|
||||
fun setText(view: TextView, text: Optional<*>) {
|
||||
view.text = text.map { obj: Any -> obj.toString() }.orElse("")
|
||||
view.text = text.map { it.toString() }.orElse("")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@@ -10,13 +10,14 @@ import android.view.ViewGroup
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.databinding.ObservableList
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.wireguard.android.BR
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* Helper class for binding an ObservableList to the children of a ViewGroup.
|
||||
*/
|
||||
internal class ItemChangeListener<T>(private val container: ViewGroup, private val layoutId: Int) {
|
||||
internal class ItemChangeListener<T>(private val container: ViewGroup, private val layoutId: Int, private val fragment: Fragment?) {
|
||||
private val callback = OnListChangedCallback(this)
|
||||
private val layoutInflater: LayoutInflater = LayoutInflater.from(container.context)
|
||||
private var list: ObservableList<T>? = null
|
||||
@@ -29,6 +30,7 @@ internal class ItemChangeListener<T>(private val container: ViewGroup, private v
|
||||
require(list != null) { "Trying to get a view while list is still null" }
|
||||
binding!!.setVariable(BR.collection, list)
|
||||
binding.setVariable(BR.item, list!![position])
|
||||
binding.setVariable(BR.fragment, fragment)
|
||||
binding.executePendingBindings()
|
||||
return binding.root
|
||||
}
|
||||
|
||||
-1
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
package com.wireguard.android.databinding
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
|
||||
@@ -7,7 +7,6 @@ package com.wireguard.android.databinding
|
||||
import java.util.AbstractList
|
||||
import java.util.Collections
|
||||
import java.util.Comparator
|
||||
import java.util.NoSuchElementException
|
||||
import java.util.Spliterator
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.wireguard.android.util.resolveAttribute
|
||||
|
||||
class AddTunnelsSheet : BottomSheetDialogFragment() {
|
||||
|
||||
private lateinit var behavior: BottomSheetBehavior<FrameLayout>
|
||||
private var behavior: BottomSheetBehavior<FrameLayout>? = null
|
||||
private val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
}
|
||||
@@ -51,7 +51,7 @@ class AddTunnelsSheet : BottomSheetDialogFragment() {
|
||||
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
val dialog = dialog as BottomSheetDialog? ?: return
|
||||
behavior = dialog.behavior
|
||||
behavior.apply {
|
||||
behavior?.apply {
|
||||
state = BottomSheetBehavior.STATE_EXPANDED
|
||||
peekHeight = 0
|
||||
addBottomSheetCallback(bottomSheetCallback)
|
||||
@@ -78,7 +78,7 @@ class AddTunnelsSheet : BottomSheetDialogFragment() {
|
||||
|
||||
override fun dismiss() {
|
||||
super.dismiss()
|
||||
behavior.removeBottomSheetCallback(bottomSheetCallback)
|
||||
behavior?.removeBottomSheetCallback(bottomSheetCallback)
|
||||
}
|
||||
|
||||
private fun requireTargetFragment(): Fragment {
|
||||
|
||||
@@ -15,9 +15,9 @@ import androidx.fragment.app.Fragment
|
||||
import com.wireguard.android.Application
|
||||
import com.wireguard.android.R
|
||||
import com.wireguard.android.databinding.AppListDialogFragmentBinding
|
||||
import com.wireguard.android.databinding.ObservableKeyedArrayList
|
||||
import com.wireguard.android.model.ApplicationData
|
||||
import com.wireguard.android.util.ErrorMessages
|
||||
import com.wireguard.android.databinding.ObservableKeyedArrayList
|
||||
|
||||
class AppListDialogFragment : DialogFragment() {
|
||||
private val appData: ObservableKeyedArrayList<String, ApplicationData> = ObservableKeyedArrayList()
|
||||
|
||||
@@ -103,6 +103,6 @@ abstract class BaseFragment : Fragment(), OnSelectedTunnelChangedListener {
|
||||
|
||||
companion object {
|
||||
private const val REQUEST_CODE_VPN_PERMISSION = 23491
|
||||
private val TAG = "WireGuard/${BaseFragment::class.simpleName}"
|
||||
private const val TAG = "WireGuard/BaseFragment"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package com.wireguard.android.fragment
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
@@ -15,15 +16,16 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.wireguard.android.Application
|
||||
import com.wireguard.android.R
|
||||
import com.wireguard.android.backend.Tunnel
|
||||
import com.wireguard.android.databinding.TunnelEditorFragmentBinding
|
||||
import com.wireguard.android.fragment.AppListDialogFragment.AppExclusionListener
|
||||
import com.wireguard.android.model.ObservableTunnel
|
||||
import com.wireguard.android.util.BiometricAuthenticator
|
||||
import com.wireguard.android.util.ErrorMessages
|
||||
import com.wireguard.android.viewmodel.ConfigProxy
|
||||
import com.wireguard.android.widget.EdgeToEdge.setUpRoot
|
||||
@@ -34,6 +36,7 @@ import com.wireguard.config.Config
|
||||
* Fragment for editing a WireGuard configuration.
|
||||
*/
|
||||
class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
|
||||
private var haveShownKeys = false
|
||||
private var binding: TunnelEditorFragmentBinding? = null
|
||||
private var tunnel: ObservableTunnel? = null
|
||||
private fun onConfigLoaded(config: Config) {
|
||||
@@ -76,12 +79,11 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
|
||||
setUpScrollingContent(mainContainer, null)
|
||||
privateKeyTextLayout.setEndIconOnClickListener { config?.`interface`?.generateKeyPair() }
|
||||
}
|
||||
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
return binding?.root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
binding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
@@ -104,7 +106,7 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
|
||||
InputMethodManager.HIDE_NOT_ALWAYS)
|
||||
}
|
||||
// Tell the activity to finish itself or go back to the detail view.
|
||||
requireActivity().runOnUiThread {
|
||||
activity.runOnUiThread {
|
||||
// TODO(smaeul): Remove this hack when fixing the Config ViewModel
|
||||
// The selected tunnel has to actually change, but we have to remember this one.
|
||||
val savedTunnel = tunnel
|
||||
@@ -226,9 +228,45 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
|
||||
super.onViewStateRestored(savedInstanceState)
|
||||
}
|
||||
|
||||
private var showingAuthenticator = false
|
||||
|
||||
fun onKeyClick(view: View) = onKeyFocusChange(view, true)
|
||||
|
||||
fun onKeyFocusChange(view: View, isFocused: Boolean) {
|
||||
if (!isFocused || showingAuthenticator) return
|
||||
val edit = view as? EditText ?: return
|
||||
if (edit.inputType == InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) return
|
||||
if (!haveShownKeys && edit.text.isNotEmpty()) {
|
||||
showingAuthenticator = true
|
||||
BiometricAuthenticator.authenticate(R.string.biometric_prompt_private_key_title, this) {
|
||||
showingAuthenticator = false
|
||||
when (it) {
|
||||
is BiometricAuthenticator.Result.Success, is BiometricAuthenticator.Result.HardwareUnavailableOrDisabled -> {
|
||||
haveShownKeys = true
|
||||
showPrivateKey(edit)
|
||||
}
|
||||
is BiometricAuthenticator.Result.Failure -> {
|
||||
Snackbar.make(
|
||||
binding!!.mainContainer,
|
||||
it.message,
|
||||
Snackbar.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showPrivateKey(edit)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showPrivateKey(edit: EditText) {
|
||||
activity?.window?.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||
edit.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_LOCAL_CONFIG = "local_config"
|
||||
private const val KEY_ORIGINAL_NAME = "original_name"
|
||||
private val TAG = "WireGuard/${TunnelEditorFragment::class.simpleName}"
|
||||
private const val TAG = "WireGuard/TunnelEditorFragment"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,6 +397,6 @@ class TunnelListFragment : BaseFragment() {
|
||||
const val REQUEST_IMPORT = 1
|
||||
private const val REQUEST_TARGET_FRAGMENT = 2
|
||||
private const val CHECKED_ITEMS = "CHECKED_ITEMS"
|
||||
private val TAG = "WireGuard/${TunnelListFragment::class.simpleName}"
|
||||
private const val TAG = "WireGuard/TunnelListFragment"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import androidx.databinding.Bindable
|
||||
import com.wireguard.android.BR
|
||||
import com.wireguard.android.backend.Statistics
|
||||
import com.wireguard.android.backend.Tunnel
|
||||
import com.wireguard.android.databinding.Keyed
|
||||
import com.wireguard.android.util.ExceptionLoggers
|
||||
import com.wireguard.config.Config
|
||||
import com.wireguard.android.databinding.Keyed
|
||||
import java9.util.concurrent.CompletableFuture
|
||||
import java9.util.concurrent.CompletionStage
|
||||
|
||||
@@ -24,7 +24,8 @@ class ObservableTunnel internal constructor(
|
||||
config: Config?,
|
||||
state: Tunnel.State
|
||||
) : BaseObservable(), Keyed<String>, Tunnel {
|
||||
override val key = name
|
||||
override val key
|
||||
get() = name
|
||||
|
||||
@Bindable
|
||||
override fun getName() = name
|
||||
@@ -71,10 +72,11 @@ class ObservableTunnel internal constructor(
|
||||
}
|
||||
private set
|
||||
|
||||
val configAsync: CompletionStage<Config> = if (config == null)
|
||||
manager.getTunnelConfig(this)
|
||||
else
|
||||
CompletableFuture.completedFuture(config)
|
||||
val configAsync: CompletionStage<Config>
|
||||
get() = if (config == null)
|
||||
manager.getTunnelConfig(this)
|
||||
else
|
||||
CompletableFuture.completedFuture(config)
|
||||
|
||||
fun setConfigAsync(config: Config): CompletionStage<Config> = if (config != this.config)
|
||||
manager.setTunnelConfig(this, config)
|
||||
|
||||
@@ -25,7 +25,9 @@ object TunnelComparator : Comparator<String> {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
val tokens: MutableList<NaturalSortToken> = ArrayList()
|
||||
|
||||
init {
|
||||
for (s in NATURAL_SORT_DIGIT_FINDER.findAll(originalString.split(WHITESPACE_FINDER).joinToString(" ").toLowerCase(Locale.ENGLISH))) {
|
||||
try {
|
||||
@@ -36,6 +38,7 @@ object TunnelComparator : Comparator<String> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private val NATURAL_SORT_DIGIT_FINDER = Regex("""\d+|\D+""")
|
||||
private val WHITESPACE_FINDER = Regex("""\s""")
|
||||
|
||||
@@ -21,8 +21,8 @@ import com.wireguard.android.R
|
||||
import com.wireguard.android.backend.Statistics
|
||||
import com.wireguard.android.backend.Tunnel
|
||||
import com.wireguard.android.configStore.ConfigStore
|
||||
import com.wireguard.android.util.ExceptionLoggers
|
||||
import com.wireguard.android.databinding.ObservableSortedKeyedArrayList
|
||||
import com.wireguard.android.util.ExceptionLoggers
|
||||
import com.wireguard.config.Config
|
||||
import java9.util.concurrent.CompletableFuture
|
||||
import java9.util.concurrent.CompletionStage
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import com.wireguard.android.Application
|
||||
import com.wireguard.android.R
|
||||
import com.wireguard.android.model.ObservableTunnel
|
||||
import com.wireguard.android.util.BiometricAuthenticator
|
||||
import com.wireguard.android.util.DownloadsFileSaver
|
||||
import com.wireguard.android.util.ErrorMessages
|
||||
import com.wireguard.android.util.FragmentUtils
|
||||
@@ -81,16 +82,31 @@ class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference
|
||||
override fun getTitle() = context.getString(R.string.zip_export_title)
|
||||
|
||||
override fun onClick() {
|
||||
FragmentUtils.getPrefActivity(this)
|
||||
.ensurePermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, grantResults ->
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
isEnabled = false
|
||||
exportZip()
|
||||
val prefActivity = FragmentUtils.getPrefActivity(this)
|
||||
val fragment = prefActivity.supportFragmentManager.fragments.first()
|
||||
BiometricAuthenticator.authenticate(R.string.biometric_prompt_zip_exporter_title, fragment) {
|
||||
when (it) {
|
||||
// When we have successful authentication, or when there is no biometric hardware available.
|
||||
is BiometricAuthenticator.Result.Success, is BiometricAuthenticator.Result.HardwareUnavailableOrDisabled -> {
|
||||
prefActivity.ensurePermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, grantResults ->
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
isEnabled = false
|
||||
exportZip()
|
||||
}
|
||||
}
|
||||
}
|
||||
is BiometricAuthenticator.Result.Failure -> {
|
||||
Snackbar.make(
|
||||
prefActivity.findViewById(android.R.id.content),
|
||||
it.message,
|
||||
Snackbar.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "WireGuard/${ZipExporterPreference::class.simpleName}"
|
||||
private const val TAG = "WireGuard/ZipExporterPreference"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
package com.wireguard.android.util
|
||||
|
||||
import android.os.Handler
|
||||
import java.util.concurrent.Executor
|
||||
import java9.util.concurrent.CompletableFuture
|
||||
import java9.util.concurrent.CompletionStage
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
/**
|
||||
* Helper class for running asynchronous tasks and ensuring they are completed on the main thread.
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.KeyguardManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.biometric.BiometricConstants
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.wireguard.android.R
|
||||
|
||||
|
||||
object BiometricAuthenticator {
|
||||
private const val TAG = "WireGuard/BiometricAuthenticator"
|
||||
private val handler = Handler()
|
||||
|
||||
sealed class Result {
|
||||
data class Success(val cryptoObject: BiometricPrompt.CryptoObject?) : Result()
|
||||
data class Failure(val code: Int?, val message: CharSequence) : Result()
|
||||
object HardwareUnavailableOrDisabled : Result()
|
||||
object Cancelled : Result()
|
||||
}
|
||||
|
||||
@SuppressLint("PrivateApi")
|
||||
private fun isPinEnabled(context: Context): Boolean {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
return context.getSystemService<KeyguardManager>()!!.isDeviceSecure
|
||||
return try {
|
||||
val lockUtilsClass = Class.forName("com.android.internal.widget.LockPatternUtils")
|
||||
val lockUtils = lockUtilsClass.getConstructor(Context::class.java).newInstance(context)
|
||||
val method = lockUtilsClass.getMethod("isLockScreenDisabled")
|
||||
!(method.invoke(lockUtils) as Boolean)
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun authenticate(
|
||||
@StringRes dialogTitleRes: Int,
|
||||
fragment: Fragment,
|
||||
callback: (Result) -> Unit
|
||||
) {
|
||||
val authCallback = object : BiometricPrompt.AuthenticationCallback() {
|
||||
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
||||
super.onAuthenticationError(errorCode, errString)
|
||||
Log.d(TAG, "BiometricAuthentication error: errorCode=$errorCode, msg=$errString")
|
||||
callback(when (errorCode) {
|
||||
BiometricConstants.ERROR_CANCELED, BiometricConstants.ERROR_USER_CANCELED,
|
||||
BiometricConstants.ERROR_NEGATIVE_BUTTON -> {
|
||||
Result.Cancelled
|
||||
}
|
||||
BiometricConstants.ERROR_HW_NOT_PRESENT, BiometricConstants.ERROR_HW_UNAVAILABLE,
|
||||
BiometricConstants.ERROR_NO_BIOMETRICS, BiometricConstants.ERROR_NO_DEVICE_CREDENTIAL -> {
|
||||
Result.HardwareUnavailableOrDisabled
|
||||
}
|
||||
else -> Result.Failure(errorCode, fragment.getString(R.string.biometric_auth_error_reason, errString))
|
||||
})
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
super.onAuthenticationFailed()
|
||||
callback(Result.Failure(null, fragment.getString(R.string.biometric_auth_error)))
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||
super.onAuthenticationSucceeded(result)
|
||||
callback(Result.Success(result.cryptoObject))
|
||||
}
|
||||
}
|
||||
val biometricPrompt = BiometricPrompt(fragment, { handler.post(it) }, authCallback)
|
||||
val promptInfo = BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(fragment.getString(dialogTitleRes))
|
||||
.setDeviceCredentialAllowed(true)
|
||||
.build()
|
||||
if (BiometricManager.from(fragment.requireContext()).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS || isPinEnabled(fragment.requireContext())) {
|
||||
biometricPrompt.authenticate(promptInfo)
|
||||
} else {
|
||||
callback(Result.HardwareUnavailableOrDisabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,6 @@ enum class ExceptionLoggers(private val priority: Int) : BiConsumer<Any?, Throwa
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "WireGuard/${ExceptionLoggers::class.simpleName}"
|
||||
private const val TAG = "WireGuard/ExceptionLoggers"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M12,12c0,-3 2.5,-5.5 5.5,-5.5S23,9 23,12L12,12zM12,12c0,3 -2.5,5.5 -5.5,5.5S1,15 1,12h11zM12,12c-3,0 -5.5,-2.5 -5.5,-5.5S9,1 12,1v11zM12,12c3,0 5.5,2.5 5.5,5.5S15,23 12,23L12,12z" />
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z" />
|
||||
</vector>
|
||||
|
||||
@@ -25,25 +25,25 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="?attr/colorBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?attr/colorBackground">
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/tunnel_detail_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:cardBackgroundColor="?attr/elevationOverlayColor"
|
||||
app:cardCornerRadius="0dp"
|
||||
app:cardElevation="2dp"
|
||||
app:contentPadding="8dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -55,8 +55,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/interface_title"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.wireguard.android.widget.ToggleSwitch
|
||||
android:id="@+id/tunnel_switch"
|
||||
@@ -64,9 +64,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:checked="@{tunnel.state == State.UP}"
|
||||
app:onBeforeCheckedChanged="@{fragment::setTunnelState}"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/interface_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:onBeforeCheckedChanged="@{fragment::setTunnelState}" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interface_name_label"
|
||||
@@ -76,7 +76,7 @@
|
||||
android:labelFor="@+id/interface_name_text"
|
||||
android:text="@string/name"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/interface_title"/>
|
||||
app:layout_constraintTop_toBottomOf="@+id/interface_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interface_name_text"
|
||||
@@ -86,7 +86,7 @@
|
||||
android:text="@{tunnel.name}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/interface_name_label"
|
||||
tools:text="wg0"/>
|
||||
tools:text="wg0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/public_key_label"
|
||||
@@ -96,7 +96,7 @@
|
||||
android:labelFor="@+id/public_key_text"
|
||||
android:text="@string/public_key"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/interface_name_text"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/interface_name_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/public_key_text"
|
||||
@@ -107,10 +107,11 @@
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:onClick="@{ClipboardUtils::copyTextView}"
|
||||
android:singleLine="true"
|
||||
android:text="@{config.interface.keyPair.publicKey.toBase64}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/public_key_label"
|
||||
tools:text="wOs2eguFEohqIZxlSJ1CAT9584tc6ejj9hfGFsoBVkA="/>
|
||||
tools:text="wOs2eguFEohqIZxlSJ1CAT9584tc6ejj9hfGFsoBVkA=" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/addresses_label"
|
||||
@@ -120,7 +121,7 @@
|
||||
android:labelFor="@+id/addresses_text"
|
||||
android:text="@string/addresses"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/public_key_text"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/public_key_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/addresses_text"
|
||||
@@ -131,7 +132,7 @@
|
||||
android:text="@{config.interface.addresses}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/addresses_label"
|
||||
tools:text="fc00:bbbb:bbbb:bb11::3:368b/128"/>
|
||||
tools:text="fc00:bbbb:bbbb:bb11::3:368b/128" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
@@ -144,8 +145,8 @@
|
||||
android:orientation="vertical"
|
||||
app:items="@{config.peers}"
|
||||
app:layout="@{@layout/tunnel_detail_peer}"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tunnel_detail_card"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:ignore="UselessLeaf"/>
|
||||
app:layout_constraintTop_toBottomOf="@+id/tunnel_detail_card"
|
||||
tools:ignore="UselessLeaf" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/peer"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/public_key_label"
|
||||
@@ -42,7 +42,7 @@
|
||||
android:labelFor="@+id/public_key_text"
|
||||
android:text="@string/public_key"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/peer_title"/>
|
||||
app:layout_constraintTop_toBottomOf="@id/peer_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/public_key_text"
|
||||
@@ -53,10 +53,11 @@
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:onClick="@{ClipboardUtils::copyTextView}"
|
||||
android:singleLine="true"
|
||||
android:text="@{item.publicKey.toBase64}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/public_key_label"
|
||||
tools:text="wOs2eguFEohqIZxlSJ1CAT9584tc6ejj9hfGFsoBVkA="/>
|
||||
tools:text="wOs2eguFEohqIZxlSJ1CAT9584tc6ejj9hfGFsoBVkA=" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/allowed_ips_label"
|
||||
@@ -66,7 +67,7 @@
|
||||
android:labelFor="@+id/allowed_ips_text"
|
||||
android:text="@string/allowed_ips"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/public_key_text"/>
|
||||
app:layout_constraintTop_toBottomOf="@+id/public_key_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/allowed_ips_text"
|
||||
@@ -76,7 +77,7 @@
|
||||
android:text="@{item.allowedIps}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/allowed_ips_label"
|
||||
tools:text="0.0.0.0/5, 8.0.0.0/7, 11.0.0.0/8, 12.0.0.0/6, 16.0.0.0/4, 32.0.0.0/3"/>
|
||||
tools:text="0.0.0.0/5, 8.0.0.0/7, 11.0.0.0/8, 12.0.0.0/6, 16.0.0.0/4, 32.0.0.0/3" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/endpoint_label"
|
||||
@@ -86,7 +87,7 @@
|
||||
android:labelFor="@+id/endpoint_text"
|
||||
android:text="@string/endpoint"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/allowed_ips_text"/>
|
||||
app:layout_constraintTop_toBottomOf="@+id/allowed_ips_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/endpoint_text"
|
||||
@@ -120,8 +121,8 @@
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/transfer_label"
|
||||
tools:visibility="visible"
|
||||
tools:text="1024 MB" />
|
||||
tools:text="1024 MB"
|
||||
tools:visibility="visible" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</layout>
|
||||
|
||||
@@ -102,9 +102,11 @@
|
||||
android:id="@+id/private_key_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textNoSuggestions|textVisiblePassword"
|
||||
android:inputType="textNoSuggestions|textPassword"
|
||||
android:onClick="@{fragment::onKeyClick}"
|
||||
android:text="@={config.interface.privateKey}"
|
||||
app:filter="@{KeyInputFilter.newInstance()}" />
|
||||
app:filter="@{KeyInputFilter.newInstance()}"
|
||||
app:onFocusChange="@{fragment::onKeyFocusChange}" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
@@ -234,6 +236,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:divider="@null"
|
||||
android:orientation="vertical"
|
||||
app:fragment="@{fragment}"
|
||||
app:items="@{config.peers}"
|
||||
app:layout="@{@layout/tunnel_editor_peer}"
|
||||
tools:ignore="UselessLeaf" />
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
<variable
|
||||
name="item"
|
||||
type="com.wireguard.android.viewmodel.PeerProxy" />
|
||||
|
||||
<variable
|
||||
name="fragment"
|
||||
type="com.wireguard.android.fragment.TunnelEditorFragment" />
|
||||
</data>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
@@ -91,8 +95,11 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_optional"
|
||||
android:inputType="textNoSuggestions|textVisiblePassword"
|
||||
android:text="@={item.preSharedKey}" />
|
||||
android:inputType="textNoSuggestions|textPassword"
|
||||
android:onClick="@{fragment::onKeyClick}"
|
||||
android:text="@={item.preSharedKey}"
|
||||
app:filter="@{KeyInputFilter.newInstance()}"
|
||||
app:onFocusChange="@{fragment::onKeyFocusChange}" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="delete_error">
|
||||
<item quantity="one">%d Tunnel konnte nicht gelöscht werden: %s</item>
|
||||
<item quantity="other">%d Tunnel konnten nicht gelöscht werden: %s</item>
|
||||
</plurals>
|
||||
<plurals name="delete_success">
|
||||
<item quantity="one">%d Tunnel erfolgreich gelöscht</item>
|
||||
<item quantity="other">%d Tunnel erfolgreich gelöscht</item>
|
||||
</plurals>
|
||||
<plurals name="delete_title">
|
||||
<item quantity="one">%d Tunnel ausgewählt</item>
|
||||
<item quantity="other">%d Tunnel ausgewählt</item>
|
||||
</plurals>
|
||||
<plurals name="import_partial_success">
|
||||
<item quantity="one">%1$d von %2$d Tunnel importiert</item>
|
||||
<item quantity="other">%1$d von %2$d Tunnel importiert</item>
|
||||
</plurals>
|
||||
<plurals name="import_total_success">
|
||||
<item quantity="one">%d Tunnel importiert</item>
|
||||
<item quantity="other">%d Tunnel importiert</item>
|
||||
</plurals>
|
||||
<plurals name="set_excluded_applications">
|
||||
<item quantity="one">%d ausgeschlossene Anwendung</item>
|
||||
<item quantity="other">%d ausgeschlossene Anwendungen</item>
|
||||
</plurals>
|
||||
<string name="add_peer">Teilnehmer hinzufügen</string>
|
||||
<string name="addresses">Adressen</string>
|
||||
<string name="allow_remote_control_intents_summary_off">Externe Apps dürfen keine Tunnel umschalten (empfohlen)</string>
|
||||
<string name="allow_remote_control_intents_summary_on">Externe Apps dürfen Tunnel umschalten (erweitert)</string>
|
||||
<string name="allow_remote_control_intents_title">Erlaube Steuerung über externe Apps</string>
|
||||
<string name="allowed_ips">Erlaubte IPs</string>
|
||||
<string name="app_name">WireGuard</string>
|
||||
<string name="bad_config_context">%1$ss %2$s</string>
|
||||
<string name="bad_config_context_top_level">%s</string>
|
||||
<string name="bad_config_error">%1$s in %2$s</string>
|
||||
<string name="bad_config_explanation_pka">: Muss positiv sein und nicht größer als 65535</string>
|
||||
<string name="bad_config_explanation_positive_number">: Muss positiv sein</string>
|
||||
<string name="bad_config_explanation_udp_port">: Muss eine gültige UDP-Portnummer sein</string>
|
||||
<string name="bad_config_reason_invalid_key">Ungültiger Schlüssel</string>
|
||||
<string name="bad_config_reason_invalid_number">Ungültige Zahl</string>
|
||||
<string name="bad_config_reason_invalid_value">Ungültiger Wert</string>
|
||||
<string name="bad_config_reason_missing_attribute">Attribut fehlt</string>
|
||||
<string name="bad_config_reason_missing_section">Fehlende Sektion</string>
|
||||
<string name="bad_config_reason_syntax_error">Syntaxfehler</string>
|
||||
<string name="bad_config_reason_unknown_attribute">Unbekanntes Attribut</string>
|
||||
<string name="bad_config_reason_unknown_section">Unbekannter Abschnitt</string>
|
||||
<string name="bad_config_reason_value_out_of_range">Wert ist außerhalb des gültigen Bereichs</string>
|
||||
<string name="bad_extension_error">Dateiendung muss .conf oder .zip sein</string>
|
||||
<string name="cancel">Abbrechen</string>
|
||||
<string name="config_delete_error">Konfigurationsdatei %s kann nicht gelöscht werden</string>
|
||||
<string name="config_exists_error">Konfiguration für „%s“ existiert bereits</string>
|
||||
<string name="config_file_exists_error">Konfigurationsdatei „%s“ existiert bereits</string>
|
||||
<string name="config_not_found_error">Konfigurationsdatei „%s“ nicht gefunden</string>
|
||||
<string name="config_rename_error">Konfigurationsdatei „%s “ kann nicht umbenannt werden</string>
|
||||
<string name="config_save_error">Konfiguration für „%1$s“ kann nicht gespeichert werden: %2$s</string>
|
||||
<string name="config_save_success">Konfiguration für „%s ” erfolgreich gespeichert</string>
|
||||
<string name="create_activity_title">WireGuard-Tunnel erstellen</string>
|
||||
<string name="create_bin_dir_error">Lokales Anwendungsverzeichnis konnte nicht erstellt werden</string>
|
||||
<string name="create_downloads_file_error">Datei konnte im Download-Verzeichnis nicht erstellt werden</string>
|
||||
<string name="create_empty">Neu erstellen</string>
|
||||
<string name="create_from_file">Aus Datei oder Archiv importieren</string>
|
||||
<string name="create_from_qr_code">Vom QR-Code scannen</string>
|
||||
<string name="create_output_dir_error">Konnte das Ausgabeverzeichnis nicht erstellen</string>
|
||||
<string name="create_temp_dir_error">Lokales temporäres Verzeichnis konnte nicht erstellt werden</string>
|
||||
<string name="create_tunnel">Tunnel erstellen</string>
|
||||
<string name="dark_theme_summary_off">Helles (Tag) Design in Verwendung</string>
|
||||
<string name="dark_theme_summary_on">Dunkles (Nacht) Design in Verwendung</string>
|
||||
<string name="dark_theme_title">Dunkles Design verwenden</string>
|
||||
<string name="delete">Entfernen</string>
|
||||
<string name="dns_servers">DNS-Server</string>
|
||||
<string name="edit">Bearbeiten</string>
|
||||
<string name="endpoint">Endpunkt</string>
|
||||
<string name="error_down">Fehler beim Herunterfahren des Tunnels: %s</string>
|
||||
<string name="error_fetching_apps">Fehler beim Abrufen der App-Liste: %s</string>
|
||||
<string name="error_root">Bitte root-Zugriff anfordern und erneut versuchen</string>
|
||||
<string name="error_up">Fehler beim Starten des Tunnels: %s</string>
|
||||
<string name="exclude_private_ips">Private IPs ausschließen</string>
|
||||
<string name="excluded_applications">Ausgeschlossene Anwendungen</string>
|
||||
<string name="generate_new_private_key">Neuen privaten Schlüssel generieren</string>
|
||||
<string name="generic_error">Unbekannter „%s“ Fehler</string>
|
||||
<string name="hint_automatic">(auto)</string>
|
||||
<string name="hint_generated">(generiert)</string>
|
||||
<string name="hint_optional">(optional)</string>
|
||||
<string name="hint_optional_discouraged">(optional, nicht empfohlen)</string>
|
||||
<string name="hint_random">(zufällig)</string>
|
||||
<string name="illegal_filename_error">Ungültiger Dateiname „%s“</string>
|
||||
<string name="import_error">Kann Tunnel nicht importieren: %s</string>
|
||||
<string name="import_from_qr_code">Tunnel aus QR-Code importieren</string>
|
||||
<string name="import_success">„%s” importiert</string>
|
||||
<string name="interface_title">Schnittstelle</string>
|
||||
<string name="key_contents_error">Falsche Zeichen in Schlüssel</string>
|
||||
<string name="key_length_error">Falsche Schlüssellänge</string>
|
||||
<string name="key_length_explanation_base64">: WireGuard base64-Schlüssel müssen 44 Zeichen enthalten (32 Bytes)</string>
|
||||
<string name="key_length_explanation_binary">: WireGuard-Schlüssel müssen 32 Bytes groß sein</string>
|
||||
<string name="key_length_explanation_hex">: WireGuard Hex-Schlüssel müssen 64 Zeichen (32 Bytes) groß sein</string>
|
||||
<string name="listen_port">Eingangs-Port</string>
|
||||
<string name="log_export_error">Fehler beim Exportieren des Logs: %s</string>
|
||||
<string name="log_export_subject">WireGuard Android Log Datei</string>
|
||||
<string name="log_export_success">Gespeichert in „%s”</string>
|
||||
<string name="log_export_title">Logdatei exportieren</string>
|
||||
<string name="log_saver_activity_label">Log speichern</string>
|
||||
<string name="log_viewer_pref_summary">Logs können beim Debuggen helfen</string>
|
||||
<string name="log_viewer_pref_title">Protokoll der Anwendung anzeigen</string>
|
||||
<string name="log_viewer_title">Loggen</string>
|
||||
<string name="logcat_error">Konnte logcat nicht ausführen: </string>
|
||||
<string name="module_disabler_disabled_summary">Das experimentelle Kernelmodul kann die Leistung verbessern</string>
|
||||
<string name="module_disabler_disabled_title">Kernelmodul-Backend aktivieren</string>
|
||||
<string name="module_disabler_enabled_summary">Das langsamere Userspace-Backend kann die Stabilität verbessern</string>
|
||||
<string name="module_disabler_enabled_title">Kernelmodul-Backend deaktivieren</string>
|
||||
<string name="module_installer_error">Es ist etwas schief gelaufen. Bitte versuchen Sie es später erneut</string>
|
||||
<string name="module_installer_initial">Das experimentelle Kernelmodul kann die Leistung verbessern</string>
|
||||
<string name="module_installer_not_found">Für Ihr Gerät sind keine Module verfügbar</string>
|
||||
<string name="module_installer_title">Kernelmodul herunterladen und installieren</string>
|
||||
<string name="module_installer_working">Herunterladen und Installieren…</string>
|
||||
<string name="module_version_error">Konnte Version des Kernel-Moduls nicht ermitteln</string>
|
||||
<string name="mtu">MTU</string>
|
||||
<string name="multiple_tunnels_summary_off">Einschalten eines Tunnels wird andere ausschalten</string>
|
||||
<string name="multiple_tunnels_summary_on">Mehrere Tunnel können gleichzeitig aktiviert werden</string>
|
||||
<string name="multiple_tunnels_title">Erlaube mehrere Tunnel gleichzeitig</string>
|
||||
<string name="name">Name</string>
|
||||
<string name="no_config_error">Versucht einen Tunnel ohne Konfiguration zu starten</string>
|
||||
<string name="no_configs_error">Keine Konfigurationen gefunden</string>
|
||||
<string name="no_tunnels_error">Keine Tunnel vorhanden</string>
|
||||
<string name="parse_error_generic">String (Zeichenkette)</string>
|
||||
<string name="parse_error_inet_address">IP-Adresse</string>
|
||||
<string name="parse_error_inet_endpoint">Endpunkt</string>
|
||||
<string name="parse_error_inet_network">IP-Netzwerk</string>
|
||||
<string name="parse_error_integer">Zahl</string>
|
||||
<string name="parse_error_reason">Kann %1$s “%2$s ” nicht verarbeiten</string>
|
||||
<string name="peer">Teilnehmer</string>
|
||||
<string name="permission_description">WireGuard-Tunnel steuern, Tunnel aktivieren und nach Belieben deaktivieren, wodurch der Internetverkehr möglicherweise falsch geleitet wird</string>
|
||||
<string name="permission_label">WireGuard-Tunnel steuern</string>
|
||||
<string name="persistent_keepalive">Dauerhafte Erhaltung</string>
|
||||
<string name="pre_shared_key">Vorab geteilter Schlüssel</string>
|
||||
<string name="private_key">Privater Schlüssel</string>
|
||||
<string name="public_key">Öffentlicher Schlüssel</string>
|
||||
<string name="public_key_description">Öffentlicher Schlüssel</string>
|
||||
<string name="qr_code_hint">Tipp: Mit `qrencode -t ansiutf8 < tunnel.conf` generieren.</string>
|
||||
<string name="restore_on_boot_summary_off">Wird aktivierte Tunnel beim Systemstart nicht automatisch starten</string>
|
||||
<string name="restore_on_boot_summary_on">Wird aktivierte Tunnel beim Systemstart automatisch wieder starten</string>
|
||||
<string name="restore_on_boot_title">Beim Neustart wiederherstellen</string>
|
||||
<string name="save">Speichern</string>
|
||||
<string name="select_all">Alle auswählen</string>
|
||||
<string name="set_exclusions">Ausnahmen festlegen</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="shell_exit_status_read_error">Shell kann den Exit-Status nicht lesen</string>
|
||||
<string name="shell_marker_count_error">Die Shell erwartete 4 Marker, erhielt aber %d</string>
|
||||
<string name="shell_start_error">Shell konnte nicht gestartet werden: %d</string>
|
||||
<string name="success_application_will_restart">Erfolgreich. Die Anwendung wird nun neu starten…</string>
|
||||
<string name="toggle_all">Alle umschalten</string>
|
||||
<string name="toggle_error">Fehler beim Umschalten des WireGuard-Tunnels: %s</string>
|
||||
<string name="tools_installer_already">wg und wg-quick sind bereits installiert</string>
|
||||
<string name="tools_installer_failure">Kommandozeilenwerkzeuge konnten nicht installiert werden (kein Root?)</string>
|
||||
<string name="tools_installer_initial">Optionale Tools für Skripte installieren</string>
|
||||
<string name="tools_installer_initial_magisk">Optionale Werkzeuge für das Skripten als Magisk-Modul installieren</string>
|
||||
<string name="tools_installer_initial_system">Optionale Werkzeuge für Skripte auf der Systempartition installieren</string>
|
||||
<string name="tools_installer_success_magisk">wg und wg-quick als Magisk-Modul installiert (Neustart erforderlich)</string>
|
||||
<string name="tools_installer_success_system">wg und wg-quick auf der Systempartition installiert</string>
|
||||
<string name="tools_installer_title">Kommandozeilenwerkzeuge installieren</string>
|
||||
<string name="tools_installer_working">Installiere wg und wg-quick</string>
|
||||
<string name="tools_unavailable_error">Erforderliche Tools nicht verfügbar</string>
|
||||
<string name="transfer">Transfer</string>
|
||||
<string name="transfer_bytes">%d B</string>
|
||||
<string name="transfer_gibibytes">%.2f GiB</string>
|
||||
<string name="transfer_kibibytes">%.2f KiB</string>
|
||||
<string name="transfer_mibibytes">%.2f MiB</string>
|
||||
<string name="transfer_rx_tx">empfangen: %1$s, gesendet: %2$s</string>
|
||||
<string name="transfer_tibibytes">%.2f TiB</string>
|
||||
<string name="tun_create_error">Konnte tun Gerät nicht erstellen</string>
|
||||
<string name="tunnel_config_error">Tunnel konnte nicht konfiguriert werden (wg-quick gab %d zurück)</string>
|
||||
<string name="tunnel_create_error">Kann Tunnel nicht erstellen: %s</string>
|
||||
<string name="tunnel_create_success">Tunnel „%s “ erfolgreich erstellt</string>
|
||||
<string name="tunnel_error_already_exists">Tunnel „%s“ existiert bereits</string>
|
||||
<string name="tunnel_error_invalid_name">Ungültiger Name</string>
|
||||
<string name="tunnel_list_placeholder">Füge einen Tunnel mit der blauen Taste hinzu</string>
|
||||
<string name="tunnel_name">Tunnelname</string>
|
||||
<string name="tunnel_on_error">Tunnel kann nicht eingeschaltet werden (wgTurnOn gab %d zurück)</string>
|
||||
<string name="tunnel_rename_error">Kann Tunnel nicht umbenennen: %s</string>
|
||||
<string name="tunnel_rename_success">Tunnel erfolgreich in „%s “ umbenannt</string>
|
||||
<string name="type_name_go_userspace">Go userspace</string>
|
||||
<string name="type_name_kernel_module">Kernelmodul</string>
|
||||
<string name="unknown_error">Unbekannter Fehler</string>
|
||||
<string name="version_summary">%1$s Backend v%2$s</string>
|
||||
<string name="version_summary_checking">Überprüfe %s Backend-Version</string>
|
||||
<string name="version_summary_unknown">Unbekannte %s Version</string>
|
||||
<string name="version_title">WireGuard für Android v%s</string>
|
||||
<string name="vpn_not_authorized_error">VPN-Dienst nicht vom Benutzer autorisiert</string>
|
||||
<string name="vpn_start_error">Android VPN-Dienst konnte nicht gestartet werden</string>
|
||||
<string name="zip_export_error">Kann Tunnel nicht exportieren: %s</string>
|
||||
<string name="zip_export_success">Gespeichert unter “%s”</string>
|
||||
<string name="zip_export_summary">Zip-Datei wird im Download-Verzeichnis gespeichert</string>
|
||||
<string name="zip_export_title">Tunnel als Zip-Datei exportieren</string>
|
||||
</resources>
|
||||
@@ -26,6 +26,9 @@
|
||||
</plurals>
|
||||
<string name="add_peer">Aggiungi peer</string>
|
||||
<string name="addresses">Indirizzi</string>
|
||||
<string name="allow_remote_control_intents_summary_off">Le applicazioni esterne non possono attivare tunnel (consigliato)</string>
|
||||
<string name="allow_remote_control_intents_summary_on">Le applicazioni esterne possono attivare tunnel (avanzato)</string>
|
||||
<string name="allow_remote_control_intents_title">Consenti applicazioni di controllo remoto</string>
|
||||
<string name="allowed_ips">IP consentiti</string>
|
||||
<string name="app_name">WireGuard</string>
|
||||
<string name="bad_config_context">%2$s di %1$s</string>
|
||||
@@ -54,61 +57,79 @@
|
||||
<string name="config_save_success">Configurazione per “%s” salvata correttamente</string>
|
||||
<string name="create_activity_title">Crea un tunnel WireGuard</string>
|
||||
<string name="create_bin_dir_error">Impossibile creare cartella locale binari</string>
|
||||
<string name="create_empty">Crea da zero</string>
|
||||
<string name="create_output_dir_error">Impossibile creare la cartella di output</string>
|
||||
<string name="create_downloads_file_error">Impossibile creare il file nella cartella di download</string>
|
||||
<string name="create_empty">Crea da zero</string>
|
||||
<string name="create_from_file">Importa da file o archivio</string>
|
||||
<string name="create_from_qr_code">Scansione da codice QR</string>
|
||||
<string name="create_output_dir_error">Impossibile creare la cartella di output</string>
|
||||
<string name="create_temp_dir_error">Impossibile creare la cartella locale temporanea</string>
|
||||
<string name="create_tunnel">Crea tunnel</string>
|
||||
<string name="dark_theme_summary_off">Stai usando il tema chiaro (giorno)</string>
|
||||
<string name="dark_theme_summary_on">Stai usando il tema scuro (notte)</string>
|
||||
<string name="dark_theme_title">Usa tema scuro</string>
|
||||
<string name="delete">Elimina</string>
|
||||
<string name="toggle_all">Inverti tutto</string>
|
||||
<string name="dns_servers">Server DNS</string>
|
||||
<string name="edit">Modifica</string>
|
||||
<string name="endpoint">Endpoint</string>
|
||||
<string name="error_down">Errore di disattivazione del tunnel: %s</string>
|
||||
<string name="error_fetching_apps">Errore di recupero della lista app: %s</string>
|
||||
<string name="error_root">Si prega di ottenere l\'accesso root e riprovare</string>
|
||||
<string name="error_fetching_apps">Errore di recupero dell\'elenco applicazioni: %s</string>
|
||||
<string name="error_root">Accedi come root e riprova</string>
|
||||
<string name="error_up">Errore di attivazione del tunnel: %s</string>
|
||||
<string name="exclude_private_ips">Escludi IP privati</string>
|
||||
<string name="excluded_applications">Applicazioni escluse</string>
|
||||
<string name="generate_new_private_key">Genera nuova chiave privata</string>
|
||||
<string name="generic_error">Errore “%s” sconosciuto</string>
|
||||
<string name="hint_automatic">(auto)</string>
|
||||
<string name="hint_generated">(generato)</string>
|
||||
<string name="hint_optional">(facoltativo)</string>
|
||||
<string name="hint_optional_discouraged">(facoltativo, non consigliato)</string>
|
||||
<string name="hint_random">(casuale)</string>
|
||||
<string name="illegal_filename_error">Nome file “%s” non valido</string>
|
||||
<string name="import_error">Impossibile importare il tunnel: %s</string>
|
||||
<string name="import_from_qr_code">Importa tunnel da codice QR</string>
|
||||
<string name="import_success">Importato “%s”</string>
|
||||
<string name="interface_title">Interfaccia</string>
|
||||
<string name="key_contents_error">Caratteri non validi nella chiave</string>
|
||||
<string name="key_length_error">Lunghezza non valida della chiave</string>
|
||||
<string name="key_length_explanation_base64">: le chiavi base64 di WireGuard devono essere di 44 caratteri (32 byte)</string>
|
||||
<string name="key_length_explanation_binary">: le chiavi di WireGuard devono essere di 32 byte</string>
|
||||
<string name="key_length_explanation_hex">: le chiavi hex di WireGuard devono essere di 64 caratteri (32 byte)</string>
|
||||
<string name="key_length_explanation_hex">: le chiavi esadecimali di WireGuard devono essere di 64 caratteri (32 byte)</string>
|
||||
<string name="listen_port">Porta in ascolto</string>
|
||||
<string name="log_export_error">Impossibile esportare il registro: %s</string>
|
||||
<string name="log_export_error">Impossibile esportare il log: %s</string>
|
||||
<string name="log_export_subject">File di log WireGuard Android</string>
|
||||
<string name="log_export_success">Salvato in “%s”</string>
|
||||
<string name="log_export_title">Esporta file registro</string>
|
||||
<string name="log_export_title">Esporta file di log</string>
|
||||
<string name="log_saver_activity_label">Salva log</string>
|
||||
<string name="log_viewer_pref_summary">I log possono aiutare in fase di debug</string>
|
||||
<string name="log_viewer_pref_title">Visualizza log dell\'applicazione</string>
|
||||
<string name="log_viewer_title">Log</string>
|
||||
<string name="logcat_error">Impossibile eseguire logcat: </string>
|
||||
<string name="module_version_error">Impossibile determinare la versione modulo del kernel</string>
|
||||
<string name="module_disabler_disabled_summary">Il modulo sperimentale del kernel può migliorare le prestazioni</string>
|
||||
<string name="module_disabler_disabled_title">Abilita il backend del modulo del kernel</string>
|
||||
<string name="module_disabler_enabled_summary">Il backend in userspace più lento potrebbe migliorare la stabilità</string>
|
||||
<string name="module_disabler_enabled_title">Disabilita il backend del modulo del kernel</string>
|
||||
<string name="module_installer_error">Qualcosa non ha funzionato. Riprova</string>
|
||||
<string name="module_installer_initial">Il modulo sperimentale del kernel può migliorare le prestazioni</string>
|
||||
<string name="module_installer_not_found">Nessun modulo disponibile per il tuo dispositivo</string>
|
||||
<string name="module_installer_initial">Il modulo del kernel sperimentale può migliorare le prestazioni</string>
|
||||
<string name="module_installer_title">Scarica e installa il modulo del kernel</string>
|
||||
<string name="module_installer_working">Scaricamento e installazione…</string>
|
||||
<string name="module_installer_error">Qualcosa è andato storto. Riprova</string>
|
||||
<string name="module_version_error">Impossibile determinare la versione modulo del kernel</string>
|
||||
<string name="mtu">MTU</string>
|
||||
<string name="multiple_tunnels_summary_off">L\'attivazione di un tunnel disattiverà gli altri</string>
|
||||
<string name="multiple_tunnels_summary_on">Più tunnel possono essere attivati contemporaneamente</string>
|
||||
<string name="multiple_tunnels_title">Consenti più tunnel contemporanei</string>
|
||||
<string name="name">Nome</string>
|
||||
<string name="no_config_error">Tentativo di attivare un tunnel senza configurazione</string>
|
||||
<string name="no_configs_error">Nessuna configurazione trovata</string>
|
||||
<string name="no_tunnels_error">Non esistono tunnel</string>
|
||||
<string name="parse_error_generic">stringa</string>
|
||||
<string name="parse_error_inet_address">indirizzo IP</string>
|
||||
<string name="parse_error_inet_address">Indirizzo IP</string>
|
||||
<string name="parse_error_inet_endpoint">endpoint</string>
|
||||
<string name="parse_error_inet_network">rete IP</string>
|
||||
<string name="parse_error_inet_network">IP rete</string>
|
||||
<string name="parse_error_integer">numero</string>
|
||||
<string name="parse_error_reason">Impossibile analizzare %1$s “%2$s”</string>
|
||||
<string name="peer">Peer</string>
|
||||
<string name="permission_description">controllare i tunnel WireGuard, abilitando e disabilitando i tunnel a piacimento, potenzialmente dirottando il traffico Internet</string>
|
||||
<string name="permission_label">controlla tunnel WireGuard</string>
|
||||
<string name="persistent_keepalive">Tieni sempre attivo</string>
|
||||
<string name="pre_shared_key">Chiave condivisa (PSK)</string>
|
||||
@@ -116,14 +137,18 @@
|
||||
<string name="public_key">Chiave pubblica</string>
|
||||
<string name="public_key_description">La chiave pubblica</string>
|
||||
<string name="qr_code_hint">Suggerimento: genera con `qrencode -t ansiutf8 < tunnel.conf`.</string>
|
||||
<string name="restore_on_boot_summary_off">Non attiverà i tunnel configurati all\'avvio</string>
|
||||
<string name="restore_on_boot_summary_on">Attiverà i tunnel configurati all\'avvio</string>
|
||||
<string name="restore_on_boot_title">Ripristina all\'avvio</string>
|
||||
<string name="save">Salva</string>
|
||||
<string name="select_all">Seleziona tutto</string>
|
||||
<string name="set_exclusions">Imposta esclusioni</string>
|
||||
<string name="settings">Impostazioni</string>
|
||||
<string name="shell_exit_status_read_error">La shell non riesce a leggere l\'exit status</string>
|
||||
<string name="shell_exit_status_read_error">La shell non riesce a leggere lo stato di uscita</string>
|
||||
<string name="shell_marker_count_error">La shell si aspettava 4 marker, ne ha ricevuti %d</string>
|
||||
<string name="shell_start_error">Avvio della shell fallito: %d</string>
|
||||
<string name="shell_start_error">Avvio della shell non riuscito: %d</string>
|
||||
<string name="success_application_will_restart">Successo. L\'applicazione si riavvierà…</string>
|
||||
<string name="toggle_all">Inverti tutto</string>
|
||||
<string name="toggle_error">Errore di commutazione tunnel WireGuard: %s</string>
|
||||
<string name="tools_installer_already">wg e wg-quick sono già installati</string>
|
||||
<string name="tools_installer_failure">Impossibile installare strumenti di riga di comando (non root?)</string>
|
||||
@@ -136,11 +161,11 @@
|
||||
<string name="tools_installer_working">Installazione di wg e wg-quick</string>
|
||||
<string name="tools_unavailable_error">Strumenti necessari non disponibili</string>
|
||||
<string name="transfer">Trasferisci</string>
|
||||
<string name="transfer_rx_tx">rx: %1$s, tx: %2$s</string>
|
||||
<string name="transfer_bytes">%d B</string>
|
||||
<string name="transfer_gibibytes">%.2f GiB</string>
|
||||
<string name="transfer_kibibytes">%.2f KiB</string>
|
||||
<string name="transfer_mibibytes">%.2f MiB</string>
|
||||
<string name="transfer_gibibytes">%.2f GiB</string>
|
||||
<string name="transfer_rx_tx">rx: %1$s, tx: %2$s</string>
|
||||
<string name="transfer_tibibytes">%.2f TiB</string>
|
||||
<string name="tun_create_error">Impossibile creare il dispositivo tun</string>
|
||||
<string name="tunnel_config_error">Impossibile configurare il tunnel (wg-quick ha risposto %d)</string>
|
||||
@@ -156,7 +181,7 @@
|
||||
<string name="type_name_go_userspace">Spazio utente Go</string>
|
||||
<string name="type_name_kernel_module">Modulo kernel</string>
|
||||
<string name="unknown_error">Errore sconosciuto</string>
|
||||
<string name="version_summary">%1$s backend v%2$s</string>
|
||||
<string name="version_summary">Backend %1$s v%2$s</string>
|
||||
<string name="version_summary_checking">Controllo versione backend %s</string>
|
||||
<string name="version_summary_unknown">Versione %s sconosciuta</string>
|
||||
<string name="version_title">WireGuard per Android v%s</string>
|
||||
@@ -166,6 +191,4 @@
|
||||
<string name="zip_export_success">Salvato in “%s”</string>
|
||||
<string name="zip_export_summary">Il file zip verrà salvato nella cartella di download</string>
|
||||
<string name="zip_export_title">Esporta i tunnel in un file zip</string>
|
||||
<string name="key_length_error">Lunghezza chiave non valida</string>
|
||||
<string name="key_contents_error">Caratteri errati nella chiave</string>
|
||||
</resources>
|
||||
|
||||
@@ -71,10 +71,12 @@
|
||||
<string name="error_up">トンネル起動時エラー: %s</string>
|
||||
<string name="exclude_private_ips">プライベート IP アドレスを除外</string>
|
||||
<string name="excluded_applications">対象外とするアプリケーション</string>
|
||||
<string name="generate_new_private_key">新しい秘密鍵を生成する</string>
|
||||
<string name="generic_error">未知の “%s” エラー</string>
|
||||
<string name="hint_automatic">(自動)</string>
|
||||
<string name="hint_generated">(生成済み)</string>
|
||||
<string name="hint_optional">(任意)</string>
|
||||
<string name="hint_optional_discouraged">(任意項目ですが、設定は推奨しません)</string>
|
||||
<string name="hint_random">(ランダム)</string>
|
||||
<string name="illegal_filename_error">不正なファイル名 “%s”</string>
|
||||
<string name="import_error">トンネル設定をインポートできません: %s</string>
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
<item quantity="other">%d туннелей выбрано</item>
|
||||
</plurals>
|
||||
<plurals name="import_partial_success">
|
||||
<item quantity="one">Импортирован %1$d из %1$d туннелей</item>
|
||||
<item quantity="few">Импортировано %1$d из %1$d туннелей</item>
|
||||
<item quantity="many">Импортировано %1$d из %1$d туннелей</item>
|
||||
<item quantity="other">Импортировано %1$d из %1$d туннелей</item>
|
||||
<item quantity="one">Импортирован %1$d из %2$d туннелей</item>
|
||||
<item quantity="few">Импортировано %1$d из %2$d туннелей</item>
|
||||
<item quantity="many">Импортировано %1$d из %2$d туннелей</item>
|
||||
<item quantity="other">Импортировано %1$d из %2$d туннелей</item>
|
||||
</plurals>
|
||||
<plurals name="import_total_success">
|
||||
<item quantity="one">Импортированный %d туннель</item>
|
||||
@@ -89,10 +89,12 @@
|
||||
<string name="error_up">Ошибка при запуске туннеля: %s</string>
|
||||
<string name="exclude_private_ips">Исключить частные IP-адреса</string>
|
||||
<string name="excluded_applications">Исключенные приложения</string>
|
||||
<string name="generate_new_private_key">Сгенерировать новый приватный ключ</string>
|
||||
<string name="generic_error">Неизвестная “%s” ошибка</string>
|
||||
<string name="hint_automatic">(авто)</string>
|
||||
<string name="hint_generated">(авто)</string>
|
||||
<string name="hint_optional">(авто)</string>
|
||||
<string name="hint_optional_discouraged">(опционально, не рекомендуется)</string>
|
||||
<string name="hint_random">(авто)</string>
|
||||
<string name="illegal_filename_error">Неверное имя файла “%s”</string>
|
||||
<string name="import_error">Не удалось импортировать туннель: %s</string>
|
||||
|
||||
@@ -71,10 +71,12 @@
|
||||
<string name="error_up">建立连接时出错:%s</string>
|
||||
<string name="exclude_private_ips">排除局域网</string>
|
||||
<string name="excluded_applications">排除的应用</string>
|
||||
<string name="generate_new_private_key">生成新的私钥</string>
|
||||
<string name="generic_error">未知的 “%s” 错误</string>
|
||||
<string name="hint_automatic">(自动)</string>
|
||||
<string name="hint_generated">(生成)</string>
|
||||
<string name="hint_optional">(可选)</string>
|
||||
<string name="hint_optional_discouraged">(可选,不建议设置)</string>
|
||||
<string name="hint_random">(随机)</string>
|
||||
<string name="illegal_filename_error">文件名 “%s” 不合法</string>
|
||||
<string name="import_error">无法导入隧道:%s</string>
|
||||
@@ -115,11 +117,11 @@
|
||||
<string name="no_configs_error">未找到配置</string>
|
||||
<string name="no_tunnels_error">无隧道</string>
|
||||
<string name="parse_error_generic">字符串</string>
|
||||
<string name="parse_error_inet_address"> IP 地址</string>
|
||||
<string name="parse_error_inet_address">\u0020IP 地址</string>
|
||||
<string name="parse_error_inet_endpoint">对端</string>
|
||||
<string name="parse_error_inet_network"> IP 网络</string>
|
||||
<string name="parse_error_inet_network">\u0020IP 网络</string>
|
||||
<string name="parse_error_integer">数字</string>
|
||||
<string name="parse_error_reason">无法解析%1$s “%2$s” </string>
|
||||
<string name="parse_error_reason">无法解析%1$s “%2$s”\u0020</string>
|
||||
<string name="peer">节点 / Peer</string>
|
||||
<string name="permission_description">自由控制 WireGuard 隧道的开启或关闭,但可能会导致流量误传</string>
|
||||
<string name="permission_label">控制 WireGuard 隧道</string>
|
||||
|
||||
@@ -191,4 +191,8 @@
|
||||
<string name="zip_export_success">Saved to “%s”</string>
|
||||
<string name="zip_export_summary">Zip file will be saved to downloads folder</string>
|
||||
<string name="zip_export_title">Export tunnels to zip file</string>
|
||||
<string name="biometric_prompt_zip_exporter_title">Authenticate to export tunnels</string>
|
||||
<string name="biometric_prompt_private_key_title">Authenticate to view private key</string>
|
||||
<string name="biometric_auth_error">Authentication failure</string>
|
||||
<string name="biometric_auth_error_reason">Authentication failure: %s</string>
|
||||
</resources>
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
</style>
|
||||
|
||||
<style name="DetailText" parent="TextAppearance.MaterialComponents.Body1" />
|
||||
|
||||
<style name="SectionText" parent="TextAppearance.MaterialComponents.Subtitle1" />
|
||||
|
||||
<style name="ThemeOverlay.AppTheme.TextInputEditText.OutlinedBox" parent="ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox">
|
||||
@@ -64,6 +65,8 @@
|
||||
<style name="TextInputLayoutBase" parent="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
<item name="boxStrokeColor">?attr/colorSecondary</item>
|
||||
<item name="hintTextColor">?attr/colorOnPrimary</item>
|
||||
<item name="materialThemeOverlay">@style/ThemeOverlay.AppTheme.TextInputEditText.OutlinedBox</item>
|
||||
<item name="materialThemeOverlay">
|
||||
@style/ThemeOverlay.AppTheme.TextInputEditText.OutlinedBox
|
||||
</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
buildscript {
|
||||
ext {
|
||||
wireguardVersionCode = 469
|
||||
wireguardVersionName = '0.0.20200328'
|
||||
wireguardVersionCode = 472
|
||||
wireguardVersionName = '1.0.20200401'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user