Quick tile Selection Dialog (#12)

* Feature Quick tile Server Selection dialog

* Fix Bug

* Add Toast Messages
This commit is contained in:
Praveen Kumar 2024-06-13 16:08:56 +05:30 committed by GitHub
parent 96e345606e
commit 9aabfa6261
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 243 additions and 43 deletions

View file

@ -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>

View file

@ -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"
}
}

View file

@ -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()
}

View file

@ -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)
}
}

View file

@ -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
}
}

View 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>

View file

@ -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>

View file

@ -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>