Compare commits

...

26 Commits

Author SHA1 Message Date
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
39 changed files with 871 additions and 299 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
}
}
}
@@ -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
@@ -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)
@@ -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>
+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/dns_servers"
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"
+4 -3
View File
@@ -76,7 +76,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>
@@ -135,14 +134,12 @@
<string name="pre_shared_key">Vorab geteilter Schlüssel</string>
<string name="private_key">Privater Schlüssel</string>
<string name="public_key">Öffentlicher Schlüssel</string>
<string name="public_key_description">Öffentlicher Schlüssel</string>
<string name="qr_code_hint">Tipp: Mit `qrencode -t ansiutf8 &lt; tunnel.conf` generieren.</string>
<string name="restore_on_boot_summary_off">Wird aktivierte Tunnel beim Systemstart nicht automatisch starten</string>
<string name="restore_on_boot_summary_on">Wird aktivierte Tunnel beim Systemstart automatisch wieder starten</string>
<string name="restore_on_boot_title">Beim Neustart wiederherstellen</string>
<string name="save">Speichern</string>
<string name="select_all">Alle auswählen</string>
<string name="set_exclusions">Ausnahmen festlegen</string>
<string name="settings">Einstellungen</string>
<string name="shell_exit_status_read_error">Shell kann den Exit-Status nicht lesen</string>
<string name="shell_marker_count_error">Die Shell erwartete 4 Marker, erhielt aber %d</string>
@@ -191,4 +188,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>
+207
View File
@@ -0,0 +1,207 @@
<?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 od %2$d tunelov uvožen</item>
<item quantity="two">%1$d od %2$d tunelov uvožena</item>
<item quantity="few">%1$d od %2$d tunelov uvoženi</item>
<item quantity="other">%1$d od %2$d tunelov 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>
<string name="add_peer">Dodaj soležnike</string>
<string name="addresses">Naslovi</string>
<string name="allow_remote_control_intents_summary_off">Zunanjim aplikacijam ni dovoljeno preklopiti tunela (priporočeno) </string>
<string name="allow_remote_control_intents_summary_on">Zunanje aplikacije lahko preklopijo tunel (napredno)</string>
<string name="allow_remote_control_intents_title">Dovoljeno upravljanje preko zunanji aplikacij</string>
<string name="allowed_ips">Dovoljeni IP naslovi</string>
<string name="app_name">WireGuard</string>
<string name="bad_config_context">%1$ss %2$s</string>
<string name="bad_config_context_top_level">%s</string>
<string name="bad_config_error">%1$s v %2$s</string>
<string name="bad_config_explanation_pka">: Zahtevano pozitivno število, manjše ali enako 65535</string>
<string name="bad_config_explanation_positive_number">: Zahtevano pozitivno število</string>
<string name="bad_config_explanation_udp_port">: Zahtevana veljavna številka UDP vrat</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">Manjkajoča sekcija</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">Končnica 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 za „%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 WireGuard tunel</string>
<string name="create_bin_dir_error">Lokalnega imenika za aplikacijo ni bilo mogoče kreirati</string>
<string name="create_downloads_file_error">Datoteke ni bilo mogoče shraniti v mapi Prenosi</string>
<string name="create_empty">Ustvari na novo</string>
<string name="create_from_file">Uvoz iz arhiva ali datoteke</string>
<string name="create_from_qr_code">Skreniraj s QR kode</string>
<string name="create_output_dir_error">Ni bilo mogoče kreirati imenika za izvoz</string>
<string name="create_temp_dir_error">Ni bilo mogoče kreirati začasnega lokalnega imenikaLokales</string>
<string name="create_tunnel">Ustvari tunel</string>
<string name="dark_theme_summary_off">V uporabi svetla (Dan) tema</string>
<string name="dark_theme_summary_on">V uporabi temna (Noč) tema</string>
<string name="dark_theme_title">Uporabi temnejšo temo</string>
<string name="delete">Odstrani</string>
<string name="dns_servers">DNS-strežnik</string>
<string name="edit">Uredi</string>
<string name="endpoint">Končna točka</string>
<string name="error_down">Napaka pri zapiranju tunela: %s</string>
<string name="error_fetching_apps">Napaka pri poizvedovanju seznama aplikacij: %s</string>
<string name="error_root">Prosim omogočite root dostop in poskusite ponovno</string>
<string name="error_up">Napaka pri vzpostavitvi tunela: %s</string>
<string name="exclude_private_ips">Izključitev privatnih IP naslovov</string>
<string name="generate_new_private_key">Generiraj nov privatni ključ</string>
<string name="generic_error">Neznana napaka „%s“</string>
<string name="hint_automatic">(samodejno)</string>
<string name="hint_generated">(generirano)</string>
<string name="hint_optional">(opcijsko)</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">Ne morem uvoziti tunela: %s</string>
<string name="import_from_qr_code">Uvoz tunela iz QR kode</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">: WireGuard base64-ključi morajo vsebovati 44 znakov (32 bytov)</string>
<string name="key_length_explanation_binary">: WireGuard-ključi morajo biti veliki 32 bytov</string>
<string name="key_length_explanation_hex">: WireGuard Hex-ključi morajo biti veliki 64 znakov (32 bytov)</string>
<string name="listen_port">Vrata na katerih se posluša</string>
<string name="log_export_error">Napaka pri izvozu dnevnika: %s</string>
<string name="log_export_subject">Dnevniška datoteka WireGuard za Android</string>
<string name="log_export_success">Shranjena v „%s“</string>
<string name="log_export_title">Izvoz dnevniške datoteke</string>
<string name="log_saver_activity_label">Shrani dnevniško datoteko</string>
<string name="log_viewer_pref_summary">Dnevniške datoteke lahko pomagajo pri razhroščevanju</string>
<string name="log_viewer_pref_title">Prikaži dnevniško datoteko aplikacije</string>
<string name="log_viewer_title">Beleženje</string>
<string name="logcat_error">Ni bilo mogoče izvesti \"logcat\": </string>
<string name="module_disabler_disabled_summary">Eksperimentalni modul v jedru lahko izboljša delovanje</string>
<string name="module_disabler_disabled_title">Aktiviranje modula v jedru</string>
<string name="module_disabler_enabled_summary">Počasnejše uporabniško zaledje (Userspace-Backend) lahko izboljša stabilnost</string>
<string name="module_disabler_enabled_title">Deaktiviranje modula v jedru</string>
<string name="module_installer_error">Nekaj je šlo narobe, prosimo poizkusite znova</string>
<string name="module_installer_initial">Eksperimentalni modul v jedru lahko izboljša delovanje</string>
<string name="module_installer_not_found">Za vašo napravo modulov ni razpoložljivih modulov</string>
<string name="module_installer_title">Prenos in namestitev modula</string>
<string name="module_installer_working">Prenašanje in nameščanje...</string>
<string name="module_version_error">Ni bilo mogoče pridobiti verzije modula</string>
<string name="mtu">MTU</string>
<string name="multiple_tunnels_summary_off">Vklop enega tunela, bo druge izklopil</string>
<string name="multiple_tunnels_summary_on">Več tunelov je lahko aktiviranih istočasno</string>
<string name="multiple_tunnels_title">Dovoljeno več istočasnih tunelov</string>
<string name="name">Ime</string>
<string name="no_config_error">Poskus vzpostavitve tunel brez konfiguracije</string>
<string name="no_configs_error">Ni bilo mogoče najti konfiguracijo</string>
<string name="no_tunnels_error">Ni razporožljivih 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">IP omrežje</string>
<string name="parse_error_integer">število</string>
<string name="parse_error_reason">Ni mogoče obdelati %1$s “%2$s”</string>
<string name="peer">Soležnik (Peer)</string>
<string name="permission_description">Upravljanje z Wireguard tunelom, aktivacija in deaktivacija tunela, zaradi česar bo internetni prometno mogoče napačno usmerjen</string>
<string name="permission_label">Upravljanje z Wireguard tunelom</string>
<string name="persistent_keepalive">Trajna povezava (Keepalive)</string>
<string name="pre_shared_key">Vnaprej deljen ključ</string>
<string name="private_key">Privatni ključ</string>
<string name="public_key">Javni ključ</string>
<string name="qr_code_hint">Namig: Generirajte z `qrencode -t ansiutf8 &lt; tunnel.conf`.</string>
<string name="restore_on_boot_summary_off">Pri vklopu telefona, aktivirani tuneli ne bodo avtimatično vzpostavljeni</string>
<string name="restore_on_boot_summary_on">Pri vklopu telefona bodo aktivirani tuneli avtimatično vzpostavljeni</string>
<string name="restore_on_boot_title">Ponovna vzpostavitev pri ponovnem zagonu</string>
<string name="save">Shrani</string>
<string name="select_all">Izbor vseh</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 markerje, 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">Prestavi vse</string>
<string name="toggle_error">Napaka pri prestavljanju Wireguard tunela: %s</string>
<string name="tools_installer_already">wg in wg-quick sta že nameščena</string>
<string name="tools_installer_failure">Orodja ukazne lupina niso bila nameščena (Ni root dostopa?)</string>
<string name="tools_installer_initial">Namestitev izbirnih orodij za skripte</string>
<string name="tools_installer_initial_magisk">Namestitev izbirnih orodij za skripte preko Magisk modula</string>
<string name="tools_installer_initial_system">Namestitev izbirnih orodij za skripte na sistemski particiji</string>
<string name="tools_installer_success_magisk">wg in wg-quick kot Magisk modul nameščen (Zahtevan ponovni zagon)</string>
<string name="tools_installer_success_system">wg und wg-quick nameščena na sistemski particiji</string>
<string name="tools_installer_title">Namestitev orodij za ukazno vrstico</string>
<string name="tools_installer_working">Nameščam wg in wg-quick</string>
<string name="tools_unavailable_error">Zahtevana orodja niso razpoložljiva</string>
<string name="transfer">Premik (Transfer)</string>
<string name="transfer_bytes">%d B</string>
<string name="transfer_gibibytes">%.2f GiB</string>
<string name="transfer_kibibytes">%.2f KiB</string>
<string name="transfer_mibibytes">%.2f MiB</string>
<string name="transfer_rx_tx">prejeto: %1$s, poslano: %2$s</string>
<string name="transfer_tibibytes">%.2f TiB</string>
<string name="tun_create_error">Ni bilo mogoče ustvariti tun naprave</string>
<string name="tunnel_config_error">Tunela ni bilo mogoče konfigurirati (wg-quick je vrnil %d)</string>
<string name="tunnel_create_error">Ne morem ustvariti tunela: %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">Ne morem preimenovati tunela: %s</string>
<string name="tunnel_rename_success">Tunnel uspešno preimenovan v „%s “</string>
<string name="type_name_go_userspace">Go userspace</string>
<string name="type_name_kernel_module">Jedrni modul</string>
<string name="unknown_error">Neznanna napaka</string>
<string name="version_summary">%1$s različica aplikacij v%2$s</string>
<string name="version_summary_checking">Preverjam %s različico aplikacije</string>
<string name="version_summary_unknown">Neznana %s različica</string>
<string name="version_title">WireGuard za Android v%s</string>
<string name="vpn_not_authorized_error">VPN-storitev ni bila odobrena s strani uporabnika</string>
<string name="vpn_start_error">Android VPN-storitve ni bilo mogoče zagnati</string>
<string name="zip_export_error">Ne morem izvoziti tunela: %s</string>
<string name="zip_export_success">Shranjeno pod “%s”</string>
<string name="zip_export_summary">Zip datoteka je bila shranjena v mapi Prenosi</string>
<string name="zip_export_title">Izvoz tunela kot Zip datoteka</string>
<string name="biometric_prompt_zip_exporter_title">Za izvoz tunelov se prijavite</string>
<string name="biometric_prompt_private_key_title">Za privatni ključ se prijavite</string>
<string name="biometric_auth_error">Napaka pri avtentikaciji</string>
<string name="biometric_auth_error_reason">Napaka pri avtentikaciji: %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>
+5 -4
View File
@@ -70,7 +70,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>
@@ -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">提示:使用命令 `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 +181,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 = 473
wireguardVersionName = '1.0.20200406'
}
}