Option to edit the server (#29)

* Option to edit the server

* Empty List of Severs notified
This commit is contained in:
Stephen Vaz 2024-08-12 17:48:47 +05:30 committed by GitHub
parent 5fe2354e7d
commit 470f8445f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 129 additions and 30 deletions

2
.idea/vcs.xml generated
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -14,7 +14,7 @@ import com.google.common.net.InternetDomainName
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
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. */
interface NoticeDialogListener {
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
@ -59,6 +61,30 @@ class AddServerDialogFragment : DialogFragment() {
val view = binding.root
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
if (position != null) {
binding.editTextServerHint.setText(label)
binding.editTextServerAddr.setText(server)
builder.setTitle(R.string.edit_server).setView(view)
.setPositiveButton(
R.string.menu_save
) { _, _ ->
listener.onDialogPositiveClick(
binding.editTextServerHint.text.toString().trim(),
binding.editTextServerAddr.text.toString().trim(),
position)
}
.setNegativeButton(
R.string.cancel
) { _, _ ->
dialog?.cancel()
}
.setNeutralButton(
R.string.delete
) { _, _ ->
listener.onDeleteItemClicked(position)
}
}
else {
builder.setTitle(R.string.add_server)
.setView(view)
// Add action buttons
@ -75,6 +101,7 @@ class AddServerDialogFragment : DialogFragment() {
) { _, _ ->
dialog?.cancel()
}
}
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}

View file

@ -44,7 +44,7 @@ class DNSServerDialogFragment : DialogFragment() {
items.add(0, resources.getString(R.string.dns_auto))
items.add(0, resources.getString(R.string.dns_off))
adapter = RecyclerAdapter(items, false)
adapter = RecyclerAdapter(items, false) {}
binding.recyclerView.adapter = adapter

View file

@ -1,11 +1,9 @@
package ru.karasevm.privatednstoggle
import android.Manifest
import android.app.Activity
import android.content.ClipData
import android.content.ClipDescription.MIMETYPE_TEXT_PLAIN
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.IPackageManager
@ -17,6 +15,7 @@ import android.os.Bundle
import android.permission.IPermissionManager
import android.util.Log
import android.view.Menu
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
@ -139,27 +138,41 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
binding.recyclerView.layoutManager = linearLayoutManager
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()
items = sharedPrefs.dns_servers
if (items[0] == "") {
items.removeAt(0)
}
adapter = RecyclerAdapter(items, true)
updateEmptyView()
adapter = RecyclerAdapter(items, true) { updateEmptyView() }
adapter.onItemClick = { position ->
val newFragment = DeleteServerDialogFragment(position)
newFragment.show(supportFragmentManager, "delete_server")
val data = items[position].split(" : ")
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 ->
items = swappedItems
sharedPrefs.dns_servers = swappedItems
updateEmptyView()
}
adapter.onDragStart = { viewHolder ->
itemTouchHelper.startDrag(viewHolder)
}
binding.floatingActionButton.setOnClickListener {
val newFragment = AddServerDialogFragment()
val newFragment = AddServerDialogFragment(null, null, null)
newFragment.show(supportFragmentManager, "add_server")
}
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 =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
if (result.resultCode == RESULT_OK) {
val data: Intent? = result.data
data?.data?.also { uri ->
val jsonData = gson.toJson(sharedPrefs.export())
@ -262,7 +285,7 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
private var importResultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
if (result.resultCode == RESULT_OK) {
val data: Intent? = result.data
data?.data?.also { uri ->
val contentResolver = applicationContext.contentResolver
@ -336,6 +359,11 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
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) {
if (server.isEmpty()) {
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
}
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
*/

View file

@ -10,7 +10,7 @@ import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
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>() {
var onItemClick: ((Int) -> Unit)? = null
@ -77,6 +77,7 @@ class RecyclerAdapter(private val items: MutableList<String>, private val showDr
clear()
addAll(newItems)
}
onDataChanged()
}

View file

@ -30,6 +30,30 @@
android:layout_height="wrap_content"
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
android:id="@+id/floating_action_button"
android:layout_width="wrap_content"

View file

@ -7,6 +7,7 @@
<string name="dns_unknown">Unknown</string>
<string name="add_server">Add Server</string>
<string name="menu_add">Add</string>
<string name="menu_save">Save</string>
<string name="menu_privacy_policy">Privacy Policy</string>
<string name="select_server">Select Server</string>
<string name="done">Done</string>
@ -43,4 +44,7 @@
<string name="menu_export_to_file">To file</string>
<string name="export_failure">Saving failed</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>

View file

@ -5,7 +5,7 @@ buildscript {
mavenCentral()
}
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"
// NOTE: Do not place your application dependencies here; they belong