mirror of
https://github.com/amnezia-vpn/amneziawg-android.git
synced 2026-07-03 14:05:13 +02:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 914917036e | |||
| 6e9ca435ca | |||
| 0797e99559 | |||
| 0ba064ff48 | |||
| b4fe9efadb | |||
| b1b08ce716 | |||
| 20480992c4 | |||
| 9df90b259a | |||
| b94a412c09 | |||
| 0b0a553123 | |||
| 6d4d2f4ebf | |||
| e55358f685 | |||
| c2fdd0ca93 | |||
| 19044b0f53 | |||
| d4e77e6d57 | |||
| 6ba3e4f001 | |||
| 8152191277 | |||
| 8c6c909e98 | |||
| fdda87fd31 | |||
| 3907f6a4c1 | |||
| 652971d746 | |||
| 1bf8d7e4c4 | |||
| 3c834ac8e3 | |||
| 8669ae78e1 | |||
| 53f8c8dbc4 | |||
| 1328660b0b |
+1
-1
@@ -14,5 +14,5 @@ build/
|
||||
*.dex
|
||||
*.iml
|
||||
*.jks
|
||||
keystore.properties
|
||||
gradlew.bat
|
||||
maint/
|
||||
|
||||
+6
-6
@@ -1,20 +1,20 @@
|
||||
buildscript {
|
||||
ext {
|
||||
activityVersion = '1.6.1'
|
||||
activityVersion = '1.7.1'
|
||||
annotationsVersion = '1.6.0'
|
||||
appcompatVersion = '1.6.1'
|
||||
biometricVersion = '1.1.0'
|
||||
collectionVersion = '1.2.0'
|
||||
constraintLayoutVersion = '2.1.4'
|
||||
coordinatorLayoutVersion = '1.2.0'
|
||||
coreKtxVersion = '1.10.0-rc01'
|
||||
coreKtxVersion = '1.10.0'
|
||||
coroutinesVersion = '1.6.4'
|
||||
datastoreVersion = '1.0.0'
|
||||
desugarVersion = '1.1.5'
|
||||
fragmentVersion = '1.5.5'
|
||||
fragmentVersion = '1.5.7'
|
||||
jsr305Version = '3.0.2'
|
||||
junitVersion = '4.13.2'
|
||||
lifecycleRuntimeKtxVersion = '2.6.0'
|
||||
lifecycleRuntimeKtxVersion = '2.6.1'
|
||||
materialComponentsVersion = '1.8.0'
|
||||
preferenceVersion = '1.2.0'
|
||||
zxingEmbeddedVersion = '4.3.0'
|
||||
@@ -25,8 +25,8 @@ buildscript {
|
||||
|
||||
tasks {
|
||||
wrapper {
|
||||
gradleVersion = "8.0.2"
|
||||
distributionSha256Sum = "ff7bf6a86f09b9b2c40bb8f48b25fc19cf2b2664fd1d220cd7ab833ec758d0d7"
|
||||
gradleVersion = "8.1.1"
|
||||
distributionSha256Sum = "e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -42,8 +42,6 @@ android.generateManifestClass=true
|
||||
android.experimental.enableSourceSetPathsMap=true
|
||||
# Use relative paths for better Gradle caching of library build tasks
|
||||
android.experimental.cacheCompileLibResources=true
|
||||
# Disable automatic creation of publishing components
|
||||
android.disableAutomaticComponentCreation=true
|
||||
|
||||
# Default Android build features
|
||||
# Disable BuildConfig generation by default
|
||||
@@ -61,8 +59,8 @@ android.library.defaults.buildfeatures.androidresources=false
|
||||
|
||||
# Suppress warnings for some features that aren't yet stabilized
|
||||
android.suppressUnsupportedOptionWarnings=android.keepWorkerActionServicesBetweenBuilds,\
|
||||
android.experimental.enableNewResourceShrinker,android.experimental.enableNewResourceShrinker.preciseShrinking,\
|
||||
android.enableAppCompileTimeRClass,android.experimental.enableSourceSetPathsMap,android.experimental.cacheCompileLibResources,\
|
||||
android.experimental.enableNewResourceShrinker.preciseShrinking,\
|
||||
android.enableAppCompileTimeRClass,\
|
||||
android.suppressUnsupportedOptionWarnings
|
||||
|
||||
# OSSRH sometimes struggles with slow deployments, so this makes Gradle
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+2
-2
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=ff7bf6a86f09b9b2c40bb8f48b25fc19cf2b2664fd1d220cd7ab833ec758d0d7
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
|
||||
distributionSha256Sum=e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -85,9 +85,6 @@ done
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
@@ -197,6 +194,10 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
pluginManagement {
|
||||
def agpVersion = '7.4.2'
|
||||
def agpVersion = '8.0.0'
|
||||
def kotlinVersion = "1.8.0"
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.wireguard.crypto.KeyFormatException;
|
||||
import com.wireguard.util.NonNullForAll;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -125,12 +126,14 @@ public final class GoBackend implements Backend {
|
||||
Key key = null;
|
||||
long rx = 0;
|
||||
long tx = 0;
|
||||
long latestHandshakeMSec = 0;
|
||||
for (final String line : config.split("\\n")) {
|
||||
if (line.startsWith("public_key=")) {
|
||||
if (key != null)
|
||||
stats.add(key, rx, tx);
|
||||
stats.add(key, rx, tx, latestHandshakeMSec);
|
||||
rx = 0;
|
||||
tx = 0;
|
||||
latestHandshakeMSec = 0;
|
||||
try {
|
||||
key = Key.fromHex(line.substring(11));
|
||||
} catch (final KeyFormatException ignored) {
|
||||
@@ -152,10 +155,26 @@ public final class GoBackend implements Backend {
|
||||
} catch (final NumberFormatException ignored) {
|
||||
tx = 0;
|
||||
}
|
||||
} else if (line.startsWith("last_handshake_time_sec=")) {
|
||||
if (key == null)
|
||||
continue;
|
||||
try {
|
||||
latestHandshakeMSec += Long.parseLong(line.substring(24)) * 1000;
|
||||
} catch (final NumberFormatException ignored) {
|
||||
latestHandshakeMSec = 0;
|
||||
}
|
||||
} else if (line.startsWith("last_handshake_time_nsec=")) {
|
||||
if (key == null)
|
||||
continue;
|
||||
try {
|
||||
latestHandshakeMSec += Long.parseLong(line.substring(25)) / 1000000;
|
||||
} catch (final NumberFormatException ignored) {
|
||||
latestHandshakeMSec = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key != null)
|
||||
stats.add(key, rx, tx);
|
||||
stats.add(key, rx, tx, latestHandshakeMSec);
|
||||
return stats;
|
||||
}
|
||||
|
||||
@@ -324,6 +343,9 @@ public final class GoBackend implements Backend {
|
||||
currentTunnelHandle = -1;
|
||||
currentConfig = null;
|
||||
wgTurnOff(handleToClose);
|
||||
try {
|
||||
vpnService.get(0, TimeUnit.NANOSECONDS).stopSelf();
|
||||
} catch (final TimeoutException ignored) { }
|
||||
}
|
||||
|
||||
tunnel.onStateChange(state);
|
||||
|
||||
@@ -6,36 +6,65 @@
|
||||
package com.wireguard.android.backend;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.wireguard.crypto.Key;
|
||||
import com.wireguard.util.NonNullForAll;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Class representing transfer statistics for a {@link Tunnel} instance.
|
||||
*/
|
||||
@NonNullForAll
|
||||
public class Statistics {
|
||||
private final Map<Key, Pair<Long, Long>> peerBytes = new HashMap<>();
|
||||
|
||||
// TODO: switch to Java Record class once R8 supports desugaring those.
|
||||
public final class PeerStats {
|
||||
public final long rxBytes, txBytes, latestHandshakeEpochMillis;
|
||||
|
||||
PeerStats(final long rxBytes, final long txBytes, final long latestHandshakeEpochMillis) {
|
||||
this.rxBytes = rxBytes;
|
||||
this.txBytes = txBytes;
|
||||
this.latestHandshakeEpochMillis = latestHandshakeEpochMillis;
|
||||
}
|
||||
|
||||
@Override public boolean equals(final Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
final PeerStats stats = (PeerStats) o;
|
||||
return rxBytes == stats.rxBytes && txBytes == stats.txBytes && latestHandshakeEpochMillis == stats.latestHandshakeEpochMillis;
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return Objects.hash(rxBytes, txBytes, latestHandshakeEpochMillis);
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<Key, PeerStats> stats = new HashMap<>();
|
||||
private long lastTouched = SystemClock.elapsedRealtime();
|
||||
|
||||
Statistics() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a peer and its current data usage to the internal map.
|
||||
* Add a peer and its current stats to the internal map.
|
||||
*
|
||||
* @param key A WireGuard public key bound to a particular peer
|
||||
* @param rx The received traffic for the {@link com.wireguard.config.Peer} referenced by
|
||||
* the provided {@link Key}. This value is in bytes
|
||||
* @param tx The transmitted traffic for the {@link com.wireguard.config.Peer} referenced by
|
||||
* the provided {@link Key}. This value is in bytes.
|
||||
* @param key A WireGuard public key bound to a particular peer
|
||||
* @param rxBytes The received traffic for the {@link com.wireguard.config.Peer} referenced by
|
||||
* the provided {@link Key}. This value is in bytes
|
||||
* @param txBytes The transmitted traffic for the {@link com.wireguard.config.Peer} referenced by
|
||||
* the provided {@link Key}. This value is in bytes.
|
||||
* @param latestHandshake The timestamp of the latest handshake for the {@link com.wireguard.config.Peer}
|
||||
* referenced by the provided {@link Key}. The value is in epoch milliseconds.
|
||||
*/
|
||||
void add(final Key key, final long rx, final long tx) {
|
||||
peerBytes.put(key, Pair.create(rx, tx));
|
||||
void add(final Key key, final long rxBytes, final long txBytes, final long latestHandshake) {
|
||||
stats.put(key, new PeerStats(rxBytes, txBytes, latestHandshake));
|
||||
lastTouched = SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
@@ -49,31 +78,14 @@ public class Statistics {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the received traffic (in bytes) for the {@link com.wireguard.config.Peer} referenced by
|
||||
* the provided {@link Key}
|
||||
* Get the statistics for the {@link com.wireguard.config.Peer} referenced by the provided {@link Key}
|
||||
*
|
||||
* @param peer A {@link Key} representing a {@link com.wireguard.config.Peer}.
|
||||
* @return a long representing the number of bytes received by this peer.
|
||||
* @return a {@link PeerStats} representing various statistics about this peer.
|
||||
*/
|
||||
public long peerRx(final Key peer) {
|
||||
final Pair<Long, Long> rxTx = peerBytes.get(peer);
|
||||
if (rxTx == null)
|
||||
return 0;
|
||||
return rxTx.first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transmitted traffic (in bytes) for the {@link com.wireguard.config.Peer} referenced by
|
||||
* the provided {@link Key}
|
||||
*
|
||||
* @param peer A {@link Key} representing a {@link com.wireguard.config.Peer}.
|
||||
* @return a long representing the number of bytes transmitted by this peer.
|
||||
*/
|
||||
public long peerTx(final Key peer) {
|
||||
final Pair<Long, Long> rxTx = peerBytes.get(peer);
|
||||
if (rxTx == null)
|
||||
return 0;
|
||||
return rxTx.second;
|
||||
@Nullable
|
||||
public PeerStats peer(final Key peer) {
|
||||
return this.stats.get(peer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +95,7 @@ public class Statistics {
|
||||
* {@link com.wireguard.config.Peer}s
|
||||
*/
|
||||
public Key[] peers() {
|
||||
return peerBytes.keySet().toArray(new Key[0]);
|
||||
return stats.keySet().toArray(new Key[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,8 +105,8 @@ public class Statistics {
|
||||
*/
|
||||
public long totalRx() {
|
||||
long rx = 0;
|
||||
for (final Pair<Long, Long> val : peerBytes.values()) {
|
||||
rx += val.first;
|
||||
for (final PeerStats val : stats.values()) {
|
||||
rx += val.rxBytes;
|
||||
}
|
||||
return rx;
|
||||
}
|
||||
@@ -106,8 +118,8 @@ public class Statistics {
|
||||
*/
|
||||
public long totalTx() {
|
||||
long tx = 0;
|
||||
for (final Pair<Long, Long> val : peerBytes.values()) {
|
||||
tx += val.second;
|
||||
for (final PeerStats val : stats.values()) {
|
||||
tx += val.txBytes;
|
||||
}
|
||||
return tx;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.wireguard.util.NonNullForAll;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -83,17 +84,17 @@ public final class WgQuickBackend implements Backend {
|
||||
final Statistics stats = new Statistics();
|
||||
final Collection<String> output = new ArrayList<>();
|
||||
try {
|
||||
if (rootShell.run(output, String.format("wg show '%s' transfer", tunnel.getName())) != 0)
|
||||
if (rootShell.run(output, String.format("wg show '%s' dump", tunnel.getName())) != 0)
|
||||
return stats;
|
||||
} catch (final Exception ignored) {
|
||||
return stats;
|
||||
}
|
||||
for (final String line : output) {
|
||||
final String[] parts = line.split("\\t");
|
||||
if (parts.length != 3)
|
||||
if (parts.length != 8)
|
||||
continue;
|
||||
try {
|
||||
stats.add(Key.fromBase64(parts[0]), Long.parseLong(parts[1]), Long.parseLong(parts[2]));
|
||||
stats.add(Key.fromBase64(parts[0]), Long.parseLong(parts[5]), Long.parseLong(parts[6]), Long.parseLong(parts[4]) * 1000);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,16 @@ cmake_minimum_required(VERSION 3.4.1)
|
||||
project("WireGuard")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
|
||||
add_link_options(LINKER:--build-id=none)
|
||||
add_compile_options(-Wall -Werror)
|
||||
|
||||
add_executable(libwg-quick.so wireguard-tools/src/wg-quick/android.c ndk-compat/compat.c)
|
||||
target_compile_options(libwg-quick.so PUBLIC -O3 -std=gnu11 -Wall -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DWG_PACKAGE_NAME=\"${ANDROID_PACKAGE_NAME}\")
|
||||
target_compile_options(libwg-quick.so PUBLIC -std=gnu11 -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DWG_PACKAGE_NAME=\"${ANDROID_PACKAGE_NAME}\")
|
||||
target_link_libraries(libwg-quick.so -ldl)
|
||||
|
||||
file(GLOB WG_SOURCES wireguard-tools/src/*.c ndk-compat/compat.c)
|
||||
add_executable(libwg.so ${WG_SOURCES})
|
||||
target_include_directories(libwg.so PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/wireguard-tools/src/uapi/linux/" "${CMAKE_CURRENT_SOURCE_DIR}/wireguard-tools/src/")
|
||||
target_compile_options(libwg.so PUBLIC -O3 -std=gnu11 -D_GNU_SOURCE -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DHAVE_VISIBILITY_HIDDEN -DRUNSTATEDIR=\"/data/data/${ANDROID_PACKAGE_NAME}/cache\")
|
||||
target_compile_options(libwg.so PUBLIC -std=gnu11 -include ${CMAKE_CURRENT_SOURCE_DIR}/ndk-compat/compat.h -DRUNSTATEDIR=\"/data/data/${ANDROID_PACKAGE_NAME}/cache\")
|
||||
|
||||
add_custom_target(libwg-go.so WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/libwg-go" COMMENT "Building wireguard-go" VERBATIM COMMAND "${ANDROID_HOST_PREBUILTS}/bin/make"
|
||||
ANDROID_ARCH_NAME=${ANDROID_ARCH_NAME}
|
||||
@@ -31,7 +32,7 @@ add_custom_target(libwg-go.so WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib
|
||||
|
||||
# Strip unwanted ELF sections to prevent DT_FLAGS_1 warnings on old Android versions
|
||||
file(GLOB ELF_CLEANER_SOURCES elf-cleaner/*.c elf-cleaner/*.cpp)
|
||||
add_custom_target(elf-cleaner COMMENT "Building elf-cleaner" VERBATIM COMMAND "${CMAKE_C_COMPILER}"
|
||||
add_custom_target(elf-cleaner COMMENT "Building elf-cleaner" VERBATIM COMMAND cc
|
||||
-O2 -DPACKAGE_NAME="elf-cleaner" -DPACKAGE_VERSION="" -DCOPYRIGHT=""
|
||||
-o "${CMAKE_CURRENT_BINARY_DIR}/elf-cleaner" ${ELF_CLEANER_SOURCES}
|
||||
)
|
||||
|
||||
@@ -20,12 +20,12 @@ export GOARCH := $(NDK_GO_ARCH_MAP_$(ANDROID_ARCH_NAME))
|
||||
export GOOS := android
|
||||
export CGO_ENABLED := 1
|
||||
|
||||
GO_VERSION := 1.20.2
|
||||
GO_VERSION := 1.20.3
|
||||
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 := c93b8ced9517d07e1cd4c362c6e2d5242cb139e29b417a328fbf19aded08764c
|
||||
GO_HASH_darwin-arm64 := 7343c87f19e79c0063532e82e1c4d6f42175a32d99f7a4d15e658e88bf97f885
|
||||
GO_HASH_linux-amd64 := 4eaea32f59cde4dc635fbc42161031d13e1c780b87097f4b4234cfce671f1768
|
||||
GO_HASH_darwin-amd64 := c1e1161d6d859deb576e6cfabeb40e3d042ceb1c6f444f617c3c9d76269c3565
|
||||
GO_HASH_darwin-arm64 := 86b0ed0f2b2df50fa8036eea875d1cf2d76cefdacf247c44639a1464b7e36b95
|
||||
GO_HASH_linux-amd64 := 979694c2c25c735755bf26f4f45e19e64e4811d661dd07b8c010f7a8e18adfca
|
||||
|
||||
default: $(DESTDIR)/libwg-go.so
|
||||
|
||||
@@ -47,6 +47,6 @@ $(BUILDDIR)/go-$(GO_VERSION)/.prepared: $(GRADLE_USER_HOME)/caches/golang/$(GO_T
|
||||
|
||||
$(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 -buildid=" -v -trimpath -o "$@" -buildmode c-shared
|
||||
go build -tags linux -ldflags="-X golang.zx2c4.com/wireguard/ipc.socketDirectory=/data/data/$(ANDROID_PACKAGE_NAME)/cache/wireguard -buildid=" -v -trimpath -buildvcs=false -o "$@" -buildmode c-shared
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
@@ -6,58 +6,6 @@
|
||||
|
||||
#define FILE_IS_EMPTY
|
||||
|
||||
#if defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 18
|
||||
#undef FILE_IS_EMPTY
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
|
||||
{
|
||||
char *ptr, *eptr;
|
||||
|
||||
if (*buf == NULL || *bufsiz == 0) {
|
||||
*bufsiz = BUFSIZ;
|
||||
if ((*buf = malloc(*bufsiz)) == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ptr = *buf, eptr = *buf + *bufsiz;;) {
|
||||
int c = fgetc(fp);
|
||||
if (c == -1) {
|
||||
if (feof(fp)) {
|
||||
ssize_t diff = (ssize_t)(ptr - *buf);
|
||||
if (diff != 0) {
|
||||
*ptr = '\0';
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
*ptr++ = c;
|
||||
if (c == delimiter) {
|
||||
*ptr = '\0';
|
||||
return ptr - *buf;
|
||||
}
|
||||
if (ptr + 2 >= eptr) {
|
||||
char *nbuf;
|
||||
size_t nbufsiz = *bufsiz * 2;
|
||||
ssize_t d = ptr - *buf;
|
||||
if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
|
||||
return -1;
|
||||
*buf = nbuf;
|
||||
*bufsiz = nbufsiz;
|
||||
eptr = nbuf + nbufsiz;
|
||||
ptr = nbuf + d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t getline(char **buf, size_t *bufsiz, FILE *fp)
|
||||
{
|
||||
return getdelim(buf, bufsiz, '\n', fp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 24
|
||||
#undef FILE_IS_EMPTY
|
||||
#include <string.h>
|
||||
|
||||
@@ -4,12 +4,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 18
|
||||
#include <stdio.h>
|
||||
ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp);
|
||||
ssize_t getline(char **buf, size_t *bufsiz, FILE *fp);
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID_MIN_SDK_VERSION__) && __ANDROID_MIN_SDK_VERSION__ < 24
|
||||
char *strchrnul(const char *s, int c);
|
||||
#endif
|
||||
|
||||
+2
-19
@@ -9,10 +9,6 @@ plugins {
|
||||
version wireguardVersionName
|
||||
group groupName
|
||||
|
||||
// Create a variable called keystorePropertiesFile, and initialize it to your
|
||||
// keystore.properties file, in the rootProject folder.
|
||||
final def keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||
|
||||
android {
|
||||
compileSdk 33
|
||||
buildFeatures {
|
||||
@@ -34,24 +30,11 @@ android {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
coreLibraryDesugaringEnabled = true
|
||||
}
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
final def keystoreProperties = new Properties()
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias keystoreProperties['keyAlias']
|
||||
keyPassword keystoreProperties['keyPassword']
|
||||
storeFile rootProject.file(keystoreProperties['storeFile'])
|
||||
storePassword keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
if (keystorePropertiesFile.exists()) signingConfig signingConfigs.release
|
||||
minifyEnabled true
|
||||
proguardFiles "proguard-android-optimize.txt", "proguard-rules.pro"
|
||||
shrinkResources true
|
||||
proguardFiles "proguard-android-optimize.txt"
|
||||
packagingOptions {
|
||||
exclude "DebugProbesKt.bin"
|
||||
exclude "kotlin-tooling-metadata.json"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
-allowaccessmodification
|
||||
-dontpreverify
|
||||
-dontusemixedcaseclassnames
|
||||
-dontskipnonpubliclibraryclasses
|
||||
-dontobfuscate
|
||||
-verbose
|
||||
|
||||
-keepattributes *Annotation*
|
||||
|
||||
Vendored
-14
@@ -1,14 +0,0 @@
|
||||
# Squelch all warnings, they're harmless but ProGuard
|
||||
# escalates them as errors.
|
||||
-dontwarn sun.misc.Unsafe
|
||||
|
||||
# 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>();
|
||||
}
|
||||
|
||||
# Don't obfuscate
|
||||
-dontobfuscate
|
||||
@@ -58,10 +58,6 @@ class Application : android.app.Application() {
|
||||
startActivity(intent)
|
||||
System.exit(0)
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
StrictMode.setVmPolicy(VmPolicy.Builder().detectAll().penaltyLog().build())
|
||||
StrictMode.setThreadPolicy(ThreadPolicy.Builder().detectAll().penaltyLog().build())
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun determineBackend(): Backend {
|
||||
@@ -119,6 +115,11 @@ class Application : android.app.Application() {
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
}
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
StrictMode.setVmPolicy(VmPolicy.Builder().detectAll().penaltyLog().build())
|
||||
StrictMode.setThreadPolicy(ThreadPolicy.Builder().detectAll().penaltyLog().build())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTerminate() {
|
||||
|
||||
@@ -157,9 +157,7 @@ class LogViewerActivity : AppCompatActivity() {
|
||||
builder.append(rawLogLines[i])
|
||||
builder.append('\n')
|
||||
}
|
||||
val ret = builder.toString().toByteArray(Charsets.UTF_8)
|
||||
builder.clear()
|
||||
return ret
|
||||
return builder.toString().toByteArray(Charsets.UTF_8)
|
||||
}
|
||||
|
||||
private suspend fun saveLog() {
|
||||
@@ -203,7 +201,7 @@ class LogViewerActivity : AppCompatActivity() {
|
||||
var priorModified = false
|
||||
val bufferedLogLines = arrayListOf<LogLine>()
|
||||
var timeout = 1000000000L / 2 // The timeout is initially small so that the view gets populated immediately.
|
||||
val MAX_LINES = (1 shl 17) - 1
|
||||
val MAX_LINES = (1 shl 16) - 1
|
||||
val MAX_BUFFERED_LINES = (1 shl 14) - 1
|
||||
|
||||
while (true) {
|
||||
|
||||
@@ -63,8 +63,8 @@ class AppListDialogFragment : DialogFragment() {
|
||||
withContext(Dispatchers.Main.immediate) {
|
||||
appData.clear()
|
||||
appData.addAll(applicationData)
|
||||
setButtonText()
|
||||
}
|
||||
setButtonText()
|
||||
} catch (e: Throwable) {
|
||||
withContext(Dispatchers.Main.immediate) {
|
||||
val error = ErrorMessages[e]
|
||||
|
||||
@@ -112,16 +112,25 @@ class TunnelDetailFragment : BaseFragment(), MenuProvider {
|
||||
val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i))
|
||||
?: continue
|
||||
val publicKey = peer.item!!.publicKey
|
||||
val rx = statistics.peerRx(publicKey)
|
||||
val tx = statistics.peerTx(publicKey)
|
||||
if (rx == 0L && tx == 0L) {
|
||||
val peerStats = statistics.peer(publicKey)
|
||||
if (peerStats == null || (peerStats.rxBytes == 0L && peerStats.txBytes == 0L)) {
|
||||
peer.transferLabel.visibility = View.GONE
|
||||
peer.transferText.visibility = View.GONE
|
||||
continue
|
||||
} else {
|
||||
peer.transferText.text = getString(R.string.transfer_rx_tx,
|
||||
QuantityFormatter.formatBytes(peerStats.rxBytes),
|
||||
QuantityFormatter.formatBytes(peerStats.txBytes))
|
||||
peer.transferLabel.visibility = View.VISIBLE
|
||||
peer.transferText.visibility = View.VISIBLE
|
||||
}
|
||||
if (peerStats == null || peerStats.latestHandshakeEpochMillis == 0L) {
|
||||
peer.latestHandshakeLabel.visibility = View.GONE
|
||||
peer.latestHandshakeText.visibility = View.GONE
|
||||
} else {
|
||||
peer.latestHandshakeText.text = QuantityFormatter.formatEpochAgo(peerStats.latestHandshakeEpochMillis)
|
||||
peer.latestHandshakeLabel.visibility = View.VISIBLE
|
||||
peer.latestHandshakeText.visibility = View.VISIBLE
|
||||
}
|
||||
peer.transferText.text = getString(R.string.transfer_rx_tx, QuantityFormatter.formatBytes(rx), QuantityFormatter.formatBytes(tx))
|
||||
peer.transferLabel.visibility = View.VISIBLE
|
||||
peer.transferText.visibility = View.VISIBLE
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
for (i in 0 until binding.peersLayout.childCount) {
|
||||
@@ -129,6 +138,8 @@ class TunnelDetailFragment : BaseFragment(), MenuProvider {
|
||||
?: continue
|
||||
peer.transferLabel.visibility = View.GONE
|
||||
peer.transferText.visibility = View.GONE
|
||||
peer.latestHandshakeLabel.visibility = View.GONE
|
||||
peer.latestHandshakeText.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,8 +92,8 @@ object ErrorMessages {
|
||||
rootCause is ChecksumException -> {
|
||||
resources.getString(R.string.error_qr_checksum)
|
||||
}
|
||||
rootCause.message != null -> {
|
||||
rootCause.message!!
|
||||
rootCause.localizedMessage != null -> {
|
||||
rootCause.localizedMessage!!
|
||||
}
|
||||
else -> {
|
||||
val errorType = rootCause.javaClass.simpleName
|
||||
@@ -109,7 +109,7 @@ object ErrorMessages {
|
||||
if (kfe!!.type == KeyFormatException.Type.LENGTH) return resources.getString(KFE_FORMAT_MAP.getValue(kfe.format))
|
||||
} else if (bce.cause is ParseException) {
|
||||
val pe = bce.cause as ParseException?
|
||||
if (pe!!.message != null) return ": ${pe.message}"
|
||||
if (pe!!.localizedMessage != null) return ": ${pe.localizedMessage}"
|
||||
} else if (bce.location == BadConfigException.Location.LISTEN_PORT) {
|
||||
return resources.getString(R.string.bad_config_explanation_udp_port)
|
||||
} else if (bce.location == BadConfigException.Location.MTU) {
|
||||
|
||||
@@ -5,8 +5,17 @@
|
||||
|
||||
package com.wireguard.android.util
|
||||
|
||||
import android.icu.text.ListFormatter
|
||||
import android.icu.text.MeasureFormat
|
||||
import android.icu.text.RelativeDateTimeFormatter
|
||||
import android.icu.util.Measure
|
||||
import android.icu.util.MeasureUnit
|
||||
import android.os.Build
|
||||
import com.wireguard.android.Application
|
||||
import com.wireguard.android.R
|
||||
import java.util.Locale
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.toDuration
|
||||
|
||||
object QuantityFormatter {
|
||||
fun formatBytes(bytes: Long): String {
|
||||
@@ -19,4 +28,40 @@ object QuantityFormatter {
|
||||
else -> context.getString(R.string.transfer_tibibytes, bytes / (1024.0 * 1024.0 * 1024.0) / 1024.0)
|
||||
}
|
||||
}
|
||||
|
||||
fun formatEpochAgo(epochMillis: Long): String {
|
||||
var span = (System.currentTimeMillis() - epochMillis) / 1000
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
|
||||
return Application.get().applicationContext.getString(R.string.latest_handshake_ago, span.toDuration(DurationUnit.SECONDS).toString())
|
||||
|
||||
if (span <= 0L)
|
||||
return RelativeDateTimeFormatter.getInstance().format(RelativeDateTimeFormatter.Direction.PLAIN, RelativeDateTimeFormatter.AbsoluteUnit.NOW)
|
||||
val measureFormat = MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
|
||||
val parts = ArrayList<CharSequence>(4)
|
||||
if (span >= 24 * 60 * 60L) {
|
||||
val v = span / (24 * 60 * 60L)
|
||||
parts.add(measureFormat.format(Measure(v, MeasureUnit.DAY)))
|
||||
span -= v * (24 * 60 * 60L)
|
||||
}
|
||||
if (span >= 60 * 60L) {
|
||||
val v = span / (60 * 60L)
|
||||
parts.add(measureFormat.format(Measure(v, MeasureUnit.HOUR)))
|
||||
span -= v * (60 * 60L)
|
||||
}
|
||||
if (span >= 60L) {
|
||||
val v = span / 60L
|
||||
parts.add(measureFormat.format(Measure(v, MeasureUnit.MINUTE)))
|
||||
span -= v * 60L
|
||||
}
|
||||
if (span > 0L)
|
||||
parts.add(measureFormat.format(Measure(span, MeasureUnit.SECOND)))
|
||||
|
||||
val joined = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
|
||||
parts.joinToString()
|
||||
else
|
||||
ListFormatter.getInstance(Locale.getDefault(), ListFormatter.Type.UNITS, ListFormatter.Width.SHORT).format(parts)
|
||||
|
||||
return Application.get().applicationContext.getString(R.string.latest_handshake_ago, joined)
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interface_name_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/interface_name_text"
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/interface_name_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/name"
|
||||
android:nextFocusUp="@id/tunnel_switch"
|
||||
@@ -96,7 +96,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/public_key_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/public_key_text"
|
||||
@@ -106,7 +106,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/public_key_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/public_key"
|
||||
android:ellipsize="end"
|
||||
@@ -124,7 +124,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/addresses_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/addresses_text"
|
||||
@@ -135,7 +135,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/addresses_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/addresses"
|
||||
android:nextFocusUp="@id/public_key_text"
|
||||
@@ -151,7 +151,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dns_servers_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/dns_servers_text"
|
||||
@@ -162,7 +162,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dns_servers_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/dns_servers"
|
||||
android:nextFocusUp="@id/addresses_text"
|
||||
@@ -178,7 +178,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dns_search_domains_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/dns_search_domain_text"
|
||||
@@ -189,7 +189,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dns_search_domains_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/dns_search_domains"
|
||||
android:nextFocusUp="@id/dns_servers_text"
|
||||
@@ -277,7 +277,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/applications_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/applications_text"
|
||||
@@ -288,7 +288,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/applications_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/applications"
|
||||
android:nextFocusUp="@id/mtu_text"
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/peer_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/peer"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/public_key_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/public_key_text"
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/public_key_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/public_key"
|
||||
android:ellipsize="end"
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pre_shared_key_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/pre_shared_key_text"
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pre_shared_key_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/pre_shared_key"
|
||||
android:ellipsize="end"
|
||||
@@ -88,7 +88,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/allowed_ips_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/allowed_ips_text"
|
||||
@@ -99,7 +99,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/allowed_ips_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/allowed_ips"
|
||||
android:nextFocusUp="@id/pre_shared_key_text"
|
||||
@@ -115,7 +115,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/endpoint_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/endpoint_text"
|
||||
@@ -126,7 +126,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/endpoint_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/endpoint"
|
||||
android:nextFocusUp="@id/allowed_ips_text"
|
||||
@@ -142,7 +142,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/persistent_keepalive_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/persistent_keepalive_text"
|
||||
@@ -153,7 +153,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/persistent_keepalive_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/persistent_keepalive"
|
||||
android:nextFocusUp="@id/endpoint_text"
|
||||
@@ -169,9 +169,9 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/transfer_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/endpoint_text"
|
||||
android:layout_below="@+id/persistent_keepalive_text"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/transfer_text"
|
||||
android:text="@string/transfer"
|
||||
@@ -182,7 +182,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/transfer_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/transfer_label"
|
||||
android:contentDescription="@string/transfer"
|
||||
@@ -194,6 +194,34 @@
|
||||
app:layout_constraintTop_toBottomOf="@+id/transfer_label"
|
||||
tools:text="1024 MB"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/latest_handshake_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/transfer_text"
|
||||
android:layout_marginTop="8dp"
|
||||
android:labelFor="@+id/latest_handshake_text"
|
||||
android:text="@string/latest_handshake"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/transfer_text"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/latest_handshake_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/latest_handshake_label"
|
||||
android:contentDescription="@string/latest_handshake"
|
||||
android:nextFocusUp="@id/transfer_text"
|
||||
android:onClick="@{ClipboardUtils::copyTextView}"
|
||||
android:textAppearance="?attr/textAppearanceBodyLarge"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/latest_handshake_label"
|
||||
tools:text="4 minutes, 27 seconds ago"
|
||||
tools:visibility="visible" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</layout>
|
||||
|
||||
@@ -111,6 +111,8 @@
|
||||
<string name="tv_select_a_storage_drive">تحديد محرك تخزين</string>
|
||||
<string name="tv_no_file_picker">الرجاء تثبيت مدير ملفات لتتمكن من تصفح الملفات</string>
|
||||
<string name="tv_add_tunnel_get_started">أضف نفقاً لتبدأ</string>
|
||||
<string name="donate_title">♥️ تبرع لمشروع وايرجارد</string>
|
||||
<string name="donate_summary">كل مساهمة تساعد</string>
|
||||
<string name="disable_config_export_title">تعطيل تصدير التكوين</string>
|
||||
<string name="disable_config_export_description">تعطيل تصدير الإعدادات يجعل المفاتيح الخاصة غير متاحة</string>
|
||||
<string name="dns_servers">خوادم DNS</string>
|
||||
@@ -120,6 +122,7 @@
|
||||
<string name="error_down">خطأ في فصل النفق:%s</string>
|
||||
<string name="error_fetching_apps">خطأ في جلب قائمة التطبيقات: %s</string>
|
||||
<string name="error_root">الرجاء الحصول على صلاحيات الروت وحاول مرة أخرى</string>
|
||||
<string name="error_prepare">خطأ في تحضير النفق: %s</string>
|
||||
<string name="error_up">خطأ خلال إنشاء النفق: %s</string>
|
||||
<string name="exclude_private_ips">استبعاد عناوين بروتوكول الإنترنت (IP) الخاصة</string>
|
||||
<string name="generate_new_private_key">توليد مفتاح خاص جديد</string>
|
||||
@@ -215,6 +218,7 @@
|
||||
<string name="tunnel_create_success">تم إنشاء نفق \"%s\" بنجاح</string>
|
||||
<string name="tunnel_error_already_exists">النفق \"%s\" موجود بالفعل</string>
|
||||
<string name="tunnel_error_invalid_name">إسم غير صالح</string>
|
||||
<string name="tunnel_list_placeholder">أضف نفق باستخدام الزر أدناه</string>
|
||||
<string name="tunnel_name">إسم النفق</string>
|
||||
<string name="tunnel_on_error">غير قادر على تشغيل النفق (wgTurnOn أعاد %d)</string>
|
||||
<string name="tunnel_dns_failure">غير قادر على حل اسم مضيف نظام أسماء النطاقات (DNS hostname): \"%s\"</string>
|
||||
|
||||
@@ -72,6 +72,8 @@
|
||||
<string name="config_file_exists_error">Konfigurationsfilen \"%s\" findes allerede</string>
|
||||
<string name="config_not_found_error">Konfigurationsfilen \"%s\" blev ikke fundet</string>
|
||||
<string name="config_rename_error">Kan ikke omdøbe konfigurationsfilen \"%s\"</string>
|
||||
<string name="config_save_error">Kan ikke gemme konfigurationen for \"%1$s\": %2$s</string>
|
||||
<string name="config_save_success">Konfiguration for \"%s\" blev gemt</string>
|
||||
<string name="create_activity_title">Opret WireGuard tunnel</string>
|
||||
<string name="create_empty">Opret fra ny</string>
|
||||
<string name="create_from_file">Importér fra fil eller arkiv</string>
|
||||
@@ -88,6 +90,7 @@
|
||||
<string name="dns_search_domains">DNS-søgedomæner</string>
|
||||
<string name="edit">Redigér</string>
|
||||
<string name="endpoint">Slutpunkt</string>
|
||||
<string name="error_prepare">Fejl ved forberedelse af tunnel: %s</string>
|
||||
<string name="error_up">Fejl under aktivering af tunnel: %s</string>
|
||||
<string name="exclude_private_ips">Eksludér private IP-adresser</string>
|
||||
<string name="generate_new_private_key">Generér ny privat nøgle</string>
|
||||
@@ -152,6 +155,7 @@
|
||||
<string name="tunnel_create_success">Tunnelen blev succesfuldt oprettet \"%s\"</string>
|
||||
<string name="tunnel_error_already_exists">Tunnel \"%s\" eksisterer allerede</string>
|
||||
<string name="tunnel_error_invalid_name">Ugyldigt navn</string>
|
||||
<string name="tunnel_list_placeholder">Tilføj en tunnel ved hjælp af knappen nedenfor</string>
|
||||
<string name="tunnel_name">Tunnel Navn</string>
|
||||
<string name="tunnel_dns_failure">Kunne ikke opslå DNS adresse: \"%s\"</string>
|
||||
<string name="tunnel_rename_error">Kan ikke omdøbe tunnel: %s</string>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<item quantity="other">Importerte %d tunneler</item>
|
||||
</plurals>
|
||||
<plurals name="set_excluded_applications">
|
||||
<item quantity="one">%d ekskludert program</item>
|
||||
<item quantity="one">%d ekskludert app</item>
|
||||
<item quantity="other">%d ekskluderte apper</item>
|
||||
</plurals>
|
||||
<plurals name="set_included_applications">
|
||||
@@ -79,6 +79,8 @@
|
||||
<string name="bad_config_reason_unknown_section">Ukjent seksjon</string>
|
||||
<string name="bad_config_reason_value_out_of_range">Verdien er utenfor gyldig område</string>
|
||||
<string name="bad_extension_error">Filen må være .conf eller .zip</string>
|
||||
<string name="error_no_qr_found">Ingen QR-kode funnet i bildet</string>
|
||||
<string name="error_qr_checksum">Feil ved sjekksumverifisering av QR-kode</string>
|
||||
<string name="cancel">Avbryt</string>
|
||||
<string name="config_delete_error">Kan ikke slette konfigurasjonsfilen %s</string>
|
||||
<string name="config_exists_error">Konfigurasjon for «%s» finnes allerede</string>
|
||||
@@ -105,14 +107,19 @@
|
||||
<string name="tv_select_a_storage_drive">Velg en lagringsenhet</string>
|
||||
<string name="tv_no_file_picker">Vennligst installer et filhåndteringsverktøy for å bla i filer</string>
|
||||
<string name="tv_add_tunnel_get_started">Opprett en ny tunnel for å komme i gang</string>
|
||||
<string name="donate_title">♥ Donér til WireGuard-prosjektet</string>
|
||||
<string name="donate_summary">Hvert bidrag hjelper</string>
|
||||
<string name="donate_google_play_disappointment">Takk for at du støtter WireGuard-prosjektet!\n\nPå grunn av Googles retningslinjer, kan vi dessverre ikke linke til den delen av prosjektets nettside der du kan donere. Forhåpentligvis klarer du å finne denne selv!\n\nVi takker igjen for ditt bidrag.</string>
|
||||
<string name="disable_config_export_title">Deaktiver eksport av konfigurasjon</string>
|
||||
<string name="disable_config_export_description">Deaktivering av konfigurasjonseksport gjør private nøkler mindre tilgjengelig</string>
|
||||
<string name="dns_servers">DNS tjenere</string>
|
||||
<string name="dns_search_domains">Søk gjennom domener</string>
|
||||
<string name="edit">Rediger</string>
|
||||
<string name="endpoint">Endepunkt</string>
|
||||
<string name="error_down">Feil når tunnel skulle tas ned: %s</string>
|
||||
<string name="error_fetching_apps">Feil ved henting av applikasjonsliste: %s</string>
|
||||
<string name="error_root">Vennligst få root-tilgang og prøv igjen</string>
|
||||
<string name="error_prepare">Feil ved klargjøring av tunnel: %s</string>
|
||||
<string name="error_up">Feil når tunnel skulle tas opp: %s</string>
|
||||
<string name="exclude_private_ips">Utelukk private IP-adresser</string>
|
||||
<string name="generate_new_private_key">Lag ny privat nøkkel</string>
|
||||
@@ -210,13 +217,16 @@
|
||||
<string name="tunnel_create_success">Opprettet tunnelen «%s»</string>
|
||||
<string name="tunnel_error_already_exists">Tunnel «%s» finnes allerede</string>
|
||||
<string name="tunnel_error_invalid_name">Ugyldig navn</string>
|
||||
<string name="tunnel_list_placeholder">Legg til en tunnel ved å bruke knappen under</string>
|
||||
<string name="tunnel_name">Tunnelnavn</string>
|
||||
<string name="tunnel_on_error">Kan ikke slå på tunnel (wgTurnOn returnerte %d)</string>
|
||||
<string name="tunnel_dns_failure">Kan ikke slå opp DNS-vertsnavn: “%s\"</string>
|
||||
<string name="tunnel_rename_error">Kan ikke endre navn på tunnel: %s</string>
|
||||
<string name="tunnel_rename_success">Endret navn på tunnelen til «%s»</string>
|
||||
<string name="type_name_go_userspace">Bruk userspace</string>
|
||||
<string name="type_name_kernel_module">Kjernemodul</string>
|
||||
<string name="unknown_error">Ukjent feil</string>
|
||||
<string name="version_summary">%1$s backend %2$s</string>
|
||||
<string name="version_summary_checking">Sjekker %s backend versjon</string>
|
||||
<string name="version_summary_unknown">Ukjent %s versjon</string>
|
||||
<string name="version_title">WireGuard for Android v%s</string>
|
||||
|
||||
@@ -133,6 +133,9 @@
|
||||
<string name="tv_select_a_storage_drive">Выберите накопитель</string>
|
||||
<string name="tv_no_file_picker">Пожалуйста, установите утилиту управления файлами для их просмотра</string>
|
||||
<string name="tv_add_tunnel_get_started">Добавьте туннель, чтобы начать</string>
|
||||
<string name="donate_title">♥ Пожертвовать проекту WireGuard</string>
|
||||
<string name="donate_summary">Каждое пожертвование помогает</string>
|
||||
<string name="donate_google_play_disappointment">Спасибо за поддержку проекта WireGuard!\n\nК сожалению, из-за политики Google, нельзя размещать ссылку на тот раздел сайта проекта, где можно сделать пожертвование. Надеемся, вы сможете разобраться самостоятельно!\n\nЕщё раз спасибо за ваш вклад.</string>
|
||||
<string name="disable_config_export_title">Отключить экспорт конфигурации</string>
|
||||
<string name="disable_config_export_description">Отключение экспорта конфигурации делает приватные ключи менее доступными</string>
|
||||
<string name="dns_servers">DNS-серверы</string>
|
||||
@@ -142,6 +145,7 @@
|
||||
<string name="error_down">Ошибка при выходе из туннеля: %s</string>
|
||||
<string name="error_fetching_apps">Ошибка при получении списка приложений: %s</string>
|
||||
<string name="error_root">Пожалуйста, получите root-доступ и попробуйте снова</string>
|
||||
<string name="error_prepare">Ошибка при подготовке туннеля: %s</string>
|
||||
<string name="error_up">Ошибка при запуске туннеля: %s</string>
|
||||
<string name="exclude_private_ips">Исключить частные IP-адреса</string>
|
||||
<string name="generate_new_private_key">Сгенерировать новый приватный ключ</string>
|
||||
@@ -161,6 +165,8 @@
|
||||
<string name="key_length_explanation_base64">: ключи WireGuard base64 должны содержать 44 символа (32 байта)</string>
|
||||
<string name="key_length_explanation_binary">: ключи WireGuard должны быть 32 байта</string>
|
||||
<string name="key_length_explanation_hex">: HEX-ключи WireGuard должны содержать 64 символа (32 байта)</string>
|
||||
<string name="latest_handshake">Последнее рукопожатие</string>
|
||||
<string name="latest_handshake_ago">%s назад</string>
|
||||
<string name="listen_port">Порт</string>
|
||||
<string name="log_export_error">Не удалось экспортировать журнал: %s</string>
|
||||
<string name="log_export_subject">Файл журнала WireGuard Android</string>
|
||||
@@ -239,6 +245,7 @@
|
||||
<string name="tunnel_create_success">Успешно создан туннель “%s”</string>
|
||||
<string name="tunnel_error_already_exists">Туннель “%s” уже существует</string>
|
||||
<string name="tunnel_error_invalid_name">Неправильное имя</string>
|
||||
<string name="tunnel_list_placeholder">Добавьте туннель с помощью кнопки ниже</string>
|
||||
<string name="tunnel_name">Название туннеля</string>
|
||||
<string name="tunnel_on_error">Не удалось включить туннель (wgTurnOn вернул %d)</string>
|
||||
<string name="tunnel_dns_failure">Не удалось определить DNS имя: “%s”</string>
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
<string name="error_down">断开连接时出错:%s</string>
|
||||
<string name="error_fetching_apps">获取应用列表时出错:%s</string>
|
||||
<string name="error_root">请获取 root 权限并重试</string>
|
||||
<string name="error_prepare">准备连接时出错:%s</string>
|
||||
<string name="error_up">建立连接时出错:%s</string>
|
||||
<string name="exclude_private_ips">排除局域网</string>
|
||||
<string name="generate_new_private_key">生成新的私钥</string>
|
||||
@@ -125,6 +126,8 @@
|
||||
<string name="key_length_explanation_base64">:WireGuard 的 Base64 密钥长度必须为 44 个字符(32 字节)</string>
|
||||
<string name="key_length_explanation_binary">:WireGuard 密钥大小必须为 32 字节</string>
|
||||
<string name="key_length_explanation_hex">:WireGuard 的十六进制密钥长度必须为 64 个字符(32 字节)</string>
|
||||
<string name="latest_handshake">上次握手时间</string>
|
||||
<string name="latest_handshake_ago">%s之前</string>
|
||||
<string name="listen_port">监听端口</string>
|
||||
<string name="log_export_error">无法导出日志:%s</string>
|
||||
<string name="log_export_subject">WireGuard 日志文件</string>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
<dimen name="extra_margin">12dp</dimen>
|
||||
<dimen name="bottom_sheet_item_height">56dp</dimen>
|
||||
<dimen name="normal_margin">8dp</dimen>
|
||||
<dimen name="bottom_sheet_top_padding">8dp</dimen>
|
||||
|
||||
@@ -140,6 +140,8 @@
|
||||
<string name="key_length_explanation_base64">: WireGuard base64 keys must be 44 characters (32 bytes)</string>
|
||||
<string name="key_length_explanation_binary">: WireGuard keys must be 32 bytes</string>
|
||||
<string name="key_length_explanation_hex">: WireGuard hex keys must be 64 characters (32 bytes)</string>
|
||||
<string name="latest_handshake">Latest handshake</string>
|
||||
<string name="latest_handshake_ago">%s ago</string>
|
||||
<string name="listen_port">Listen port</string>
|
||||
<string name="log_export_error">Unable to export log: %s</string>
|
||||
<string name="log_export_subject">WireGuard Android Log File</string>
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
buildscript {
|
||||
ext {
|
||||
wireguardVersionCode = 498
|
||||
wireguardVersionName = '1.0.20230405'
|
||||
wireguardVersionCode = 500
|
||||
wireguardVersionName = '1.0.20230427'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user