Compare commits

..

32 Commits

Author SHA1 Message Date
Jason A. Donenfeld 9d76b354f0 version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-08 00:02:37 -06:00
Harsh Shandilya 1856a50c56 strings: sync translations
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-04-08 03:37:01 +05:30
Harsh Shandilya ba0f3ece04 ui: tweak FABs to use the correct type
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-04-08 03:34:07 +05:30
Harsh Shandilya e8ad75d924 ui: codestyle nit
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-04-07 14:19:54 +05:30
Jason A. Donenfeld b858284b1e KernelModuleDisablerPreference: do not make synchronous calls to getBackend
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-07 02:28:34 -06:00
Jason A. Donenfeld 3c6e06f8d5 ui: set proper content description for applications
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-06 19:25:05 -06:00
Jason A. Donenfeld 0d33a00e72 version: bump
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-06 19:09:25 -06:00
Jason A. Donenfeld 830d0992a7 ui: show excluded/included apps in details view
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-06 19:08:57 -06:00
Jason A. Donenfeld e4192ea172 ui: align listen port and mtu in detail editor
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-06 18:43:41 -06:00
Jason A. Donenfeld 4bbb1a0fcd ui: add suffix to persistent keepalive in editor
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-06 18:24:08 -06:00
Jason A. Donenfeld 77b5937fbb ui: add missing fields to detail view
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-06 18:09:03 -06:00
Jason A. Donenfeld 3144d36056 TunnelListFragment: onCreateActionMode is called before adapter is alive
Long press a tunnel item. SIGKILL the app. Reenter it. Boom:

kotlin.KotlinNullPointerException:
  at com.wireguard.android.fragment.TunnelListFragment$ActionModeListener.onCreateActionMode (TunnelListFragment.java:347)
  at androidx.appcompat.app.AppCompatDelegateImpl$ActionModeCallbackWrapperV9.onCreateActionMode (AppCompatDelegateImpl.java:2442)
  at androidx.appcompat.app.WindowDecorActionBar$ActionModeImpl.dispatchOnCreate (WindowDecorActionBar.java:1062)
  at androidx.appcompat.app.WindowDecorActionBar.startActionMode (WindowDecorActionBar.java:530)
  at androidx.appcompat.app.AppCompatDelegateImpl.startSupportActionMode (AppCompatDelegateImpl.java:1055)
  at androidx.appcompat.app.AppCompatActivity.startSupportActionMode (AppCompatActivity.java:316)
  at com.wireguard.android.fragment.TunnelListFragment$ActionModeListener.setItemChecked (TunnelListFragment.java:371)
  at com.wireguard.android.fragment.TunnelListFragment.onActivityCreated (TunnelListFragment.java:174)
  at androidx.fragment.app.Fragment.performActivityCreated (Fragment.java:2717)
  at androidx.fragment.app.FragmentStateManager.activityCreated (FragmentStateManager.java:346)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:1188)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:161)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:1356)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:5)
  at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState (FragmentManager.java:1434)
  at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState (FragmentManager.java:5)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:1497)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:389)
  at androidx.fragment.app.FragmentManager.dispatchStateChange (FragmentManager.java:2625)
  at androidx.fragment.app.FragmentManager.dispatchStateChange (FragmentManager.java:677)
  at androidx.fragment.app.FragmentManager.dispatchActivityCreated (FragmentManager.java:2577)
  at androidx.fragment.app.FragmentManager.dispatchActivityCreated (FragmentManager.java:9)
  at androidx.fragment.app.FragmentController.dispatchActivityCreated (FragmentController.java:247)
  at androidx.fragment.app.FragmentActivity.onStart (FragmentActivity.java:541)
  at androidx.appcompat.app.AppCompatActivity.onStart (AppCompatActivity.java:201)
  at android.app.Instrumentation.callActivityOnStart (Instrumentation.java:1440)
  at android.app.Activity.performStart (Activity.java:8109)
  at android.app.ActivityThread.handleStartActivity (ActivityThread.java:3806)
  at android.app.servertransaction.TransactionExecutor.performLifecycleSequence (TransactionExecutor.java:235)
  at android.app.servertransaction.TransactionExecutor.cycleToPath (TransactionExecutor.java:215)
  at android.app.servertransaction.TransactionExecutor.executeLifecycleState (TransactionExecutor.java:187)
  at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:105)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2386)
  at android.os.Handler.dispatchMessage (Handler.java:107)
  at android.os.Looper.loop (Looper.java:213)
  at android.app.ActivityThread.main (ActivityThread.java:8178)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:513)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1101)

This commit avoids the crash. But it's not clear to me that this is
really the right solution. However, in testing it appears to work.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-06 15:56:07 -06:00
Jason A. Donenfeld 521bbb4b4c AppListDialogFragment: refine singular grammar
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-06 19:56:53 +05:30
Jason A. Donenfeld 0a55e10b94 AppListDialogFragment: remove colons from tabs
Harsh's club became ill upon seeing them.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-06 19:56:53 +05:30
Harsh Shandilya d6e5fd9301 ui: tweak cards to make them nicer on the eyes
Surprisingly-requested-by: Jason A. Donenfeld <Jason@zx2c4.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-04-06 19:56:53 +05:30
Harsh Shandilya 2c625f56fd ui: misc cleanups to AppListDialogFragment
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-04-06 19:56:46 +05:30
Jason A. Donenfeld 7db0fa915e AppListDialogFragment: support both inclusion and exclusion
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-06 02:40:49 -06:00
Jason A. Donenfeld e424765a61 tunnel: support IncludedApplications as whitelist
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2020-04-05 20:04:51 -06:00
Harsh Shandilya 1ca4dbf1a2 tunnel: hold peers in an ArrayList
A set will cause identical peers to be dropped during save

Reported-by: Jason A. Donenfeld <Jason@zx2c4.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-04-06 01:22:44 +05:30
Harsh Shandilya 065893e31d MultiselectableRelativeLayout: use JvmOverloads constructor
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-04-06 00:39:22 +05:30
Harsh Shandilya ef70aa88e1 ui: fix scrolling in detail view
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-04-06 00:17:14 +05:30
Harsh Shandilya 6a11a199ca strings: sync translations
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-04-04 16:19:21 +05:30
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
44 changed files with 1011 additions and 321 deletions
+2
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).
+4 -3
View File
@@ -1,3 +1,4 @@
allprojects {
repositories {
google()
@@ -7,7 +8,7 @@ allprojects {
buildscript {
ext {
agpVersion = '3.6.1'
agpVersion = '3.6.2'
annotationsVersion = '1.1.0'
appcompatVersion = '1.1.0'
bintrayPluginVersion = '1.8.4'
@@ -19,11 +20,11 @@ buildscript {
coreKtxVersion = '1.2.0'
coroutinesVersion = '1.3.5'
eddsaVersion = '0.3.0'
fragmentVersion = '1.2.3'
fragmentVersion = '1.2.4'
jsr305Version = '3.0.2'
junitVersion = '4.13'
kotlinVersion = '1.3.71'
materialComponentsVersion = '1.1.0'
materialComponentsVersion = '1.2.0-alpha05'
mavenPluginVersion = '2.1'
preferenceVersion = '1.1.0'
streamsupportVersion = '1.7.2'
@@ -193,6 +193,9 @@ public final class GoBackend implements Backend {
for (final String excludedApplication : config.getInterface().getExcludedApplications())
builder.addDisallowedApplication(excludedApplication);
for (final String includedApplication : config.getInterface().getIncludedApplications())
builder.addAllowedApplication(includedApplication);
for (final InetNetwork addr : config.getInterface().getAddresses())
builder.addAddress(addr.getAddress(), addr.getMask());
@@ -72,6 +72,7 @@ public class BadConfigException extends Exception {
DNS("DNS"),
ENDPOINT("Endpoint"),
EXCLUDED_APPLICATIONS("ExcludedApplications"),
INCLUDED_APPLICATIONS("IncludedApplications"),
LISTEN_PORT("ListenPort"),
MTU("MTU"),
PERSISTENT_KEEPALIVE("PersistentKeepalive"),
@@ -17,10 +17,8 @@ import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import androidx.annotation.Nullable;
@@ -187,7 +185,7 @@ public final class Config {
@SuppressWarnings("UnusedReturnValue")
public static final class Builder {
// Defaults to an empty set.
private final Set<Peer> peers = new LinkedHashSet<>();
private final ArrayList<Peer> peers = new ArrayList<>();
// No default; must be provided before building.
@Nullable private Interface interfaze;
@@ -43,6 +43,7 @@ public final class Interface {
private final Set<InetNetwork> addresses;
private final Set<InetAddress> dnsServers;
private final Set<String> excludedApplications;
private final Set<String> includedApplications;
private final KeyPair keyPair;
private final Optional<Integer> listenPort;
private final Optional<Integer> mtu;
@@ -52,6 +53,7 @@ public final class Interface {
addresses = Collections.unmodifiableSet(new LinkedHashSet<>(builder.addresses));
dnsServers = Collections.unmodifiableSet(new LinkedHashSet<>(builder.dnsServers));
excludedApplications = Collections.unmodifiableSet(new LinkedHashSet<>(builder.excludedApplications));
includedApplications = Collections.unmodifiableSet(new LinkedHashSet<>(builder.includedApplications));
keyPair = Objects.requireNonNull(builder.keyPair, "Interfaces must have a private key");
listenPort = builder.listenPort;
mtu = builder.mtu;
@@ -81,6 +83,9 @@ public final class Interface {
case "excludedapplications":
builder.parseExcludedApplications(attribute.getValue());
break;
case "includedapplications":
builder.parseIncludedApplications(attribute.getValue());
break;
case "listenport":
builder.parseListenPort(attribute.getValue());
break;
@@ -106,6 +111,7 @@ public final class Interface {
return addresses.equals(other.addresses)
&& dnsServers.equals(other.dnsServers)
&& excludedApplications.equals(other.excludedApplications)
&& includedApplications.equals(other.includedApplications)
&& keyPair.equals(other.keyPair)
&& listenPort.equals(other.listenPort)
&& mtu.equals(other.mtu);
@@ -141,6 +147,16 @@ public final class Interface {
return excludedApplications;
}
/**
* Returns the set of applications included exclusively for using the interface.
*
* @return a set of package names
*/
public Set<String> getIncludedApplications() {
// The collection is already immutable.
return includedApplications;
}
/**
* Returns the public/private key pair used by the interface.
*
@@ -174,6 +190,7 @@ public final class Interface {
hash = 31 * hash + addresses.hashCode();
hash = 31 * hash + dnsServers.hashCode();
hash = 31 * hash + excludedApplications.hashCode();
hash = 31 * hash + includedApplications.hashCode();
hash = 31 * hash + keyPair.hashCode();
hash = 31 * hash + listenPort.hashCode();
hash = 31 * hash + mtu.hashCode();
@@ -213,6 +230,8 @@ public final class Interface {
}
if (!excludedApplications.isEmpty())
sb.append("ExcludedApplications = ").append(Attribute.join(excludedApplications)).append('\n');
if (!includedApplications.isEmpty())
sb.append("IncludedApplications = ").append(Attribute.join(includedApplications)).append('\n');
listenPort.ifPresent(lp -> sb.append("ListenPort = ").append(lp).append('\n'));
mtu.ifPresent(m -> sb.append("MTU = ").append(m).append('\n'));
sb.append("PrivateKey = ").append(keyPair.getPrivateKey().toBase64()).append('\n');
@@ -240,6 +259,8 @@ public final class Interface {
private final Set<InetAddress> dnsServers = new LinkedHashSet<>();
// Defaults to an empty set.
private final Set<String> excludedApplications = new LinkedHashSet<>();
// Defaults to an empty set.
private final Set<String> includedApplications = new LinkedHashSet<>();
// No default; must be provided before building.
@Nullable private KeyPair keyPair;
// Defaults to not present.
@@ -271,6 +292,9 @@ public final class Interface {
if (keyPair == null)
throw new BadConfigException(Section.INTERFACE, Location.PRIVATE_KEY,
Reason.MISSING_ATTRIBUTE, null);
if (!includedApplications.isEmpty() && !excludedApplications.isEmpty())
throw new BadConfigException(Section.INTERFACE, Location.INCLUDED_APPLICATIONS,
Reason.INVALID_KEY, null);
return new Interface(this);
}
@@ -284,6 +308,16 @@ public final class Interface {
return this;
}
public Builder includeApplication(final String application) {
includedApplications.add(application);
return this;
}
public Builder includeApplications(final Collection<String> applications) {
includedApplications.addAll(applications);
return this;
}
public Builder parseAddresses(final CharSequence addresses) throws BadConfigException {
try {
for (final String address : Attribute.split(addresses))
@@ -308,6 +342,10 @@ public final class Interface {
return excludeApplications(Lists.of(Attribute.split(apps)));
}
public Builder parseIncludedApplications(final CharSequence apps) {
return includeApplications(Lists.of(Attribute.split(apps)));
}
public Builder parseListenPort(final String listenPort) throws BadConfigException {
try {
return setListenPort(Integer.parseInt(listenPort));
+3 -3
View File
@@ -14,7 +14,7 @@ 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
@@ -30,7 +30,7 @@ default: $(DESTDIR)/libwg-go.so
$(GRADLE_USER_HOME)/caches/golang/$(GO_TARBALL):
mkdir -p "$(dir $@)"
flock -x "$@.lock" -c ' \
flock "$@.lock" -c ' \
[ -f "$@" ] && exit 0; \
curl -o "$@.tmp" "https://dl.google.com/go/$(GO_TARBALL)" && \
echo "$(GO_HASH_$(GO_PLATFORM)) $@.tmp" | sha256sum -c && \
@@ -38,7 +38,7 @@ $(GRADLE_USER_HOME)/caches/golang/$(GO_TARBALL):
$(BUILDDIR)/go-$(GO_VERSION)/.prepared: $(GRADLE_USER_HOME)/caches/golang/$(GO_TARBALL)
mkdir -p "$(dir $@)"
flock -x "$@.lock" -c ' \
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 && \
+3
View File
@@ -59,6 +59,9 @@ android {
versionNameSuffix "-debug"
}
}
lintOptions {
disable('LongLogTag')
}
}
dependencies {
+8
View File
@@ -4,3 +4,11 @@
# 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>();
}
@@ -26,6 +26,7 @@ import com.wireguard.android.widget.ToggleSwitch.OnBeforeCheckedChangeListener
import com.wireguard.config.Attribute
import com.wireguard.config.InetNetwork
import java9.util.Optional
import java.net.InetAddress
/**
* Static methods for use by generated code in the Android data binding library.
@@ -135,13 +136,30 @@ object BindingAdapters {
@JvmStatic
@BindingAdapter("android:text")
fun setText(view: TextView, text: Optional<*>) {
view.text = text.map { it.toString() }.orElse("")
fun setOptionalText(view: TextView, text: Optional<*>?) {
view.text = text?.map { it.toString() }?.orElse("") ?: ""
}
@JvmStatic
@BindingAdapter("android:text")
fun setText(view: TextView, networks: Iterable<InetNetwork?>?) {
fun setInetNetworkSetText(view: TextView, networks: Iterable<InetNetwork?>?) {
view.text = if (networks != null) Attribute.join(networks) else ""
}
@JvmStatic
@BindingAdapter("android:text")
fun setInetAddressSetText(view: TextView, addresses: Iterable<InetAddress?>?) {
view.text = if (addresses != null) Attribute.join(addresses.map { it?.hostAddress }) else ""
}
@JvmStatic
fun tryParseInt(s: String?): Int {
if (s == null)
return 0
return try {
Integer.parseInt(s)
} catch (_: Exception) {
0
}
}
}
@@ -17,8 +17,10 @@ import java.lang.ref.WeakReference
/**
* A generic `RecyclerView.Adapter` backed by a `ObservableKeyedArrayList`.
*/
class ObservableKeyedRecyclerViewAdapter<K, E : Keyed<out K>> internal constructor(context: Context, private val layoutId: Int,
list: ObservableKeyedArrayList<K, E>?) : RecyclerView.Adapter<ObservableKeyedRecyclerViewAdapter.ViewHolder>() {
class ObservableKeyedRecyclerViewAdapter<K, E : Keyed<out K>> internal constructor(
context: Context, private val layoutId: Int,
list: ObservableKeyedArrayList<K, E>?
) : RecyclerView.Adapter<ObservableKeyedRecyclerViewAdapter.ViewHolder>() {
private val callback = OnListChangedCallback(this)
private val layoutInflater: LayoutInflater = LayoutInflater.from(context)
private var list: ObservableKeyedArrayList<K, E>? = null
@@ -12,13 +12,13 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.zxing.integration.android.IntentIntegrator
import com.wireguard.android.R
import com.wireguard.android.activity.TunnelCreatorActivity
import com.wireguard.android.util.requireTargetFragment
import com.wireguard.android.util.resolveAttribute
class AddTunnelsSheet : BottomSheetDialogFragment() {
@@ -81,10 +81,6 @@ class AddTunnelsSheet : BottomSheetDialogFragment() {
behavior?.removeBottomSheetCallback(bottomSheetCallback)
}
private fun requireTargetFragment(): Fragment {
return requireNotNull(targetFragment) { "A target fragment should always be set" }
}
private fun onRequestCreateConfig() {
startActivity(Intent(activity, TunnelCreatorActivity::class.java))
}
@@ -5,23 +5,30 @@
package com.wireguard.android.fragment
import android.app.Dialog
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.databinding.Observable
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import com.google.android.material.tabs.TabLayout
import com.wireguard.android.Application
import com.wireguard.android.BR
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.util.requireTargetFragment
class AppListDialogFragment : DialogFragment() {
private val appData: ObservableKeyedArrayList<String, ApplicationData> = ObservableKeyedArrayList()
private var currentlyExcludedApps = emptyList<String>()
private val appData = ObservableKeyedArrayList<String, ApplicationData>()
private var currentlySelectedApps = emptyList<String>()
private var initiallyExcluded = false
private var button: Button? = null
private var tabs: TabLayout? = null
private fun loadData() {
val activity = activity ?: return
@@ -33,7 +40,14 @@ class AppListDialogFragment : DialogFragment() {
val applicationData: MutableList<ApplicationData> = ArrayList()
resolveInfos.forEach {
val packageName = it.activityInfo.packageName
applicationData.add(ApplicationData(it.loadIcon(pm), it.loadLabel(pm).toString(), packageName, currentlyExcludedApps.contains(packageName)))
val appData = ApplicationData(it.loadIcon(pm), it.loadLabel(pm).toString(), packageName, currentlySelectedApps.contains(packageName))
applicationData.add(appData)
appData.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
if (propertyId == BR.selected)
setButtonText()
}
})
}
applicationData.sortWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name })
applicationData
@@ -52,17 +66,37 @@ class AppListDialogFragment : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val excludedApps = requireArguments().getStringArrayList(KEY_EXCLUDED_APPS)
currentlyExcludedApps = (excludedApps ?: emptyList())
require(requireTargetFragment() is AppSelectionListener) { "${requireTargetFragment()} must implement AppSelectionListener" }
currentlySelectedApps = (arguments?.getStringArrayList(KEY_SELECTED_APPS) ?: emptyList())
initiallyExcluded = arguments?.getBoolean(KEY_IS_EXCLUDED) ?: true
}
private fun setButtonText() {
val numSelected = appData.count { it.isSelected }
button?.text = if (numSelected == 0)
getString(R.string.use_all_applications)
else when (tabs?.selectedTabPosition) {
0 -> resources.getQuantityString(R.plurals.exclude_n_applications, numSelected, numSelected)
1 -> resources.getQuantityString(R.plurals.include_n_applications, numSelected, numSelected)
else -> null
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val alertDialogBuilder = AlertDialog.Builder(requireActivity())
alertDialogBuilder.setTitle(R.string.excluded_applications)
val binding = AppListDialogFragmentBinding.inflate(requireActivity().layoutInflater, null, false)
binding.executePendingBindings()
alertDialogBuilder.setView(binding.root)
alertDialogBuilder.setPositiveButton(R.string.set_exclusions) { _, _ -> setExclusionsAndDismiss() }
tabs = binding.tabs
tabs?.apply {
selectTab(binding.tabs.getTabAt(if (initiallyExcluded) 0 else 1))
addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) = Unit
override fun onTabUnselected(tab: TabLayout.Tab?) = Unit
override fun onTabSelected(tab: TabLayout.Tab?) = setButtonText()
})
}
alertDialogBuilder.setPositiveButton(" ") { _, _ -> setSelectionAndDismiss() }
alertDialogBuilder.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
alertDialogBuilder.setNeutralButton(R.string.toggle_all) { _, _ -> }
binding.fragment = this
@@ -70,39 +104,40 @@ class AppListDialogFragment : DialogFragment() {
loadData()
val dialog = alertDialogBuilder.create()
dialog.setOnShowListener {
dialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener {
val selectedItems = appData
.filter { it.isExcludedFromTunnel }
val excludeAll = selectedItems.isEmpty()
button = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
setButtonText()
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener { _ ->
val selectAll = appData.none { it.isSelected }
appData.forEach {
it.isExcludedFromTunnel = excludeAll
it.isSelected = selectAll
}
}
}
return dialog
}
private fun setExclusionsAndDismiss() {
val excludedApps: MutableList<String> = ArrayList()
private fun setSelectionAndDismiss() {
val selectedApps: MutableList<String> = ArrayList()
for (data in appData) {
if (data.isExcludedFromTunnel) {
excludedApps.add(data.packageName)
if (data.isSelected) {
selectedApps.add(data.packageName)
}
}
(targetFragment as AppExclusionListener?)!!.onExcludedAppsSelected(excludedApps)
(requireTargetFragment() as AppSelectionListener).onSelectedAppsSelected(selectedApps, tabs?.selectedTabPosition == 0)
dismiss()
}
interface AppExclusionListener {
fun onExcludedAppsSelected(excludedApps: List<String>)
interface AppSelectionListener {
fun onSelectedAppsSelected(selectedApps: List<String>, isExcluded: Boolean)
}
companion object {
private const val KEY_EXCLUDED_APPS = "excludedApps"
fun <T> newInstance(excludedApps: ArrayList<String?>?, target: T): AppListDialogFragment where T : Fragment?, T : AppExclusionListener? {
private const val KEY_SELECTED_APPS = "selected_apps"
private const val KEY_IS_EXCLUDED = "is_excluded"
fun <T> newInstance(selectedApps: ArrayList<String?>?, isExcluded: Boolean, target: T): AppListDialogFragment where T : Fragment?, T : AppSelectionListener? {
val extras = Bundle()
extras.putStringArrayList(KEY_EXCLUDED_APPS, excludedApps)
extras.putStringArrayList(KEY_SELECTED_APPS, selectedApps)
extras.putBoolean(KEY_IS_EXCLUDED, isExcluded)
val fragment = AppListDialogFragment()
fragment.setTargetFragment(target, 0)
fragment.arguments = extras
@@ -23,7 +23,7 @@ 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.fragment.AppListDialogFragment.AppSelectionListener
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.util.BiometricAuthenticator
import com.wireguard.android.util.ErrorMessages
@@ -35,7 +35,7 @@ import com.wireguard.config.Config
/**
* Fragment for editing a WireGuard configuration.
*/
class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
class TunnelEditorFragment : BaseFragment(), AppSelectionListener {
private var haveShownKeys = false
private var binding: TunnelEditorFragmentBinding? = null
private var tunnel: ObservableTunnel? = null
@@ -88,11 +88,20 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
super.onDestroyView()
}
override fun onExcludedAppsSelected(excludedApps: List<String>) {
requireNotNull(binding) { "Tried to set excluded apps while no view was loaded" }
binding!!.config!!.`interface`.excludedApplications.apply {
clear()
addAll(excludedApps)
override fun onSelectedAppsSelected(selectedApps: List<String>, isExcluded: Boolean) {
requireNotNull(binding) { "Tried to set excluded/included apps while no view was loaded" }
if (isExcluded) {
binding!!.config!!.`interface`.includedApplications.clear()
binding!!.config!!.`interface`.excludedApplications.apply {
clear()
addAll(selectedApps)
}
} else {
binding!!.config!!.`interface`.excludedApplications.clear()
binding!!.config!!.`interface`.includedApplications.apply {
clear()
addAll(selectedApps)
}
}
}
@@ -150,10 +159,16 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
}
@Suppress("UNUSED_PARAMETER")
fun onRequestSetExcludedApplications(view: View?) {
fun onRequestSetExcludedIncludedApplications(view: View?) {
if (binding != null) {
val excludedApps = ArrayList(binding!!.config!!.`interface`.excludedApplications)
val fragment = AppListDialogFragment.newInstance(excludedApps, this)
var isExcluded = true
var selectedApps = ArrayList(binding!!.config!!.`interface`.excludedApplications)
if (selectedApps.isEmpty()) {
selectedApps = ArrayList(binding!!.config!!.`interface`.includedApplications)
if (selectedApps.isNotEmpty())
isExcluded = false
}
val fragment = AppListDialogFragment.newInstance(selectedApps, isExcluded, this)
fragment.show(parentFragmentManager, null)
}
}
@@ -235,6 +250,7 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
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) {
@@ -344,7 +344,7 @@ class TunnelListFragment : BaseFragment() {
resources = activity!!.resources
}
mode.menuInflater.inflate(R.menu.tunnel_list_action_mode, menu)
binding!!.tunnelList.adapter!!.notifyDataSetChanged()
binding?.tunnelList?.adapter?.notifyDataSetChanged()
return true
}
@@ -10,13 +10,13 @@ import androidx.databinding.Bindable
import com.wireguard.android.BR
import com.wireguard.android.databinding.Keyed
class ApplicationData(val icon: Drawable, val name: String, val packageName: String, isExcludedFromTunnel: Boolean) : BaseObservable(), Keyed<String> {
class ApplicationData(val icon: Drawable, val name: String, val packageName: String, isSelected: Boolean) : BaseObservable(), Keyed<String> {
override val key = name
@get:Bindable
var isExcludedFromTunnel = isExcludedFromTunnel
var isSelected = isSelected
set(value) {
field = value
notifyPropertyChanged(BR.excludedFromTunnel)
notifyPropertyChanged(BR.selected)
}
}
@@ -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
@@ -18,11 +18,18 @@ import java9.util.concurrent.CompletableFuture
import kotlin.system.exitProcess
class KernelModuleDisablerPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
private var state = if (Application.getBackend() is WgQuickBackend) State.ENABLED else State.DISABLED
private var state = State.UNKNOWN
override fun getSummary() = context.getString(state.summaryResourceId)
init {
isVisible = false
Application.getBackendAsync().thenAccept { backend ->
setState(if (backend is WgQuickBackend) State.ENABLED else State.DISABLED)
}
}
override fun getTitle() = context.getString(state.titleResourceId)
override fun getSummary() = if (state == State.UNKNOWN) "" else context.getString(state.summaryResourceId)
override fun getTitle() = if (state == State.UNKNOWN) "" else context.getString(state.titleResourceId)
@SuppressLint("ApplySharedPref")
override fun onClick() {
@@ -51,13 +58,15 @@ class KernelModuleDisablerPreference(context: Context, attrs: AttributeSet?) : P
if (this.state == state) return
this.state = state
if (isEnabled != state.shouldEnableView) isEnabled = state.shouldEnableView
if (isVisible != state.visible) isVisible = state.visible
notifyChanged()
}
private enum class State(val titleResourceId: Int, val summaryResourceId: Int, val shouldEnableView: Boolean) {
ENABLED(R.string.module_disabler_enabled_title, R.string.module_disabler_enabled_summary, true),
DISABLED(R.string.module_disabler_disabled_title, R.string.module_disabler_disabled_summary, true),
ENABLING(R.string.module_disabler_disabled_title, R.string.success_application_will_restart, false),
DISABLING(R.string.module_disabler_enabled_title, R.string.success_application_will_restart, false);
private enum class State(val titleResourceId: Int, val summaryResourceId: Int, val shouldEnableView: Boolean, val visible: Boolean) {
UNKNOWN(0, 0, false, false),
ENABLED(R.string.module_disabler_enabled_title, R.string.module_disabler_enabled_summary, true, true),
DISABLED(R.string.module_disabler_disabled_title, R.string.module_disabler_disabled_summary, true, true),
ENABLING(R.string.module_disabler_disabled_title, R.string.success_application_will_restart, false, true),
DISABLING(R.string.module_disabler_enabled_title, R.string.success_application_will_restart, false, true);
}
}
@@ -8,9 +8,14 @@ package com.wireguard.android.util
import android.content.Context
import android.util.TypedValue
import androidx.annotation.AttrRes
import androidx.fragment.app.Fragment
fun Context.resolveAttribute(@AttrRes attrRes: Int): Int {
val typedValue = TypedValue()
theme.resolveAttribute(attrRes, typedValue, true)
return typedValue.data
}
fun Fragment.requireTargetFragment(): Fragment {
return requireNotNull(targetFragment) { "A target fragment should always be set for $this" }
}
@@ -22,6 +22,9 @@ class InterfaceProxy : BaseObservable, Parcelable {
@get:Bindable
val excludedApplications: ObservableList<String> = ObservableArrayList()
@get:Bindable
val includedApplications: ObservableList<String> = ObservableArrayList()
@get:Bindable
var addresses: String = ""
set(value) {
@@ -70,6 +73,7 @@ class InterfaceProxy : BaseObservable, Parcelable {
addresses = parcel.readString() ?: ""
dnsServers = parcel.readString() ?: ""
parcel.readStringList(excludedApplications)
parcel.readStringList(includedApplications)
listenPort = parcel.readString() ?: ""
mtu = parcel.readString() ?: ""
privateKey = parcel.readString() ?: ""
@@ -80,6 +84,7 @@ class InterfaceProxy : BaseObservable, Parcelable {
val dnsServerStrings = other.dnsServers.map { it.hostAddress }
dnsServers = Attribute.join(dnsServerStrings)
excludedApplications.addAll(other.excludedApplications)
includedApplications.addAll(other.includedApplications)
listenPort = other.listenPort.map { it.toString() }.orElse("")
mtu = other.mtu.map { it.toString() }.orElse("")
val keyPair = other.keyPair
@@ -103,6 +108,7 @@ class InterfaceProxy : BaseObservable, Parcelable {
if (addresses.isNotEmpty()) builder.parseAddresses(addresses)
if (dnsServers.isNotEmpty()) builder.parseDnsServers(dnsServers)
if (excludedApplications.isNotEmpty()) builder.excludeApplications(excludedApplications)
if (includedApplications.isNotEmpty()) builder.includeApplications(includedApplications)
if (listenPort.isNotEmpty()) builder.parseListenPort(listenPort)
if (mtu.isNotEmpty()) builder.parseMtu(mtu)
if (privateKey.isNotEmpty()) builder.parsePrivateKey(privateKey)
@@ -113,6 +119,7 @@ class InterfaceProxy : BaseObservable, Parcelable {
dest.writeString(addresses)
dest.writeString(dnsServers)
dest.writeStringList(excludedApplications)
dest.writeStringList(includedApplications)
dest.writeString(listenPort)
dest.writeString(mtu)
dest.writeString(privateKey)
@@ -12,7 +12,7 @@ import androidx.core.view.marginRight
import androidx.core.view.marginTop
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton
/**
* A utility for edge-to-edge display. It provides several features needed to make the app
@@ -27,7 +27,7 @@ object EdgeToEdge {
}
@JvmStatic
fun setUpScrollingContent(scrollingContent: ViewGroup, fab: ExtendedFloatingActionButton?) {
fun setUpScrollingContent(scrollingContent: ViewGroup, fab: FloatingActionButton?) {
val originalPaddingLeft = scrollingContent.paddingLeft
val originalPaddingRight = scrollingContent.paddingRight
val originalPaddingBottom = scrollingContent.paddingBottom
@@ -50,7 +50,7 @@ object EdgeToEdge {
}
@JvmStatic
fun setUpFAB(fab: ExtendedFloatingActionButton) {
fun setUpFAB(fab: FloatingActionButton) {
val originalMarginLeft = fab.marginLeft
val originalMarginRight = fab.marginRight
val originalMarginBottom = fab.marginBottom
@@ -10,14 +10,14 @@ import android.view.View
import android.widget.RelativeLayout
import com.wireguard.android.R
class MultiselectableRelativeLayout : RelativeLayout {
class MultiselectableRelativeLayout @JvmOverloads constructor(
context: Context? = null,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr, defStyleRes) {
private var multiselected = false
constructor(context: Context?) : super(context) {}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
override fun onCreateDrawableState(extraSpace: Int): IntArray {
if (multiselected) {
val drawableState = super.onCreateDrawableState(extraSpace + 1)
@@ -18,30 +18,50 @@
type="com.wireguard.android.databinding.ObservableKeyedArrayList&lt;String, ApplicationData&gt;" />
</data>
<FrameLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="200dp">
android:orientation="vertical">
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
android:visibility="@{appData.isEmpty() ? View.VISIBLE : View.GONE}"
tools:visibility="gone" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
style="@style/Widget.MaterialComponents.TabLayout.Colored"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/app_list"
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/exclude_from_tunnel" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/include_in_tunnel" />
</com.google.android.material.tabs.TabLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:items="@{appData}"
app:layout="@{@layout/app_list_item}"
tools:itemCount="10"
tools:listitem="@layout/app_list_item" />
</FrameLayout>
android:minHeight="200dp">
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
android:visibility="@{appData.isEmpty() ? View.VISIBLE : View.GONE}"
tools:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/app_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:items="@{appData}"
app:layout="@{@layout/app_list_item}"
tools:itemCount="10"
tools:listitem="@layout/app_list_item" />
</FrameLayout>
</LinearLayout>
</layout>
+3 -3
View File
@@ -24,7 +24,7 @@
android:layout_height="wrap_content"
android:background="@drawable/list_item_background"
android:gravity="center_vertical"
android:onClick="@{(view) -> item.setExcludedFromTunnel(!item.excludedFromTunnel)}"
android:onClick="@{(view) -> item.setSelected(!item.selected)}"
android:orientation="horizontal"
android:paddingTop="8dp"
android:paddingBottom="8dp">
@@ -51,10 +51,10 @@
tools:text="@tools:sample/full_names" />
<CheckBox
android:id="@+id/excluded_checkbox"
android:id="@+id/selected_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={item.excludedFromTunnel}"
android:checked="@={item.selected}"
tools:checked="true" />
</LinearLayout>
@@ -16,13 +16,12 @@
tools:itemCount="20"
tools:listitem="@layout/log_viewer_entry" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/share_fab"
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:icon="@drawable/ic_action_share_white" />
app:srcCompat="@drawable/ic_action_share_white" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
+222 -111
View File
@@ -22,131 +22,242 @@
type="com.wireguard.config.Config" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical">
android:focusable="true">
<com.google.android.material.card.MaterialCardView
android:id="@+id/tunnel_detail_card"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
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_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
<com.google.android.material.card.MaterialCardView
android:id="@+id/tunnel_detail_card"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/interface_title"
style="@style/SectionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/interface_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.wireguard.android.widget.ToggleSwitch
android:id="@+id/tunnel_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:checked="@{tunnel.state == State.UP}"
app:layout_constraintBaseline_toBaselineOf="@+id/interface_title"
app:layout_constraintEnd_toEndOf="parent"
app:onBeforeCheckedChanged="@{fragment::setTunnelState}" />
<TextView
android:id="@+id/interface_name_label"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/interface_name_text"
android:text="@string/name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/interface_title" />
android:layout_height="wrap_content">
<TextView
android:id="@+id/interface_name_text"
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{tunnel.name}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/interface_name_label"
tools:text="wg0" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/interface_title"
style="@style/SectionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/interface_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/public_key_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/public_key_text"
android:text="@string/public_key"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/interface_name_text" />
<com.wireguard.android.widget.ToggleSwitch
android:id="@+id/tunnel_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:checked="@{tunnel.state == State.UP}"
app:layout_constraintBaseline_toBaselineOf="@+id/interface_title"
app:layout_constraintEnd_toEndOf="parent"
app:onBeforeCheckedChanged="@{fragment::setTunnelState}" />
<TextView
android:id="@+id/public_key_text"
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/public_key_description"
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=" />
<TextView
android:id="@+id/interface_name_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/interface_name_text"
android:text="@string/name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/interface_title" />
<TextView
android:id="@+id/addresses_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/addresses_text"
android:text="@string/addresses"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/public_key_text" />
<TextView
android:id="@+id/interface_name_text"
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/name"
android:onClick="@{ClipboardUtils::copyTextView}"
android:text="@{tunnel.name}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/interface_name_label"
tools:text="wg0" />
<TextView
android:id="@+id/addresses_text"
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/addresses"
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" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/public_key_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/public_key_text"
android:text="@string/public_key"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/interface_name_text" />
<LinearLayout
android:id="@+id/peers_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:divider="@null"
android:orientation="vertical"
app:items="@{config.peers}"
app:layout="@{@layout/tunnel_detail_peer}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tunnel_detail_card"
tools:ignore="UselessLeaf" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/public_key_text"
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/public_key"
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=" />
<TextView
android:id="@+id/addresses_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/addresses_text"
android:text="@string/addresses"
android:visibility="@{config.interface.addresses.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/public_key_text" />
<TextView
android:id="@+id/addresses_text"
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/addresses"
android:onClick="@{ClipboardUtils::copyTextView}"
android:text="@{config.interface.addresses}"
android:visibility="@{config.interface.addresses.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/addresses_label"
tools:text="fc00:bbbb:bbbb:bb11::3:368b/128" />
<TextView
android:id="@+id/dns_servers_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/dns_servers_text"
android:text="@string/dns_servers"
android:visibility="@{config.interface.dnsServers.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/addresses_text" />
<TextView
android:id="@+id/dns_servers_text"
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/dns_servers"
android:onClick="@{ClipboardUtils::copyTextView}"
android:text="@{config.interface.dnsServers}"
android:visibility="@{config.interface.dnsServers.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/dns_servers_label"
tools:text="8.8.8.8, 8.8.4.4" />
<TextView
android:id="@+id/listen_port_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/listen_port_text"
android:text="@string/listen_port"
android:visibility="@{config.interface.listenPort.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintEnd_toStartOf="@id/mtu_label"
app:layout_constraintHorizontal_weight="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/dns_servers_text" />
<TextView
android:id="@+id/listen_port_text"
style="@style/DetailText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:contentDescription="@string/listen_port"
android:onClick="@{ClipboardUtils::copyTextView}"
android:text="@{config.interface.listenPort}"
android:visibility="@{config.interface.listenPort.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintEnd_toStartOf="@id/mtu_label"
app:layout_constraintHorizontal_weight="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/listen_port_label"
tools:text="51820" />
<TextView
android:id="@+id/mtu_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/mtu_text"
android:text="@string/mtu"
android:visibility="@{config.interface.mtu.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="0.5"
app:layout_constraintLeft_toRightOf="@id/listen_port_label"
app:layout_constraintStart_toEndOf="@id/listen_port_label"
app:layout_constraintTop_toBottomOf="@id/dns_servers_text" />
<TextView
android:id="@+id/mtu_text"
style="@style/DetailText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:contentDescription="@string/mtu"
android:onClick="@{ClipboardUtils::copyTextView}"
android:text="@{config.interface.mtu}"
android:visibility="@{config.interface.mtu.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="0.5"
app:layout_constraintStart_toEndOf="@id/listen_port_label"
app:layout_constraintStart_toStartOf="@+id/mtu_label"
app:layout_constraintTop_toBottomOf="@+id/mtu_label"
tools:text="1500" />
<TextView
android:id="@+id/applications_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/applications_text"
android:text="@string/applications"
android:visibility="@{config.interface.includedApplications.isEmpty() &amp;&amp; config.interface.excludedApplications.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/listen_port_text" />
<TextView
android:id="@+id/applications_text"
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/applications"
android:onClick="@{ClipboardUtils::copyTextView}"
android:text="@{config.interface.includedApplications.isEmpty() ? @plurals/n_excluded_applications(config.interface.excludedApplications.size(), config.interface.excludedApplications.size()) : @plurals/n_included_applications(config.interface.includedApplications.size(), config.interface.includedApplications.size())}"
android:visibility="@{config.interface.includedApplications.isEmpty() &amp;&amp; config.interface.excludedApplications.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/applications_label"
tools:text="8 excluded" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:id="@+id/peers_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:divider="@null"
android:orientation="vertical"
app:items="@{config.peers}"
app:layout="@{@layout/tunnel_detail_peer}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tunnel_detail_card"
tools:ignore="UselessLeaf" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>
+64 -8
View File
@@ -15,11 +15,7 @@
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardBackgroundColor="?attr/elevationOverlayColor"
app:cardCornerRadius="0dp"
app:cardElevation="2dp"
app:contentPadding="8dp">
android:layout_margin="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@@ -49,7 +45,7 @@
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/public_key_description"
android:contentDescription="@string/public_key"
android:ellipsize="end"
android:maxLines="1"
android:onClick="@{ClipboardUtils::copyTextView}"
@@ -59,6 +55,32 @@
app:layout_constraintTop_toBottomOf="@+id/public_key_label"
tools:text="wOs2eguFEohqIZxlSJ1CAT9584tc6ejj9hfGFsoBVkA=" />
<TextView
android:id="@+id/pre_shared_key_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/pre_shared_key_text"
android:text="@string/pre_shared_key"
android:visibility="@{item.preSharedKey.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/public_key_text" />
<TextView
android:id="@+id/pre_shared_key_text"
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/pre_shared_key"
android:ellipsize="end"
android:maxLines="1"
android:singleLine="true"
android:text="@string/pre_shared_key_enabled"
android:visibility="@{item.preSharedKey.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pre_shared_key_label"
tools:text="8VyS8W8XeMcBWfKp1GuG3/fZlnUQFkqMNbrdmZtVQIM=" />
<TextView
android:id="@+id/allowed_ips_label"
android:layout_width="match_parent"
@@ -66,15 +88,19 @@
android:layout_marginTop="8dp"
android:labelFor="@+id/allowed_ips_text"
android:text="@string/allowed_ips"
android:visibility="@{item.allowedIps.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/public_key_text" />
app:layout_constraintTop_toBottomOf="@+id/pre_shared_key_text" />
<TextView
android:id="@+id/allowed_ips_text"
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/allowed_ips"
android:onClick="@{ClipboardUtils::copyTextView}"
android:text="@{item.allowedIps}"
android:visibility="@{item.allowedIps.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
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" />
@@ -86,6 +112,7 @@
android:layout_marginTop="8dp"
android:labelFor="@+id/endpoint_text"
android:text="@string/endpoint"
android:visibility="@{item.endpoint.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/allowed_ips_text" />
@@ -94,11 +121,38 @@
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/endpoint"
android:onClick="@{ClipboardUtils::copyTextView}"
android:text="@{item.endpoint}"
android:visibility="@{item.endpoint.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/endpoint_label"
tools:text="192.168.0.1:51820" />
<TextView
android:id="@+id/persistent_keepalive_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:labelFor="@+id/persistent_keepalive_text"
android:text="@string/persistent_keepalive"
android:visibility="@{item.persistentKeepalive.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/endpoint_text" />
<TextView
android:id="@+id/persistent_keepalive_text"
style="@style/DetailText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/persistent_keepalive"
android:onClick="@{ClipboardUtils::copyTextView}"
android:text="@{@plurals/persistent_keepalive_seconds_unit(item.persistentKeepalive.orElse(0), item.persistentKeepalive.orElse(0))}"
android:visibility="@{item.persistentKeepalive.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/persistent_keepalive_label"
tools:text="every 3 seconds" />
<TextView
android:id="@+id/transfer_label"
android:layout_width="match_parent"
@@ -109,7 +163,7 @@
android:text="@string/transfer"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/endpoint_text"
app:layout_constraintTop_toBottomOf="@+id/persistent_keepalive_text"
tools:visibility="visible" />
<TextView
@@ -118,6 +172,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/transfer_label"
android:contentDescription="@string/transfer"
android:onClick="@{ClipboardUtils::copyTextView}"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/transfer_label"
@@ -45,11 +45,7 @@
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
app:cardBackgroundColor="?attr/elevationOverlayColor"
app:cardCornerRadius="0dp"
app:cardElevation="2dp"
app:contentPadding="8dp">
android:layout_marginBottom="16dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@@ -220,8 +216,8 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:onClick="@{fragment::onRequestSetExcludedApplications}"
android:text="@{@plurals/set_excluded_applications(config.interface.excludedApplications.size, config.interface.excludedApplications.size)}"
android:onClick="@{fragment::onRequestSetExcludedIncludedApplications}"
android:text="@{config.interface.includedApplications.size > 0 ? @plurals/set_included_applications(config.interface.includedApplications.size, config.interface.includedApplications.size) : config.interface.excludedApplications.size > 0 ? @plurals/set_excluded_applications(config.interface.excludedApplications.size, config.interface.excludedApplications.size) : @string/all_applications}"
android:textColor="?attr/colorSecondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@@ -8,6 +8,8 @@
<import type="com.wireguard.android.widget.KeyInputFilter" />
<import type="com.wireguard.android.databinding.BindingAdapters" />
<variable
name="collection"
type="androidx.databinding.ObservableList&lt;com.wireguard.android.viewmodel.PeerProxy&gt;" />
@@ -27,11 +29,7 @@
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
app:cardBackgroundColor="?attr/elevationOverlayColor"
app:cardCornerRadius="0dp"
app:cardElevation="2dp"
app:contentPadding="8dp">
android:layout_marginBottom="4dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@@ -108,6 +106,7 @@
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:hint="@string/persistent_keepalive"
app:suffixText="@{@plurals/persistent_keepalive_seconds_suffix(BindingAdapters.tryParseInt(item.persistentKeepalive))}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/pre_shared_key_label_layout">
@@ -118,7 +117,7 @@
android:layout_height="wrap_content"
android:hint="@string/hint_optional_discouraged"
android:inputType="number"
android:text="@={item.persistentKeepalive}" />
android:text="@={item.persistentKeepalive}"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
@@ -159,7 +158,7 @@
</com.google.android.material.textfield.TextInputLayout>
<CheckBox
android:id="@+id/excluded_checkbox"
android:id="@+id/selected_checkbox"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginStart="4dp"
@@ -66,14 +66,13 @@
android:textSize="20sp" />
</LinearLayout>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/create_fab"
style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:icon="@drawable/ic_action_add_white" />
app:srcCompat="@drawable/ic_action_add_white" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
+40 -5
View File
@@ -24,8 +24,41 @@
<item quantity="one">%d ausgeschlossene Anwendung</item>
<item quantity="other">%d ausgeschlossene Anwendungen</item>
</plurals>
<plurals name="set_included_applications">
<item quantity="one">%d ausgeschlossene Anwendung</item>
<item quantity="other">%d einbezogene Anwendungen</item>
</plurals>
<plurals name="n_excluded_applications">
<item quantity="one">%d ausgeschlossen</item>
<item quantity="other">%d ausgeschlossen</item>
</plurals>
<plurals name="n_included_applications">
<item quantity="one">%d einbezogen</item>
<item quantity="other">%d einbezogen</item>
</plurals>
<string name="all_applications">Alle Anwendungen</string>
<string name="exclude_from_tunnel">Ausschließen</string>
<string name="include_in_tunnel">Nur einbeziehen</string>
<plurals name="include_n_applications">
<item quantity="one">%d App einbeziehen</item>
<item quantity="other">%d App einbeziehen</item>
</plurals>
<plurals name="exclude_n_applications">
<item quantity="one">%d App ausschließen</item>
<item quantity="other">%d App ausschließen</item>
</plurals>
<plurals name="persistent_keepalive_seconds_unit">
<item quantity="one">jede Sekunde</item>
<item quantity="other">alle %d Sekunden</item>
</plurals>
<plurals name="persistent_keepalive_seconds_suffix">
<item quantity="one">Sekunde</item>
<item quantity="other">Sekunde</item>
</plurals>
<string name="use_all_applications">Alle Apps verwenden</string>
<string name="add_peer">Teilnehmer hinzufügen</string>
<string name="addresses">Adressen</string>
<string name="applications">Applikationen</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>
@@ -76,7 +109,6 @@
<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>
@@ -133,16 +165,15 @@
<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="pre_shared_key_enabled">aktiviert</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_summary_off">Aktivierte Tunnel beim Systemstart nicht automatisch starten</string>
<string name="restore_on_boot_summary_on">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>
@@ -191,4 +222,8 @@
<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>
<string name="biometric_prompt_zip_exporter_title">Authentifizieren, um Tunnel zu exportieren</string>
<string name="biometric_prompt_private_key_title">Authentifizieren, um privaten Schlüssel anzuzeigen</string>
<string name="biometric_auth_error">Authentifizierungsfehler</string>
<string name="biometric_auth_error_reason">Authentifizierungsfehler: %s</string>
</resources>
-3
View File
@@ -72,7 +72,6 @@
<string name="error_root">कृपया रूट एक्सेस प्राप्त करें और पुनः प्रयास करें</string>
<string name="error_up">टनल को लाने में त्रुटि: %s</string>
<string name="exclude_private_ips">निजी आईपी को छोड़ दें</string>
<string name="excluded_applications">निकाले गए ऐप्स</string>
<string name="generic_error">अज्ञात “%s” त्रुटि</string>
<string name="hint_automatic">(ऑटो)</string>
<string name="hint_generated">(उत्पन्न)</string>
@@ -121,14 +120,12 @@
<string name="pre_shared_key">प्री-शेयर्ड कीस</string>
<string name="private_key">निजी कीस</string>
<string name="public_key">सार्वजनिक कीस</string>
<string name="public_key_description">सार्वजनिक कीस</string>
<string name="qr_code_hint">सुझाव: `qrencode -t ansiutf8 &lt; tunnel.conf` के साथ उत्पन्न करो</string>
<string name="restore_on_boot_summary_on">बूट पर सक्षम टनलस को लाएगा</string>
<string name="restore_on_boot_summary_off">बूट पर सक्षम टनलस को नहीं लाएगा</string>
<string name="restore_on_boot_title">बूट पर पुनर्स्थापित करें</string>
<string name="save">सहेजें</string>
<string name="select_all">सभी का चयन करे</string>
<string name="set_exclusions">बहिष्करण सेट करें</string>
<string name="settings">सेटिंग्स</string>
<string name="shell_exit_status_read_error">शेल बाहर निकलने की स्थिति नहीं पढ़ सकता</string>
<string name="shell_marker_count_error">शेल ने 4 मार्करों की अपेक्षा की, %d प्राप्त किया</string>
+63 -55
View File
@@ -1,69 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="delete_error">
<item quantity="one">Tidak dapat menghapus %d tunel: %s</item>
<item quantity="other">Tidak dapat menghapus %d tunel: %s</item>
<item quantity="other">Tidak dapat menghapus %d terowongan: %s</item>
</plurals>
<plurals name="delete_success">
<item quantity="one">Berhasil menghapus tunel %d</item>
<item quantity="other">Berhasil menghapus tunel %d</item>
<item quantity="other">Berhasil menghapus terowongan %d</item>
</plurals>
<plurals name="delete_title">
<item quantity="one">%d tunnel dipilih</item>
<item quantity="other">%d tunel dipilih</item>
<item quantity="other">%d terowongan dipilih</item>
</plurals>
<plurals name="import_partial_success">
<item quantity="one">Mengimpor tunel %d dari %d</item>
<item quantity="other">Mengimpor tunel %d dari %d</item>
<item quantity="other">Mengimpor %1$d terowongan dari %2$d</item>
</plurals>
<plurals name="import_total_success">
<item quantity="one">Mengimpor tunel %d</item>
<item quantity="other">Mengimpor tunel %d</item>
<item quantity="other">Mengimpor %d terowongan</item>
</plurals>
<plurals name="set_excluded_applications">
<item quantity="one">%d Aplikasi dikecualikan</item>
<item quantity="other">%d Aplikasi dikecualikan</item>
<item quantity="other">%d Aplikasi Dikecualikan</item>
</plurals>
<string name="add_peer">Tambahkan peer</string>
<string name="add_peer">Tambahkan rekan</string>
<string name="addresses">Alamat</string>
<string name="allow_remote_control_intents_summary_off">Apl eksternal tidak dapat mengalihkan terowongan (disarankan)</string>
<string name="allow_remote_control_intents_summary_on">Apl eksternal dapat mengalihkan terowongan (lanjutan)</string>
<string name="allow_remote_control_intents_title">Izinkan apl kendali jarak jauh</string>
<string name="allowed_ips">IP diizinkan</string>
<string name="app_name">WireGuard</string>
<string name="bad_config_context">%1$s\'s %2$s</string>
<string name="bad_config_context">%1$s %2$s</string>
<string name="bad_config_context_top_level">%s</string>
<string name="bad_config_error">%1$s di %2$s</string>
<string name="bad_config_explanation_pka">: Harus positif dan tidak lebih dari 65535</string>
<string name="bad_config_explanation_positive_number">: Angka harus positif</string>
<string name="bad_config_explanation_udp_port">: Harus nomor port UDP yang valid</string>
<string name="bad_config_reason_invalid_key">Kunci tidak valid</string>
<string name="bad_config_reason_invalid_number">Angka tidak valid</string>
<string name="bad_config_reason_invalid_value">Nilai tidak valid</string>
<string name="bad_config_reason_missing_attribute">Atribut yang hilang</string>
<string name="bad_config_reason_missing_section">Bagian yang hilang</string>
<string name="bad_config_reason_syntax_error">Kesalahan sintaks</string>
<string name="bad_config_reason_unknown_attribute">Atribut tidak diketahui</string>
<string name="bad_config_reason_unknown_section">Bagian tidak diketahui</string>
<string name="bad_config_reason_value_out_of_range">Nilai di luar jangkauan</string>
<string name="bad_extension_error">File harus .conf atau .zip</string>
<string name="cancel">Kembali</string>
<string name="config_delete_error">Tidak dapat menghapus file konfigurasi %s</string>
<string name="config_exists_error">Konfigurasi untuk “%s” sudah ada</string>
<string name="config_file_exists_error">Konfigurasi file “%s” sudah ada</string>
<string name="config_not_found_error">Konfigurasi file “%s” tidak ditemukan</string>
<string name="config_rename_error">Nama konfigurasi “%s” Tidak bisa diganti</string>
<string name="bad_config_explanation_positive_number">: Harus positif</string>
<string name="bad_config_explanation_udp_port">: Harus nomor porta UDP yang sah</string>
<string name="bad_config_reason_invalid_key">Kunci tidak sah</string>
<string name="bad_config_reason_invalid_number">Bilngan tidak sah</string>
<string name="bad_config_reason_invalid_value">Nilai tidak sah</string>
<string name="bad_config_reason_missing_attribute">Atribut hilang</string>
<string name="bad_config_reason_missing_section">Bagian hilang</string>
<string name="bad_config_reason_syntax_error">Galat sintaks</string>
<string name="bad_config_reason_unknown_attribute">Atribut tak diketahui</string>
<string name="bad_config_reason_unknown_section">Bagian tak diketahui</string>
<string name="bad_config_reason_value_out_of_range">Nilai di luar rentang</string>
<string name="bad_extension_error">Berkas harus .conf atau .zip</string>
<string name="cancel">Batalkan</string>
<string name="config_delete_error">Tidak dapat menghapus berkas konfigurasi %s</string>
<string name="config_exists_error">Sudah ada konfigurasi untuk “%s”</string>
<string name="config_file_exists_error">Sudah ada konfigurasi berkas “%s”</string>
<string name="config_not_found_error">Tidak menemukan konfigurasi berkas “%s”</string>
<string name="config_rename_error">Tidak bisa mengganti nama konfigurasi “%s”</string>
<string name="config_save_error">Konfigurasi “%1$s”: %2$s tidak bisa disimpan</string>
<string name="config_save_success">Konfigurasi “%s” berhasil disimpan</string>
<string name="create_activity_title">Buat tunel WireGuard</string>
<string name="create_bin_dir_error">Tidak dapat membuat direktori biner lokal</string>
<string name="create_empty">Buat dari awal</string>
<string name="create_output_dir_error">Tidak dapat membuat direktori keluaran</string>
<string name="create_downloads_file_error">Tidak dapat membuat file di direktori download</string>
<string name="create_empty">Buat dari awal</string>
<string name="create_from_file">Impor dari berkas atau arsip</string>
<string name="create_from_qr_code">Pindai dari kode QR</string>
<string name="create_output_dir_error">Tidak dapat membuat direktori keluaran</string>
<string name="create_temp_dir_error">Tidak dapat membuat direktori lokal sementara</string>
<string name="create_tunnel">Buat tunel</string>
<string name="dark_theme_summary_off">Saat ini menggunakan tema terang (siang)</string>
<string name="dark_theme_summary_on">Saat ini menggunakan tema gelap (malam)</string>
<string name="dark_theme_title">Gunakan tema gelap</string>
<string name="delete">Hapus</string>
<string name="toggle_all">Matikan semua</string>
<string name="dns_servers">Server DNS</string>
<string name="edit">Edit</string>
<string name="endpoint">Titik akhir</string>
@@ -72,41 +70,48 @@
<string name="error_root">Izinkan akses root dan coba lagi</string>
<string name="error_up">Kesalahan menambahkan tunel: %s</string>
<string name="exclude_private_ips">Kecualikan IP pribadi</string>
<string name="excluded_applications">Kecualikan aplikasi</string>
<string name="generate_new_private_key">Buat kunci privat baru</string>
<string name="generic_error">Eror “%s” Tidak diketahui</string>
<string name="hint_automatic">(otomatis)</string>
<string name="hint_generated">(generate)</string>
<string name="hint_optional">(pilihan)</string>
<string name="hint_optional_discouraged">(opsional, tidak disarankan)</string>
<string name="hint_random">(acak)</string>
<string name="illegal_filename_error">Nama file “%s” ilegal</string>
<string name="import_error">Tunel %s tidak bisa diimpor</string>
<string name="import_from_qr_code">Mengimpor tunel dari kode QR</string>
<string name="import_success">“%s” Sudah diimpor</string>
<string name="interface_title">Antarmuka</string>
<string name="key_contents_error">Karakter buruk pada kunci</string>
<string name="key_length_error">Panjang kunci salah</string>
<string name="key_length_explanation_base64">: Kunci WireGuard base64 harus terdiri dari 44 karakter (32 bit)</string>
<string name="key_length_explanation_binary">: Kunci WireGuard harus terdiri dari 32 bit</string>
<string name="key_length_explanation_hex">: Kunci hex WireGuard Harus terdiri dari 64 karakter (32 bit)</string>
<string name="listen_port">Isi port</string>
<string name="log_export_error">Log tidak bisa diekspor: %s</string>
<string name="log_export_subject">Berkas Log WireGuard Android</string>
<string name="log_export_success">Simpan ke “%s”</string>
<string name="log_export_title">Ekspor file log</string>
<string name="log_saver_activity_label">Simpan log</string>
<string name="log_viewer_pref_summary">Log dapat membantu dengan pengawakutuan</string>
<string name="log_viewer_pref_title">Lihat log aplikasi</string>
<string name="log_viewer_title">Log</string>
<string name="logcat_error">Tidak bisa menjalankan logcat: </string>
<string name="module_version_error">Tidak dapat menentukan versi modul kernel</string>
<string name="module_installer_not_found">Tidak tersedia modul untuk perangkat anda</string>
<string name="module_disabler_disabled_summary">Modul kernel eksperimental dapat meningkatkan kinerja</string>
<string name="module_disabler_disabled_title">Aktifkan backend modul kernel</string>
<string name="module_disabler_enabled_summary">Backend userspace yang lebih lambat dapat meningkatkan stabilitas</string>
<string name="module_disabler_enabled_title">Nonaktifkan backend modul kernel</string>
<string name="module_installer_error">Ada yang salah. Silakan coba lagi</string>
<string name="module_installer_initial">Modul kernel eksperimental dapat meningkatkan kinerja</string>
<string name="module_installer_not_found">Tidak tersedia modul untuk perangkat anda</string>
<string name="module_installer_title">Unduh dan pasang modul kernel</string>
<string name="module_installer_working">Mengunduh and memasang…</string>
<string name="module_installer_error">Ada yang salah. Silakan coba lagi</string>
<string name="module_disabler_disabled_title">Aktifkan backend modul kernel</string>
<string name="module_disabler_disabled_summary">Modul kernel eksperimental dapat meningkatkan kinerja</string>
<string name="module_disabler_enabled_title">Nonaktifkan backend modul kernel</string>
<string name="module_disabler_enabled_summary">Backend userspace yang lebih lambat dapat meningkatkan stabilitas</string>
<string name="success_application_will_restart">Berhasil. Sekarang aplikasi akan memulai ulang…</string>
<string name="module_version_error">Tidak dapat menentukan versi modul kernel</string>
<string name="mtu">MTU</string>
<string name="multiple_tunnels_title">Izinkan beberapa sekaligus tunel</string>
<string name="multiple_tunnels_summary_on">Beberapa tunel dapat dihidupkan secara bersamaan</string>
<string name="multiple_tunnels_summary_off">Menyalakan satu tunel akan mematikan semuanya</string>
<string name="name">Name</string>
<string name="multiple_tunnels_summary_on">Beberapa tunel dapat dihidupkan secara bersamaan</string>
<string name="multiple_tunnels_title">Izinkan beberapa sekaligus tunel</string>
<string name="name">Nama</string>
<string name="no_config_error">Lihat tunel tanpa konfigurasi</string>
<string name="no_configs_error">Tidak ditemukan konfigurasi</string>
<string name="no_tunnels_error">Tidak ada tunel</string>
@@ -116,24 +121,25 @@
<string name="parse_error_inet_network">Jaringan IP</string>
<string name="parse_error_integer">angka</string>
<string name="parse_error_reason">%1$s “%2$s” Tidak dapat diuraikan</string>
<string name="peer">Peer</string>
<string name="peer">Rekan</string>
<string name="permission_description">mengontrol terowongan WireGuard, mengaktifkan dan menonaktifkannya sesuka hati, berpotensi salah melalu lintas Internet</string>
<string name="permission_label">Kontrol tunel WireGuard</string>
<string name="persistent_keepalive">Keepalive persisten</string>
<string name="pre_shared_key">Kunci Pra-bersama</string>
<string name="private_key">Kunci pribadi</string>
<string name="public_key">Kunci publik</string>
<string name="public_key_description">Kunci publik</string>
<string name="qr_code_hint">Tips: generate dengan `qrencode -t ansiutf8 &lt; tunnel.conf`.</string>
<string name="restore_on_boot_summary_on">Tunel yang diaktifkan akan dimunculkan saat boot</string>
<string name="restore_on_boot_summary_off">Tunel yang diaktifkan tidak akan ditampilkan saat boot</string>
<string name="restore_on_boot_summary_on">Tunel yang diaktifkan akan dimunculkan saat boot</string>
<string name="restore_on_boot_title">Pulihkan saat boot</string>
<string name="save">Simpan</string>
<string name="select_all">Pilih semua</string>
<string name="set_exclusions">Tetapkan pengecualian</string>
<string name="settings">Pengaturan</string>
<string name="shell_exit_status_read_error">Shell tidak dapat membaca status keluar</string>
<string name="shell_marker_count_error">Shell diharapkan 4 nilai, diterima %d</string>
<string name="shell_start_error">Gagal memulai shell: %d</string>
<string name="success_application_will_restart">Berhasil. Sekarang aplikasi akan memulai ulang…</string>
<string name="toggle_all">Matikan semua</string>
<string name="toggle_error">Tunel WireGuard %s gagal dialihkan</string>
<string name="tools_installer_already">wg and wg-quick sudah terpasang</string>
<string name="tools_installer_failure">Tidak dapat memasang alat command-line (tidak root?)</string>
@@ -146,11 +152,11 @@
<string name="tools_installer_working">Memasang wg and wg-quick</string>
<string name="tools_unavailable_error">Alat yang diperlukan tidak tersedia</string>
<string name="transfer">Transfer</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">trm: %1$s, krm: %2$s</string>
<string name="transfer_tibibytes">%.2f TiB</string>
<string name="tun_create_error">Tidak dapat membuat perangkat tun</string>
<string name="tunnel_config_error">Tidak bisa mengkonfigurasikan tunel (wg-quick %d dikembalikan)</string>
@@ -176,6 +182,8 @@
<string name="zip_export_success">Disimpan ke “%s”</string>
<string name="zip_export_summary">File Zip akan disimpan di folder download</string>
<string name="zip_export_title">Ekspor tunel ke file zip</string>
<string name="key_length_error">Panjang kunci salah</string>
<string name="key_contents_error">Karakter buruk pada kunci</string>
<string name="biometric_prompt_zip_exporter_title">Otentikasi untuk mengekspor terowongan</string>
<string name="biometric_prompt_private_key_title">Otentikasi untuk melihat kunci privat</string>
<string name="biometric_auth_error">Otentikasi gagal</string>
<string name="biometric_auth_error_reason">Otentikasi gagal: %s</string>
</resources>
+4 -3
View File
@@ -76,7 +76,6 @@
<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>
@@ -135,14 +134,12 @@
<string name="pre_shared_key">Chiave condivisa (PSK)</string>
<string name="private_key">Chiave privata</string>
<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 lo stato di uscita</string>
<string name="shell_marker_count_error">La shell si aspettava 4 marker, ne ha ricevuti %d</string>
@@ -191,4 +188,8 @@
<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="biometric_prompt_zip_exporter_title">Autenticati per esportare le chiavi i tunnel</string>
<string name="biometric_prompt_private_key_title">Autenticati per visualizzare le chiavi private</string>
<string name="biometric_auth_error">Autenticazione non riuscita</string>
<string name="biometric_auth_error_reason">Autenticazione non riuscita: %s</string>
</resources>
+4 -3
View File
@@ -70,7 +70,6 @@
<string name="error_root">root 権限を取得して再試行してください</string>
<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>
@@ -129,14 +128,12 @@
<string name="pre_shared_key">事前共有鍵</string>
<string name="private_key">秘密鍵</string>
<string name="public_key">公開鍵</string>
<string name="public_key_description">公開鍵</string>
<string name="qr_code_hint">Tip: `qrencode -t ansiutf8 &lt; tunnel.conf` で生成できます</string>
<string name="restore_on_boot_summary_off">起動時にトンネルを有効化しない</string>
<string name="restore_on_boot_summary_on">起動時に、前回有効だったトンネルを有効化する</string>
<string name="restore_on_boot_title">起動時に復元</string>
<string name="save">保存</string>
<string name="select_all">すべて選択</string>
<string name="set_exclusions">対象外アプリを設定</string>
<string name="settings">設定</string>
<string name="shell_exit_status_read_error">シェルは終了ステータスを取得できません</string>
<string name="shell_marker_count_error">シェルは 4 マーカーを期待していますが、 %d マーカーを受け取りました</string>
@@ -185,4 +182,8 @@
<string name="zip_export_success">“%s” に保存</string>
<string name="zip_export_summary">Zip ファイルはダウンロードフォルダに保存されます</string>
<string name="zip_export_title">トンネル設定を zip ファイルにエクスポート</string>
<string name="biometric_prompt_zip_exporter_title">トンネル設定をエクスポートするために認証を行います</string>
<string name="biometric_prompt_private_key_title">秘密鍵を表示するために認証を行います</string>
<string name="biometric_auth_error">認証に失敗</string>
<string name="biometric_auth_error_reason">認証に失敗: %s</string>
</resources>
+7 -6
View File
@@ -72,7 +72,7 @@
<string name="create_downloads_file_error">Не удалось создать файл в каталоге загрузок</string>
<string name="create_empty">Создать вручную</string>
<string name="create_from_file">Импорт из файла или архива</string>
<string name="create_from_qr_code">Сканировать из QR-кода</string>
<string name="create_from_qr_code">Сканировать QR-код</string>
<string name="create_output_dir_error">Не удалось создать выходной каталог</string>
<string name="create_temp_dir_error">Не удалось создать временный локальный каталог</string>
<string name="create_tunnel">Создать туннель</string>
@@ -88,12 +88,11 @@
<string name="error_root">Пожалуйста, получите root-доступ и попробуйте снова</string>
<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_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>
@@ -147,14 +146,12 @@
<string name="pre_shared_key">Общий ключ</string>
<string name="private_key">Приватный ключ</string>
<string name="public_key">Публичный ключ</string>
<string name="public_key_description">Публичный ключ</string>
<string name="qr_code_hint">Совет: генерировать с `qrencode -t ansiutf8 &lt; tunnel.conf`.</string>
<string name="restore_on_boot_summary_off">Не поднимать ранее выбранные туннели при загрузке</string>
<string name="restore_on_boot_summary_on">Поднимать ранее выбранные туннели при загрузке</string>
<string name="restore_on_boot_title">Восстанавливать при загрузке</string>
<string name="save">Сохранить</string>
<string name="select_all">Выбрать все</string>
<string name="set_exclusions">ОК</string>
<string name="settings">Настройки</string>
<string name="shell_exit_status_read_error">Shell не может прочитать статус выхода</string>
<string name="shell_marker_count_error">Shell ожидает 4 маркера, получено %d</string>
@@ -203,4 +200,8 @@
<string name="zip_export_success">Сохранено в “%s”</string>
<string name="zip_export_summary">Zip-архив будет сохранен в папке загрузок</string>
<string name="zip_export_title">Экспорт туннелей в zip-архив</string>
<string name="biometric_prompt_zip_exporter_title">Аутентификация для экспорта туннелей</string>
<string name="biometric_prompt_private_key_title">Аутентификация для просмотра приватного ключа</string>
<string name="biometric_auth_error">Ошибка аутентификации</string>
<string name="biometric_auth_error_reason">Ошибка аутентификации: %s</string>
</resources>
+255
View File
@@ -0,0 +1,255 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<plurals name="delete_error">
<item quantity="one">%d tunela ni bilo mogoče izbrisati: %s</item>
<item quantity="two">%d tunelov ni bilo mogoče izbrisati: %s</item>
<item quantity="few">%d tunelov ni bilo mogoče izbrisati: %s</item>
<item quantity="other">%d tunelov ni bilo mogoče izbrisati: %s</item>
</plurals>
<plurals name="delete_success">
<item quantity="one">%d tunel uspešno izbrisan</item>
<item quantity="two">%d tunela uspešno izbrisana</item>
<item quantity="few">%d tuneli uspešno izbrisani</item>
<item quantity="other">%d tunelov uspešno izbrisanih</item>
</plurals>
<plurals name="delete_title">
<item quantity="one">%d tunel izbran</item>
<item quantity="two">%d tunela izbrana</item>
<item quantity="few">%d tuneli izbrani</item>
<item quantity="other">%d tunelov izbranih</item>
</plurals>
<plurals name="import_partial_success">
<item quantity="one">%1$d tunel od %2$d uvožen</item>
<item quantity="two">%1$d tunela od %2$d uvožena</item>
<item quantity="few">%1$d tuneli od %2$d uvoženi</item>
<item quantity="other">%1$d tunelov od %2$d uvoženih</item>
</plurals>
<plurals name="import_total_success">
<item quantity="one">%d tunel uvožen</item>
<item quantity="two">%d tunela uvožena</item>
<item quantity="few">%d tuneli uvoženi</item>
<item quantity="other">%d tunelov uvoženih</item>
</plurals>
<plurals name="set_excluded_applications">
<item quantity="one">%d izvzeta aplikacija</item>
<item quantity="two">%d izvzeti aplikaciji</item>
<item quantity="few">%d izvzete aplikacije</item>
<item quantity="other">%d izvzetih aplikacij</item>
</plurals>
<plurals name="set_included_applications">
<item quantity="one">%d vključena aplikacija</item>
<item quantity="two">%d vključeni aplikaciji</item>
<item quantity="few">%d vključene aplikacije</item>
<item quantity="other">%d vključenih aplikacij</item>
</plurals>
<plurals name="n_excluded_applications">
<item quantity="one">%d izvzeta</item>
<item quantity="two">%d izvzeti</item>
<item quantity="few">%d izvzete</item>
<item quantity="other">%d izvzetih</item>
</plurals>
<plurals name="n_included_applications">
<item quantity="one">%d vključena</item>
<item quantity="two">%d vključeni</item>
<item quantity="few">%d vključene</item>
<item quantity="other">%d vključenih</item>
</plurals>
<string name="all_applications">Vse aplikacije</string>
<string name="exclude_from_tunnel">Izvzemi</string>
<string name="include_in_tunnel">Vključi samo za</string>
<plurals name="include_n_applications">
<item quantity="one">Vključi za %d aplikacijo</item>
<item quantity="two">Vključi za %d aplikaciji</item>
<item quantity="few">Vključi za %d aplikacije</item>
<item quantity="other">Vključi za %d aplikacij</item>
</plurals>
<plurals name="exclude_n_applications">
<item quantity="one">Izvzemi %d aplikacijo</item>
<item quantity="two">Izvzemi %d aplikaciji</item>
<item quantity="few">Izvzemi %d aplikacije</item>
<item quantity="other">Izvzemi %d aplikacij</item>
</plurals>
<plurals name="persistent_keepalive_seconds_unit">
<item quantity="one">vsako %d sekundo</item>
<item quantity="two">vsaki %d sekundi</item>
<item quantity="few">vsake %d sekunde</item>
<item quantity="other">vsakih %d sekund</item>
</plurals>
<plurals name="persistent_keepalive_seconds_suffix">
<item quantity="one">sekunda</item>
<item quantity="two">sekundi</item>
<item quantity="few">sekunde</item>
<item quantity="other">sekund</item>
</plurals>
<string name="use_all_applications">Vse aplikacije</string>
<string name="add_peer">Dodaj vrstnike</string>
<string name="addresses">Naslovi</string>
<string name="applications">Aplikacije</string>
<string name="allow_remote_control_intents_summary_off">Zunanjim aplikacijam ni dovoljeno preklapljati tunelov (priporočeno)</string>
<string name="allow_remote_control_intents_summary_on">Zunanje aplikacije lahko preklapljajo tunele (napredno)</string>
<string name="allow_remote_control_intents_title">Dovoljeno upravljanje preko zunanjih aplikacij</string>
<string name="allowed_ips">Dovoljeni naslovi IP</string>
<string name="app_name">WireGuard</string>
<string name="bad_config_context">%1$s-ov %2$s</string>
<string name="bad_config_context_top_level">%s</string>
<string name="bad_config_error">%1$s v %2$s</string>
<string name="bad_config_explanation_pka">: Mora biti pozitivno in ne večje od 65535</string>
<string name="bad_config_explanation_positive_number">: Mora biti pozitivno</string>
<string name="bad_config_explanation_udp_port">: Mora biti veljavna številka vrat UDP</string>
<string name="bad_config_reason_invalid_key">Neveljaven ključ</string>
<string name="bad_config_reason_invalid_number">Neveljavno število</string>
<string name="bad_config_reason_invalid_value">Neveljavna vrednost</string>
<string name="bad_config_reason_missing_attribute">Manjka atribut</string>
<string name="bad_config_reason_missing_section">Manjka odsek</string>
<string name="bad_config_reason_syntax_error">Sintaktična napaka</string>
<string name="bad_config_reason_unknown_attribute">Neznan atribut</string>
<string name="bad_config_reason_unknown_section">Neznani odsek</string>
<string name="bad_config_reason_value_out_of_range">Vrednost je zunaj veljavnega območja</string>
<string name="bad_extension_error">Datoteka mora biti .conf ali .zip</string>
<string name="cancel">Prekliči</string>
<string name="config_delete_error">Konfiguracijske datoteke %s ni bilo mogoče izbrisati</string>
<string name="config_exists_error">Konfiguracija za „%s“ že obstaja</string>
<string name="config_file_exists_error">Konfiguracijska datoteka za „%s“ že obstaja</string>
<string name="config_not_found_error">Konfiguracijske datoteke za „%s“ ni bilo mogoče najti</string>
<string name="config_rename_error">Konfiguracijske datoteke „%s“ ni bilo mogoče preimenovati</string>
<string name="config_save_error">Konfiguracijske datoteke za „%1$s“ ni bilo mogoče shraniti: %2$s</string>
<string name="config_save_success">Konfiguracijska datoteka za „%s“ uspešno shranjena</string>
<string name="create_activity_title">Ustvarite tunel WireGuard</string>
<string name="create_bin_dir_error">Lokalnega imenika za aplikacijo ni bilo mogoče ustvariti</string>
<string name="create_downloads_file_error">Datoteke v mapi prenosov ni bilo mogoče ustvariti</string>
<string name="create_empty">Ustvari na novo</string>
<string name="create_from_file">Uvozi iz datoteke ali arhiva</string>
<string name="create_from_qr_code">Skreniraj kodo QR</string>
<string name="create_output_dir_error">Izhodnega imenika ni bilo mogoče ustvariti</string>
<string name="create_temp_dir_error">Lokalnega začasnega imenika ni bilo mogoče ustvariti</string>
<string name="create_tunnel">Ustvari tunel</string>
<string name="dark_theme_summary_off">V uporabi svetla (dnevna) tema</string>
<string name="dark_theme_summary_on">V uporabi temna (nočna) tema</string>
<string name="dark_theme_title">Uporabi temno temo</string>
<string name="delete">Izbriši</string>
<string name="dns_servers">Strežniki DNS</string>
<string name="edit">Uredi</string>
<string name="endpoint">Končna točka</string>
<string name="error_down">Napaka pri zaključevanju tunela: %s</string>
<string name="error_fetching_apps">Napaka pri poizvedovanju seznama aplikacij: %s</string>
<string name="error_root">Prosim omogočite dostop root in poskusite ponovno</string>
<string name="error_up">Napaka pri vzpostavitvi tunela: %s</string>
<string name="exclude_private_ips">Izvzemi zasebne naslove IP</string>
<string name="generate_new_private_key">Izdelaj nov zasebni ključ</string>
<string name="generic_error">Neznana napaka „%s“</string>
<string name="hint_automatic">(samodejno)</string>
<string name="hint_generated">(izdelano)</string>
<string name="hint_optional">(izbirno)</string>
<string name="hint_optional_discouraged">(izbirno, ni priporočljivo)</string>
<string name="hint_random">(naključno)</string>
<string name="illegal_filename_error">Neveljavno ime datoteke „%s“</string>
<string name="import_error">Tunela ni bilo mogoče uvoziti: %s</string>
<string name="import_from_qr_code">Uvozi tunel iz kode QR</string>
<string name="import_success">„%s“ uvožen</string>
<string name="interface_title">Vmesnik</string>
<string name="key_contents_error">Napačni znaki v ključu</string>
<string name="key_length_error">Napačna dolžina ključa</string>
<string name="key_length_explanation_base64">: WireGuardovi ključi base64 morajo vsebovati 44 znakov (32 bajtov)</string>
<string name="key_length_explanation_binary">: WireGuardovi ključi morajo biti veliki 32 bajtov</string>
<string name="key_length_explanation_hex">: WireGuardovi ključi hex morajo biti veliki 64 znakov (32 bajtov)</string>
<string name="listen_port">Vrata poslušanja</string>
<string name="log_export_error">Dnevnika ni bilo mogoče izvoziti: %s</string>
<string name="log_export_subject">Dnevniška datoteka WireGuard za Android</string>
<string name="log_export_success">Shranjeno v „%s“</string>
<string name="log_export_title">Izvozi dnevniško datoteko</string>
<string name="log_saver_activity_label">Shrani dnevnik</string>
<string name="log_viewer_pref_summary">Dnevniki lahko pomagajo pri razhroščevanju</string>
<string name="log_viewer_pref_title">Prikaži dnevnik aplikacije</string>
<string name="log_viewer_title">Dnevnik</string>
<string name="logcat_error">Ukaza logcat ni bilo mogoče izvesti: </string>
<string name="module_disabler_disabled_summary">Eksperimentalni modul jedra lahko izboljša zmogljivost</string>
<string name="module_disabler_disabled_title">Omogoči zaledje za modul jedra</string>
<string name="module_disabler_enabled_summary">Počasnejše uporabniško zaledje lahko izboljša stabilnost</string>
<string name="module_disabler_enabled_title">Onemogoči zaledje za modul jedra</string>
<string name="module_installer_error">Nekaj je šlo narobe, prosimo poskusite znova</string>
<string name="module_installer_initial">Eksperimentalni modul jedra lahko izboljša zmogljivost</string>
<string name="module_installer_not_found">Za vašo napravo ni razpoložljivih modulov</string>
<string name="module_installer_title">Prenesi in namesti modul jedra</string>
<string name="module_installer_working">Prenašanje in nameščanje …</string>
<string name="module_version_error">Verzije modula jedra ni bilo mogoče določiti</string>
<string name="mtu">MTU</string>
<string name="multiple_tunnels_summary_off">Vklop enega tunela bo izklopil druge</string>
<string name="multiple_tunnels_summary_on">Več tunelov je lahko aktiviranih sočasno</string>
<string name="multiple_tunnels_title">Dovoli več sočasnih tunelov</string>
<string name="name">Ime</string>
<string name="no_config_error">Poskus vzpostavitve tunela brez konfiguracije</string>
<string name="no_configs_error">Ni najdenih konfiguracij</string>
<string name="no_tunnels_error">Ni tunelov</string>
<string name="parse_error_generic">niz</string>
<string name="parse_error_inet_address">Naslov IP</string>
<string name="parse_error_inet_endpoint">končna točka</string>
<string name="parse_error_inet_network">Omrežje IP</string>
<string name="parse_error_integer">število</string>
<string name="parse_error_reason">Ni mogoče razčleniti %1$s „%2$s“</string>
<string name="peer">Vrstnik</string>
<string name="permission_description">upravljanje tunelov WireGuard, aktivacija in deaktivacija tunela, zaradi česar bo internetni promet mogoče napačno usmerjen</string>
<string name="permission_label">upravljanje tunelov WireGuard</string>
<string name="persistent_keepalive">Trajno ohranjanje povezave</string>
<string name="pre_shared_key">Ključ v skupni rabi</string>
<string name="pre_shared_key_enabled">omogočeno</string>
<string name="private_key">Zasebni ključ</string>
<string name="public_key">Javni ključ</string>
<string name="qr_code_hint">Namig: izdelajte z `qrencode -t ansiutf8 &lt; tunnel.conf`.</string>
<string name="restore_on_boot_summary_off">Pri zagonu telefona omogočeni tuneli ne bodo aktivirani</string>
<string name="restore_on_boot_summary_on">Pri zagonu telefona bodo omogočeni tuneli aktivirani</string>
<string name="restore_on_boot_title">Ponovna vzpostavitev ob zagonu</string>
<string name="save">Shrani</string>
<string name="select_all">Izberi vse</string>
<string name="settings">Nastavitve</string>
<string name="shell_exit_status_read_error">Lupina ne more prebrati izhodnega statusa</string>
<string name="shell_marker_count_error">Lupina je pričakovala 4 oznake, dobila pa je %d</string>
<string name="shell_start_error">Lupina se ni mogla zagnati: %d</string>
<string name="success_application_will_restart">Uspešno. Aplikacija se bo znova zagnala …</string>
<string name="toggle_all">Preklopi vse</string>
<string name="toggle_error">Napaka pri preklopu tunela WireGuard: %s</string>
<string name="tools_installer_already">wg in wg-quick sta že nameščena</string>
<string name="tools_installer_failure">Orodij ukazne vrstice ni bilo mogoče namestiti (ni dostopa root?)</string>
<string name="tools_installer_initial">Namestitev izbirnih orodij za skripte</string>
<string name="tools_installer_initial_magisk">Namestitev izbirnih orodij za skripte kot modula Magisk</string>
<string name="tools_installer_initial_system">Namestitev izbirnih orodij za skripte na sistemsko particijo</string>
<string name="tools_installer_success_magisk">wg in wg-quick kot modul Magisk nameščen (zahtevan ponovni zagon)</string>
<string name="tools_installer_success_system">wg und wg-quick nameščena na sistemsko particijo</string>
<string name="tools_installer_title">Namesti orodja za ukazno vrstico</string>
<string name="tools_installer_working">Nameščam wg in wg-quick</string>
<string name="tools_unavailable_error">Zahtevana orodja niso na voljo</string>
<string name="transfer">Prenos</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">prejeto: %1$s, poslano: %2$s</string>
<string name="transfer_tibibytes">%.2f TiB</string>
<string name="tun_create_error">Naprave tun ni bilo mogoče ustvariti</string>
<string name="tunnel_config_error">Tunela ni bilo mogoče nastaviti (wg-quick je vrnil %d)</string>
<string name="tunnel_create_error">Tunela ni bilo mogoče ustvariti: %s</string>
<string name="tunnel_create_success">Tunel „%s“ uspešno ustvarjen</string>
<string name="tunnel_error_already_exists">Tunel „%s“ že obstaja</string>
<string name="tunnel_error_invalid_name">Neveljavno ime</string>
<string name="tunnel_list_placeholder">Dodaj tunel s klikom na modri gumb</string>
<string name="tunnel_name">Ime tunela</string>
<string name="tunnel_on_error">Tunela ni bilo mogoče vključiti (wgTurnOn je vrnil %d)</string>
<string name="tunnel_rename_error">Tunela ni bilo mogoče preimenovati: %s</string>
<string name="tunnel_rename_success">Tunel uspešno preimenovan v „%s“</string>
<string name="type_name_go_userspace">Go userspace</string>
<string name="type_name_kernel_module">Modul jedra</string>
<string name="unknown_error">Neznana napaka</string>
<string name="version_summary">Zaledje %1$s v%2$s</string>
<string name="version_summary_checking">Preverjam verzijo zaledja %s</string>
<string name="version_summary_unknown">Neznana verzija %s</string>
<string name="version_title">WireGuard za Android v%s</string>
<string name="vpn_not_authorized_error">Uporabnik ni odobril storitve VPN</string>
<string name="vpn_start_error">Storitve Android VPN ni bilo mogoče zagnati</string>
<string name="zip_export_error">Tunelov ni bilo mogoče izvoziti: %s</string>
<string name="zip_export_success">Shranjeno v „%s“</string>
<string name="zip_export_summary">Datoteka zip je bila shranjena v mapo Prenosi</string>
<string name="zip_export_title">Izvozi tunele v datoteko zip</string>
<string name="biometric_prompt_zip_exporter_title">Za izvoz tunelov se prijavite</string>
<string name="biometric_prompt_private_key_title">Za ogled zasebnega ključa se prijavite</string>
<string name="biometric_auth_error">Napaka pri overovljanju</string>
<string name="biometric_auth_error_reason">Napaka pri overovljanju: %s</string>
</resources>
+1
View File
@@ -22,5 +22,6 @@
<item name="materialAlertDialogTheme">@style/AppTheme.Dialog</item>
<item name="actionBarPopupTheme">@style/ThemeOverlay.MaterialComponents.ActionBar</item>
<item name="textInputStyle">@style/TextInputLayoutBase</item>
<item name="materialCardViewStyle">@style/AppTheme.MaterialCardView</item>
</style>
</resources>
+1
View File
@@ -25,5 +25,6 @@
<item name="materialAlertDialogTheme">@style/AppTheme.Dialog</item>
<item name="actionBarPopupTheme">@style/ThemeOverlay.MaterialComponents.ActionBar</item>
<item name="textInputStyle">@style/TextInputLayoutBase</item>
<item name="materialCardViewStyle">@style/AppTheme.MaterialCardView</item>
</style>
</resources>
+33 -5
View File
@@ -16,10 +16,36 @@
<item quantity="other">导入了 %d 项</item>
</plurals>
<plurals name="set_excluded_applications">
<item quantity="other">%d 个排除应用</item>
<item quantity="other">对 %d 个应用不生效</item>
</plurals>
<plurals name="set_included_applications">
<item quantity="other">对 %d 个应用生效</item>
</plurals>
<plurals name="n_excluded_applications">
<item quantity="other">对 %d 个应用不生效</item>
</plurals>
<plurals name="n_included_applications">
<item quantity="other">对 %d 个应用生效</item>
</plurals>
<string name="all_applications">对全部应用生效</string>
<string name="exclude_from_tunnel">不生效</string>
<string name="include_in_tunnel">生效</string>
<plurals name="include_n_applications">
<item quantity="other">设定 %d 个应用</item>
</plurals>
<plurals name="exclude_n_applications">
<item quantity="other">设定 %d 个应用</item>
</plurals>
<plurals name="persistent_keepalive_seconds_unit">
<item quantity="other">每隔 %d 秒</item>
</plurals>
<plurals name="persistent_keepalive_seconds_suffix">
<item quantity="other">秒</item>
</plurals>
<string name="use_all_applications">不设定过滤</string>
<string name="add_peer">添加节点</string>
<string name="addresses">局域网 IP 地址</string>
<string name="applications">应用过滤</string>
<string name="allow_remote_control_intents_summary_off">外部应用当前不能控制隧道(不建议勾选)</string>
<string name="allow_remote_control_intents_summary_on">外部应用当前能够控制隧道(面向高级用户)</string>
<string name="allow_remote_control_intents_title">授权外部控制</string>
@@ -70,7 +96,6 @@
<string name="error_root">请获取 root 权限并重试</string>
<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>
@@ -127,16 +152,15 @@
<string name="permission_label">控制 WireGuard 隧道</string>
<string name="persistent_keepalive">连接保活间隔</string>
<string name="pre_shared_key">预共享密钥</string>
<string name="pre_shared_key_enabled">已启用</string>
<string name="private_key">私钥</string>
<string name="public_key">公钥</string>
<string name="public_key_description">公钥</string>
<string name="qr_code_hint">提示:使用命令 `qrencode -t ansiutf8 &lt; tunnel.conf` 生成二维码</string>
<string name="restore_on_boot_summary_off">未启用</string>
<string name="restore_on_boot_summary_on">设备启动时自动开启上次使用的隧道</string>
<string name="restore_on_boot_title">启动时恢复</string>
<string name="save">保存</string>
<string name="select_all">全选</string>
<string name="set_exclusions">确定</string>
<string name="settings">设置</string>
<string name="shell_exit_status_read_error">Shell 无法读取退出状态</string>
<string name="shell_marker_count_error">Shell 应获取 4 个标记,获取到 %d 个</string>
@@ -184,5 +208,9 @@
<string name="zip_export_error">无法导出隧道配置:%s</string>
<string name="zip_export_success">已保存至 “%s”</string>
<string name="zip_export_summary">zip 压缩包将保存至下载文件夹</string>
<string name="zip_export_title">导出隧道配置为 zip 压缩包</string>
<string name="zip_export_title">导出隧道配置</string>
<string name="biometric_prompt_zip_exporter_title">导出配置需要认证指纹</string>
<string name="biometric_prompt_private_key_title">查看私钥需要认证指纹</string>
<string name="biometric_auth_error">认证失败</string>
<string name="biometric_auth_error_reason">认证失败:%s</string>
</resources>
+34 -3
View File
@@ -24,8 +24,41 @@
<item quantity="one">%d Excluded Application</item>
<item quantity="other">%d Excluded Applications</item>
</plurals>
<plurals name="set_included_applications">
<item quantity="one">%d Included Application</item>
<item quantity="other">%d Included Applications</item>
</plurals>
<plurals name="n_excluded_applications">
<item quantity="one">%d excluded</item>
<item quantity="other">%d excluded</item>
</plurals>
<plurals name="n_included_applications">
<item quantity="one">%d included</item>
<item quantity="other">%d included</item>
</plurals>
<string name="all_applications">All Applications</string>
<string name="exclude_from_tunnel">Exclude</string>
<string name="include_in_tunnel">Include only</string>
<plurals name="include_n_applications">
<item quantity="one">Include %d app</item>
<item quantity="other">Include %d apps</item>
</plurals>
<plurals name="exclude_n_applications">
<item quantity="one">Exclude %d app</item>
<item quantity="other">Exclude %d apps</item>
</plurals>
<plurals name="persistent_keepalive_seconds_unit">
<item quantity="one">every second</item>
<item quantity="other">every %d seconds</item>
</plurals>
<plurals name="persistent_keepalive_seconds_suffix">
<item quantity="one">second</item>
<item quantity="other">seconds</item>
</plurals>
<string name="use_all_applications">Use all apps</string>
<string name="add_peer">Add peer</string>
<string name="addresses">Addresses</string>
<string name="applications">Applications</string>
<string name="allow_remote_control_intents_summary_off">External apps may not toggle tunnels (recommended)</string>
<string name="allow_remote_control_intents_summary_on">External apps may toggle tunnels (advanced)</string>
<string name="allow_remote_control_intents_title">Allow remote control apps</string>
@@ -76,7 +109,6 @@
<string name="error_root">Please obtain root access and try again</string>
<string name="error_up">Error bringing up tunnel: %s</string>
<string name="exclude_private_ips">Exclude private IPs</string>
<string name="excluded_applications">Excluded Applications</string>
<string name="generate_new_private_key">Generate new private key</string>
<string name="generic_error">Unknown “%s” error</string>
<string name="hint_automatic">(auto)</string>
@@ -133,16 +165,15 @@
<string name="permission_label">control WireGuard tunnels</string>
<string name="persistent_keepalive">Persistent keepalive</string>
<string name="pre_shared_key">Pre-shared key</string>
<string name="pre_shared_key_enabled">enabled</string>
<string name="private_key">Private key</string>
<string name="public_key">Public key</string>
<string name="public_key_description">Public key</string>
<string name="qr_code_hint">Tip: generate with `qrencode -t ansiutf8 &lt; tunnel.conf`.</string>
<string name="restore_on_boot_summary_off">Will not bring up enabled tunnels at boot</string>
<string name="restore_on_boot_summary_on">Will bring up enabled tunnels at boot</string>
<string name="restore_on_boot_title">Restore on boot</string>
<string name="save">Save</string>
<string name="select_all">Select all</string>
<string name="set_exclusions">Set Exclusions</string>
<string name="settings">Settings</string>
<string name="shell_exit_status_read_error">Shell cannot read exit status</string>
<string name="shell_marker_count_error">Shell expected 4 markers, received %d</string>
+8
View File
@@ -21,6 +21,14 @@
<item name="materialAlertDialogTheme">@style/AppTheme.Dialog</item>
<item name="actionBarPopupTheme">@style/ThemeOverlay.MaterialComponents.ActionBar</item>
<item name="textInputStyle">@style/TextInputLayoutBase</item>
<item name="materialCardViewStyle">@style/AppTheme.MaterialCardView</item>
</style>
<style name="AppTheme.MaterialCardView" parent="Widget.MaterialComponents.CardView">
<item name="cornerRadius">4dp</item>
<item name="cardElevation">4dp</item>
<item name="contentPadding">8dp</item>
<item name="cardBackgroundColor">?attr/elevationOverlayColor</item>
</style>
<style name="AppTheme.Dialog" parent="Theme.MaterialComponents.DayNight.Dialog.Alert">
+2 -2
View File
@@ -1,6 +1,6 @@
buildscript {
ext {
wireguardVersionCode = 470
wireguardVersionName = '1.0.20200329'
wireguardVersionCode = 474
wireguardVersionName = '1.0.20200407'
}
}