From 470f8445f9daa2a4aefd137fd35440799adfc7cf Mon Sep 17 00:00:00 2001 From: Stephen Vaz <54773712+stephenvaz@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:48:47 +0530 Subject: [PATCH] Option to edit the server (#29) * Option to edit the server * Empty List of Severs notified --- .idea/vcs.xml | 2 +- .../AddServerDialogFragment.kt | 61 +++++++++++++------ .../DNSServerDialogFragment.kt | 2 +- .../karasevm/privatednstoggle/MainActivity.kt | 61 ++++++++++++++++--- .../privatednstoggle/RecyclerAdapter.kt | 3 +- app/src/main/res/layout/activity_main.xml | 24 ++++++++ app/src/main/res/values/strings.xml | 4 ++ build.gradle | 2 +- 8 files changed, 129 insertions(+), 30 deletions(-) diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..35eb1dd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/src/main/java/ru/karasevm/privatednstoggle/AddServerDialogFragment.kt b/app/src/main/java/ru/karasevm/privatednstoggle/AddServerDialogFragment.kt index 246883c..c05b39e 100644 --- a/app/src/main/java/ru/karasevm/privatednstoggle/AddServerDialogFragment.kt +++ b/app/src/main/java/ru/karasevm/privatednstoggle/AddServerDialogFragment.kt @@ -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,22 +61,47 @@ 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 - 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() - } + 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 + .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() } ?: throw IllegalStateException("Activity cannot be null") } diff --git a/app/src/main/java/ru/karasevm/privatednstoggle/DNSServerDialogFragment.kt b/app/src/main/java/ru/karasevm/privatednstoggle/DNSServerDialogFragment.kt index 4666321..3ea8912 100644 --- a/app/src/main/java/ru/karasevm/privatednstoggle/DNSServerDialogFragment.kt +++ b/app/src/main/java/ru/karasevm/privatednstoggle/DNSServerDialogFragment.kt @@ -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 diff --git a/app/src/main/java/ru/karasevm/privatednstoggle/MainActivity.kt b/app/src/main/java/ru/karasevm/privatednstoggle/MainActivity.kt index 98e096c..230eadb 100644 --- a/app/src/main/java/ru/karasevm/privatednstoggle/MainActivity.kt +++ b/app/src/main/java/ru/karasevm/privatednstoggle/MainActivity.kt @@ -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 */ diff --git a/app/src/main/java/ru/karasevm/privatednstoggle/RecyclerAdapter.kt b/app/src/main/java/ru/karasevm/privatednstoggle/RecyclerAdapter.kt index c4c1730..0d61cfc 100644 --- a/app/src/main/java/ru/karasevm/privatednstoggle/RecyclerAdapter.kt +++ b/app/src/main/java/ru/karasevm/privatednstoggle/RecyclerAdapter.kt @@ -10,7 +10,7 @@ import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import java.util.Collections -class RecyclerAdapter(private val items: MutableList, private val showDragHandle: Boolean) : +class RecyclerAdapter(private val items: MutableList, private val showDragHandle: Boolean, private val onDataChanged: () -> Unit) : RecyclerView.Adapter() { var onItemClick: ((Int) -> Unit)? = null @@ -77,6 +77,7 @@ class RecyclerAdapter(private val items: MutableList, private val showDr clear() addAll(newItems) } + onDataChanged() } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 3b78639..eb0201d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -30,6 +30,30 @@ android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/topAppBarLayout" /> + + + + Unknown Add Server Add + Save Privacy Policy Select Server Done @@ -43,4 +44,7 @@ To file Saving failed Saved successfully + Edit server + No Servers Added + Tap on the button below to add one \ No newline at end of file diff --git a/build.gradle b/build.gradle index 01ac801..5a61030 100644 --- a/build.gradle +++ b/build.gradle @@ -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