Compare commits

...

34 Commits

Author SHA1 Message Date
Jason A. Donenfeld 7b5ceac9f7 version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-03 19:07:33 -06:00
Jason A. Donenfeld 9fe0019ec1 tunnel: libwg-go: use deterministic verdef name
From the ld.gold source:

  const char* name = parameters->options().soname();
  if (name == NULL)
    name = parameters->options().output_file_name();

That means by default it uses `-o {output}` as the verdef aux name,
which is random every time due to Go's build system. By passing in
`--soname={somethingexplicit}` we can instead have a deterministic
verdef.

This commit makes wireguard-android reproducible.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-03 19:07:33 -06:00
Jason A. Donenfeld 84654a0302 gradle: upgrade to 6.3
It looks like F-Droid finally updated.

This reverts commit cd43444d1f.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-03 19:07:33 -06:00
Jason A. Donenfeld cd43444d1f gradle: downgrade to 6.2.1 to deal with f-droid organizational mismanagement
The fdroidserver repo hardcodes hashes of all gradle versions that are
allowed to be used. This is currently up to date, having 6.3. However,
the f-droid build server is not up to date, because it is controlled by
the founder of f-droid, who is generally unavailable and does not update
it. Meanwhile other members of the project would like to stand up their
own build server instance, but this seems potentially arduous and
there's still the question of the signing key. At least that's the story
I was able to glean from asking around.

So, in order to work around this organizational brokeness, we just
downgrade to 6.2.1.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-01 23:43:24 -06:00
Jason A. Donenfeld b7028896c7 ObservableTunnel: do not cache key
Reported-by: Reza Island's <rezza.aji.ras@gmail.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-01 22:05:24 -06:00
Jason A. Donenfeld c1e86acb3c tunnel: libwg-go: remove -x option from flock
It's already the default and the macOS port of flock doesn't support it.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-01 22:05:24 -06:00
Harsh Shandilya 7d31bd2be9 build: update fragment to 1.2.4
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-04-01 22:05:24 -06:00
Jason A. Donenfeld 2e573a66a4 build: update agp to 3.6.2
Harsh likes to be first in line.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-01 22:05:24 -06:00
Harsh Shandilya 8d128cf2e9 ui: disable LongLogTag lint
We know what we're doing

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-03-31 03:48:00 +05:30
Jason A. Donenfeld d5ffa08480 TunnelEditorFragment: don't show bioauth if already visible
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-30 14:53:25 -06:00
Jason A. Donenfeld 43ce69bef4 version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-30 14:14:54 -06:00
Harsh Shandilya 44a0f53e58 strings: sync translations
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-03-30 14:08:24 -06:00
Jason A. Donenfeld d74b988f75 global: cleanup code style
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-30 13:57:43 -06:00
Harsh Shandilya 10e910186e ui: update proguard rules to keep crash logs readable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-03-30 13:49:41 -06:00
Jason A. Donenfeld 017f420d42 LogViewerActivity: don't crash if pipe closes
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-30 03:25:47 -06:00
Jason A. Donenfeld bc186fe6ad TunnelDetail: set singleLine=true for API <=23
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-30 03:23:32 -06:00
Jason A. Donenfeld 09b40cdec7 BiometricAuthenticator: rework logic and bugs
Otherwise there's a frameworks bug that causes the fragment's activity
to become null.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-30 03:23:32 -06:00
Harsh Shandilya d2721f2d7d BiometricAuthenticator: implement biometric authentication for sensitive operations
When biometric hardware is available, it will be used to authenticate
the user before private keys are shown on screen or when zip exports
are executed.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-03-30 03:23:32 -06:00
Harsh Shandilya 3095e19e13 ObservableTunnel: Don't cache configAsync
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-03-30 03:23:32 -06:00
Jason A. Donenfeld c547d033c3 ui: replace generate icon with 'sync' instead of 'toys'
I dislike this change, but Harsh thinks the toys icon is utterly insane
and maybe he's right.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-30 03:23:32 -06:00
Jason A. Donenfeld 4d4764eefb BindingAdapters: use sleeker lambda
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-30 03:23:32 -06:00
Jason A. Donenfeld d44a83faaa TunnelEditorFragment: add hooks for biometric auth
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-30 03:23:32 -06:00
Vincenzo Reale 2337fe37be strings: update Italian translation
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-29 17:26:14 -06:00
Jason A. Donenfeld c5b71cb484 README: mention translations
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-29 17:16:09 -06:00
Jason A. Donenfeld 480c95d4d6 ui: remove unused stream support
The tunnel/ module still uses it and exposes it as an "api", but nothing
inside of ui/ should be using it now.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-29 17:00:31 -06:00
Harsh Shandilya 8a45e965eb strings: Sync German translations
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-03-29 14:10:39 +05:30
Jason A. Donenfeld c9717693d0 tunnel: libwg-go: check sha256 of downloaded tarball
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-29 02:36:29 -06:00
Jason A. Donenfeld 0fa3fe3d43 tunnel: libwg-go: stick go tarball in gradle cache
This way we don't have to build over and over.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-29 02:28:41 -06:00
Jason A. Donenfeld ed090f7ecb strings: fix positional specifier in russian translation
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-29 01:52:01 -06:00
Jason A. Donenfeld f916f96761 tunnel: libwg-go: prevent parallel downloads
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-29 01:31:39 -06:00
Jason A. Donenfeld 79e766c4e1 tunnel: fix package name being passed through to cmake
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-29 01:18:46 -06:00
Harsh Shandilya 183273dcf5 AddTunnelsSheet: Make behaviour nullable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-03-29 11:04:59 +05:30
Harsh Shandilya b3bb7c694b build: Improve R8 rules and enable aggressive optimization modes
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-03-29 10:52:54 +05:30
Jason A. Donenfeld 456a74db05 global: hardcode tags so that minification doesn't ruin the log
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-03-28 23:21:12 -06:00
54 changed files with 664 additions and 155 deletions
+6
View File
@@ -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
View File
@@ -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'
}
+14
View File
@@ -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
+16
View File
@@ -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 {
+2 -1
View File
@@ -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);
}
}
}
+1
View File
@@ -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}
+24 -8
View File
@@ -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:
+5 -12
View File
@@ -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) {
+36
View File
@@ -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>(...);
}
+11
View File
@@ -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
}
@@ -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>
+10 -9
View File
@@ -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
+194
View File
@@ -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 &lt; 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>
+43 -20
View File
@@ -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 &lt; 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>
+2
View File
@@ -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>
+6 -4
View File
@@ -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>
+5 -3
View File
@@ -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">&#x0020;IP 地址</string>
<string name="parse_error_inet_address">\u0020IP 地址</string>
<string name="parse_error_inet_endpoint">对端</string>
<string name="parse_error_inet_network">&#x0020;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”&#x0020;</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>
+4
View File
@@ -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>
+4 -1
View File
@@ -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
View File
@@ -1,6 +1,6 @@
buildscript {
ext {
wireguardVersionCode = 469
wireguardVersionName = '0.0.20200328'
wireguardVersionCode = 472
wireguardVersionName = '1.0.20200401'
}
}