mirror of
https://github.com/karasevm/PrivateDNSAndroid.git
synced 2025-06-28 12:19:57 +00:00
Quick tile Selection Dialog (#12)
* Feature Quick tile Server Selection dialog * Fix Bug * Add Toast Messages
This commit is contained in:
parent
96e345606e
commit
9aabfa6261
8 changed files with 243 additions and 43 deletions
|
@ -3,41 +3,53 @@
|
|||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
|
||||
<application
|
||||
android:name=".PrivateDNSApp"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:name=".PrivateDNSApp"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.MyApplication">
|
||||
android:theme="@style/Theme.Transparent">
|
||||
|
||||
<provider
|
||||
android:name="rikka.shizuku.ShizukuProvider"
|
||||
android:authorities="${applicationId}.shizuku"
|
||||
android:multiprocess="false"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:theme="@style/Theme.MyApplication"
|
||||
android:taskAffinity="${applicationId}.main"
|
||||
android:launchMode="singleInstance"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service
|
||||
android:name=".DnsTileService"
|
||||
android:icon="@drawable/ic_unknown_black_24dp"
|
||||
android:label="@string/tile_name"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
|
||||
|
||||
<activity
|
||||
android:name=".SettingsDialogActivity"
|
||||
android:theme="@style/Theme.Transparent"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action
|
||||
android:name="android.service.quicksettings.action.QS_TILE"/>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".DnsTileService"
|
||||
android:exported="true"
|
||||
android:icon="@drawable/ic_unknown_black_24dp"
|
||||
android:label="@string/tile_name"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,88 @@
|
|||
package ru.karasevm.privatednstoggle
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import ru.karasevm.privatednstoggle.databinding.SheetDnsSelectorBinding
|
||||
import ru.karasevm.privatednstoggle.utils.PreferenceHelper.defaultPreference
|
||||
import ru.karasevm.privatednstoggle.utils.PreferenceHelper.dns_servers
|
||||
import ru.karasevm.privatednstoggle.utils.PrivateDNSUtils
|
||||
|
||||
class DNSServerDialogFragment: DialogFragment() {
|
||||
|
||||
private var _binding: SheetDnsSelectorBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||
private lateinit var adapter: RecyclerAdapter
|
||||
private var items = mutableListOf<String>()
|
||||
private lateinit var sharedPrefs: SharedPreferences
|
||||
|
||||
override fun onCreateDialog(
|
||||
savedInstanceState: Bundle?
|
||||
): Dialog {
|
||||
return activity?.let {
|
||||
val builder = MaterialAlertDialogBuilder(it)
|
||||
val inflater = requireActivity().layoutInflater
|
||||
_binding = SheetDnsSelectorBinding.inflate(inflater)
|
||||
|
||||
linearLayoutManager = LinearLayoutManager(context)
|
||||
binding.recyclerView.layoutManager = linearLayoutManager
|
||||
|
||||
sharedPrefs = defaultPreference(requireContext())
|
||||
items = sharedPrefs.dns_servers
|
||||
if(items[0] == "") {
|
||||
items.removeAt(0)
|
||||
items.add("dns.google")
|
||||
}
|
||||
|
||||
adapter = RecyclerAdapter(items)
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
builder.setTitle(R.string.select_server)
|
||||
.setView(binding.root)
|
||||
.setPositiveButton(R.string.done
|
||||
) { _, _ ->
|
||||
dialog?.dismiss()
|
||||
}
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val dnsMode = PrivateDNSUtils.getPrivateMode(requireActivity().contentResolver)
|
||||
binding.autoSwitch.isChecked = dnsMode.lowercase() == "opportunistic"
|
||||
|
||||
adapter.onItemClick = { position ->
|
||||
binding.autoSwitch.isChecked = false
|
||||
val server = items[position]
|
||||
PrivateDNSUtils.setPrivateMode(requireActivity().contentResolver, PrivateDNSUtils.DNS_MODE_PRIVATE)
|
||||
PrivateDNSUtils.setPrivateProvider(requireActivity().contentResolver, server)
|
||||
Toast.makeText(context, "DNS Server Set", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
binding.autoSwitch.setOnClickListener {
|
||||
if(binding.autoSwitch.isChecked) {
|
||||
PrivateDNSUtils.setPrivateMode(requireActivity().contentResolver, PrivateDNSUtils.DNS_MODE_AUTO)
|
||||
Toast.makeText(context, "DNS Server Set to Auto", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
PrivateDNSUtils.setPrivateMode(requireActivity().contentResolver, PrivateDNSUtils.DNS_MODE_PRIVATE)
|
||||
Toast.makeText(context, "DNS Server Set", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
activity?.finish()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "DNSServerDialogFragment"
|
||||
}
|
||||
}
|
|
@ -1,34 +1,23 @@
|
|||
package ru.karasevm.privatednstoggle
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Icon
|
||||
import android.provider.Settings
|
||||
import android.service.quicksettings.Tile
|
||||
import android.service.quicksettings.TileService
|
||||
import android.widget.Toast
|
||||
import ru.karasevm.privatednstoggle.utils.PreferenceHelper
|
||||
import ru.karasevm.privatednstoggle.utils.PreferenceHelper.autoMode
|
||||
import ru.karasevm.privatednstoggle.utils.PreferenceHelper.dns_servers
|
||||
|
||||
const val DNS_MODE_OFF = "off"
|
||||
const val DNS_MODE_AUTO = "opportunistic"
|
||||
const val DNS_MODE_PRIVATE = "hostname"
|
||||
import ru.karasevm.privatednstoggle.utils.PrivateDNSUtils
|
||||
import ru.karasevm.privatednstoggle.utils.PrivateDNSUtils.DNS_MODE_AUTO
|
||||
import ru.karasevm.privatednstoggle.utils.PrivateDNSUtils.DNS_MODE_OFF
|
||||
import ru.karasevm.privatednstoggle.utils.PrivateDNSUtils.DNS_MODE_PRIVATE
|
||||
import ru.karasevm.privatednstoggle.utils.PrivateDNSUtils.checkForPermission
|
||||
|
||||
class DnsTileService : TileService() {
|
||||
|
||||
|
||||
private fun checkForPermission(): Boolean {
|
||||
if (checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_GRANTED) {
|
||||
return true
|
||||
}
|
||||
Toast.makeText(this, R.string.permission_missing, Toast.LENGTH_SHORT).show()
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onTileAdded() {
|
||||
super.onTileAdded()
|
||||
checkForPermission()
|
||||
checkForPermission(this)
|
||||
// Update state
|
||||
qsTile.state = Tile.STATE_INACTIVE
|
||||
|
||||
|
@ -38,7 +27,7 @@ class DnsTileService : TileService() {
|
|||
|
||||
override fun onClick() {
|
||||
super.onClick()
|
||||
if (!checkForPermission()) {
|
||||
if (!checkForPermission(this)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -104,7 +93,7 @@ class DnsTileService : TileService() {
|
|||
|
||||
override fun onStartListening() {
|
||||
super.onStartListening()
|
||||
if (!checkForPermission()) {
|
||||
if (!checkForPermission(this)) {
|
||||
return
|
||||
}
|
||||
val dnsMode = Settings.Global.getString(contentResolver, "private_dns_mode")
|
||||
|
@ -137,16 +126,12 @@ class DnsTileService : TileService() {
|
|||
)
|
||||
} else if (dnsMode.equals(DNS_MODE_PRIVATE, ignoreCase = true)) {
|
||||
val dnsProvider = Settings.Global.getString(contentResolver, "private_dns_specifier")
|
||||
if (dnsProvider != null) {
|
||||
refreshTile(
|
||||
qsTile,
|
||||
Tile.STATE_ACTIVE,
|
||||
dnsProvider,
|
||||
R.drawable.ic_private_black_24dp
|
||||
)
|
||||
} else {
|
||||
Toast.makeText(this, R.string.permission_missing, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
refreshTile(
|
||||
qsTile,
|
||||
Tile.STATE_ACTIVE,
|
||||
dnsProvider,
|
||||
R.drawable.ic_private_black_24dp
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -187,8 +172,8 @@ class DnsTileService : TileService() {
|
|||
tile.label = label
|
||||
tile.state = state
|
||||
tile.icon = Icon.createWithResource(this, icon)
|
||||
Settings.Global.putString(contentResolver, "private_dns_mode", dnsMode)
|
||||
Settings.Global.putString(contentResolver, "private_dns_specifier", dnsProvider)
|
||||
PrivateDNSUtils.setPrivateMode(contentResolver, dnsMode)
|
||||
PrivateDNSUtils.setPrivateProvider(contentResolver, dnsProvider)
|
||||
tile.updateTile()
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package ru.karasevm.privatednstoggle
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
class SettingsDialogActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val newFragment = DNSServerDialogFragment()
|
||||
newFragment.show(supportFragmentManager, DNSServerDialogFragment.TAG)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package ru.karasevm.privatednstoggle.utils
|
||||
|
||||
import android.Manifest
|
||||
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
|
||||
|
||||
object PrivateDNSUtils {
|
||||
const val DNS_MODE_OFF = "off"
|
||||
const val DNS_MODE_AUTO = "opportunistic"
|
||||
const val DNS_MODE_PRIVATE = "hostname"
|
||||
|
||||
private const val PRIVATE_DNS_MODE = "private_dns_mode"
|
||||
private const val PRIVATE_DNS_PROVIDER = "private_dns_specifier"
|
||||
|
||||
fun getPrivateMode(contentResolver: ContentResolver): String {
|
||||
return Settings.Global.getString(contentResolver, PRIVATE_DNS_MODE)
|
||||
}
|
||||
|
||||
fun getPrivateProvider(contentResolver: ContentResolver): String {
|
||||
return Settings.Global.getString(contentResolver, PRIVATE_DNS_PROVIDER)
|
||||
}
|
||||
|
||||
fun setPrivateMode(contentResolver: ContentResolver, value: String) {
|
||||
Settings.Global.putString(contentResolver, PRIVATE_DNS_MODE, value)
|
||||
}
|
||||
|
||||
fun setPrivateProvider(contentResolver: ContentResolver, value: String?) {
|
||||
Settings.Global.putString(contentResolver, PRIVATE_DNS_PROVIDER, value)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
44
app/src/main/res/layout/sheet_dns_selector.xml
Normal file
44
app/src/main/res/layout/sheet_dns_selector.xml
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom_sheet"
|
||||
style="@style/Widget.Material3.BottomSheet"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginVertical="5dp" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/auto_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/menu_auto_enabled"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginVertical="5dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxWidth="300dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -9,7 +9,9 @@
|
|||
<string name="menu_add">Add</string>
|
||||
<string name="menu_privacy_policy">Privacy Policy</string>
|
||||
<string name="menu_auto_enabled">Enable auto</string>
|
||||
<string name="select_server">Select Server</string>
|
||||
<string name="auto_mode_clarification">Automatic (opportunistic) DNS mode will now be available in the tile</string>
|
||||
<string name="done">Done</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="delete_question">Delete</string>
|
||||
<string name="delete_message">Are you sure you want to delete server?</string>
|
||||
|
|
|
@ -10,4 +10,17 @@
|
|||
<item name="android:windowLightStatusBar">true</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Transparent" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowAnimationStyle">@null</item>
|
||||
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
</resources>
|
Loading…
Add table
Add a link
Reference in a new issue