mirror of
https://github.com/karasevm/PrivateDNSAndroid.git
synced 2025-06-28 20:29:56 +00:00
Option to edit the server (#29)
* Option to edit the server * Empty List of Severs notified
This commit is contained in:
parent
5fe2354e7d
commit
470f8445f9
8 changed files with 129 additions and 30 deletions
2
.idea/vcs.xml
generated
2
.idea/vcs.xml
generated
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -14,7 +14,7 @@ import com.google.common.net.InternetDomainName
|
||||||
import ru.karasevm.privatednstoggle.databinding.DialogAddBinding
|
import ru.karasevm.privatednstoggle.databinding.DialogAddBinding
|
||||||
|
|
||||||
|
|
||||||
class AddServerDialogFragment : DialogFragment() {
|
class AddServerDialogFragment(private val position: Int?, private val label: String?, private val server: String?) : DialogFragment() {
|
||||||
// Use this instance of the interface to deliver action events
|
// Use this instance of the interface to deliver action events
|
||||||
private lateinit var listener: NoticeDialogListener
|
private lateinit var listener: NoticeDialogListener
|
||||||
|
|
||||||
|
@ -29,6 +29,8 @@ class AddServerDialogFragment : DialogFragment() {
|
||||||
* Each method passes the DialogFragment in case the host needs to query it. */
|
* Each method passes the DialogFragment in case the host needs to query it. */
|
||||||
interface NoticeDialogListener {
|
interface NoticeDialogListener {
|
||||||
fun onDialogPositiveClick(label: String? ,server: String)
|
fun onDialogPositiveClick(label: String? ,server: String)
|
||||||
|
fun onDialogPositiveClick(label: String?, server: String, position: Int)
|
||||||
|
fun onDeleteItemClicked(position: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
|
// Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
|
||||||
|
@ -59,22 +61,47 @@ class AddServerDialogFragment : DialogFragment() {
|
||||||
val view = binding.root
|
val view = binding.root
|
||||||
// Inflate and set the layout for the dialog
|
// Inflate and set the layout for the dialog
|
||||||
// Pass null as the parent view because its going in the dialog layout
|
// Pass null as the parent view because its going in the dialog layout
|
||||||
builder.setTitle(R.string.add_server)
|
if (position != null) {
|
||||||
.setView(view)
|
binding.editTextServerHint.setText(label)
|
||||||
// Add action buttons
|
binding.editTextServerAddr.setText(server)
|
||||||
.setPositiveButton(
|
builder.setTitle(R.string.edit_server).setView(view)
|
||||||
R.string.menu_add
|
.setPositiveButton(
|
||||||
) { _, _ ->
|
R.string.menu_save
|
||||||
listener.onDialogPositiveClick(
|
) { _, _ ->
|
||||||
binding.editTextServerHint.text.toString().trim(),
|
listener.onDialogPositiveClick(
|
||||||
binding.editTextServerAddr.text.toString().trim()
|
binding.editTextServerHint.text.toString().trim(),
|
||||||
)
|
binding.editTextServerAddr.text.toString().trim(),
|
||||||
}
|
position)
|
||||||
.setNegativeButton(
|
}
|
||||||
R.string.cancel
|
.setNegativeButton(
|
||||||
) { _, _ ->
|
R.string.cancel
|
||||||
dialog?.cancel()
|
) { _, _ ->
|
||||||
}
|
dialog?.cancel()
|
||||||
|
}
|
||||||
|
.setNeutralButton(
|
||||||
|
R.string.delete
|
||||||
|
) { _, _ ->
|
||||||
|
listener.onDeleteItemClicked(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
builder.setTitle(R.string.add_server)
|
||||||
|
.setView(view)
|
||||||
|
// Add action buttons
|
||||||
|
.setPositiveButton(
|
||||||
|
R.string.menu_add
|
||||||
|
) { _, _ ->
|
||||||
|
listener.onDialogPositiveClick(
|
||||||
|
binding.editTextServerHint.text.toString().trim(),
|
||||||
|
binding.editTextServerAddr.text.toString().trim()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.setNegativeButton(
|
||||||
|
R.string.cancel
|
||||||
|
) { _, _ ->
|
||||||
|
dialog?.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
builder.create()
|
builder.create()
|
||||||
} ?: throw IllegalStateException("Activity cannot be null")
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ class DNSServerDialogFragment : DialogFragment() {
|
||||||
items.add(0, resources.getString(R.string.dns_auto))
|
items.add(0, resources.getString(R.string.dns_auto))
|
||||||
items.add(0, resources.getString(R.string.dns_off))
|
items.add(0, resources.getString(R.string.dns_off))
|
||||||
|
|
||||||
adapter = RecyclerAdapter(items, false)
|
adapter = RecyclerAdapter(items, false) {}
|
||||||
binding.recyclerView.adapter = adapter
|
binding.recyclerView.adapter = adapter
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package ru.karasevm.privatednstoggle
|
package ru.karasevm.privatednstoggle
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.Activity
|
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipDescription.MIMETYPE_TEXT_PLAIN
|
import android.content.ClipDescription.MIMETYPE_TEXT_PLAIN
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.IPackageManager
|
import android.content.pm.IPackageManager
|
||||||
|
@ -17,6 +15,7 @@ import android.os.Bundle
|
||||||
import android.permission.IPermissionManager
|
import android.permission.IPermissionManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
@ -139,27 +138,41 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
||||||
binding.recyclerView.layoutManager = linearLayoutManager
|
binding.recyclerView.layoutManager = linearLayoutManager
|
||||||
|
|
||||||
sharedPrefs = PreferenceHelper.defaultPreference(this)
|
sharedPrefs = PreferenceHelper.defaultPreference(this)
|
||||||
clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
gson = GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).create()
|
gson = GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).create()
|
||||||
|
|
||||||
items = sharedPrefs.dns_servers
|
items = sharedPrefs.dns_servers
|
||||||
if (items[0] == "") {
|
if (items[0] == "") {
|
||||||
items.removeAt(0)
|
items.removeAt(0)
|
||||||
}
|
}
|
||||||
adapter = RecyclerAdapter(items, true)
|
|
||||||
|
updateEmptyView()
|
||||||
|
adapter = RecyclerAdapter(items, true) { updateEmptyView() }
|
||||||
adapter.onItemClick = { position ->
|
adapter.onItemClick = { position ->
|
||||||
val newFragment = DeleteServerDialogFragment(position)
|
val data = items[position].split(" : ")
|
||||||
newFragment.show(supportFragmentManager, "delete_server")
|
val label: String?
|
||||||
|
val server: String
|
||||||
|
if (data.size == 2) {
|
||||||
|
label = data[0]
|
||||||
|
server = data[1]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
label = null
|
||||||
|
server = data[0]
|
||||||
|
}
|
||||||
|
val newFragment = AddServerDialogFragment(position, label, server)
|
||||||
|
newFragment.show(supportFragmentManager, "edit_server")
|
||||||
}
|
}
|
||||||
adapter.onItemsChanged = { swappedItems ->
|
adapter.onItemsChanged = { swappedItems ->
|
||||||
items = swappedItems
|
items = swappedItems
|
||||||
sharedPrefs.dns_servers = swappedItems
|
sharedPrefs.dns_servers = swappedItems
|
||||||
|
updateEmptyView()
|
||||||
}
|
}
|
||||||
adapter.onDragStart = { viewHolder ->
|
adapter.onDragStart = { viewHolder ->
|
||||||
itemTouchHelper.startDrag(viewHolder)
|
itemTouchHelper.startDrag(viewHolder)
|
||||||
}
|
}
|
||||||
binding.floatingActionButton.setOnClickListener {
|
binding.floatingActionButton.setOnClickListener {
|
||||||
val newFragment = AddServerDialogFragment()
|
val newFragment = AddServerDialogFragment(null, null, null)
|
||||||
newFragment.show(supportFragmentManager, "add_server")
|
newFragment.show(supportFragmentManager, "add_server")
|
||||||
}
|
}
|
||||||
binding.recyclerView.adapter = adapter
|
binding.recyclerView.adapter = adapter
|
||||||
|
@ -235,9 +248,19 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateEmptyView() {
|
||||||
|
if (items.isEmpty()) {
|
||||||
|
binding.emptyView.visibility = View.VISIBLE
|
||||||
|
binding.emptyViewHint.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
binding.emptyView.visibility = View.GONE
|
||||||
|
binding.emptyViewHint.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var saveResultLauncher =
|
private var saveResultLauncher =
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == RESULT_OK) {
|
||||||
val data: Intent? = result.data
|
val data: Intent? = result.data
|
||||||
data?.data?.also { uri ->
|
data?.data?.also { uri ->
|
||||||
val jsonData = gson.toJson(sharedPrefs.export())
|
val jsonData = gson.toJson(sharedPrefs.export())
|
||||||
|
@ -262,7 +285,7 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
||||||
|
|
||||||
private var importResultLauncher =
|
private var importResultLauncher =
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == RESULT_OK) {
|
||||||
val data: Intent? = result.data
|
val data: Intent? = result.data
|
||||||
data?.data?.also { uri ->
|
data?.data?.also { uri ->
|
||||||
val contentResolver = applicationContext.contentResolver
|
val contentResolver = applicationContext.contentResolver
|
||||||
|
@ -336,6 +359,11 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
||||||
Shizuku.removeRequestPermissionResultListener(this::onRequestPermissionResult)
|
Shizuku.removeRequestPermissionResultListener(this::onRequestPermissionResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDeleteItemClicked(position: Int) {
|
||||||
|
val newFragment = DeleteServerDialogFragment(position)
|
||||||
|
newFragment.show(supportFragmentManager, "delete_server")
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDialogPositiveClick(label: String?, server: String) {
|
override fun onDialogPositiveClick(label: String?, server: String) {
|
||||||
if (server.isEmpty()) {
|
if (server.isEmpty()) {
|
||||||
Toast.makeText(this, R.string.server_length_error, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.server_length_error, Toast.LENGTH_SHORT).show()
|
||||||
|
@ -358,6 +386,21 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
|
||||||
sharedPrefs.dns_servers = items
|
sharedPrefs.dns_servers = items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDialogPositiveClick(label: String?, server: String, position: Int) {
|
||||||
|
if (server.isEmpty()) {
|
||||||
|
Toast.makeText(this, R.string.server_length_error, Toast.LENGTH_SHORT).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (label.isNullOrEmpty()) {
|
||||||
|
items[position] = server
|
||||||
|
} else {
|
||||||
|
items[position] = "$label : $server"
|
||||||
|
}
|
||||||
|
adapter.notifyItemChanged(position)
|
||||||
|
sharedPrefs.dns_servers = items
|
||||||
|
binding.recyclerView.adapter?.notifyItemChanged(position)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to grant WRITE_SECURE_SETTINGS permission with Shizuku
|
* Attempts to grant WRITE_SECURE_SETTINGS permission with Shizuku
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,7 +10,7 @@ import android.widget.TextView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
|
|
||||||
class RecyclerAdapter(private val items: MutableList<String>, private val showDragHandle: Boolean) :
|
class RecyclerAdapter(private val items: MutableList<String>, private val showDragHandle: Boolean, private val onDataChanged: () -> Unit) :
|
||||||
RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {
|
RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {
|
||||||
|
|
||||||
var onItemClick: ((Int) -> Unit)? = null
|
var onItemClick: ((Int) -> Unit)? = null
|
||||||
|
@ -77,6 +77,7 @@ class RecyclerAdapter(private val items: MutableList<String>, private val showDr
|
||||||
clear()
|
clear()
|
||||||
addAll(newItems)
|
addAll(newItems)
|
||||||
}
|
}
|
||||||
|
onDataChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,30 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/topAppBarLayout" />
|
app:layout_constraintTop_toBottomOf="@+id/topAppBarLayout" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/no_servers_added"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.45" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_view_hint"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/empty_hint"
|
||||||
|
android:textColor="@color/material_dynamic_neutral50"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/empty_view" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/floating_action_button"
|
android:id="@+id/floating_action_button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<string name="dns_unknown">Unknown</string>
|
<string name="dns_unknown">Unknown</string>
|
||||||
<string name="add_server">Add Server</string>
|
<string name="add_server">Add Server</string>
|
||||||
<string name="menu_add">Add</string>
|
<string name="menu_add">Add</string>
|
||||||
|
<string name="menu_save">Save</string>
|
||||||
<string name="menu_privacy_policy">Privacy Policy</string>
|
<string name="menu_privacy_policy">Privacy Policy</string>
|
||||||
<string name="select_server">Select Server</string>
|
<string name="select_server">Select Server</string>
|
||||||
<string name="done">Done</string>
|
<string name="done">Done</string>
|
||||||
|
@ -43,4 +44,7 @@
|
||||||
<string name="menu_export_to_file">To file</string>
|
<string name="menu_export_to_file">To file</string>
|
||||||
<string name="export_failure">Saving failed</string>
|
<string name="export_failure">Saving failed</string>
|
||||||
<string name="export_success">Saved successfully</string>
|
<string name="export_success">Saved successfully</string>
|
||||||
|
<string name="edit_server">Edit server</string>
|
||||||
|
<string name="no_servers_added">No Servers Added</string>
|
||||||
|
<string name="empty_hint">Tap on the button below to add one</string>
|
||||||
</resources>
|
</resources>
|
|
@ -5,7 +5,7 @@ buildscript {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.5.0'
|
classpath 'com.android.tools.build:gradle:8.5.1'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue