mirror of
https://github.com/amnezia-vpn/amneziawg-android.git
synced 2026-07-03 14:05:13 +02:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 997edf4e4c | |||
| beb40ad09c | |||
| 4bac284083 | |||
| eae9216fe4 | |||
| dc1860c74d | |||
| 6209d3ee3d | |||
| 33aded09c6 | |||
| f360034f29 | |||
| 01e72064d4 | |||
| 91a75b681d | |||
| 0efdf0cdd7 | |||
| 75efd13556 | |||
| cd812b03cd | |||
| 794a2a1925 | |||
| 75899594c3 | |||
| 4944762d7c | |||
| d6431db248 | |||
| b8f649d1cb | |||
| 9fa8ca8c77 | |||
| 59da677c23 | |||
| 18a06b0a51 | |||
| 5e844bed5e | |||
| 46bf98d7f6 | |||
| 3e9ddd8720 |
@@ -1,3 +1,6 @@
|
||||
[submodule "tunnel/tools/wireguard-tools"]
|
||||
path = tunnel/tools/wireguard-tools
|
||||
url = https://git.zx2c4.com/wireguard-tools
|
||||
[submodule "tunnel/tools/elf-cleaner"]
|
||||
path = tunnel/tools/elf-cleaner
|
||||
url = https://github.com/termux/termux-elf-cleaner
|
||||
|
||||
+10
-2
@@ -29,17 +29,25 @@ android {
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
all {
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
targets 'libwg-go.so', 'libwg.so', 'libwg-quick.so'
|
||||
arguments "-DGRADLE_USER_HOME=${project.gradle.gradleUserHomeDir}"
|
||||
}
|
||||
}
|
||||
}
|
||||
release {
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_PACKAGE_NAME=${groupName}", "-DGRADLE_USER_HOME=${project.gradle.gradleUserHomeDir}"
|
||||
arguments "-DANDROID_PACKAGE_NAME=${groupName}"
|
||||
}
|
||||
}
|
||||
}
|
||||
debug {
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_PACKAGE_NAME=${groupName}.debug", "-DGRADLE_USER_HOME=${project.gradle.gradleUserHomeDir}"
|
||||
arguments "-DANDROID_PACKAGE_NAME=${groupName}.debug"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,5 +28,16 @@ add_custom_target(libwg-go.so WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib
|
||||
DESTDIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
BUILDDIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/../generated-src
|
||||
)
|
||||
# Hack to make it actually build as part of the default target
|
||||
add_dependencies(libwg.so libwg-go.so)
|
||||
|
||||
# Strip unwanted ELF sections to prevent DT_FLAGS_1 warnings on old Android versions
|
||||
file(GLOB ELF_CLEANER_SOURCES elf-cleaner/*.c elf-cleaner/*.cpp)
|
||||
add_custom_target(elf-cleaner COMMENT "Building elf-cleaner" VERBATIM COMMAND "${CMAKE_C_COMPILER}"
|
||||
-O2 -DPACKAGE_NAME="elf-cleaner" -DPACKAGE_VERSION="" -DCOPYRIGHT=""
|
||||
-o "${CMAKE_CURRENT_BINARY_DIR}/elf-cleaner" ${ELF_CLEANER_SOURCES}
|
||||
)
|
||||
add_custom_command(TARGET libwg.so POST_BUILD VERBATIM COMMAND "${CMAKE_CURRENT_BINARY_DIR}/elf-cleaner"
|
||||
--api-level "${ANDROID_NATIVE_API_LEVEL}" "$<TARGET_FILE:libwg.so>")
|
||||
add_dependencies(libwg.so elf-cleaner)
|
||||
add_custom_command(TARGET libwg-quick.so POST_BUILD VERBATIM COMMAND "${CMAKE_CURRENT_BINARY_DIR}/elf-cleaner"
|
||||
--api-level "${ANDROID_NATIVE_API_LEVEL}" "$<TARGET_FILE:libwg-quick.so>")
|
||||
add_dependencies(libwg-quick.so elf-cleaner)
|
||||
|
||||
Submodule
+1
Submodule tunnel/tools/elf-cleaner added at 7efc050906
@@ -1,4 +1,4 @@
|
||||
From 729c58cb1c0496497dac6de3d0bf540f6149618f Mon Sep 17 00:00:00 2001
|
||||
From 61f3ae8298d1c503cbc31539e0f3a73446c7db9d Mon Sep 17 00:00:00 2001
|
||||
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
|
||||
Date: Tue, 21 Mar 2023 15:33:56 +0100
|
||||
Subject: [PATCH] [release-branch.go1.20] runtime: use CLOCK_BOOTTIME in
|
||||
@@ -22,11 +22,11 @@ Change-Id: I7b2a6ca0c5bc5fce57ec0eeafe7b68270b429321
|
||||
src/runtime/sys_linux_amd64.s | 2 +-
|
||||
src/runtime/sys_linux_arm.s | 4 ++--
|
||||
src/runtime/sys_linux_arm64.s | 4 ++--
|
||||
src/runtime/sys_linux_mips64x.s | 2 +-
|
||||
src/runtime/sys_linux_mips64x.s | 4 ++--
|
||||
src/runtime/sys_linux_mipsx.s | 2 +-
|
||||
src/runtime/sys_linux_ppc64x.s | 2 +-
|
||||
src/runtime/sys_linux_s390x.s | 2 +-
|
||||
8 files changed, 11 insertions(+), 11 deletions(-)
|
||||
8 files changed, 12 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/src/runtime/sys_linux_386.s b/src/runtime/sys_linux_386.s
|
||||
index 12a294153d..17e3524b40 100644
|
||||
@@ -106,7 +106,7 @@ index 38ff6ac330..6b819c5441 100644
|
||||
CBZ R2, fallback
|
||||
|
||||
diff --git a/src/runtime/sys_linux_mips64x.s b/src/runtime/sys_linux_mips64x.s
|
||||
index 47f2da524d..6c1a9a2801 100644
|
||||
index 47f2da524d..a8b387f193 100644
|
||||
--- a/src/runtime/sys_linux_mips64x.s
|
||||
+++ b/src/runtime/sys_linux_mips64x.s
|
||||
@@ -326,7 +326,7 @@ noswitch:
|
||||
@@ -118,6 +118,15 @@ index 47f2da524d..6c1a9a2801 100644
|
||||
MOVV $0(R29), R5
|
||||
|
||||
MOVV runtime·vdsoClockgettimeSym(SB), R25
|
||||
@@ -336,7 +336,7 @@ noswitch:
|
||||
// see walltime for detail
|
||||
BEQ R2, R0, finish
|
||||
MOVV R0, runtime·vdsoClockgettimeSym(SB)
|
||||
- MOVW $1, R4 // CLOCK_MONOTONIC
|
||||
+ MOVW $7, R4 // CLOCK_BOOTTIME
|
||||
MOVV $0(R29), R5
|
||||
JMP fallback
|
||||
|
||||
diff --git a/src/runtime/sys_linux_mipsx.s b/src/runtime/sys_linux_mipsx.s
|
||||
index 5e6b6c1504..7f5fd2a80e 100644
|
||||
--- a/src/runtime/sys_linux_mipsx.s
|
||||
@@ -158,5 +167,5 @@ index 1448670b91..7d2ee3231c 100644
|
||||
MOVD R15, R7 // Backup stack pointer
|
||||
|
||||
--
|
||||
2.40.0
|
||||
2.17.1
|
||||
|
||||
|
||||
@@ -48,9 +48,6 @@ android {
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
all {
|
||||
buildConfigField('boolean', 'IS_GOOGLE_PLAY', (findProperty('build.google_play') == 'true').toString())
|
||||
}
|
||||
release {
|
||||
if (keystorePropertiesFile.exists()) signingConfig signingConfigs.release
|
||||
minifyEnabled true
|
||||
|
||||
@@ -17,8 +17,6 @@ import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.preferencesDataStoreFile
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import com.google.android.material.color.DynamicColorsOptions
|
||||
import com.wireguard.android.activity.TvMainActivity
|
||||
import com.wireguard.android.backend.Backend
|
||||
import com.wireguard.android.backend.GoBackend
|
||||
import com.wireguard.android.backend.WgQuickBackend
|
||||
@@ -37,6 +35,7 @@ import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.Locale
|
||||
|
||||
@@ -89,17 +88,24 @@ class Application : android.app.Application() {
|
||||
override fun onCreate() {
|
||||
Log.i(TAG, USER_AGENT)
|
||||
super.onCreate()
|
||||
DynamicColors.applyToActivitiesIfAvailable(this,
|
||||
// TODO: Remove this second argument once the TV theme has a proper M3 color palette.
|
||||
DynamicColorsOptions.Builder().setPrecondition { activity, _ -> activity !is TvMainActivity }.build())
|
||||
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||
rootShell = RootShell(applicationContext)
|
||||
toolsInstaller = ToolsInstaller(applicationContext, rootShell)
|
||||
preferencesDataStore = PreferenceDataStoreFactory.create { applicationContext.preferencesDataStoreFile("settings") }
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
coroutineScope.launch {
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
if (UserKnobs.darkTheme.first()) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO)
|
||||
runBlocking {
|
||||
AppCompatDelegate.setDefaultNightMode(if (UserKnobs.darkTheme.first()) AppCompatDelegate.MODE_NIGHT_YES else AppCompatDelegate.MODE_NIGHT_NO)
|
||||
}
|
||||
UserKnobs.darkTheme.onEach {
|
||||
val newMode = if (it) {
|
||||
AppCompatDelegate.MODE_NIGHT_YES
|
||||
} else {
|
||||
AppCompatDelegate.MODE_NIGHT_NO
|
||||
}
|
||||
if (AppCompatDelegate.getDefaultNightMode() != newMode) {
|
||||
AppCompatDelegate.setDefaultNightMode(newMode)
|
||||
}
|
||||
}.launchIn(coroutineScope)
|
||||
} else {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package com.wireguard.android.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.databinding.CallbackRegistry
|
||||
import androidx.databinding.CallbackRegistry.NotifierCallback
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -15,15 +16,21 @@ import kotlinx.coroutines.launch
|
||||
/**
|
||||
* Base class for activities that need to remember the currently-selected tunnel.
|
||||
*/
|
||||
abstract class BaseActivity : ThemeChangeAwareActivity() {
|
||||
abstract class BaseActivity : AppCompatActivity() {
|
||||
private val selectionChangeRegistry = SelectionChangeRegistry()
|
||||
private var created = false
|
||||
var selectedTunnel: ObservableTunnel? = null
|
||||
set(value) {
|
||||
val oldTunnel = field
|
||||
if (oldTunnel == value) return
|
||||
field = value
|
||||
onSelectedTunnelChanged(oldTunnel, value)
|
||||
selectionChangeRegistry.notifyCallbacks(oldTunnel, 0, value)
|
||||
if (created) {
|
||||
if (!onSelectedTunnelChanged(oldTunnel, value)) {
|
||||
field = oldTunnel
|
||||
} else {
|
||||
selectionChangeRegistry.notifyCallbacks(oldTunnel, 0, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addOnSelectedTunnelChangedListener(listener: OnSelectedTunnelChangedListener) {
|
||||
@@ -39,10 +46,17 @@ abstract class BaseActivity : ThemeChangeAwareActivity() {
|
||||
intent != null -> intent.getStringExtra(KEY_SELECTED_TUNNEL)
|
||||
else -> null
|
||||
}
|
||||
if (savedTunnelName != null)
|
||||
if (savedTunnelName != null) {
|
||||
lifecycleScope.launch {
|
||||
selectedTunnel = Application.getTunnelManager().getTunnels()[savedTunnelName]
|
||||
val tunnel = Application.getTunnelManager().getTunnels()[savedTunnelName]
|
||||
if (tunnel == null)
|
||||
created = true
|
||||
selectedTunnel = tunnel
|
||||
created = true
|
||||
}
|
||||
} else {
|
||||
created = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
@@ -50,7 +64,7 @@ abstract class BaseActivity : ThemeChangeAwareActivity() {
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
protected abstract fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?)
|
||||
protected abstract fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?): Boolean
|
||||
|
||||
fun removeOnSelectedTunnelChangedListener(
|
||||
listener: OnSelectedTunnelChangedListener) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.collection.CircularArray
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -62,8 +63,8 @@ import java.util.regex.Pattern
|
||||
class LogViewerActivity : AppCompatActivity() {
|
||||
private lateinit var binding: LogViewerActivityBinding
|
||||
private lateinit var logAdapter: LogEntryAdapter
|
||||
private var logLines = arrayListOf<LogLine>()
|
||||
private var rawLogLines = StringBuffer()
|
||||
private var logLines = CircularArray<LogLine>()
|
||||
private var rawLogLines = CircularArray<String>()
|
||||
private var recyclerView: RecyclerView? = null
|
||||
private var saveButton: MenuItem? = null
|
||||
private val year by lazy {
|
||||
@@ -113,7 +114,7 @@ class LogViewerActivity : AppCompatActivity() {
|
||||
binding.shareFab.setOnClickListener {
|
||||
revokeLastUri()
|
||||
val key = KeyPair().privateKey.toHex()
|
||||
LOGS[key] = rawLogLines.toString().toByteArray(Charsets.UTF_8)
|
||||
LOGS[key] = rawLogBytes()
|
||||
lastUri = Uri.parse("content://${BuildConfig.APPLICATION_ID}.exported-log/$key")
|
||||
val shareIntent = ShareCompat.IntentBuilder(this)
|
||||
.setType("text/plain")
|
||||
@@ -150,13 +151,24 @@ class LogViewerActivity : AppCompatActivity() {
|
||||
|
||||
private val downloadsFileSaver = DownloadsFileSaver(this)
|
||||
|
||||
private fun rawLogBytes() : ByteArray {
|
||||
val builder = StringBuilder()
|
||||
for (i in 0 until rawLogLines.size()) {
|
||||
builder.append(rawLogLines[i])
|
||||
builder.append('\n')
|
||||
}
|
||||
val ret = builder.toString().toByteArray(Charsets.UTF_8)
|
||||
builder.clear()
|
||||
return ret
|
||||
}
|
||||
|
||||
private suspend fun saveLog() {
|
||||
var exception: Throwable? = null
|
||||
var outputFile: DownloadsFileSaver.DownloadsFile? = null
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
outputFile = downloadsFileSaver.save("wireguard-log.txt", "text/plain", true)
|
||||
outputFile?.outputStream?.write(rawLogLines.toString().toByteArray(Charsets.UTF_8))
|
||||
outputFile?.outputStream?.write(rawLogBytes())
|
||||
} catch (e: Throwable) {
|
||||
outputFile?.delete()
|
||||
exception = e
|
||||
@@ -191,24 +203,27 @@ class LogViewerActivity : AppCompatActivity() {
|
||||
var priorModified = false
|
||||
val bufferedLogLines = arrayListOf<LogLine>()
|
||||
var timeout = 1000000000L / 2 // The timeout is initially small so that the view gets populated immediately.
|
||||
val MAX_LINES = (1 shl 17) - 1
|
||||
val MAX_BUFFERED_LINES = (1 shl 14) - 1
|
||||
|
||||
while (true) {
|
||||
val line = stdout.readLine() ?: break
|
||||
rawLogLines.append(line)
|
||||
rawLogLines.append('\n')
|
||||
if (rawLogLines.size() >= MAX_LINES)
|
||||
rawLogLines.popFirst()
|
||||
rawLogLines.addLast(line)
|
||||
val logLine = parseLine(line)
|
||||
if (logLine != null) {
|
||||
bufferedLogLines.add(logLine)
|
||||
} else {
|
||||
if (bufferedLogLines.isNotEmpty()) {
|
||||
bufferedLogLines.last().msg += "\n$line"
|
||||
} else if (logLines.isNotEmpty()) {
|
||||
logLines.last().msg += "\n$line"
|
||||
} else if (!logLines.isEmpty) {
|
||||
logLines[logLines.size() - 1].msg += "\n$line"
|
||||
priorModified = true
|
||||
}
|
||||
}
|
||||
val timeNow = System.nanoTime()
|
||||
if ((timeNow - timeLastNotify) < timeout && stdout.ready())
|
||||
if (bufferedLogLines.size < MAX_BUFFERED_LINES && (timeNow - timeLastNotify) < timeout && stdout.ready())
|
||||
continue
|
||||
timeout = 1000000000L * 5 / 2 // Increase the timeout after the initial view has something in it.
|
||||
timeLastNotify = timeNow
|
||||
@@ -219,13 +234,23 @@ class LogViewerActivity : AppCompatActivity() {
|
||||
logAdapter.notifyItemChanged(posStart - 1)
|
||||
priorModified = false
|
||||
}
|
||||
logLines.addAll(bufferedLogLines)
|
||||
val fullLen = logLines.size() + bufferedLogLines.size
|
||||
if (fullLen >= MAX_LINES) {
|
||||
val numToRemove = fullLen - MAX_LINES + 1
|
||||
logLines.removeFromStart(numToRemove)
|
||||
logAdapter.notifyItemRangeRemoved(0, numToRemove)
|
||||
posStart -= numToRemove
|
||||
|
||||
}
|
||||
for (bufferedLine in bufferedLogLines) {
|
||||
logLines.addLast(bufferedLine)
|
||||
}
|
||||
bufferedLogLines.clear()
|
||||
logAdapter.notifyItemRangeInserted(posStart, logLines.size - posStart)
|
||||
posStart = logLines.size
|
||||
logAdapter.notifyItemRangeInserted(posStart, logLines.size() - posStart)
|
||||
posStart = logLines.size()
|
||||
|
||||
if (isScrolledToBottomAlready) {
|
||||
recyclerView?.scrollToPosition(logLines.size - 1)
|
||||
recyclerView?.scrollToPosition(logLines.size() - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,7 +304,7 @@ class LogViewerActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = logLines.size
|
||||
override fun getItemCount() = logLines.size()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
|
||||
@@ -96,13 +96,17 @@ class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener
|
||||
}
|
||||
|
||||
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?,
|
||||
newTunnel: ObservableTunnel?) {
|
||||
newTunnel: ObservableTunnel?): Boolean {
|
||||
val fragmentManager = supportFragmentManager
|
||||
if (fragmentManager.isStateSaved) {
|
||||
return false
|
||||
}
|
||||
|
||||
val backStackEntries = fragmentManager.backStackEntryCount
|
||||
if (newTunnel == null) {
|
||||
// Clear everything off the back stack (all editors and detail fragments).
|
||||
fragmentManager.popBackStackImmediate(0, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
return
|
||||
return true
|
||||
}
|
||||
if (backStackEntries == 2) {
|
||||
// Pop the editor off the back stack to reveal the detail fragment. Use the immediate
|
||||
@@ -116,5 +120,6 @@ class MainActivity : BaseActivity(), FragmentManager.OnBackStackChangedListener
|
||||
addToBackStack(null)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.Preference
|
||||
@@ -24,7 +25,7 @@ import kotlinx.coroutines.withContext
|
||||
/**
|
||||
* Interface for changing application-global persistent settings.
|
||||
*/
|
||||
class SettingsActivity : ThemeChangeAwareActivity() {
|
||||
class SettingsActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (supportFragmentManager.findFragmentById(android.R.id.content) == null) {
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package com.wireguard.android.activity
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.wireguard.android.util.UserKnobs
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
abstract class ThemeChangeAwareActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
UserKnobs.darkTheme.onEach {
|
||||
val newMode = if (it) {
|
||||
AppCompatDelegate.MODE_NIGHT_YES
|
||||
} else {
|
||||
AppCompatDelegate.MODE_NIGHT_NO
|
||||
}
|
||||
if (AppCompatDelegate.getDefaultNightMode() != newMode) {
|
||||
AppCompatDelegate.setDefaultNightMode(newMode)
|
||||
recreate()
|
||||
}
|
||||
}.launchIn(lifecycleScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,8 @@ class TunnelCreatorActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?) {
|
||||
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?): Boolean {
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
package com.wireguard.android.activity
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
@@ -19,6 +22,7 @@ import android.widget.Toast
|
||||
import androidx.activity.addCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.view.forEach
|
||||
@@ -29,6 +33,7 @@ import androidx.databinding.ObservableField
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.wireguard.android.Application
|
||||
import com.wireguard.android.R
|
||||
import com.wireguard.android.backend.GoBackend
|
||||
@@ -43,6 +48,8 @@ import com.wireguard.android.model.ObservableTunnel
|
||||
import com.wireguard.android.util.ErrorMessages
|
||||
import com.wireguard.android.util.QuantityFormatter
|
||||
import com.wireguard.android.util.TunnelImporter
|
||||
import com.wireguard.android.util.UserKnobs
|
||||
import com.wireguard.android.util.applicationScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -50,7 +57,27 @@ import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class TvMainActivity : AppCompatActivity() {
|
||||
private val tunnelFileImportResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { data ->
|
||||
private val tunnelFileImportResultLauncher = registerForActivityResult(object : ActivityResultContracts.GetContent() {
|
||||
override fun createIntent(context: Context, input: String): Intent {
|
||||
val intent = super.createIntent(context, input)
|
||||
|
||||
/* AndroidTV now comes with stubs that do nothing but display a Toast less helpful than
|
||||
* what we can do, so detect this and throw an exception that we can catch later. */
|
||||
val activitiesToResolveIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
context.packageManager.queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()))
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
context.packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||
}
|
||||
if (activitiesToResolveIntent.all {
|
||||
val name = it.activityInfo.packageName
|
||||
name.startsWith("com.google.android.tv.frameworkpackagestubs") || name.startsWith("com.android.tv.frameworkpackagestubs")
|
||||
}) {
|
||||
throw ActivityNotFoundException()
|
||||
}
|
||||
return intent
|
||||
}
|
||||
}) { data ->
|
||||
if (data == null) return@registerForActivityResult
|
||||
lifecycleScope.launch {
|
||||
TunnelImporter.importTunnel(contentResolver, data) {
|
||||
@@ -86,6 +113,14 @@ class TvMainActivity : AppCompatActivity() {
|
||||
private val filesRoot = ObservableField("")
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (AppCompatDelegate.getDefaultNightMode() != AppCompatDelegate.MODE_NIGHT_YES) {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
applicationScope.launch {
|
||||
UserKnobs.setDarkTheme(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = TvActivityBinding.inflate(layoutInflater)
|
||||
lifecycleScope.launch {
|
||||
@@ -176,7 +211,12 @@ class TvMainActivity : AppCompatActivity() {
|
||||
try {
|
||||
tunnelFileImportResultLauncher.launch("*/*")
|
||||
} catch (_: Throwable) {
|
||||
Toast.makeText(this@TvMainActivity, getString(R.string.tv_no_file_picker), Toast.LENGTH_LONG).show()
|
||||
MaterialAlertDialogBuilder(binding.root.context).setMessage(R.string.tv_no_file_picker).setCancelable(false).setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
try {
|
||||
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://webstoreredirect")))
|
||||
} catch (_: Throwable) {
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.wireguard.android.R
|
||||
import com.wireguard.android.databinding.ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler
|
||||
import com.wireguard.android.widget.ToggleSwitch
|
||||
import com.wireguard.android.widget.ToggleSwitch.OnBeforeCheckedChangeListener
|
||||
import com.wireguard.android.widget.TvCardView
|
||||
import com.wireguard.config.Attribute
|
||||
import com.wireguard.config.InetNetwork
|
||||
import java.net.InetAddress
|
||||
@@ -168,4 +169,16 @@ object BindingAdapters {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("isUp")
|
||||
fun setIsUp(card: TvCardView, up: Boolean) {
|
||||
card.isUp = up
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@BindingAdapter("isDeleting")
|
||||
fun setIsDeleting(card: TvCardView, deleting: Boolean) {
|
||||
card.isDeleting = deleting
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,12 +67,20 @@ abstract class BaseFragment : Fragment(), OnSelectedTunnelChangedListener {
|
||||
val activity = activity ?: return
|
||||
activity.lifecycleScope.launch {
|
||||
if (Application.getBackend() is GoBackend) {
|
||||
val intent = GoBackend.VpnService.prepare(activity)
|
||||
if (intent != null) {
|
||||
pendingTunnel = tunnel
|
||||
pendingTunnelUp = checked
|
||||
permissionActivityResultLauncher.launch(intent)
|
||||
return@launch
|
||||
try {
|
||||
val intent = GoBackend.VpnService.prepare(activity)
|
||||
if (intent != null) {
|
||||
pendingTunnel = tunnel
|
||||
pendingTunnelUp = checked
|
||||
permissionActivityResultLauncher.launch(intent)
|
||||
return@launch
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
val message = activity.getString(R.string.error_prepare, ErrorMessages[e])
|
||||
Snackbar.make(view, message, Snackbar.LENGTH_LONG)
|
||||
.setAnchorView(view.findViewById(R.id.create_fab))
|
||||
.show()
|
||||
Log.e(TAG, message, e)
|
||||
}
|
||||
}
|
||||
setTunnelStateWithPermissionsResult(tunnel, checked)
|
||||
|
||||
@@ -115,7 +115,7 @@ class TunnelListFragment : BaseFragment() {
|
||||
}
|
||||
}
|
||||
}
|
||||
bottomSheet.show(childFragmentManager, "BOTTOM_SHEET")
|
||||
bottomSheet.showNow(childFragmentManager, "BOTTOM_SHEET")
|
||||
}
|
||||
executePendingBindings()
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@
|
||||
|
||||
package com.wireguard.android.preference
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.widget.Toast
|
||||
import androidx.preference.Preference
|
||||
import com.wireguard.android.BuildConfig
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.wireguard.android.R
|
||||
import com.wireguard.android.util.ErrorMessages
|
||||
|
||||
class DonatePreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
|
||||
override fun getSummary() = context.getString(R.string.donate_summary)
|
||||
@@ -21,18 +22,34 @@ class DonatePreference(context: Context, attrs: AttributeSet?) : Preference(cont
|
||||
override fun getTitle() = context.getString(R.string.donate_title)
|
||||
|
||||
override fun onClick() {
|
||||
if (BuildConfig.IS_GOOGLE_PLAY) {
|
||||
AlertDialog.Builder(context)
|
||||
val installer = try {
|
||||
val packageName = context.packageName
|
||||
val pm = context.packageManager
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
pm.getInstallSourceInfo(packageName).installingPackageName ?: ""
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
pm.getInstallerPackageName(packageName) ?: ""
|
||||
}
|
||||
} catch (_: Throwable) {
|
||||
""
|
||||
}
|
||||
|
||||
/* Google Play Store forbids links to our donation page. */
|
||||
if (installer == "com.android.vending") {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.donate_title)
|
||||
.setMessage(R.string.donate_google_play_disappointment)
|
||||
.show()
|
||||
return
|
||||
}
|
||||
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = Uri.parse("https://www.wireguard.com/donations/")
|
||||
try {
|
||||
context.startActivity(intent)
|
||||
} catch (ignored: ActivityNotFoundException) {
|
||||
} catch (e: Throwable) {
|
||||
Toast.makeText(context, ErrorMessages[e], Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
*/
|
||||
package com.wireguard.android.preference
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.util.AttributeSet
|
||||
import android.widget.Toast
|
||||
import androidx.preference.Preference
|
||||
import com.wireguard.android.Application
|
||||
import com.wireguard.android.BuildConfig
|
||||
@@ -16,6 +16,7 @@ import com.wireguard.android.R
|
||||
import com.wireguard.android.backend.Backend
|
||||
import com.wireguard.android.backend.GoBackend
|
||||
import com.wireguard.android.backend.WgQuickBackend
|
||||
import com.wireguard.android.util.ErrorMessages
|
||||
import com.wireguard.android.util.lifecycleScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -33,7 +34,8 @@ class VersionPreference(context: Context, attrs: AttributeSet?) : Preference(con
|
||||
intent.data = Uri.parse("https://www.wireguard.com/")
|
||||
try {
|
||||
context.startActivity(intent)
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
} catch (e: Throwable) {
|
||||
Toast.makeText(context, ErrorMessages[e], Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,12 @@ object UserKnobs {
|
||||
it[DARK_THEME] ?: false
|
||||
}
|
||||
|
||||
suspend fun setDarkTheme(on: Boolean) {
|
||||
Application.getPreferencesDataStore().edit {
|
||||
it[DARK_THEME] = on
|
||||
}
|
||||
}
|
||||
|
||||
private val ALLOW_REMOTE_CONTROL_INTENTS = booleanPreferencesKey("allow_remote_control_intents")
|
||||
val allowRemoteControlIntents: Flow<Boolean>
|
||||
get() = Application.getPreferencesDataStore().data.map {
|
||||
|
||||
@@ -4,22 +4,27 @@
|
||||
*/
|
||||
package com.wireguard.android.viewmodel
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import androidx.core.os.ParcelCompat
|
||||
import androidx.databinding.ObservableArrayList
|
||||
import androidx.databinding.ObservableList
|
||||
import com.wireguard.config.BadConfigException
|
||||
import com.wireguard.config.Config
|
||||
import com.wireguard.config.Peer
|
||||
import java.util.ArrayList
|
||||
|
||||
class ConfigProxy : Parcelable {
|
||||
val `interface`: InterfaceProxy
|
||||
val peers: ObservableList<PeerProxy> = ObservableArrayList()
|
||||
|
||||
private constructor(parcel: Parcel) {
|
||||
`interface` = InterfaceProxy.CREATOR.createFromParcel(parcel)
|
||||
parcel.readTypedList(peers, PeerProxy.CREATOR)
|
||||
`interface` = ParcelCompat.readParcelable(parcel, InterfaceProxy::class.java.classLoader, InterfaceProxy::class.java) ?: InterfaceProxy()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
ParcelCompat.readParcelableList(parcel, peers, PeerProxy::class.java.classLoader, PeerProxy::class.java)
|
||||
} else {
|
||||
parcel.readTypedList(peers, PeerProxy.CREATOR)
|
||||
}
|
||||
peers.forEach { it.bind(this) }
|
||||
}
|
||||
|
||||
@@ -57,7 +62,11 @@ class ConfigProxy : Parcelable {
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeParcelable(`interface`, flags)
|
||||
dest.writeTypedList(peers)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
dest.writeParcelableList(peers, flags)
|
||||
} else {
|
||||
dest.writeTypedList(peers)
|
||||
}
|
||||
}
|
||||
|
||||
private class ConfigProxyCreator : Parcelable.Creator<ConfigProxy> {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.wireguard.android.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.wireguard.android.R
|
||||
|
||||
class TvCardView(context: Context?, attrs: AttributeSet?) : MaterialCardView(context, attrs) {
|
||||
var isUp: Boolean = false
|
||||
set(value) {
|
||||
field = value
|
||||
refreshDrawableState()
|
||||
}
|
||||
var isDeleting: Boolean = false
|
||||
set(value) {
|
||||
field = value
|
||||
refreshDrawableState()
|
||||
}
|
||||
|
||||
override fun onCreateDrawableState(extraSpace: Int): IntArray {
|
||||
if (isUp || isDeleting) {
|
||||
val drawableState = super.onCreateDrawableState(extraSpace + (if (isUp) 1 else 0) + (if (isDeleting) 1 else 0))
|
||||
if (isUp) {
|
||||
View.mergeDrawableStates(drawableState, STATE_IS_UP)
|
||||
}
|
||||
if (isDeleting) {
|
||||
View.mergeDrawableStates(drawableState, STATE_IS_DELETING)
|
||||
}
|
||||
return drawableState
|
||||
}
|
||||
return super.onCreateDrawableState(extraSpace)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val STATE_IS_UP = intArrayOf(R.attr.state_isUp)
|
||||
private val STATE_IS_DELETING = intArrayOf(R.attr.state_isDeleting)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item app:state_isUp="true" app:state_isDeleting="false" android:color="?attr/colorPrimaryInverse" />
|
||||
<item android:state_focused="true" app:state_isDeleting="true" android:color="?attr/colorErrorContainer" />
|
||||
<item android:color="?attr/colorOnSurfaceInverse" />
|
||||
</selector>
|
||||
@@ -6,7 +6,7 @@
|
||||
<item
|
||||
android:state_activated="true"
|
||||
app:state_multiselected="true">
|
||||
<color android:color="?attr/colorMultiselectActiveBackground" />
|
||||
<color android:color="?attr/colorSurfaceVariant" />
|
||||
</item>
|
||||
<item
|
||||
android:state_activated="true">
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/import_button"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
@@ -135,13 +135,12 @@
|
||||
android:visibility="@{isDeleting ? View.GONE : View.VISIBLE}"
|
||||
app:icon="@{filesRoot.isEmpty ? @drawable/ic_action_add_white : @drawable/ic_arrow_back}"
|
||||
app:iconPadding="0dp"
|
||||
app:iconTint="?attr/colorOnPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/delete_button"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
@@ -149,7 +148,6 @@
|
||||
android:visibility="@{((tunnels.isEmpty && !isDeleting) || !filesRoot.isEmpty) ? View.GONE : View.VISIBLE}"
|
||||
app:icon="@{isDeleting ? @drawable/ic_arrow_back : @drawable/ic_action_delete}"
|
||||
app:iconPadding="0dp"
|
||||
app:iconTint="?attr/colorOnPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
android:layout_margin="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:backgroundTint="@color/tv_card_background"
|
||||
android:checkable="true"
|
||||
android:focusable="true"
|
||||
app:contentPadding="8dp">
|
||||
@@ -35,7 +34,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{key}"
|
||||
android:textAppearance="?attr/textAppearanceTitleLarge"
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
@@ -28,16 +28,18 @@
|
||||
type="com.wireguard.android.model.ObservableTunnel" />
|
||||
</data>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
<com.wireguard.android.widget.TvCardView
|
||||
android:layout_width="225dp"
|
||||
android:layout_height="110dp"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:backgroundTint="@color/tv_list_item_tint"
|
||||
android:checkable="true"
|
||||
android:focusable="true"
|
||||
app:contentPadding="8dp"
|
||||
android:backgroundTint="@{(item.state == State.UP && !isDeleting) ? @color/tv_secondary_dark_color : (isDeleting && isFocused) ? @color/tv_card_delete_background : @color/tv_card_background}">
|
||||
app:isDeleting="@{isDeleting}"
|
||||
app:isUp="@{item.state == State.UP}">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -49,7 +51,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{item.name}"
|
||||
android:textAppearance="?attr/textAppearanceTitleLarge"
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@sample/interface_names.json/names/names/name" />
|
||||
@@ -76,6 +77,6 @@
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</com.wireguard.android.widget.TvCardView>
|
||||
|
||||
</layout>
|
||||
|
||||
@@ -107,6 +107,8 @@
|
||||
<string name="tv_select_a_storage_drive">Sélectionner un disque de stockage</string>
|
||||
<string name="tv_no_file_picker">Veuillez installer un utilitaire de gestion de fichiers pour parcourir les fichiers</string>
|
||||
<string name="tv_add_tunnel_get_started">Ajouter un tunnel pour commencer</string>
|
||||
<string name="donate_title">♥ Faire un don au projet WireGuard</string>
|
||||
<string name="donate_summary">Chaque contribution aide</string>
|
||||
<string name="disable_config_export_title">Désactiver l\'export de configuration</string>
|
||||
<string name="disable_config_export_description">La désactivation de l\'export de configuration rend les clés privées moins accessibles</string>
|
||||
<string name="dns_servers">Serveurs DNS</string>
|
||||
@@ -213,6 +215,7 @@
|
||||
<string name="tunnel_create_success">Tunnel «%s » créé avec succès</string>
|
||||
<string name="tunnel_error_already_exists">Le tunnel « %s » existe déjà</string>
|
||||
<string name="tunnel_error_invalid_name">Nom invalide</string>
|
||||
<string name="tunnel_list_placeholder">Ajoutez un tunnel en utilisant le bouton ci-dessous</string>
|
||||
<string name="tunnel_name">Nom du tunnel</string>
|
||||
<string name="tunnel_on_error">Impossible d\'activer le tunnel (wgTurnOn a retourné %d)</string>
|
||||
<string name="tunnel_dns_failure">Impossible de résoudre le nom d\'hôte DNS: “%s”</string>
|
||||
|
||||
@@ -27,6 +27,5 @@
|
||||
<item name="colorOnSurfaceInverse">@color/md_theme_dark_inverseOnSurface</item>
|
||||
<item name="colorSurfaceInverse">@color/md_theme_dark_inverseSurface</item>
|
||||
<item name="colorPrimaryInverse">@color/md_theme_dark_inversePrimary</item>
|
||||
<item name="colorMultiselectActiveBackground">@color/md_theme_dark_surfaceVariant</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -107,6 +107,9 @@
|
||||
<string name="tv_select_a_storage_drive">Bir depolama sürücüsü seçin</string>
|
||||
<string name="tv_no_file_picker">Dosyalara göz atmak için lütfen bir dosya yönetim aracı yükleyin</string>
|
||||
<string name="tv_add_tunnel_get_started">Başlamak için bir tünel ekleyin</string>
|
||||
<string name="donate_title">♥ WireGuard Projesine Bağış Yapın</string>
|
||||
<string name="donate_summary">Her katkı yardımcı olur</string>
|
||||
<string name="donate_google_play_disappointment">WireGuard Projesini desteklediğiniz için teşekkür ederiz!\n\nNe yazık ki, Google\'ın politikaları nedeniyle, proje web sayfasının bağış yapabileceğiniz bölümüne bağlantı vermemize izin verilmiyor. Umarım bunu çözebilirsin!\n\nKatkılarınız için tekrar teşekkürler.</string>
|
||||
<string name="disable_config_export_title">Ayarları dışa aktarmayı kapa</string>
|
||||
<string name="disable_config_export_description">Ayarları dışa aktarmayı kapamak gizli anahtarları daha az erişilebilir kılar</string>
|
||||
<string name="dns_servers">DNS sunucuları</string>
|
||||
@@ -213,6 +216,7 @@
|
||||
<string name="tunnel_create_success">Tünel başarıyla oluşturuldu “%s”</string>
|
||||
<string name="tunnel_error_already_exists">“%s” tüneli zaten mevcut</string>
|
||||
<string name="tunnel_error_invalid_name">Geçersiz isim</string>
|
||||
<string name="tunnel_list_placeholder">Aşağıdaki düğmeyi kullanarak bir tünel ekleyin</string>
|
||||
<string name="tunnel_name">Tünel İsmi</string>
|
||||
<string name="tunnel_on_error">Tünel aktif edilemiyor (wgTornOn döndürdü: %d)</string>
|
||||
<string name="tunnel_dns_failure">DNS adı çözülemedi: \"%s\"</string>
|
||||
|
||||
@@ -94,6 +94,9 @@
|
||||
<string name="tv_select_a_storage_drive">选择一个存储驱动器</string>
|
||||
<string name="tv_no_file_picker">请安装一个文件管理工具以浏览文件</string>
|
||||
<string name="tv_add_tunnel_get_started">添加第一条网络隧道</string>
|
||||
<string name="donate_title">♥ 为 WireGuard 捐赠</string>
|
||||
<string name="donate_summary">无论多寡,聚沙成塔</string>
|
||||
<string name="donate_google_play_disappointment">感谢您对 WireGuard 项目的支持!\n\n只可惜,受谷歌的政策所限,我们不能在此展示项目捐赠页面的链接,还望您自行访问捐赠页面!\n\n再次感谢您的贡献。</string>
|
||||
<string name="disable_config_export_title">禁止导出配置</string>
|
||||
<string name="disable_config_export_description">禁止导出配置可降低私钥泄露的风险</string>
|
||||
<string name="dns_servers">DNS 服务器</string>
|
||||
@@ -132,7 +135,7 @@
|
||||
<string name="log_viewer_pref_title">查看应用日志</string>
|
||||
<string name="log_viewer_title">日志</string>
|
||||
<string name="logcat_error">无法运行 logcat: </string>
|
||||
<string name="module_enabler_disabled_summary">WireGuard-Andorid 的内核模块尚处于实验阶段。它能够增强性能,但存在潜在的不稳定因素,请谨慎开启</string>
|
||||
<string name="module_enabler_disabled_summary">内核模块(实验性)能够增强性能,启用时需谨慎</string>
|
||||
<string name="module_enabler_disabled_title">启用内核模块</string>
|
||||
<string name="module_enabler_enabled_summary">用户空间的模块性能较弱,但稳定性更好</string>
|
||||
<string name="module_enabler_enabled_title">停用内核模块</string>
|
||||
@@ -200,6 +203,7 @@
|
||||
<string name="tunnel_create_success">成功创建隧道 “%s”</string>
|
||||
<string name="tunnel_error_already_exists">名称 “%s” 已存在</string>
|
||||
<string name="tunnel_error_invalid_name">名称无效</string>
|
||||
<string name="tunnel_list_placeholder">点击下方按钮添加隧道</string>
|
||||
<string name="tunnel_name">隧道名称</string>
|
||||
<string name="tunnel_on_error">无法开启隧道(wgTurnOn returned %d)</string>
|
||||
<string name="tunnel_dns_failure">无法解析 DNS 主机名:“%s”</string>
|
||||
@@ -208,7 +212,7 @@
|
||||
<string name="type_name_go_userspace">Go userspace</string>
|
||||
<string name="type_name_kernel_module">Kernel module</string>
|
||||
<string name="unknown_error">未知错误</string>
|
||||
<string name="version_summary">%1$s 后端 %2$s</string>
|
||||
<string name="version_summary">%1$s backend %2$s</string>
|
||||
<string name="version_summary_checking">正在检查 %s backend 版本</string>
|
||||
<string name="version_summary_unknown">未知的 %s 版本</string>
|
||||
<string name="version_title">WireGuard for Android v%s</string>
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
<resources>
|
||||
<declare-styleable name="Multiselected">
|
||||
<attr name="state_multiselected" format="boolean" />
|
||||
<attr name="colorMultiselectActiveBackground" format="reference|color" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="TvCardView">
|
||||
<attr name="state_isUp" format="boolean" />
|
||||
<attr name="state_isDeleting" format="boolean" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
<string name="error_down">Error bringing down tunnel: %s</string>
|
||||
<string name="error_fetching_apps">Error fetching apps list: %s</string>
|
||||
<string name="error_root">Please obtain root access and try again</string>
|
||||
<string name="error_prepare">Error preparing tunnel: %s</string>
|
||||
<string name="error_up">Error bringing up tunnel: %s</string>
|
||||
<string name="exclude_private_ips">Exclude private IPs</string>
|
||||
<string name="generate_new_private_key">Generate new private key</string>
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
<item name="materialCardViewStyle">@style/WireGuardTheme.MaterialCardView</item>
|
||||
<item name="toolbarStyle">@style/WireGuardTheme.Toolbar</item>
|
||||
<item name="bottomSheetDialogTheme">@style/WireGuardTheme.BottomSheetDialog</item>
|
||||
<item name="android:statusBarColor">@null</item>
|
||||
</style>
|
||||
|
||||
<!-- Various additional API-specific features in values-v*/styles.xml -->
|
||||
<style name="AppTheme" parent="AppThemeBase"/>
|
||||
<style name="AppTheme" parent="AppThemeBase" />
|
||||
|
||||
<style name="WireGuardTheme.MaterialCardView" parent="Widget.Material3.CardView.Elevated">
|
||||
<item name="cornerRadius">4dp</item>
|
||||
@@ -36,4 +37,9 @@
|
||||
<item name="android:windowEnterAnimation">@android:anim/fade_in</item>
|
||||
<item name="android:windowExitAnimation">@android:anim/fade_out</item>
|
||||
</style>
|
||||
|
||||
<style name="TvTheme" parent="AppTheme">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -27,6 +27,5 @@
|
||||
<item name="colorOnSurfaceInverse">@color/md_theme_light_inverseOnSurface</item>
|
||||
<item name="colorSurfaceInverse">@color/md_theme_light_inverseSurface</item>
|
||||
<item name="colorPrimaryInverse">@color/md_theme_light_inversePrimary</item>
|
||||
<item name="colorMultiselectActiveBackground">@color/md_theme_light_surfaceVariant</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="tv_primary_color">#ff212121</color>
|
||||
<color name="tv_secondary_dark_color">#ff0059c1</color>
|
||||
<color name="tv_card_background">@color/tv_primary_color</color>
|
||||
<color name="tv_card_delete_background">#b00020</color>
|
||||
<color name="tv_list_multiselect_background">#ffeeeeee</color>
|
||||
</resources>
|
||||
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="TvTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="colorPrimary">@color/tv_primary_color</item>
|
||||
<item name="colorOnPrimary">#fffafafa</item>
|
||||
<item name="colorPrimaryDark">@color/tv_primary_color</item>
|
||||
<item name="colorPrimaryVariant">#ff484848</item>
|
||||
<item name="colorSecondary">#ff4285f4</item>
|
||||
<item name="colorOnSecondary">#ff0059c1</item>
|
||||
<item name="colorSurface">@color/tv_primary_color</item>
|
||||
<item name="colorOnSurface">#fffafafa</item>
|
||||
<item name="colorMultiselectActiveBackground">@color/tv_list_multiselect_background</item>
|
||||
<item name="colorControlNormal">#fffafafa</item>
|
||||
<item name="elevationOverlayEnabled">false</item>
|
||||
<item name="android:statusBarColor">@color/tv_primary_color</item>
|
||||
<item name="android:windowBackground">@color/tv_primary_color</item>
|
||||
<item name="alertDialogTheme">@style/TvTheme.Dialog</item>
|
||||
<item name="materialAlertDialogTheme">@style/TvTheme.Dialog</item>
|
||||
<item name="materialCardViewStyle">@style/TvTheme.MaterialCardView</item>
|
||||
</style>
|
||||
|
||||
<style name="TvTheme.Dialog" parent="Theme.Material3.DayNight.Dialog.Alert">
|
||||
<item name="android:windowBackground">?attr/colorSurface</item>
|
||||
</style>
|
||||
|
||||
<style name="TvTheme.MaterialCardView" parent="Widget.Material3.CardView.Elevated">
|
||||
<item name="cornerRadius">4dp</item>
|
||||
<item name="contentPadding">8dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
buildscript {
|
||||
ext {
|
||||
wireguardVersionCode = 497
|
||||
wireguardVersionName = '1.0.20230330'
|
||||
wireguardVersionCode = 498
|
||||
wireguardVersionName = '1.0.20230405'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user