mirror of
https://github.com/karasevm/PrivateDNSAndroid.git
synced 2025-06-28 20:29:56 +00:00
Merge e477df4cf5
into bd61fe6748
This commit is contained in:
commit
78c98595d9
9 changed files with 241 additions and 89 deletions
14
app/src/debug/res/xml/shortcuts.xml
Normal file
14
app/src/debug/res/xml/shortcuts.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<shortcut
|
||||
android:shortcutId="switch_mode"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_outline_next_24dp"
|
||||
android:shortcutShortLabel="@string/shortcut_switch_mode">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetPackage="ru.karasevm.privatednstoggle.dev"
|
||||
android:targetClass="ru.karasevm.privatednstoggle.ui.SettingsDialogActivity"
|
||||
android:data="privatednstoggle://switch_mode"/>
|
||||
</shortcut>
|
||||
</shortcuts>
|
|
@ -1,7 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_SECURE_SETTINGS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
|
||||
<application
|
||||
|
@ -11,6 +13,10 @@
|
|||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Transparent">
|
||||
<service
|
||||
android:name=".service.ShortcutService"
|
||||
android:enabled="true"
|
||||
android:exported="true"></service>
|
||||
|
||||
<provider
|
||||
android:name="rikka.shizuku.ShizukuProvider"
|
||||
|
@ -18,23 +24,24 @@
|
|||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:theme="@style/Theme.MyApplication"
|
||||
android:taskAffinity="${applicationId}.main"
|
||||
android:exported="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:exported="true">
|
||||
android:taskAffinity="${applicationId}.main"
|
||||
android:theme="@style/Theme.MyApplication">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.SettingsDialogActivity"
|
||||
android:theme="@style/Theme.Transparent"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true">
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Transparent">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||
</intent-filter>
|
||||
|
|
|
@ -5,10 +5,8 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
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
|
||||
|
@ -18,26 +16,20 @@ import kotlinx.coroutines.launch
|
|||
import ru.karasevm.privatednstoggle.PrivateDNSApp
|
||||
import ru.karasevm.privatednstoggle.R
|
||||
import ru.karasevm.privatednstoggle.data.DnsServerRepository
|
||||
import ru.karasevm.privatednstoggle.model.DnsServer
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper.autoMode
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper.requireUnlock
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.AUTO_MODE_OPTION_AUTO
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.AUTO_MODE_OPTION_OFF_AUTO
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.AUTO_MODE_OPTION_PRIVATE
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.DNS_MODE_AUTO
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.DNS_MODE_OFF
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.DNS_MODE_PRIVATE
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.checkForPermission
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.getNextAddress
|
||||
|
||||
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,35 +45,11 @@ class DnsTileService : TileService() {
|
|||
* Set's the state of the tile and system settings to the next state
|
||||
*/
|
||||
private fun cycleState() {
|
||||
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 (sharedPreferences.autoMode == AUTO_MODE_OPTION_AUTO || sharedPreferences.autoMode == AUTO_MODE_OPTION_OFF_AUTO) {
|
||||
changeDNSServer(DNS_MODE_AUTO, dnsProvider)
|
||||
} else {
|
||||
changeDNSServer(DNS_MODE_PRIVATE, dnsProvider)
|
||||
}
|
||||
|
||||
} else if (dnsMode == null || dnsMode.equals(DNS_MODE_AUTO, ignoreCase = true)) {
|
||||
changeDNSServer(DNS_MODE_PRIVATE, null)
|
||||
} else if (dnsMode.equals(DNS_MODE_PRIVATE, ignoreCase = true)) {
|
||||
scope.launch {
|
||||
if (getNextAddress(dnsProvider) == null) {
|
||||
if (sharedPreferences.autoMode == AUTO_MODE_OPTION_PRIVATE) {
|
||||
changeDNSServer(DNS_MODE_PRIVATE, null)
|
||||
} else {
|
||||
if (sharedPreferences.autoMode == AUTO_MODE_OPTION_AUTO) {
|
||||
changeDNSServer(DNS_MODE_AUTO, dnsProvider)
|
||||
} else {
|
||||
changeDNSServer(DNS_MODE_OFF, dnsProvider)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
changeDNSServer(DNS_MODE_PRIVATE, dnsProvider)
|
||||
}
|
||||
}
|
||||
}
|
||||
PrivateDNSUtils.getNextProvider(sharedPrefs, scope, repository, contentResolver, onNext = { mode, provider ->
|
||||
changeDNSServer(mode, provider)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,7 +83,7 @@ class DnsTileService : TileService() {
|
|||
|
||||
DNS_MODE_PRIVATE -> {
|
||||
scope.launch {
|
||||
val nextDnsServer = getNextAddress(dnsProvider)
|
||||
val nextDnsServer = getNextAddress(repository, dnsProvider)
|
||||
if (nextDnsServer != null) {
|
||||
changeTileState(
|
||||
qsTile,
|
||||
|
@ -123,7 +91,7 @@ class DnsTileService : TileService() {
|
|||
nextDnsServer.label.ifEmpty { nextDnsServer.server },
|
||||
R.drawable.ic_private_black_24dp,
|
||||
DNS_MODE_PRIVATE,
|
||||
getNextAddress(dnsProvider)?.server
|
||||
getNextAddress(repository, dnsProvider)?.server
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -136,9 +104,10 @@ class DnsTileService : TileService() {
|
|||
if (!checkForPermission(this)) {
|
||||
return
|
||||
}
|
||||
val sharedPrefs = PreferenceHelper.defaultPreference(this)
|
||||
|
||||
// Require unlock to change mode according to user preference
|
||||
val requireUnlock = sharedPreferences.requireUnlock
|
||||
val requireUnlock = sharedPrefs.requireUnlock
|
||||
if (isLocked && requireUnlock) {
|
||||
unlockAndRun(this::cycleState)
|
||||
} else {
|
||||
|
@ -152,13 +121,12 @@ 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")
|
||||
val dnsMode = PrivateDNSUtils.getPrivateMode(contentResolver)
|
||||
when (dnsMode?.lowercase()) {
|
||||
DNS_MODE_OFF -> {
|
||||
setTile(
|
||||
qsTile,
|
||||
if (!isPermissionGranted) Tile.STATE_UNAVAILABLE else Tile.STATE_INACTIVE,
|
||||
Tile.STATE_INACTIVE,
|
||||
getString(R.string.dns_off),
|
||||
R.drawable.ic_off_black_24dp
|
||||
)
|
||||
|
@ -167,7 +135,7 @@ class DnsTileService : TileService() {
|
|||
DNS_MODE_AUTO -> {
|
||||
setTile(
|
||||
qsTile,
|
||||
if (!isPermissionGranted) Tile.STATE_UNAVAILABLE else Tile.STATE_INACTIVE,
|
||||
Tile.STATE_INACTIVE,
|
||||
getString(R.string.dns_auto),
|
||||
R.drawable.ic_auto_black_24dp
|
||||
)
|
||||
|
@ -176,11 +144,11 @@ class DnsTileService : TileService() {
|
|||
DNS_MODE_PRIVATE -> {
|
||||
scope.launch {
|
||||
val activeAddress =
|
||||
Settings.Global.getString(contentResolver, "private_dns_specifier")
|
||||
val dnsServer = repository.getFirstByServer(activeAddress)
|
||||
PrivateDNSUtils.getPrivateProvider(contentResolver)
|
||||
val dnsServer = repository.getFirstByServer(activeAddress!!)
|
||||
setTile(
|
||||
qsTile,
|
||||
if (!isPermissionGranted) Tile.STATE_UNAVAILABLE else Tile.STATE_ACTIVE,
|
||||
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
|
||||
|
@ -191,7 +159,7 @@ class DnsTileService : TileService() {
|
|||
else -> {
|
||||
setTile(
|
||||
qsTile,
|
||||
if (!isPermissionGranted) Tile.STATE_UNAVAILABLE else Tile.STATE_INACTIVE,
|
||||
Tile.STATE_INACTIVE,
|
||||
getString(R.string.dns_unknown),
|
||||
R.drawable.ic_unknown_black_24dp
|
||||
)
|
||||
|
@ -207,10 +175,12 @@ 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,17 +192,14 @@ class DnsTileService : TileService() {
|
|||
IntentFilter("refresh_tile"),
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED
|
||||
)
|
||||
isBroadcastReceiverRegistered = true
|
||||
|
||||
refreshTile()
|
||||
|
||||
}
|
||||
|
||||
override fun onStopListening() {
|
||||
super.onStopListening()
|
||||
if (isBroadcastReceiverRegistered) {
|
||||
unregisterReceiver(broadcastReceiver)
|
||||
isBroadcastReceiverRegistered = false
|
||||
}
|
||||
|
||||
unregisterReceiver(broadcastReceiver)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -280,23 +247,4 @@ class DnsTileService : TileService() {
|
|||
PrivateDNSUtils.setPrivateProvider(contentResolver, dnsProvider)
|
||||
tile.updateTile()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets next dns address from the database,
|
||||
* if current address is last or unknown returns null
|
||||
*
|
||||
* @param currentAddress currently set address
|
||||
* @return next address
|
||||
*/
|
||||
private suspend fun getNextAddress(currentAddress: String?): DnsServer? {
|
||||
return if (currentAddress.isNullOrEmpty()) {
|
||||
repository.getFirstEnabled()
|
||||
} else {
|
||||
repository.getNextByServer(currentAddress)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "DnsTileService"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package ru.karasevm.privatednstoggle.service
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import android.widget.Toast
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.karasevm.privatednstoggle.PrivateDNSApp
|
||||
import ru.karasevm.privatednstoggle.data.DnsServerRepository
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.DNS_MODE_AUTO
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.DNS_MODE_OFF
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.DNS_MODE_PRIVATE
|
||||
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.checkForPermission
|
||||
|
||||
class ShortcutService : Service() {
|
||||
|
||||
private val repository: DnsServerRepository by lazy { (application as PrivateDNSApp).repository }
|
||||
private val job = SupervisorJob()
|
||||
private val scope = CoroutineScope(Dispatchers.IO + job)
|
||||
|
||||
companion object {
|
||||
private const val ACTION_SWITCH_MODE = "privatednstoggle://switch_mode"
|
||||
}
|
||||
|
||||
private fun setDnsModeAndShowToast(dnsMode: String) {
|
||||
if(dnsMode == DNS_MODE_PRIVATE) {
|
||||
val provider = PrivateDNSUtils.getPrivateProvider(contentResolver)
|
||||
if (provider == null) {
|
||||
PrivateDNSUtils.setPrivateProvider(contentResolver, "dns.google")
|
||||
}
|
||||
}
|
||||
PrivateDNSUtils.setPrivateMode(contentResolver, dnsMode)
|
||||
val text = when (dnsMode) {
|
||||
DNS_MODE_OFF -> "DNS set to Off"
|
||||
DNS_MODE_AUTO -> "DNS set to Auto"
|
||||
DNS_MODE_PRIVATE -> "DNS set to Private Provider"
|
||||
else -> "Unknown"
|
||||
}
|
||||
scope.launch {
|
||||
launch(Dispatchers.Main) {
|
||||
Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val data = intent?.data.toString()
|
||||
val sharedPrefs = PreferenceHelper.defaultPreference(this)
|
||||
if(checkForPermission(this)) {
|
||||
if (data == ACTION_SWITCH_MODE) {
|
||||
PrivateDNSUtils.getNextProvider(
|
||||
sharedPrefs,
|
||||
scope,
|
||||
repository,
|
||||
contentResolver,
|
||||
skipProvider = true,
|
||||
onNext = { dnsMode, _ ->
|
||||
setDnsModeAndShowToast(dnsMode)
|
||||
})
|
||||
}
|
||||
}
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -1,12 +1,25 @@
|
|||
package ru.karasevm.privatednstoggle.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import ru.karasevm.privatednstoggle.service.ShortcutService
|
||||
|
||||
class SettingsDialogActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val newFragment = DNSServerDialogFragment()
|
||||
newFragment.show(supportFragmentManager, DNSServerDialogFragment.TAG)
|
||||
|
||||
if (intent != null && intent.data != null) {
|
||||
// Start the service when the shortcut is clicked
|
||||
val serviceIntent = Intent(this, ShortcutService::class.java)
|
||||
serviceIntent.data = intent.data
|
||||
startService(serviceIntent)
|
||||
finish()
|
||||
} else {
|
||||
val newFragment = DNSServerDialogFragment()
|
||||
newFragment.show(supportFragmentManager, DNSServerDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,11 +3,18 @@ package ru.karasevm.privatednstoggle.util
|
|||
import android.Manifest
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat.checkSelfPermission
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.karasevm.privatednstoggle.R
|
||||
import ru.karasevm.privatednstoggle.data.DnsServerRepository
|
||||
import ru.karasevm.privatednstoggle.model.DnsServer
|
||||
import ru.karasevm.privatednstoggle.util.PreferenceHelper.autoMode
|
||||
|
||||
@Suppress("unused")
|
||||
object PrivateDNSUtils {
|
||||
const val DNS_MODE_OFF = "off"
|
||||
const val DNS_MODE_AUTO = "opportunistic"
|
||||
|
@ -23,12 +30,12 @@ object PrivateDNSUtils {
|
|||
|
||||
|
||||
// Gets the system dns mode
|
||||
fun getPrivateMode(contentResolver: ContentResolver): String {
|
||||
fun getPrivateMode(contentResolver: ContentResolver): String? {
|
||||
return Settings.Global.getString(contentResolver, PRIVATE_DNS_MODE)
|
||||
}
|
||||
|
||||
// Gets the system dns provider
|
||||
fun getPrivateProvider(contentResolver: ContentResolver): String {
|
||||
fun getPrivateProvider(contentResolver: ContentResolver): String? {
|
||||
return Settings.Global.getString(contentResolver, PRIVATE_DNS_PROVIDER)
|
||||
}
|
||||
|
||||
|
@ -43,10 +50,72 @@ object PrivateDNSUtils {
|
|||
}
|
||||
|
||||
fun checkForPermission(context: Context): Boolean {
|
||||
return checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.WRITE_SECURE_SETTINGS
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets next dns address from the database,
|
||||
* if current address is last or unknown returns null
|
||||
*
|
||||
* @param currentAddress currently set address
|
||||
* @return next address
|
||||
*/
|
||||
suspend fun getNextAddress(
|
||||
repository: DnsServerRepository,
|
||||
currentAddress: String?
|
||||
): DnsServer? {
|
||||
return if (currentAddress.isNullOrEmpty()) {
|
||||
repository.getFirstEnabled()
|
||||
} else {
|
||||
repository.getNextByServer(currentAddress)
|
||||
}
|
||||
}
|
||||
|
||||
fun getNextProvider(
|
||||
sharedPrefs: SharedPreferences,
|
||||
scope: CoroutineScope,
|
||||
repository: DnsServerRepository,
|
||||
contentResolver: ContentResolver,
|
||||
skipProvider: Boolean = false,
|
||||
onNext: ((String, String?) -> Unit)
|
||||
) {
|
||||
val dnsMode = getPrivateMode(contentResolver)
|
||||
val dnsProvider = getPrivateProvider(contentResolver)
|
||||
|
||||
if (dnsMode.equals(DNS_MODE_OFF, ignoreCase = true)) {
|
||||
if (sharedPrefs.autoMode == AUTO_MODE_OPTION_AUTO || sharedPrefs.autoMode == AUTO_MODE_OPTION_OFF_AUTO) {
|
||||
onNext.invoke(DNS_MODE_AUTO, dnsProvider)
|
||||
} else {
|
||||
onNext.invoke(DNS_MODE_PRIVATE, dnsProvider)
|
||||
}
|
||||
|
||||
} else if (dnsMode == null || dnsMode.equals(DNS_MODE_AUTO, ignoreCase = true)) {
|
||||
onNext.invoke(DNS_MODE_PRIVATE, null)
|
||||
} else if (dnsMode.equals(DNS_MODE_PRIVATE, ignoreCase = true)) {
|
||||
scope.launch {
|
||||
if (getNextAddress(repository, dnsProvider) == null) {
|
||||
if (!skipProvider && sharedPrefs.autoMode == AUTO_MODE_OPTION_PRIVATE) {
|
||||
onNext.invoke(DNS_MODE_PRIVATE, null)
|
||||
} else {
|
||||
if (sharedPrefs.autoMode == AUTO_MODE_OPTION_AUTO) {
|
||||
onNext.invoke(DNS_MODE_AUTO, dnsProvider)
|
||||
} else {
|
||||
onNext.invoke(DNS_MODE_OFF, dnsProvider)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
onNext.invoke(DNS_MODE_OFF, dnsProvider)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
13
app/src/main/res/drawable/ic_outline_next_24dp.xml
Normal file
13
app/src/main/res/drawable/ic_outline_next_24dp.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:autoMirrored="true">
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10s10,-4.48 10,-10C22,6.48 17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8s8,3.58 8,8S16.42,20 12,20z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M15.97,11.03C14.87,9.79 13.28,9 11.5,9c-2.82,0 -5.18,1.95 -5.82,4.56l0.96,0.32C7.15,11.66 9.13,10 11.5,10c1.51,0 2.85,0.68 3.76,1.74L13,14h5V9L15.97,11.03z"/>
|
||||
</vector>
|
|
@ -50,4 +50,5 @@
|
|||
<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>
|
||||
<string name="shortcut_switch_mode">Switch Mode</string>
|
||||
</resources>
|
14
app/src/main/res/xml/shortcuts.xml
Normal file
14
app/src/main/res/xml/shortcuts.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<shortcut
|
||||
android:shortcutId="switch_mode"
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_outline_next_24dp"
|
||||
android:shortcutShortLabel="@string/shortcut_switch_mode">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetPackage="ru.karasevm.privatednstoggle"
|
||||
android:targetClass="ru.karasevm.privatednstoggle.ui.SettingsDialogActivity"
|
||||
android:data="privatednstoggle://switch_mode"/>
|
||||
</shortcut>
|
||||
</shortcuts>
|
Loading…
Add table
Add a link
Reference in a new issue