mirror of
https://github.com/karasevm/PrivateDNSAndroid.git
synced 2025-06-30 13:19:57 +00:00
Compare commits
35 commits
v1.9.0-bet
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
bd61fe6748 | ||
|
87fe66af20 | ||
|
c375776575 | ||
|
3b24d24ba4 | ||
|
e8885409b5 | ||
|
2aa895cd5e | ||
|
bdd98109ed | ||
|
3b09f605f9 | ||
|
9d62e91b60 | ||
|
e155d17dd7 | ||
|
e2104952bc | ||
|
24800f7f2d | ||
|
6a5f2af6f6 | ||
|
0e0e0bf9b4 | ||
|
4c6240bd34 | ||
|
e4b9e84f8c | ||
|
a379c81cb9 | ||
|
6164a35f04 | ||
|
e4498ca64a | ||
|
75413fddcd | ||
|
6cc99b7820 | ||
|
efd48b8984 | ||
|
b39d7e3624 | ||
|
4f6dc13c12 | ||
|
5ba03acbcc | ||
|
627771d4b1 | ||
|
8c7ff2ca8f | ||
|
402b084954 | ||
|
8c927d6b26 | ||
|
7cdc2bbb84 | ||
|
835e9381ea | ||
|
14b320ac68 | ||
|
681e6ceef4 | ||
|
6a5f405211 | ||
|
fa9d259a21 |
33 changed files with 896 additions and 254 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -67,3 +67,8 @@ fastlane/Preview.html
|
|||
fastlane/screenshots
|
||||
fastlane/test_output
|
||||
fastlane/readme.md
|
||||
|
||||
# kotlin
|
||||
.kotlin/
|
||||
|
||||
*~
|
||||
|
|
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.9.10" />
|
||||
<option name="version" value="2.0.20" />
|
||||
</component>
|
||||
</project>
|
20
README.md
20
README.md
|
@ -1,6 +1,7 @@
|
|||
[](https://github.com/karasevm/PrivateDNSAndroid/releases/latest)
|
||||
[](https://github.com/karasevm/PrivateDNSAndroid/releases/latest)
|
||||
[](https://apt.izzysoft.de/fdroid/index/apk/ru.karasevm.privatednstoggle)
|
||||
[](https://hosted.weblate.org/engage/privatednsandroid/)
|
||||
|
||||
# Private DNS Quick Toggle
|
||||
A quick settings tile to switch your private dns provider. Supports any number of providers. Makes it easy to turn adblocking dns servers on or off with just
|
||||
|
@ -12,11 +13,11 @@ a single tap.
|
|||
Get the latest apk on the [releases page](https://github.com/karasevm/PrivateDNSAndroid/releases/latest)
|
||||
or from [IzzyOnDroid repo](https://apt.izzysoft.de/fdroid/index/apk/ru.karasevm.privatednstoggle).
|
||||
|
||||
## Automatic (Shizuku)
|
||||
### Automatic (Shizuku)
|
||||
1. Install and start [Shizuku](https://shizuku.rikka.app/).
|
||||
2. Start the app and allow Shizuku access when prompted.
|
||||
|
||||
## Manual
|
||||
### Manual
|
||||
For the app to work properly you'll need to provide it permissions via ADB:
|
||||
|
||||
1. Get to your PC and download platform tools from google [here](https://developer.android.com/studio/releases/platform-tools).
|
||||
|
@ -31,3 +32,18 @@ For the app to work properly you'll need to provide it permissions via ADB:
|
|||
|
||||
6. That's it, you should have the app installed.
|
||||
|
||||
## Contributing
|
||||
|
||||
### Translation
|
||||
The easiest way to contribute would be to submit a translation to your language. Thanks to Weblate gratis hosting for open-source projects you can do it without any programming knowledge on [their website](https://hosted.weblate.org/engage/privatednsandroid/).
|
||||
#### Translation status
|
||||
<a href="https://hosted.weblate.org/engage/privatednsandroid/">
|
||||
<img src="https://hosted.weblate.org/widget/privatednsandroid/private-dns-quick-toggle/multi-auto.svg" alt="Translation status" />
|
||||
</a>
|
||||
|
||||
### Code
|
||||
If you want to contribute code please try to adhere to the following guidelines:
|
||||
- Include javadoc comments for all the public methods you add
|
||||
- Keep the code neatly formatted, you can you the built-in Android Studio formatter
|
||||
- Please describe what your code does and how does it do that when sending a PR
|
||||
- Before sending a PR please test your change on the oldest and latest supported Android versions (9 and 14 at the time of writing)
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'com.google.devtools.ksp'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId "ru.karasevm.privatednstoggle"
|
||||
versionCode 16
|
||||
versionName "1.9.0-beta1"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
targetSdkVersion 34
|
||||
minSdkVersion 28
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix ".dev"
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '17'
|
||||
}
|
||||
dependenciesInfo {
|
||||
// Disables dependency metadata when building APKs.
|
||||
includeInApk = false
|
||||
// Disables dependency metadata when building Android App Bundles.
|
||||
includeInBundle = false
|
||||
}
|
||||
namespace 'ru.karasevm.privatednstoggle'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.13.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||
implementation 'androidx.activity:activity-ktx:1.9.2'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.8.3'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'com.google.guava:guava:33.1.0-android'
|
||||
implementation 'com.google.code.gson:gson:2.11.0'
|
||||
|
||||
def shizuku_version = '13.1.5'
|
||||
implementation "dev.rikka.shizuku:api:$shizuku_version"
|
||||
implementation "dev.rikka.shizuku:provider:$shizuku_version"
|
||||
|
||||
implementation 'org.lsposed.hiddenapibypass:hiddenapibypass:4.3'
|
||||
compileOnly 'dev.rikka.hidden:stub:4.3.2'
|
||||
|
||||
// Room components
|
||||
def roomVersion = '2.6.1'
|
||||
implementation "androidx.room:room-ktx:$roomVersion"
|
||||
ksp "androidx.room:room-compiler:$roomVersion"
|
||||
androidTestImplementation "androidx.room:room-testing:$roomVersion"
|
||||
|
||||
// Lifecycle components
|
||||
def lifecycleVersion = '2.8.5'
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||
}
|
88
app/build.gradle.kts
Normal file
88
app/build.gradle.kts
Normal file
|
@ -0,0 +1,88 @@
|
|||
plugins {
|
||||
id("com.android.application")
|
||||
id("kotlin-android")
|
||||
id("com.google.devtools.ksp")
|
||||
id("org.jetbrains.kotlin.plugin.serialization")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 35
|
||||
androidResources {
|
||||
generateLocaleConfig = true
|
||||
}
|
||||
defaultConfig {
|
||||
applicationId = "ru.karasevm.privatednstoggle"
|
||||
versionCode = 18
|
||||
versionName = "1.10.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
targetSdk = 35
|
||||
minSdk = 28
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
buildConfig = true
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix = ".dev"
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
dependenciesInfo {
|
||||
// Disables dependency metadata when building APKs.
|
||||
includeInApk = false
|
||||
// Disables dependency metadata when building Android App Bundles.
|
||||
includeInBundle = false
|
||||
}
|
||||
namespace = "ru.karasevm.privatednstoggle"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.core:core-ktx:1.15.0")
|
||||
implementation("androidx.appcompat:appcompat:1.7.0")
|
||||
implementation("androidx.recyclerview:recyclerview:1.4.0")
|
||||
implementation("androidx.activity:activity-ktx:1.10.0")
|
||||
implementation("androidx.fragment:fragment-ktx:1.8.6")
|
||||
implementation("com.google.android.material:material:1.12.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.2.0")
|
||||
implementation("com.google.guava:guava:33.1.0-android")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
|
||||
|
||||
val shizukuVersion = "13.1.5"
|
||||
implementation("dev.rikka.shizuku:api:$shizukuVersion")
|
||||
implementation("dev.rikka.shizuku:provider:$shizukuVersion")
|
||||
compileOnly("dev.rikka.hidden:stub:4.3.3")
|
||||
|
||||
implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3")
|
||||
|
||||
// Room components
|
||||
val roomVersion = "2.6.1"
|
||||
implementation("androidx.room:room-ktx:$roomVersion")
|
||||
ksp("androidx.room:room-compiler:$roomVersion")
|
||||
androidTestImplementation("androidx.room:room-testing:$roomVersion")
|
||||
|
||||
// Lifecycle components
|
||||
val lifecycleVersion = "2.8.7"
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion")
|
||||
implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion")
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.2.1")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package ru.karasevm.privatednstoggle
|
||||
|
||||
import android.app.Application
|
||||
import android.os.StrictMode
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import ru.karasevm.privatednstoggle.data.DnsServerRepository
|
||||
import ru.karasevm.privatednstoggle.data.database.DnsServerRoomDatabase
|
||||
|
@ -13,5 +14,20 @@ class PrivateDNSApp : Application() {
|
|||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||
|
||||
if (BuildConfig.DEBUG){
|
||||
StrictMode.setThreadPolicy(
|
||||
StrictMode.ThreadPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build()
|
||||
)
|
||||
StrictMode.setVmPolicy(
|
||||
StrictMode.VmPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,19 +3,21 @@ package ru.karasevm.privatednstoggle.model
|
|||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
// All fields must have default values for proper deserialization
|
||||
@Serializable
|
||||
@Entity(tableName = "dns_servers")
|
||||
data class DnsServer(
|
||||
@SerializedName("id")
|
||||
@SerialName("id")
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Int = 0,
|
||||
@SerializedName("server")
|
||||
@SerialName("server")
|
||||
val server: String = "",
|
||||
@SerializedName("label")
|
||||
@SerialName("label")
|
||||
val label: String = "",
|
||||
@SerializedName("enabled")
|
||||
@SerialName("enabled")
|
||||
@ColumnInfo(defaultValue = "1")
|
||||
val enabled: Boolean = true,
|
||||
val sortOrder: Int? = null
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.graphics.drawable.Icon
|
|||
import android.provider.Settings
|
||||
import android.service.quicksettings.Tile
|
||||
import android.service.quicksettings.TileService
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -35,6 +36,8 @@ class DnsTileService : TileService() {
|
|||
private val repository: DnsServerRepository by lazy { (application as PrivateDNSApp).repository }
|
||||
private val job = SupervisorJob()
|
||||
private val scope = CoroutineScope(Dispatchers.IO + job)
|
||||
private val sharedPreferences by lazy { PreferenceHelper.defaultPreference(this) }
|
||||
private var isBroadcastReceiverRegistered = false
|
||||
|
||||
override fun onTileAdded() {
|
||||
super.onTileAdded()
|
||||
|
@ -53,9 +56,8 @@ class DnsTileService : TileService() {
|
|||
val dnsMode = Settings.Global.getString(contentResolver, "private_dns_mode")
|
||||
val dnsProvider = Settings.Global.getString(contentResolver, "private_dns_specifier")
|
||||
|
||||
val sharedPrefs = PreferenceHelper.defaultPreference(this)
|
||||
if (dnsMode.equals(DNS_MODE_OFF, ignoreCase = true)) {
|
||||
if (sharedPrefs.autoMode == AUTO_MODE_OPTION_AUTO || sharedPrefs.autoMode == AUTO_MODE_OPTION_OFF_AUTO) {
|
||||
if (sharedPreferences.autoMode == AUTO_MODE_OPTION_AUTO || sharedPreferences.autoMode == AUTO_MODE_OPTION_OFF_AUTO) {
|
||||
changeDNSServer(DNS_MODE_AUTO, dnsProvider)
|
||||
} else {
|
||||
changeDNSServer(DNS_MODE_PRIVATE, dnsProvider)
|
||||
|
@ -66,10 +68,10 @@ class DnsTileService : TileService() {
|
|||
} else if (dnsMode.equals(DNS_MODE_PRIVATE, ignoreCase = true)) {
|
||||
scope.launch {
|
||||
if (getNextAddress(dnsProvider) == null) {
|
||||
if (sharedPrefs.autoMode == AUTO_MODE_OPTION_PRIVATE) {
|
||||
if (sharedPreferences.autoMode == AUTO_MODE_OPTION_PRIVATE) {
|
||||
changeDNSServer(DNS_MODE_PRIVATE, null)
|
||||
} else {
|
||||
if (sharedPrefs.autoMode == AUTO_MODE_OPTION_AUTO) {
|
||||
if (sharedPreferences.autoMode == AUTO_MODE_OPTION_AUTO) {
|
||||
changeDNSServer(DNS_MODE_AUTO, dnsProvider)
|
||||
} else {
|
||||
changeDNSServer(DNS_MODE_OFF, dnsProvider)
|
||||
|
@ -134,10 +136,9 @@ class DnsTileService : TileService() {
|
|||
if (!checkForPermission(this)) {
|
||||
return
|
||||
}
|
||||
val sharedPrefs = PreferenceHelper.defaultPreference(this)
|
||||
|
||||
// Require unlock to change mode according to user preference
|
||||
val requireUnlock = sharedPrefs.requireUnlock
|
||||
val requireUnlock = sharedPreferences.requireUnlock
|
||||
if (isLocked && requireUnlock) {
|
||||
unlockAndRun(this::cycleState)
|
||||
} else {
|
||||
|
@ -151,12 +152,13 @@ class DnsTileService : TileService() {
|
|||
* Refreshes the state of the tile
|
||||
*/
|
||||
private fun refreshTile() {
|
||||
val isPermissionGranted = checkForPermission(this)
|
||||
val dnsMode = Settings.Global.getString(contentResolver, "private_dns_mode")
|
||||
when (dnsMode?.lowercase()) {
|
||||
DNS_MODE_OFF -> {
|
||||
setTile(
|
||||
qsTile,
|
||||
Tile.STATE_INACTIVE,
|
||||
if (!isPermissionGranted) Tile.STATE_UNAVAILABLE else Tile.STATE_INACTIVE,
|
||||
getString(R.string.dns_off),
|
||||
R.drawable.ic_off_black_24dp
|
||||
)
|
||||
|
@ -165,7 +167,7 @@ class DnsTileService : TileService() {
|
|||
DNS_MODE_AUTO -> {
|
||||
setTile(
|
||||
qsTile,
|
||||
Tile.STATE_INACTIVE,
|
||||
if (!isPermissionGranted) Tile.STATE_UNAVAILABLE else Tile.STATE_INACTIVE,
|
||||
getString(R.string.dns_auto),
|
||||
R.drawable.ic_auto_black_24dp
|
||||
)
|
||||
|
@ -178,7 +180,7 @@ class DnsTileService : TileService() {
|
|||
val dnsServer = repository.getFirstByServer(activeAddress)
|
||||
setTile(
|
||||
qsTile,
|
||||
Tile.STATE_ACTIVE,
|
||||
if (!isPermissionGranted) Tile.STATE_UNAVAILABLE else Tile.STATE_ACTIVE,
|
||||
// display server address if either there is no label or the server is not known
|
||||
dnsServer?.label?.ifBlank { activeAddress } ?: activeAddress,
|
||||
R.drawable.ic_private_black_24dp
|
||||
|
@ -189,7 +191,7 @@ class DnsTileService : TileService() {
|
|||
else -> {
|
||||
setTile(
|
||||
qsTile,
|
||||
Tile.STATE_INACTIVE,
|
||||
if (!isPermissionGranted) Tile.STATE_UNAVAILABLE else Tile.STATE_INACTIVE,
|
||||
getString(R.string.dns_unknown),
|
||||
R.drawable.ic_unknown_black_24dp
|
||||
)
|
||||
|
@ -205,12 +207,10 @@ class DnsTileService : TileService() {
|
|||
|
||||
override fun onStartListening() {
|
||||
super.onStartListening()
|
||||
if (!checkForPermission(this)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Prevent some crashes
|
||||
if (qsTile == null) {
|
||||
Log.w(TAG, "onStartListening: qsTile is null")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -222,14 +222,17 @@ class DnsTileService : TileService() {
|
|||
IntentFilter("refresh_tile"),
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED
|
||||
)
|
||||
|
||||
isBroadcastReceiverRegistered = true
|
||||
refreshTile()
|
||||
|
||||
}
|
||||
|
||||
override fun onStopListening() {
|
||||
super.onStopListening()
|
||||
unregisterReceiver(broadcastReceiver)
|
||||
if (isBroadcastReceiverRegistered) {
|
||||
unregisterReceiver(broadcastReceiver)
|
||||
isBroadcastReceiverRegistered = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -292,4 +295,8 @@ class DnsTileService : TileService() {
|
|||
repository.getNextByServer(currentAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "DnsTileService"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import ru.karasevm.privatednstoggle.data.DnsServerViewModelFactory
|
|||
import ru.karasevm.privatednstoggle.databinding.SheetDnsSelectorBinding
|
||||
import ru.karasevm.privatednstoggle.model.DnsServer
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.checkForPermission
|
||||
|
||||
class DNSServerDialogFragment : DialogFragment() {
|
||||
|
||||
|
@ -27,7 +28,7 @@ class DNSServerDialogFragment : DialogFragment() {
|
|||
private lateinit var adapter: ServerListRecyclerAdapter
|
||||
private var servers: MutableList<DnsServer> = mutableListOf()
|
||||
private val dnsServerViewModel: DnsServerViewModel by viewModels { DnsServerViewModelFactory((requireActivity().application as PrivateDNSApp).repository) }
|
||||
|
||||
private val contentResolver by lazy { requireActivity().contentResolver }
|
||||
|
||||
override fun onCreateDialog(
|
||||
savedInstanceState: Bundle?
|
||||
|
@ -68,22 +69,33 @@ class DNSServerDialogFragment : DialogFragment() {
|
|||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
if (!checkForPermission(requireContext())) {
|
||||
Toast.makeText(
|
||||
context, R.string.permission_missing, Toast.LENGTH_SHORT
|
||||
).show()
|
||||
dialog!!.dismiss()
|
||||
}
|
||||
adapter.onItemClick = { id ->
|
||||
when (id) {
|
||||
OFF_ID -> {
|
||||
PrivateDNSUtils.setPrivateMode(
|
||||
requireActivity().contentResolver,
|
||||
contentResolver,
|
||||
PrivateDNSUtils.DNS_MODE_OFF
|
||||
)
|
||||
PrivateDNSUtils.setPrivateProvider(
|
||||
contentResolver,
|
||||
null)
|
||||
Toast.makeText(context, R.string.set_to_off_toast, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
AUTO_ID -> {
|
||||
PrivateDNSUtils.setPrivateMode(
|
||||
requireActivity().contentResolver,
|
||||
contentResolver,
|
||||
PrivateDNSUtils.DNS_MODE_AUTO
|
||||
)
|
||||
PrivateDNSUtils.setPrivateProvider(
|
||||
contentResolver,
|
||||
null)
|
||||
Toast.makeText(context, R.string.set_to_auto_toast, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
|
@ -91,11 +103,11 @@ class DNSServerDialogFragment : DialogFragment() {
|
|||
lifecycleScope.launch {
|
||||
val server = servers.find { server -> server.id == id }
|
||||
PrivateDNSUtils.setPrivateMode(
|
||||
requireActivity().contentResolver,
|
||||
contentResolver,
|
||||
PrivateDNSUtils.DNS_MODE_PRIVATE
|
||||
)
|
||||
PrivateDNSUtils.setPrivateProvider(
|
||||
requireActivity().contentResolver,
|
||||
contentResolver,
|
||||
server?.server
|
||||
)
|
||||
Toast.makeText(
|
||||
|
|
|
@ -6,13 +6,11 @@ import android.content.ClipDescription.MIMETYPE_TEXT_PLAIN
|
|||
import android.content.ClipboardManager
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.IPackageManager
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.permission.IPermissionManager
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
|
@ -27,17 +25,11 @@ import androidx.recyclerview.widget.ItemTouchHelper.DOWN
|
|||
import androidx.recyclerview.widget.ItemTouchHelper.UP
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import com.google.gson.ToNumberPolicy
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import kotlinx.coroutines.launch
|
||||
import org.lsposed.hiddenapibypass.HiddenApiBypass
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import rikka.shizuku.Shizuku
|
||||
import rikka.shizuku.ShizukuBinderWrapper
|
||||
import rikka.shizuku.ShizukuProvider
|
||||
import rikka.shizuku.SystemServiceHelper
|
||||
import ru.karasevm.privatednstoggle.PrivateDNSApp
|
||||
import ru.karasevm.privatednstoggle.R
|
||||
import ru.karasevm.privatednstoggle.data.DnsServerViewModel
|
||||
|
@ -47,6 +39,7 @@ import ru.karasevm.privatednstoggle.model.DnsServer
|
|||
import ru.karasevm.privatednstoggle.util.BackupUtils
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper.dns_servers
|
||||
import ru.karasevm.privatednstoggle.util.ShizukuUtil.grantPermissionWithShizuku
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogListener,
|
||||
|
@ -54,11 +47,9 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
|||
|
||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private var items = mutableListOf<String>()
|
||||
private lateinit var sharedPrefs: SharedPreferences
|
||||
private lateinit var adapter: ServerListRecyclerAdapter
|
||||
private lateinit var clipboard: ClipboardManager
|
||||
private lateinit var gson: Gson
|
||||
private val dnsServerViewModel: DnsServerViewModel by viewModels { DnsServerViewModelFactory((application as PrivateDNSApp).repository) }
|
||||
|
||||
private val itemTouchHelper by lazy {
|
||||
|
@ -126,7 +117,7 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
|||
|
||||
private fun importSettings(json: String) {
|
||||
runCatching {
|
||||
val data: BackupUtils.Backup = gson.fromJson(json, BackupUtils.Backup::class.java)
|
||||
val data: BackupUtils.Backup = Json.decodeFromString<BackupUtils.Backup>(json)
|
||||
BackupUtils.import(data, dnsServerViewModel, sharedPrefs)
|
||||
}.onSuccess {
|
||||
Toast.makeText(
|
||||
|
@ -134,28 +125,18 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
|||
).show()
|
||||
}.onFailure { exception ->
|
||||
runCatching {
|
||||
val objectType = object : TypeToken<Map<String, Any>>() {}.type
|
||||
val data: Map<String, Any> = gson.fromJson(json, objectType)
|
||||
Log.e("IMPORT", "Malformed json, falling back to legacy", exception)
|
||||
val data = Json.decodeFromString<BackupUtils.LegacyBackup>(json)
|
||||
BackupUtils.importLegacy(data, dnsServerViewModel, sharedPrefs)
|
||||
}.onSuccess {
|
||||
Toast.makeText(
|
||||
this, getString(R.string.import_success), Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}.onFailure {
|
||||
}.onFailure { exception ->
|
||||
Log.e("IMPORT", "Import failed", exception)
|
||||
when (exception) {
|
||||
is JsonSyntaxException -> {
|
||||
Toast.makeText(
|
||||
this, getString(R.string.import_failure_json), Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
|
||||
else -> {
|
||||
Toast.makeText(
|
||||
this, getString(R.string.import_failure), Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
Toast.makeText(
|
||||
this, getString(R.string.import_failure), Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,18 +145,20 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
|||
* Migrate the SharedPreferences server list to Room
|
||||
*/
|
||||
private fun migrateServerList() {
|
||||
if (sharedPrefs.dns_servers.isNotEmpty() && sharedPrefs.dns_servers[0] != "") {
|
||||
Log.i(
|
||||
"migrate",
|
||||
"existing sharedPrefs list: ${sharedPrefs.dns_servers} ${sharedPrefs.dns_servers.size}"
|
||||
)
|
||||
sharedPrefs.dns_servers.forEach { server ->
|
||||
val parts = server.split(" : ").toMutableList()
|
||||
if (parts.size != 2) parts.add(0, "")
|
||||
Log.i("migrate", "migrating: $server -> $parts")
|
||||
dnsServerViewModel.insert(DnsServer(0, parts[1], parts[0]))
|
||||
dnsServerViewModel.viewModelScope.launch {
|
||||
if (sharedPrefs.dns_servers.isNotEmpty() && sharedPrefs.dns_servers[0] != "") {
|
||||
Log.i(
|
||||
"migrate",
|
||||
"existing sharedPrefs list: ${sharedPrefs.dns_servers} ${sharedPrefs.dns_servers.size}"
|
||||
)
|
||||
sharedPrefs.dns_servers.forEach { server ->
|
||||
val parts = server.split(" : ").toMutableList()
|
||||
if (parts.size != 2) parts.add(0, "")
|
||||
Log.i("migrate", "migrating: $server -> $parts")
|
||||
dnsServerViewModel.insert(DnsServer(0, parts[1], parts[0]))
|
||||
}
|
||||
sharedPrefs.dns_servers = emptyList<String>().toMutableList()
|
||||
}
|
||||
sharedPrefs.dns_servers = emptyList<String>().toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,15 +176,9 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
|||
|
||||
sharedPrefs = PreferenceHelper.defaultPreference(this)
|
||||
clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
gson = GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).create()
|
||||
|
||||
migrateServerList()
|
||||
|
||||
items = sharedPrefs.dns_servers
|
||||
if (items[0] == "") {
|
||||
items.removeAt(0)
|
||||
}
|
||||
|
||||
adapter = ServerListRecyclerAdapter(true)
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
|
@ -249,7 +226,7 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
|||
R.id.export_settings_clipboard -> {
|
||||
dnsServerViewModel.viewModelScope.launch {
|
||||
val data = BackupUtils.export(dnsServerViewModel, sharedPrefs)
|
||||
val jsonData = gson.toJson(data)
|
||||
val jsonData = Json.encodeToString(data)
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText("", jsonData))
|
||||
// Only show a toast for Android 12 and lower.
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) Toast.makeText(
|
||||
|
@ -263,7 +240,7 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
|||
val activityContext = this
|
||||
dnsServerViewModel.viewModelScope.launch {
|
||||
val data = BackupUtils.export(dnsServerViewModel, sharedPrefs)
|
||||
val jsonData = gson.toJson(data)
|
||||
val jsonData = Json.encodeToString(data)
|
||||
ShareCompat.IntentBuilder(activityContext).setText(jsonData)
|
||||
.setType("text/plain")
|
||||
.startChooser()
|
||||
|
@ -319,7 +296,7 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
|||
if (result.resultCode == RESULT_OK) {
|
||||
val data: Intent? = result.data
|
||||
data?.data?.also { uri ->
|
||||
val jsonData = gson.toJson(BackupUtils.export(dnsServerViewModel, sharedPrefs))
|
||||
val jsonData = Json.encodeToString(BackupUtils.export(dnsServerViewModel, sharedPrefs))
|
||||
val contentResolver = applicationContext.contentResolver
|
||||
runCatching {
|
||||
contentResolver.openOutputStream(uri)?.use { outputStream ->
|
||||
|
@ -378,7 +355,7 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
|||
Shizuku.requestPermission(1)
|
||||
}
|
||||
} else {
|
||||
grantPermissionWithShizuku()
|
||||
grantPermission()
|
||||
}
|
||||
} else {
|
||||
if (checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
|
||||
|
@ -386,6 +363,9 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
|||
Intent.ACTION_VIEW,
|
||||
Uri.parse("https://karasevm.github.io/PrivateDNSAndroid/")
|
||||
)
|
||||
Toast.makeText(
|
||||
this, R.string.shizuku_failure_toast, Toast.LENGTH_SHORT
|
||||
).show()
|
||||
startActivity(browserIntent)
|
||||
finish()
|
||||
}
|
||||
|
@ -469,58 +449,27 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
|||
dnsServerViewModel.update(id, server, label, null, enabled)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to grant WRITE_SECURE_SETTINGS permission with Shizuku
|
||||
*/
|
||||
private fun grantPermissionWithShizuku() {
|
||||
val packageName = applicationContext.packageName
|
||||
runCatching {
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
HiddenApiBypass.addHiddenApiExemptions("Landroid/permission")
|
||||
val binder =
|
||||
ShizukuBinderWrapper(SystemServiceHelper.getSystemService("permissionmgr"))
|
||||
val pm = IPermissionManager.Stub.asInterface(binder)
|
||||
runCatching {
|
||||
pm.grantRuntimePermission(
|
||||
packageName, Manifest.permission.WRITE_SECURE_SETTINGS, 0
|
||||
)
|
||||
}.onFailure { _ ->
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
pm.grantRuntimePermission(
|
||||
packageName,
|
||||
Manifest.permission.WRITE_SECURE_SETTINGS,
|
||||
applicationContext.deviceId,
|
||||
0
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val binder = ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package"))
|
||||
val pm = IPackageManager.Stub.asInterface(binder)
|
||||
pm.grantRuntimePermission(
|
||||
packageName, Manifest.permission.WRITE_SECURE_SETTINGS, 0
|
||||
)
|
||||
}
|
||||
}.onFailure { e ->
|
||||
Log.e("SHIZUKU", "onRequestPermissionResult: ", e)
|
||||
}.also {
|
||||
if (checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
|
||||
val browserIntent = Intent(
|
||||
Intent.ACTION_VIEW, Uri.parse("https://karasevm.github.io/PrivateDNSAndroid/")
|
||||
)
|
||||
startActivity(browserIntent)
|
||||
finish()
|
||||
}
|
||||
private fun grantPermission() {
|
||||
if (grantPermissionWithShizuku(this)) {
|
||||
Toast.makeText(
|
||||
this, R.string.shizuku_success_toast, Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this, R.string.shizuku_failure_toast, Toast.LENGTH_SHORT
|
||||
).show()
|
||||
val browserIntent = Intent(
|
||||
Intent.ACTION_VIEW, Uri.parse("https://karasevm.github.io/PrivateDNSAndroid/")
|
||||
)
|
||||
startActivity(browserIntent)
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onRequestPermissionResult(requestCode: Int, grantResult: Int) {
|
||||
val isGranted = grantResult == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
if (isGranted) {
|
||||
grantPermissionWithShizuku()
|
||||
} else if (checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
|
||||
if (!isGranted && checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
|
||||
val browserIntent = Intent(
|
||||
Intent.ACTION_VIEW, Uri.parse("https://karasevm.github.io/PrivateDNSAndroid/")
|
||||
)
|
||||
|
|
|
@ -14,6 +14,7 @@ import ru.karasevm.privatednstoggle.util.PrivateDNSUtils
|
|||
class OptionsDialogFragment : DialogFragment() {
|
||||
private var _binding: DialogOptionsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val sharedPreferences by lazy { PreferenceHelper.defaultPreference(requireContext()) }
|
||||
|
||||
override fun onCreateDialog(
|
||||
savedInstanceState: Bundle?
|
||||
|
@ -34,8 +35,7 @@ class OptionsDialogFragment : DialogFragment() {
|
|||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val sharedPrefs = PreferenceHelper.defaultPreference(requireContext())
|
||||
val autoModeOption = sharedPrefs.autoMode
|
||||
val autoModeOption = sharedPreferences.autoMode
|
||||
when (autoModeOption) {
|
||||
PrivateDNSUtils.AUTO_MODE_OPTION_OFF -> binding.autoOptionRadioGroup.check(R.id.autoOptionOff)
|
||||
PrivateDNSUtils.AUTO_MODE_OPTION_AUTO -> binding.autoOptionRadioGroup.check(R.id.autoOptionAuto)
|
||||
|
@ -44,20 +44,20 @@ class OptionsDialogFragment : DialogFragment() {
|
|||
}
|
||||
binding.autoOptionRadioGroup.setOnCheckedChangeListener { _, checkedId ->
|
||||
when (checkedId) {
|
||||
R.id.autoOptionOff -> sharedPrefs.autoMode = PrivateDNSUtils.AUTO_MODE_OPTION_OFF
|
||||
R.id.autoOptionAuto -> sharedPrefs.autoMode = PrivateDNSUtils.AUTO_MODE_OPTION_AUTO
|
||||
R.id.autoOptionOffAuto -> sharedPrefs.autoMode =
|
||||
R.id.autoOptionOff -> sharedPreferences.autoMode = PrivateDNSUtils.AUTO_MODE_OPTION_OFF
|
||||
R.id.autoOptionAuto -> sharedPreferences.autoMode = PrivateDNSUtils.AUTO_MODE_OPTION_AUTO
|
||||
R.id.autoOptionOffAuto -> sharedPreferences.autoMode =
|
||||
PrivateDNSUtils.AUTO_MODE_OPTION_OFF_AUTO
|
||||
|
||||
R.id.autoOptionPrivate -> sharedPrefs.autoMode =
|
||||
R.id.autoOptionPrivate -> sharedPreferences.autoMode =
|
||||
PrivateDNSUtils.AUTO_MODE_OPTION_PRIVATE
|
||||
}
|
||||
}
|
||||
|
||||
val requireUnlock = sharedPrefs.requireUnlock
|
||||
val requireUnlock = sharedPreferences.requireUnlock
|
||||
binding.requireUnlockSwitch.isChecked = requireUnlock
|
||||
binding.requireUnlockSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
sharedPrefs.requireUnlock = isChecked
|
||||
sharedPreferences.requireUnlock = isChecked
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,26 @@
|
|||
package ru.karasevm.privatednstoggle.util
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import ru.karasevm.privatednstoggle.data.DnsServerViewModel
|
||||
import ru.karasevm.privatednstoggle.model.DnsServer
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper.AUTO_MODE
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper.DNS_SERVERS
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper.REQUIRE_UNLOCK
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper.autoMode
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper.requireUnlock
|
||||
|
||||
object BackupUtils {
|
||||
@Serializable
|
||||
data class Backup(
|
||||
@SerializedName("dns_servers") val dnsServers: List<DnsServer>,
|
||||
@SerializedName("auto_mode") val autoMode: Int?,
|
||||
@SerializedName("require_unlock") val requireUnlock: Boolean?,
|
||||
@SerialName("dns_servers") val dnsServers: List<DnsServer>,
|
||||
@SerialName("auto_mode") val autoMode: Int?,
|
||||
@SerialName("require_unlock") val requireUnlock: Boolean?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LegacyBackup(
|
||||
@SerialName("dns_servers") val dnsServers: String,
|
||||
@SerialName("auto_mode") val autoMode: Int?,
|
||||
@SerialName("require_unlock") val requireUnlock: Boolean?,
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -47,17 +53,16 @@ object BackupUtils {
|
|||
|
||||
/**
|
||||
* Imports old server list
|
||||
* @param map Deserialized backup
|
||||
* @param legacyBackup Deserialized backup
|
||||
* @param viewModel View model
|
||||
* @param sharedPreferences Shared preferences
|
||||
*/
|
||||
fun importLegacy(
|
||||
map: Map<String, Any>,
|
||||
legacyBackup: LegacyBackup,
|
||||
viewModel: DnsServerViewModel,
|
||||
sharedPreferences: SharedPreferences
|
||||
) {
|
||||
map[DNS_SERVERS]?.let { servers ->
|
||||
if (servers is String) {
|
||||
legacyBackup.dnsServers.let { servers ->
|
||||
val serverList = servers.split(",")
|
||||
serverList.forEach { server ->
|
||||
val parts = server.split(" : ")
|
||||
|
@ -67,9 +72,8 @@ object BackupUtils {
|
|||
viewModel.insert(DnsServer(0, server, ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sharedPreferences.autoMode = map[AUTO_MODE] as? Int ?: 0
|
||||
sharedPreferences.requireUnlock = map[REQUIRE_UNLOCK] as? Boolean ?: false
|
||||
sharedPreferences.autoMode = legacyBackup.autoMode?: 0
|
||||
sharedPreferences.requireUnlock = legacyBackup.requireUnlock == true
|
||||
}
|
||||
}
|
|
@ -5,9 +5,7 @@ import android.content.ContentResolver
|
|||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat.checkSelfPermission
|
||||
import ru.karasevm.privatednstoggle.R
|
||||
|
||||
@Suppress("unused")
|
||||
object PrivateDNSUtils {
|
||||
|
@ -45,15 +43,10 @@ object PrivateDNSUtils {
|
|||
}
|
||||
|
||||
fun checkForPermission(context: Context): Boolean {
|
||||
if (checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.WRITE_SECURE_SETTINGS
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
return true
|
||||
}
|
||||
Toast.makeText(context, R.string.permission_missing, Toast.LENGTH_SHORT).show()
|
||||
return false
|
||||
return checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.WRITE_SECURE_SETTINGS
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package ru.karasevm.privatednstoggle.util
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.pm.IPackageManager
|
||||
import android.os.Build
|
||||
import android.os.Process
|
||||
import android.os.UserHandle
|
||||
import android.permission.IPermissionManager
|
||||
import android.util.Log
|
||||
import org.lsposed.hiddenapibypass.HiddenApiBypass
|
||||
import rikka.shizuku.ShizukuBinderWrapper
|
||||
import rikka.shizuku.SystemServiceHelper
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.checkForPermission
|
||||
|
||||
object ShizukuUtil {
|
||||
|
||||
private const val TAG = "ShizukuUtil"
|
||||
|
||||
/**
|
||||
* Attempts to grant the WRITE_SECURE_SETTINGS permission using Shizuku.
|
||||
*
|
||||
* @param context The context from which the method is called.
|
||||
* @return True if the permission was granted successfully, false otherwise.
|
||||
*/
|
||||
fun grantPermissionWithShizuku(context: Context): Boolean {
|
||||
val packageName = context.packageName
|
||||
var userId = 0
|
||||
runCatching {
|
||||
val userHandle = Process.myUserHandle()
|
||||
userId = UserHandle::class.java.getMethod("getIdentifier").invoke(userHandle) as? Int ?: 0
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
HiddenApiBypass.addHiddenApiExemptions("Landroid/permission")
|
||||
val binder =
|
||||
ShizukuBinderWrapper(SystemServiceHelper.getSystemService("permissionmgr"))
|
||||
val pm = IPermissionManager.Stub.asInterface(binder)
|
||||
runCatching {
|
||||
pm.grantRuntimePermission(
|
||||
packageName,
|
||||
Manifest.permission.WRITE_SECURE_SETTINGS,
|
||||
userId
|
||||
)
|
||||
}.onFailure { e ->
|
||||
Log.w(TAG, "Android 12 method failed: ", e)
|
||||
runCatching {
|
||||
pm.grantRuntimePermission(
|
||||
packageName,
|
||||
Manifest.permission.WRITE_SECURE_SETTINGS,
|
||||
0,
|
||||
userId
|
||||
)
|
||||
}.onFailure { e ->
|
||||
Log.w(TAG, "Android 14 QPR2 method failed: ", e)
|
||||
runCatching {
|
||||
pm.grantRuntimePermission(
|
||||
packageName,
|
||||
Manifest.permission.WRITE_SECURE_SETTINGS,
|
||||
"default:0",
|
||||
userId
|
||||
)
|
||||
}.onFailure { e ->
|
||||
Log.w(TAG, "Android 14 QPR3 method failed: ", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val binder = ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package"))
|
||||
val pm = IPackageManager.Stub.asInterface(binder)
|
||||
runCatching {
|
||||
pm.grantRuntimePermission(
|
||||
packageName,
|
||||
Manifest.permission.WRITE_SECURE_SETTINGS,
|
||||
userId
|
||||
)
|
||||
}.onFailure { e ->
|
||||
Log.w(TAG, "Android <12 method failed: ", e)
|
||||
}
|
||||
}
|
||||
return checkForPermission(context)
|
||||
}
|
||||
}
|
|
@ -4,7 +4,8 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.MainActivity">
|
||||
tools:context=".ui.MainActivity"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/topAppBarLayout"
|
||||
|
|
1
app/src/main/res/resources.properties
Normal file
1
app/src/main/res/resources.properties
Normal file
|
@ -0,0 +1 @@
|
|||
unqualifiedResLocale=en-US
|
54
app/src/main/res/values-fr/strings.xml
Normal file
54
app/src/main/res/values-fr/strings.xml
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="done">Terminé</string>
|
||||
<string name="app_name">Private DNS Quick Toggle</string>
|
||||
<string name="dns_unknown">Inconnu</string>
|
||||
<string name="dns_off">Éteint</string>
|
||||
<string name="add_server">Ajouter un serveur</string>
|
||||
<string name="menu_add">Ajouter</string>
|
||||
<string name="menu_privacy_policy">Politique de confidentialité</string>
|
||||
<string name="delete_message">Êtes-vous sûr de vouloir supprimer le serveur ?</string>
|
||||
<string name="delete">Supprimer</string>
|
||||
<string name="server_length_error">L\'adresse du serveur ne peut pas être vide</string>
|
||||
<string name="add_edittext_label_hint">Étiquette du serveur DNS (Facultatif)</string>
|
||||
<string name="add_edittext_hint">Adresse du serveur DNS</string>
|
||||
<string name="options">Options</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="auto_option_only_off">Seulement éteint</string>
|
||||
<string name="auto_option_only_auto">Seulement automatique</string>
|
||||
<string name="auto_option_off_and_auto">Éteint et automatique</string>
|
||||
<string name="dns_auto">Automatique</string>
|
||||
<string name="tile_name">Commutateur de DNS privé</string>
|
||||
<string name="menu_save">Enregistrer</string>
|
||||
<string name="permission_missing">Autorisation non accordée, vérifiez les instructions dans l\'application</string>
|
||||
<string name="select_server">Sélectionner le serveur</string>
|
||||
<string name="auto_option_description">Définissez les options à inclure</string>
|
||||
<string name="auto_option_only_private">DNS privé seulement</string>
|
||||
<string name="open_app">Ouvrir l\'application</string>
|
||||
<string name="set_to_off_toast">DNS privé éteint</string>
|
||||
<string name="set_to_auto_toast">DNS privé réglé sur automatique</string>
|
||||
<string name="set_to_provider_toast">DNS privé réglé sur %1$s</string>
|
||||
<string name="require_unlock_setting">Requiert le déverrouillage de l\'appareil pour changer de serveur</string>
|
||||
<string name="add_server_enabled">Activé</string>
|
||||
<string name="menu_import_from_file">À partir du fichier</string>
|
||||
<string name="no_servers_added">Aucun serveur ajouté</string>
|
||||
<string name="copy_success">Copié</string>
|
||||
<string name="edit_server">Éditer le serveur</string>
|
||||
<string name="export_failure">Échec de la sauvegarde</string>
|
||||
<string name="empty_hint">Appuyez sur le bouton ci-dessous pour en ajouter un</string>
|
||||
<string name="shizuku_failure_toast">Impossible d\'obtenir l\'autorisation, veuillez l\'accorder manuellement</string>
|
||||
<string name="shizuku_success_toast">Autorisation accordée, vous pouvez désormais révoquer l\'autorisation Shizuku</string>
|
||||
<string name="menu_import_from_clipboard">À partir du presse-papier</string>
|
||||
<string name="menu_export_to_clipboard">Vers le presse-papier</string>
|
||||
<string name="menu_export_share">Partager</string>
|
||||
<string name="menu_export_to_file">Vers le fichier</string>
|
||||
<string name="export_success">Sauvegarde réussie</string>
|
||||
<string name="import_failure">Échec de l\'importation</string>
|
||||
<string name="import_failure_json">Échec de l\'importation, le fichier JSON est incorrect</string>
|
||||
<string name="import_success">Importé</string>
|
||||
<string name="menu_export">Exporter</string>
|
||||
<string name="menu_import">Importer</string>
|
||||
<string name="a11y_drag_handle">Poignée</string>
|
||||
<string name="delete_question">Supprimer</string>
|
||||
<string name="cancel">Annuler</string>
|
||||
</resources>
|
51
app/src/main/res/values-hu/strings.xml
Normal file
51
app/src/main/res/values-hu/strings.xml
Normal file
|
@ -0,0 +1,51 @@
|
|||
<resources>
|
||||
<string name="app_name">Privát DNS Gyorskapcsoló</string>
|
||||
<string name="tile_name">Privát DNS Kapcsoló</string>
|
||||
<string name="permission_missing">Nincs engedély megadva, nézd meg az alkalmazásban, hogyan adhatod meg</string>
|
||||
<string name="dns_off">Ki</string>
|
||||
<string name="dns_auto">Automatikus</string>
|
||||
<string name="dns_unknown">Ismeretlen</string>
|
||||
<string name="add_server">Szerver hozzáadása</string>
|
||||
<string name="menu_add">Hozzáadás</string>
|
||||
<string name="menu_save">Mentés</string>
|
||||
<string name="menu_privacy_policy">Adatvédelmi irányelvek</string>
|
||||
<string name="select_server">Szerver kiválasztása</string>
|
||||
<string name="done">Kész</string>
|
||||
<string name="cancel">Mégse</string>
|
||||
<string name="delete_question">Törlés</string>
|
||||
<string name="delete_message">Biztosan törölni szeretnéd a szervert?</string>
|
||||
<string name="delete">Törlés</string>
|
||||
<string name="server_length_error">A szervercím nem lehet üres</string>
|
||||
<string name="add_edittext_label_hint">DNS szerver neve (opcionális)</string>
|
||||
<string name="add_edittext_hint">DNS szerver címe</string>
|
||||
<string name="options">Beállítások</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="auto_option_description">Válaszd ki, mely opciók jelenjenek meg a csempén</string>
|
||||
<string name="auto_option_only_off">Csak ki</string>
|
||||
<string name="auto_option_only_auto">Csak automatikus</string>
|
||||
<string name="auto_option_off_and_auto">Ki és automatikus</string>
|
||||
<string name="auto_option_only_private">Csak Privát DNS</string>
|
||||
<string name="open_app">Alkalmazás megnyitása</string>
|
||||
<string name="set_to_off_toast">Privát DNS kikapcsolva</string>
|
||||
<string name="set_to_auto_toast">Privát DNS automatikus módra állítva</string>
|
||||
<string name="set_to_provider_toast">Privát DNS beállítva: %1$s</string>
|
||||
<string name="require_unlock_setting">Eszköz feloldása szükséges a szerver módosításához</string>
|
||||
<string name="a11y_drag_handle">Húzási fogantyú</string>
|
||||
<string name="menu_import">Importálás</string>
|
||||
<string name="menu_export">Exportálás</string>
|
||||
<string name="import_success">Importálva</string>
|
||||
<string name="import_failure">Importálás sikertelen</string>
|
||||
<string name="import_failure_json">Importálás sikertelen, hibás JSON</string>
|
||||
<string name="copy_success">Másolva</string>
|
||||
<string name="menu_import_from_file">Fájlból</string>
|
||||
<string name="menu_import_from_clipboard">Vágólapról</string>
|
||||
<string name="menu_export_to_clipboard">Vágólapra</string>
|
||||
<string name="menu_export_share">Megosztás</string>
|
||||
<string name="menu_export_to_file">Fájlba</string>
|
||||
<string name="export_failure">Mentés sikertelen</string>
|
||||
<string name="export_success">Sikeresen mentve</string>
|
||||
<string name="edit_server">Szerver szerkesztése</string>
|
||||
<string name="no_servers_added">Nincsenek szerverek hozzáadva</string>
|
||||
<string name="empty_hint">Koppints az alábbi gombra, hogy hozzáadj egyet</string>
|
||||
<string name="add_server_enabled">Engedélyezve</string>
|
||||
</resources>
|
49
app/src/main/res/values-mn/strings.xml
Normal file
49
app/src/main/res/values-mn/strings.xml
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="tile_name">Хувийн DNS солих</string>
|
||||
<string name="dns_off">Унтраах</string>
|
||||
<string name="dns_unknown">Тодорхойгүй</string>
|
||||
<string name="add_server">Сервер нэмэх</string>
|
||||
<string name="menu_save">Хадгалах</string>
|
||||
<string name="done">Болсон</string>
|
||||
<string name="cancel">Болих</string>
|
||||
<string name="delete_question">Устгах</string>
|
||||
<string name="delete_message">Та серверийг устгахдаа итгэлтэй байна уу?</string>
|
||||
<string name="delete">Устгах</string>
|
||||
<string name="server_length_error">Серверийн хаяг хоосон байж болохгүй</string>
|
||||
<string name="add_edittext_label_hint">DNS серверийн шошго (заавал биш)</string>
|
||||
<string name="add_edittext_hint">DNS серверийн хаяг</string>
|
||||
<string name="options">Сонголтууд</string>
|
||||
<string name="ok">ОК</string>
|
||||
<string name="auto_option_description">Хавтан дээр ямар сонголтыг оруулахаа сонгоно уу</string>
|
||||
<string name="auto_option_only_off">Зөвхөн унтарсан</string>
|
||||
<string name="auto_option_only_auto">Зөвхөн авто</string>
|
||||
<string name="auto_option_off_and_auto">Унтарсан болон авто</string>
|
||||
<string name="set_to_provider_toast">Хувийн DNS-г %1$s болгож тохируулсан</string>
|
||||
<string name="require_unlock_setting">Серверийг өөрчлөхийн тулд төхөөрөмжийн түгжээг тайлах шаардлагатай</string>
|
||||
<string name="a11y_drag_handle">Бариулыг чирэх</string>
|
||||
<string name="menu_import">Импорт</string>
|
||||
<string name="import_success">Импортолсон</string>
|
||||
<string name="import_failure">Импорт хийж чадсангүй</string>
|
||||
<string name="menu_import_from_file">Файлаас</string>
|
||||
<string name="export_failure">Хадгалж чадсангүй</string>
|
||||
<string name="export_success">Амжилттай хадгалсан</string>
|
||||
<string name="edit_server">Сервер засах</string>
|
||||
<string name="app_name">Хувийн DNS хурдан сэлгэх</string>
|
||||
<string name="menu_privacy_policy">Нууцлалын бодлого</string>
|
||||
<string name="permission_missing">Зөвшөөрөл олгоогүй. Үүнийг хэрхэн хийхийг харна уу</string>
|
||||
<string name="dns_auto">Авто</string>
|
||||
<string name="menu_add">Нэмэх</string>
|
||||
<string name="select_server">Сервер сонгох</string>
|
||||
<string name="auto_option_only_private">Зөвхөн хувийн DNS</string>
|
||||
<string name="open_app">Апп нээх</string>
|
||||
<string name="menu_export_to_clipboard">Түр санах ой руу</string>
|
||||
<string name="set_to_off_toast">Хувийн DNS унтарсан</string>
|
||||
<string name="set_to_auto_toast">Хувийн DNS-г автоматаар тохируулсан</string>
|
||||
<string name="menu_export">Экспорт</string>
|
||||
<string name="copy_success">Хуулагдсан</string>
|
||||
<string name="menu_export_share">Хуваалцах</string>
|
||||
<string name="import_failure_json">Импорт хийж чадсангүй, алдаатай JSON</string>
|
||||
<string name="menu_import_from_clipboard">Түр санах ойноос</string>
|
||||
<string name="menu_export_to_file">Файлруу</string>
|
||||
</resources>
|
54
app/src/main/res/values-pl/strings.xml
Normal file
54
app/src/main/res/values-pl/strings.xml
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="delete">Usuń</string>
|
||||
<string name="menu_save">Zapisz</string>
|
||||
<string name="menu_privacy_policy">Polityka prywatności</string>
|
||||
<string name="select_server">Wybierz serwer</string>
|
||||
<string name="cancel">Anuluj</string>
|
||||
<string name="add_edittext_hint">Adres serwera DNS</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="open_app">Otwórz aplikację</string>
|
||||
<string name="menu_import">Importuj</string>
|
||||
<string name="menu_export">Eksportuj</string>
|
||||
<string name="copy_success">Skopiowano</string>
|
||||
<string name="menu_import_from_file">Z pliku</string>
|
||||
<string name="menu_import_from_clipboard">Ze schowka</string>
|
||||
<string name="menu_export_to_clipboard">Do schowka</string>
|
||||
<string name="menu_export_share">Udostępnij</string>
|
||||
<string name="menu_export_to_file">Do pliku</string>
|
||||
<string name="edit_server">Edytuj serwer</string>
|
||||
<string name="no_servers_added">Brak dodanych serwerów</string>
|
||||
<string name="add_server_enabled">Włączone</string>
|
||||
<string name="add_server">Dodaj serwer</string>
|
||||
<string name="menu_add">Dodaj</string>
|
||||
<string name="delete_question">Usuń</string>
|
||||
<string name="dns_unknown">Nieznane</string>
|
||||
<string name="done">Gotowe</string>
|
||||
<string name="options">Opcje</string>
|
||||
<string name="import_failure">Importowanie nie powiodło się</string>
|
||||
<string name="dns_auto">Automatycznie</string>
|
||||
<string name="dns_off">Wyłącz</string>
|
||||
<string name="import_success">Zaimportowano</string>
|
||||
<string name="server_length_error">Adres serwera nie może być pusty</string>
|
||||
<string name="import_failure_json">Import nie powiódł się, zniekształcony plik JSON</string>
|
||||
<string name="export_success">Zapisano pomyślnie</string>
|
||||
<string name="delete_message">Czy na pewno chcesz usunąć serwer?</string>
|
||||
<string name="app_name">Private DNS Quick Toggle</string>
|
||||
<string name="tile_name">Przełącznik prywatnego DNS</string>
|
||||
<string name="permission_missing">Nieprzydzielono uprawnienia, sprawdź w aplikacji, w jaki sposób można to zrobić</string>
|
||||
<string name="add_edittext_label_hint">Opis serwera DNS (opcjonalnie)</string>
|
||||
<string name="auto_option_description">Wybierz opcje, które będą dostępne w kafelku</string>
|
||||
<string name="auto_option_only_off">Tylko wyłączenie</string>
|
||||
<string name="auto_option_only_auto">Tylko automatycznie</string>
|
||||
<string name="auto_option_off_and_auto">Wyłączenie i automatycznie</string>
|
||||
<string name="auto_option_only_private">Tylko prywatny DNS</string>
|
||||
<string name="set_to_auto_toast">Prywatny DNS zmieniony na automatyczny</string>
|
||||
<string name="set_to_provider_toast">Prywatny DNS zmieniony na %1$s</string>
|
||||
<string name="require_unlock_setting">Wymagaj odblokowania urządzenia do zmiany serwera</string>
|
||||
<string name="set_to_off_toast">Wyłączono Prywatny DNS</string>
|
||||
<string name="a11y_drag_handle">Przeciągnij</string>
|
||||
<string name="export_failure">Zapisywanie nie powiodło się</string>
|
||||
<string name="empty_hint">Kliknij na poniższy przycisk, aby dodać nowy</string>
|
||||
<string name="shizuku_success_toast">Udzielono zezwolenia, możesz teraz cofnąć zezwolenie w Shizuku</string>
|
||||
<string name="shizuku_failure_toast">Uzyskanie uprawnień nie powiodło się, udziel ich ręcznie</string>
|
||||
</resources>
|
54
app/src/main/res/values-pt-rBR/strings.xml
Normal file
54
app/src/main/res/values-pt-rBR/strings.xml
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="import_success">Importado</string>
|
||||
<string name="permission_missing">Permissão não concedida, verifique o app para saber como prosseguir</string>
|
||||
<string name="app_name">Alteração de DNS privado</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="add_server">Adicionar servidor</string>
|
||||
<string name="add_edittext_hint">Endereço do servidor DNS</string>
|
||||
<string name="auto_option_description">Escolha opções disponível em atalho</string>
|
||||
<string name="tile_name">Alteração de DNS privado</string>
|
||||
<string name="dns_off">Desativado</string>
|
||||
<string name="dns_auto">Automático</string>
|
||||
<string name="dns_unknown">Indeterminado</string>
|
||||
<string name="menu_add">Adicionar</string>
|
||||
<string name="menu_save">Salvar</string>
|
||||
<string name="menu_privacy_policy">Política de privacidade</string>
|
||||
<string name="done">Concluído</string>
|
||||
<string name="delete_question">Apagar</string>
|
||||
<string name="delete_message">Tem certeza de que quer apagar o servidor?</string>
|
||||
<string name="delete">Apagar</string>
|
||||
<string name="server_length_error">O endereço do servidor não pode estar em branco</string>
|
||||
<string name="add_edittext_label_hint">Identificação do servidor DNS (opcional)</string>
|
||||
<string name="options">Opções</string>
|
||||
<string name="ok">Ok</string>
|
||||
<string name="auto_option_only_off">Somente desativado</string>
|
||||
<string name="auto_option_off_and_auto">Desativado e automático</string>
|
||||
<string name="auto_option_only_private">Somente DNS privado</string>
|
||||
<string name="open_app">Abrir app</string>
|
||||
<string name="set_to_off_toast">DNS privado desativado</string>
|
||||
<string name="set_to_auto_toast">DNS privado definido para automático</string>
|
||||
<string name="set_to_provider_toast">DNS privado definido para %1$s</string>
|
||||
<string name="a11y_drag_handle">Arrastre</string>
|
||||
<string name="menu_import">Importar</string>
|
||||
<string name="menu_export">Exportar</string>
|
||||
<string name="import_failure_json">Falha na importação, JSON malformado</string>
|
||||
<string name="copy_success">Copiado</string>
|
||||
<string name="menu_import_from_clipboard">Da memória</string>
|
||||
<string name="menu_export_share">Compartilhar</string>
|
||||
<string name="menu_export_to_file">Para arquivo</string>
|
||||
<string name="export_success">Salvo com sucesso</string>
|
||||
<string name="edit_server">Editar servidor</string>
|
||||
<string name="no_servers_added">Nenhum servidor adicionado</string>
|
||||
<string name="empty_hint">Toque no botão abaixo para adicionar</string>
|
||||
<string name="add_server_enabled">Ativado</string>
|
||||
<string name="select_server">Escolha servidor</string>
|
||||
<string name="menu_export_to_clipboard">Para memória</string>
|
||||
<string name="menu_import_from_file">De arquivo</string>
|
||||
<string name="import_failure">Falha ao importar</string>
|
||||
<string name="export_failure">Falha ao salvar</string>
|
||||
<string name="require_unlock_setting">Necessário desbloquear o dispositivo para alterar servidor</string>
|
||||
<string name="auto_option_only_auto">Somente automático</string>
|
||||
<string name="shizuku_failure_toast">Falha ao obter a permissão. Tente conceder manualmente</string>
|
||||
<string name="shizuku_success_toast">Permissão concedida, você pode revogar a permissão do Shizuku agora</string>
|
||||
</resources>
|
53
app/src/main/res/values-ru/strings.xml
Normal file
53
app/src/main/res/values-ru/strings.xml
Normal file
|
@ -0,0 +1,53 @@
|
|||
<resources>
|
||||
<string name="app_name">Private DNS Quick Toggle</string>
|
||||
<string name="tile_name">Переключить частный DNS</string>
|
||||
<string name="permission_missing">Разрешение не предоставлено, проверьте приложение для получения информации</string>
|
||||
<string name="dns_off">Выкл</string>
|
||||
<string name="dns_auto">Авто</string>
|
||||
<string name="dns_unknown">Неизвестно</string>
|
||||
<string name="add_server">Добавить сервер</string>
|
||||
<string name="menu_add">Добавить</string>
|
||||
<string name="menu_save">Сохранить</string>
|
||||
<string name="menu_privacy_policy">Политика конфиденциальности</string>
|
||||
<string name="select_server">Выбрать сервер</string>
|
||||
<string name="done">Готово</string>
|
||||
<string name="cancel">Отмена</string>
|
||||
<string name="delete_question">Удалить</string>
|
||||
<string name="delete_message">Вы уверены, что хотите удалить сервер?</string>
|
||||
<string name="delete">Удалить</string>
|
||||
<string name="server_length_error">Адрес сервера не может быть пустым</string>
|
||||
<string name="add_edittext_label_hint">Название DNS сервера (необязательно)</string>
|
||||
<string name="add_edittext_hint">Адрес DNS сервера</string>
|
||||
<string name="options">Опции</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="auto_option_description">Выберите, какие опции включить в плитке</string>
|
||||
<string name="auto_option_only_off">Только \"Выкл\"</string>
|
||||
<string name="auto_option_only_auto">Только \"Авто\"</string>
|
||||
<string name="auto_option_off_and_auto">\"Выкл\" и \"Авто\"</string>
|
||||
<string name="auto_option_only_private">Только частный DNS</string>
|
||||
<string name="open_app">Открыть приложение</string>
|
||||
<string name="set_to_off_toast">Частный DNS выключен</string>
|
||||
<string name="set_to_auto_toast">Частный DNS установлен на "Авто"</string>
|
||||
<string name="set_to_provider_toast">Частный DNS установлен на %1$s</string>
|
||||
<string name="require_unlock_setting">Смена сервера требует разблокировки устройства</string>
|
||||
<string name="a11y_drag_handle">Ручка перетаскивания</string>
|
||||
<string name="menu_import">Импорт</string>
|
||||
<string name="menu_export">Экспорт</string>
|
||||
<string name="import_success">Успешно импортировано</string>
|
||||
<string name="import_failure">Импорт не удался</string>
|
||||
<string name="import_failure_json">Импорт не удался, некорректный JSON</string>
|
||||
<string name="copy_success">Скопировано</string>
|
||||
<string name="menu_import_from_file">Из файла</string>
|
||||
<string name="menu_import_from_clipboard">Из буфера обмена</string>
|
||||
<string name="menu_export_to_clipboard">В буфер обмена</string>
|
||||
<string name="menu_export_share">Поделиться</string>
|
||||
<string name="menu_export_to_file">В файл</string>
|
||||
<string name="export_failure">Сохранение не удалось</string>
|
||||
<string name="export_success">Успешно сохранено</string>
|
||||
<string name="edit_server">Редактировать сервер</string>
|
||||
<string name="no_servers_added">Нет доступных серверов</string>
|
||||
<string name="empty_hint">Нажмите на кнопку ниже, чтобы добавить сервер</string>
|
||||
<string name="add_server_enabled">Включён</string>
|
||||
<string name="shizuku_success_toast">Разрешение получено, можно отозвать авторизацию Shizuku</string>
|
||||
<string name="shizuku_failure_toast">Не удалось получить разрешение, предоставьте его вручную</string>
|
||||
</resources>
|
54
app/src/main/res/values-ta/strings.xml
Normal file
54
app/src/main/res/values-ta/strings.xml
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="ok">சரி</string>
|
||||
<string name="app_name">தனியார் டி.என்.எச் விரைவாக மாற்று</string>
|
||||
<string name="tile_name">தனியார் டி.என் கள் மாறுகின்றன</string>
|
||||
<string name="permission_missing">இசைவு வழங்கப்படவில்லை, அதை எப்படி செய்வது என்று பார்க்க பயன்பாட்டை சரிபார்க்கவும்</string>
|
||||
<string name="dns_off">அணை</string>
|
||||
<string name="dns_auto">தானி</string>
|
||||
<string name="dns_unknown">தெரியவில்லை</string>
|
||||
<string name="add_server">சேவையகத்தைச் சேர்க்கவும்</string>
|
||||
<string name="menu_add">கூட்டு</string>
|
||||
<string name="menu_save">சேமி</string>
|
||||
<string name="menu_privacy_policy">தனியுரிமைக் கொள்கை</string>
|
||||
<string name="select_server">சேவையகத்தைத் தேர்ந்தெடுக்கவும்</string>
|
||||
<string name="done">முடிந்தது</string>
|
||||
<string name="cancel">ரத்துசெய்</string>
|
||||
<string name="delete_question">நீக்கு</string>
|
||||
<string name="delete_message">சேவையகத்தை நீக்க விரும்புகிறீர்களா?</string>
|
||||
<string name="delete">நீக்கு</string>
|
||||
<string name="add_edittext_hint">டிஎன்எச் சேவையக முகவரி</string>
|
||||
<string name="options">விருப்பங்கள்</string>
|
||||
<string name="auto_option_description">ஓடுகளில் எந்த விருப்பங்களைச் சேர்க்க வேண்டும் என்பதைத் தேர்வுசெய்க</string>
|
||||
<string name="auto_option_only_off">மட்டுமே</string>
|
||||
<string name="auto_option_only_auto">ஆட்டோ மட்டுமே</string>
|
||||
<string name="auto_option_off_and_auto">ஆஃப் மற்றும் ஆட்டோ</string>
|
||||
<string name="auto_option_only_private">தனியார் டி.என்.எச் மட்டுமே</string>
|
||||
<string name="open_app">திறந்த பயன்பாடு</string>
|
||||
<string name="set_to_off_toast">தனியார் டி.என்.எச் அணைக்கப்பட்டது</string>
|
||||
<string name="set_to_auto_toast">தனியார் டி.என்.எச் ஆட்டோவாக அமைக்கப்பட்டுள்ளது</string>
|
||||
<string name="set_to_provider_toast">தனியார் டி.என்.எச் %1$s என அமைக்கப்பட்டுள்ளது</string>
|
||||
<string name="require_unlock_setting">சேவையகத்தை மாற்ற சாதனத்தைத் திறக்க வேண்டும்</string>
|
||||
<string name="a11y_drag_handle">இழுவை கைப்பிடி</string>
|
||||
<string name="menu_import">இறக்குமதி</string>
|
||||
<string name="menu_export">ஏற்றுமதி</string>
|
||||
<string name="import_success">இறக்குமதி செய்யப்பட்டது</string>
|
||||
<string name="import_failure">இறக்குமதி தோல்வியடைந்தது</string>
|
||||
<string name="import_failure_json">இறக்குமதி தோல்வியுற்றது, தவறாக சாதொபொகு</string>
|
||||
<string name="copy_success">நகலெடுக்கப்பட்டது</string>
|
||||
<string name="menu_import_from_file">கோப்பிலிருந்து</string>
|
||||
<string name="menu_import_from_clipboard">கிளிப்போர்டிலிருந்து</string>
|
||||
<string name="menu_export_to_clipboard">இடைநிலைப்பலகைக்கு</string>
|
||||
<string name="menu_export_share">பங்கு</string>
|
||||
<string name="menu_export_to_file">தாக்கல் செய்ய</string>
|
||||
<string name="export_failure">சேமிப்பு தோல்வியடைந்தது</string>
|
||||
<string name="export_success">வெற்றிகரமாக சேமிக்கப்பட்டது</string>
|
||||
<string name="edit_server">சேவையகத்தைத் திருத்து</string>
|
||||
<string name="no_servers_added">சேவையகங்கள் எதுவும் சேர்க்கப்படவில்லை</string>
|
||||
<string name="empty_hint">ஒன்றைச் சேர்க்க கீழே உள்ள பொத்தானைத் தட்டவும்</string>
|
||||
<string name="add_server_enabled">இயக்கப்பட்டது</string>
|
||||
<string name="shizuku_success_toast">இசைவு வழங்கப்பட்டது, நீங்கள் இப்போது சிசுகு அனுமதியை ரத்து செய்யலாம்</string>
|
||||
<string name="shizuku_failure_toast">இசைவு பெறுவதில் தோல்வி, தயவுசெய்து அதை கைமுறையாக வழங்கவும்</string>
|
||||
<string name="server_length_error">சேவையக முகவரி காலியாக இருக்க முடியாது</string>
|
||||
<string name="add_edittext_label_hint">டிஎன்எச் சேவையக சிட்டை (விரும்பினால்)</string>
|
||||
</resources>
|
54
app/src/main/res/values-tr/strings.xml
Normal file
54
app/src/main/res/values-tr/strings.xml
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="delete_message">Sunucuyu silmek istediğinizden emin misiniz?</string>
|
||||
<string name="auto_option_only_off">Sadece kapalı</string>
|
||||
<string name="require_unlock_setting">Sunucuyu değiştirmek için cihazın kilidini açmanız gerekiyor</string>
|
||||
<string name="dns_auto">Oto</string>
|
||||
<string name="dns_unknown">Bilinmeyen</string>
|
||||
<string name="done">Tamam</string>
|
||||
<string name="cancel">İptal</string>
|
||||
<string name="add_edittext_label_hint">DNS sunucusu Etiketi (İsteğe bağlı)</string>
|
||||
<string name="options">Seçenekler</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="auto_option_only_auto">Sadece oto</string>
|
||||
<string name="auto_option_off_and_auto">Kapalı ve oto</string>
|
||||
<string name="set_to_off_toast">Özel DNS kapandı</string>
|
||||
<string name="open_app">Uygulamayı açınız</string>
|
||||
<string name="menu_import">İçe aktar</string>
|
||||
<string name="menu_export">Dışa aktar</string>
|
||||
<string name="import_success">içe aktarıldı</string>
|
||||
<string name="menu_export_to_file">Dosyaya</string>
|
||||
<string name="export_failure">Kaydetme başarısız oldu</string>
|
||||
<string name="no_servers_added">Hiç Sunucu Eklenmedi</string>
|
||||
<string name="empty_hint">Eklemek için aşağıdaki düğmeye dokunun</string>
|
||||
<string name="shizuku_failure_toast">İzin alınamadı, lütfen manuel olarak verin</string>
|
||||
<string name="auto_option_description">Karoya hangi seçeneklerin dahil edileceğini seçiniz</string>
|
||||
<string name="dns_off">Kapalı</string>
|
||||
<string name="menu_privacy_policy">Gizlilik Politikası</string>
|
||||
<string name="delete_question">Sil</string>
|
||||
<string name="server_length_error">Sunucu adresi boş olamaz</string>
|
||||
<string name="delete">Sil</string>
|
||||
<string name="add_edittext_hint">DNS sunucu adresi</string>
|
||||
<string name="a11y_drag_handle">tutacağı sürükle</string>
|
||||
<string name="menu_add">Ekle</string>
|
||||
<string name="add_server">Sunucu Ekleyiniz</string>
|
||||
<string name="permission_missing">İzin verilmedi, nasıl yapıldığını görmek için uygulamayı kontrol ediniz</string>
|
||||
<string name="import_failure_json">İçe aktarma başarısız oldu, hatalı biçimlendirilmiş JSON</string>
|
||||
<string name="app_name">Özel DNS Hızlı Geçiş</string>
|
||||
<string name="shizuku_success_toast">İzin verildi, şimdi Shizuku iznini iptal edebilirsiniz</string>
|
||||
<string name="import_failure">Aktarma başarısız</string>
|
||||
<string name="menu_import_from_clipboard">Panodan</string>
|
||||
<string name="export_success">Başarıyla kaydedildi</string>
|
||||
<string name="menu_save">Kaydet</string>
|
||||
<string name="set_to_auto_toast">Özel DNS otomatik olarak ayarlandı</string>
|
||||
<string name="set_to_provider_toast">Özel DNS %1$s olarak ayarlandı</string>
|
||||
<string name="auto_option_only_private">Sadece Özel DNS</string>
|
||||
<string name="select_server">Sunucuyu Seçin</string>
|
||||
<string name="copy_success">Kopyalandı</string>
|
||||
<string name="edit_server">Sunucuyu düzenle</string>
|
||||
<string name="add_server_enabled">Etkin</string>
|
||||
<string name="tile_name">Özel DNS Geçişi</string>
|
||||
<string name="menu_import_from_file">Dosyadan</string>
|
||||
<string name="menu_export_to_clipboard">Panoya</string>
|
||||
<string name="menu_export_share">Paylaş</string>
|
||||
</resources>
|
52
app/src/main/res/values-vi/strings.xml
Normal file
52
app/src/main/res/values-vi/strings.xml
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="add_server">Thêm máy chủ</string>
|
||||
<string name="auto_option_only_auto">Chỉ tự động</string>
|
||||
<string name="permission_missing">Chưa được cấp quyền, hãy kiểm tra ứng dụng để biết cách thực hiện</string>
|
||||
<string name="dns_off">Tắt</string>
|
||||
<string name="dns_auto">Tự động</string>
|
||||
<string name="menu_add">Thêm</string>
|
||||
<string name="menu_privacy_policy">Chính sách bảo mật</string>
|
||||
<string name="select_server">Chọn máy chủ</string>
|
||||
<string name="done">Hoàn thành</string>
|
||||
<string name="cancel">Hủy</string>
|
||||
<string name="delete_message">Bạn có chắc chắn muốn xóa máy chủ không?</string>
|
||||
<string name="delete">Xoá</string>
|
||||
<string name="add_edittext_hint">Địa chỉ máy chủ DNS</string>
|
||||
<string name="options">Tùy chọn</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="auto_option_description">Chọn các tùy chọn để đưa vào ô</string>
|
||||
<string name="auto_option_only_off">Chỉ tắt</string>
|
||||
<string name="auto_option_off_and_auto">Tắt và tự động</string>
|
||||
<string name="open_app">Mở ứng dụng</string>
|
||||
<string name="set_to_auto_toast">DNS cá nhân được thiết lập tự động</string>
|
||||
<string name="require_unlock_setting">Yêu cầu mở khóa thiết bị để thay đổi máy chủ</string>
|
||||
<string name="a11y_drag_handle">Tay cầm kéo</string>
|
||||
<string name="menu_export">Xuất</string>
|
||||
<string name="import_success">Đã nhập</string>
|
||||
<string name="import_failure">Nhập thất bại</string>
|
||||
<string name="import_failure_json">Nhập thất bại, JSON bị lỗi</string>
|
||||
<string name="copy_success">Đã sao chép</string>
|
||||
<string name="menu_import_from_file">Từ tập tin</string>
|
||||
<string name="menu_export_share">Chia sẻ</string>
|
||||
<string name="menu_export_to_file">Thành tập tin</string>
|
||||
<string name="export_failure">Lưu không thành công</string>
|
||||
<string name="edit_server">Chỉnh sửa máy chủ</string>
|
||||
<string name="no_servers_added">Chưa có máy chủ nào</string>
|
||||
<string name="empty_hint">Nhấn vào nút bên dưới để thêm</string>
|
||||
<string name="add_server_enabled">Đã bật</string>
|
||||
<string name="tile_name">Chuyển đổi DNS cá nhân</string>
|
||||
<string name="delete_question">Xoá</string>
|
||||
<string name="app_name">Chuyển đổi nhanh DNS cá nhân</string>
|
||||
<string name="dns_unknown">Không rõ</string>
|
||||
<string name="add_edittext_label_hint">Nhãn máy chủ DNS (Không bắt buộc)</string>
|
||||
<string name="menu_save">Lưu</string>
|
||||
<string name="server_length_error">Địa chỉ máy chủ không được để trống</string>
|
||||
<string name="auto_option_only_private">Chỉ DNS cá nhân</string>
|
||||
<string name="set_to_off_toast">Đã tắt DNS cá nhân</string>
|
||||
<string name="set_to_provider_toast">DNS cá nhân được đặt thành %1$s</string>
|
||||
<string name="menu_import">Nhập</string>
|
||||
<string name="menu_import_from_clipboard">Từ bảng nhớ tạm</string>
|
||||
<string name="menu_export_to_clipboard">Vào bảng nhớ tạm</string>
|
||||
<string name="export_success">Đã lưu thành công</string>
|
||||
</resources>
|
51
app/src/main/res/values-zh-rCN/strings.xml
Normal file
51
app/src/main/res/values-zh-rCN/strings.xml
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="tile_name">私有DNS触发</string>
|
||||
<string name="permission_missing">必要权限未授予,请看相关说明</string>
|
||||
<string name="dns_off">关闭</string>
|
||||
<string name="dns_auto">自动</string>
|
||||
<string name="dns_unknown">未知</string>
|
||||
<string name="add_server">添加DNS服务器</string>
|
||||
<string name="menu_add">添加</string>
|
||||
<string name="menu_save">存储</string>
|
||||
<string name="menu_privacy_policy">隐私策略</string>
|
||||
<string name="select_server">选择服务器</string>
|
||||
<string name="done">完成</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="delete_question">删除条目</string>
|
||||
<string name="delete_message">你确认要删除这个服务器条目吗?</string>
|
||||
<string name="delete">删除</string>
|
||||
<string name="server_length_error">服务器地址不可为空</string>
|
||||
<string name="add_edittext_label_hint">DNS服务器标识</string>
|
||||
<string name="add_edittext_hint">DNS服务器地址</string>
|
||||
<string name="options">选项</string>
|
||||
<string name="ok">确认</string>
|
||||
<string name="auto_option_description">选择要在磁贴中启用的选项</string>
|
||||
<string name="auto_option_only_off">仅“关闭”</string>
|
||||
<string name="auto_option_only_auto">仅“自动”</string>
|
||||
<string name="auto_option_off_and_auto">“关闭“与”自动“</string>
|
||||
<string name="auto_option_only_private">仅设置的私有DNS</string>
|
||||
<string name="open_app">打开软件</string>
|
||||
<string name="set_to_off_toast">不使用私有DNS</string>
|
||||
<string name="set_to_auto_toast">自动使用私有DNS</string>
|
||||
<string name="set_to_provider_toast">设置为使用私有DNS\"%1$s\"</string>
|
||||
<string name="require_unlock_setting">更改服务器设置要求设备解锁</string>
|
||||
<string name="a11y_drag_handle">拖动把手</string>
|
||||
<string name="menu_import">导入</string>
|
||||
<string name="menu_export">导出</string>
|
||||
<string name="import_success">已导入</string>
|
||||
<string name="import_failure">导入失败</string>
|
||||
<string name="import_failure_json">导入失败,json格式异常</string>
|
||||
<string name="copy_success">已复制</string>
|
||||
<string name="menu_import_from_file">从文件导入</string>
|
||||
<string name="menu_import_from_clipboard">从剪贴板导入</string>
|
||||
<string name="menu_export_to_clipboard">导出至剪贴板</string>
|
||||
<string name="menu_export_share">分享</string>
|
||||
<string name="menu_export_to_file">导出至文件</string>
|
||||
<string name="export_failure">保存失败</string>
|
||||
<string name="export_success">保存成功</string>
|
||||
<string name="edit_server">编辑服务器条目</string>
|
||||
<string name="no_servers_added">无可用服务器</string>
|
||||
<string name="empty_hint">点击下方\"+\"添加一个吧</string>
|
||||
<string name="add_server_enabled">已启用</string>
|
||||
</resources>
|
|
@ -48,4 +48,6 @@
|
|||
<string name="no_servers_added">No Servers Added</string>
|
||||
<string name="empty_hint">Tap on the button below to add one</string>
|
||||
<string name="add_server_enabled">Enabled</string>
|
||||
<string name="shizuku_success_toast">Permission granted, you can revoke the Shizuku permission now</string>
|
||||
<string name="shizuku_failure_toast">Failed to acquire permission, please grant it manually</string>
|
||||
</resources>
|
|
@ -5,18 +5,15 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.5.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.10"
|
||||
classpath("com.android.tools.build:gradle:8.8.1")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.20")
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.android' version '1.9.10' apply false
|
||||
id "com.google.devtools.ksp" version "1.9.10-1.0.13" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.0.20" apply false
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "2.0.20"
|
||||
id("com.google.devtools.ksp") version "2.0.20-1.0.25" apply false
|
||||
}
|
||||
|
||||
tasks.register('clean', Delete) {
|
||||
delete rootProject.layout.buildDirectory
|
||||
}
|
11
fastlane/metadata/android/en-US/changelogs/17.txt
Normal file
11
fastlane/metadata/android/en-US/changelogs/17.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
- Replaced server storage backend with Room, allowing for easier further expansion
|
||||
- Add option to disable saved servers
|
||||
- Improved backup handling
|
||||
- Fixed desync bug while dragging servers
|
||||
- Reorganized source file structure
|
||||
- Updated Kotlin version
|
||||
- Updated Java version
|
||||
- Replaced gson with kotlinx.serialization
|
||||
- Add Chinese Simplified translation (thanks @WeiguangTWK)
|
||||
- Add Russian translation
|
||||
- Fixed issue with provider not resetting when disabled through the dialog
|
12
fastlane/metadata/android/en-US/changelogs/18.txt
Normal file
12
fastlane/metadata/android/en-US/changelogs/18.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
- Add Shizuku support for newer Android versions
|
||||
- Fix some crashes
|
||||
- Improve Shizuku process feedback
|
||||
- Fix Shizuku when not running as the primary user
|
||||
- Hungarian translation by @Pacuka in https://github.com/karasevm/PrivateDNSAndroid/pull/43
|
||||
- Add Polish translation (Michal L (@chuckmichael), Eryk Michalak (gnu-ewm))
|
||||
- Add Mongolian translation (Purevbaatar Tuvshinjargal (@puujee0238))
|
||||
- Add Portuguese (Brazil) translation (ajan, Víctor Assunção (@JoaoVictorAS))
|
||||
- Add Vietnamese translation (tuấn nguyễn (@Tuan1-2-3))
|
||||
- Add French translation (papaindiatango)
|
||||
- Add Tamil translation (தமிழ்நேரம் (@TamilNeram))
|
||||
- Add Turkish translation (Mustafa A. (mistiik99))
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
#Mon Aug 16 15:36:35 MSK 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -6,4 +6,4 @@ dependencyResolutionManagement {
|
|||
}
|
||||
}
|
||||
rootProject.name = "Private DNS Quick Toggle"
|
||||
include ':app'
|
||||
include("app")
|
Loading…
Add table
Add a link
Reference in a new issue