mirror of
https://github.com/amnezia-vpn/amneziawg-android.git
synced 2026-07-03 14:05:13 +02:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d33a00e72 | |||
| 830d0992a7 | |||
| e4192ea172 | |||
| 4bbb1a0fcd | |||
| 77b5937fbb | |||
| 3144d36056 | |||
| 521bbb4b4c | |||
| 0a55e10b94 | |||
| d6e5fd9301 | |||
| 2c625f56fd | |||
| 7db0fa915e | |||
| e424765a61 | |||
| 1ca4dbf1a2 | |||
| 065893e31d | |||
| ef70aa88e1 | |||
| 6a11a199ca | |||
| 7b5ceac9f7 | |||
| 9fe0019ec1 | |||
| 84654a0302 | |||
| cd43444d1f | |||
| b7028896c7 | |||
| c1e86acb3c | |||
| 7d31bd2be9 | |||
| 2e573a66a4 | |||
| 8d128cf2e9 | |||
| d5ffa08480 |
@@ -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
@@ -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));
|
||||
|
||||
@@ -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 && \
|
||||
|
||||
Submodule tunnel/tools/wireguard-tools updated: 828ffc88cd...e189f9942d
@@ -59,6 +59,9 @@ android {
|
||||
versionNameSuffix "-debug"
|
||||
}
|
||||
}
|
||||
lintOptions {
|
||||
disable('LongLogTag')
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
Vendored
+8
@@ -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<String, ApplicationData>" />
|
||||
</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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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() && 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() && 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>
|
||||
|
||||
@@ -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<com.wireguard.android.viewmodel.PeerProxy>" />
|
||||
@@ -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"
|
||||
|
||||
@@ -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 < 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>
|
||||
|
||||
@@ -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 < 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>
|
||||
|
||||
@@ -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 < 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>
|
||||
|
||||
@@ -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 < 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>
|
||||
|
||||
@@ -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 < 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>
|
||||
|
||||
@@ -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 < 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>
|
||||
|
||||
@@ -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 < 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 < 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>
|
||||
|
||||
@@ -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 < 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>
|
||||
|
||||
@@ -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
@@ -1,6 +1,6 @@
|
||||
buildscript {
|
||||
ext {
|
||||
wireguardVersionCode = 470
|
||||
wireguardVersionName = '1.0.20200329'
|
||||
wireguardVersionCode = 473
|
||||
wireguardVersionName = '1.0.20200406'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user