Commit aaf1e7bd authored by Mygod's avatar Mygod Committed by Max Lv

Migrate AlertDialogFragment to fragment result API

parent f0b6ee48
...@@ -71,7 +71,6 @@ dependencies { ...@@ -71,7 +71,6 @@ dependencies {
api("androidx.appcompat:appcompat:1.2.0") api("androidx.appcompat:appcompat:1.2.0")
api("androidx.core:core-ktx:1.5.0-rc01") api("androidx.core:core-ktx:1.5.0-rc01")
api("androidx.fragment:fragment-ktx:1.3.2")
api("androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion") api("androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion")
api("androidx.lifecycle:lifecycle-livedata-core-ktx:$lifecycleVersion") api("androidx.lifecycle:lifecycle-livedata-core-ktx:$lifecycleVersion")
api("androidx.preference:preference:1.1.1") api("androidx.preference:preference:1.1.1")
......
...@@ -29,9 +29,9 @@ import androidx.appcompat.app.AppCompatActivity ...@@ -29,9 +29,9 @@ import androidx.appcompat.app.AppCompatActivity
import com.github.shadowsocks.core.R import com.github.shadowsocks.core.R
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.plugin.AlertDialogFragment import com.github.shadowsocks.plugin.fragment.AlertDialogFragment
import com.github.shadowsocks.plugin.Empty import com.github.shadowsocks.plugin.fragment.Empty
import com.github.shadowsocks.plugin.showAllowingStateLoss import com.github.shadowsocks.plugin.fragment.showAllowingStateLoss
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
class UrlImportActivity : AppCompatActivity() { class UrlImportActivity : AppCompatActivity() {
...@@ -68,6 +68,9 @@ class UrlImportActivity : AppCompatActivity() { ...@@ -68,6 +68,9 @@ class UrlImportActivity : AppCompatActivity() {
private fun handleShareIntent() = intent.data?.toString()?.let { sharedStr -> private fun handleShareIntent() = intent.data?.toString()?.let { sharedStr ->
val profiles = Profile.findAllUrls(sharedStr, Core.currentProfile?.main).toList() val profiles = Profile.findAllUrls(sharedStr, Core.currentProfile?.main).toList()
if (profiles.isEmpty()) null else ImportProfilesDialogFragment().withArg(ProfilesArg(profiles)) if (profiles.isEmpty()) null else ImportProfilesDialogFragment().apply {
arg(ProfilesArg(profiles))
key()
}
} }
} }
...@@ -30,9 +30,9 @@ import androidx.activity.result.component2 ...@@ -30,9 +30,9 @@ import androidx.activity.result.component2
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.github.shadowsocks.plugin.AlertDialogFragment
import com.github.shadowsocks.plugin.Empty
import com.github.shadowsocks.plugin.PluginContract import com.github.shadowsocks.plugin.PluginContract
import com.github.shadowsocks.plugin.fragment.AlertDialogFragment
import com.github.shadowsocks.plugin.fragment.Empty
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.widget.ListHolderListener import com.github.shadowsocks.widget.ListHolderListener
...@@ -71,8 +71,9 @@ class ProfileConfigActivity : AppCompatActivity() { ...@@ -71,8 +71,9 @@ class ProfileConfigActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem) = child.onOptionsItemSelected(item) override fun onOptionsItemSelected(item: MenuItem) = child.onOptionsItemSelected(item)
override fun onBackPressed() { override fun onBackPressed() {
if (DataStore.dirty) UnsavedChangesDialogFragment().show(child, ProfileConfigFragment.REQUEST_UNSAVED_CHANGES) if (DataStore.dirty) UnsavedChangesDialogFragment().apply {
else super.onBackPressed() key()
}.show(supportFragmentManager, null) else super.onBackPressed()
} }
val pluginHelp = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { val pluginHelp = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
......
...@@ -41,6 +41,9 @@ import androidx.preference.* ...@@ -41,6 +41,9 @@ import androidx.preference.*
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.plugin.* import com.github.shadowsocks.plugin.*
import com.github.shadowsocks.plugin.fragment.AlertDialogFragment
import com.github.shadowsocks.plugin.fragment.Empty
import com.github.shadowsocks.plugin.fragment.showAllowingStateLoss
import com.github.shadowsocks.preference.* import com.github.shadowsocks.preference.*
import com.github.shadowsocks.utils.* import com.github.shadowsocks.utils.*
import com.github.shadowsocks.widget.ListListener import com.github.shadowsocks.widget.ListListener
...@@ -54,7 +57,6 @@ class ProfileConfigFragment : PreferenceFragmentCompat(), ...@@ -54,7 +57,6 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
companion object PasswordSummaryProvider : Preference.SummaryProvider<EditTextPreference> { companion object PasswordSummaryProvider : Preference.SummaryProvider<EditTextPreference> {
override fun provideSummary(preference: EditTextPreference?) = "\u2022".repeat(preference?.text?.length ?: 0) override fun provideSummary(preference: EditTextPreference?) = "\u2022".repeat(preference?.text?.length ?: 0)
const val REQUEST_UNSAVED_CHANGES = 2
private const val REQUEST_PICK_PLUGIN = 3 private const val REQUEST_PICK_PLUGIN = 3
} }
...@@ -127,6 +129,12 @@ class ProfileConfigFragment : PreferenceFragmentCompat(), ...@@ -127,6 +129,12 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(listView, ListListener) ViewCompat.setOnApplyWindowInsetsListener(listView, ListListener)
AlertDialogFragment.setResultListener<ProfileConfigActivity.UnsavedChangesDialogFragment, Empty>(this) { which, ret ->
when (which) {
DialogInterface.BUTTON_POSITIVE -> saveAndExit()
DialogInterface.BUTTON_NEGATIVE -> requireActivity().finish()
}
}
} }
private fun initPlugins() { private fun initPlugins() {
...@@ -216,34 +224,31 @@ class ProfileConfigFragment : PreferenceFragmentCompat(), ...@@ -216,34 +224,31 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) { if (requestCode != REQUEST_PICK_PLUGIN) super.onActivityResult(requestCode, resultCode, data)
REQUEST_PICK_PLUGIN -> if (resultCode == Activity.RESULT_OK) { else if (resultCode == Activity.RESULT_OK) {
val selected = plugin.plugins.lookup.getValue( val selected = plugin.plugins.lookup.getValue(
data?.getStringExtra(PluginPreferenceDialogFragment.KEY_SELECTED_ID)!!) data?.getStringExtra(PluginPreferenceDialogFragment.KEY_SELECTED_ID)!!)
val override = pluginConfiguration.pluginsOptions.keys.firstOrNull { val override = pluginConfiguration.pluginsOptions.keys.firstOrNull {
plugin.plugins.lookup[it] == selected plugin.plugins.lookup[it] == selected
}
pluginConfiguration = PluginConfiguration(pluginConfiguration.pluginsOptions, override ?: selected.id)
DataStore.plugin = pluginConfiguration.toString()
DataStore.dirty = true
plugin.value = pluginConfiguration.selected
pluginConfigure.isEnabled = selected !is NoPlugin
pluginConfigure.text = pluginConfiguration.getOptions().toString()
if (!selected.trusted) {
Snackbar.make(requireView(), R.string.plugin_untrusted, Snackbar.LENGTH_LONG).show()
}
} }
REQUEST_UNSAVED_CHANGES -> when (resultCode) { pluginConfiguration = PluginConfiguration(pluginConfiguration.pluginsOptions, override ?: selected.id)
DialogInterface.BUTTON_POSITIVE -> saveAndExit() DataStore.plugin = pluginConfiguration.toString()
DialogInterface.BUTTON_NEGATIVE -> requireActivity().finish() DataStore.dirty = true
plugin.value = pluginConfiguration.selected
pluginConfigure.isEnabled = selected !is NoPlugin
pluginConfigure.text = pluginConfiguration.getOptions().toString()
if (!selected.trusted) {
Snackbar.make(requireView(), R.string.plugin_untrusted, Snackbar.LENGTH_LONG).show()
} }
else -> super.onActivityResult(requestCode, resultCode, data)
} }
} }
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_delete -> { R.id.action_delete -> {
DeleteConfirmationDialogFragment().withArg(ProfileIdArg(profileId)).show(this) DeleteConfirmationDialogFragment().apply {
arg(ProfileIdArg(profileId))
key()
}.show(parentFragmentManager, null)
true true
} }
R.id.action_apply -> { R.id.action_apply -> {
......
...@@ -22,7 +22,6 @@ package com.github.shadowsocks.acl ...@@ -22,7 +22,6 @@ package com.github.shadowsocks.acl
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.text.Editable import android.text.Editable
...@@ -46,7 +45,7 @@ import com.github.shadowsocks.R ...@@ -46,7 +45,7 @@ import com.github.shadowsocks.R
import com.github.shadowsocks.ToolbarFragment import com.github.shadowsocks.ToolbarFragment
import com.github.shadowsocks.bg.BaseService import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.net.Subnet import com.github.shadowsocks.net.Subnet
import com.github.shadowsocks.plugin.AlertDialogFragment import com.github.shadowsocks.plugin.fragment.AlertDialogFragment
import com.github.shadowsocks.utils.asIterable import com.github.shadowsocks.utils.asIterable
import com.github.shadowsocks.utils.readableMessage import com.github.shadowsocks.utils.readableMessage
import com.github.shadowsocks.utils.resolveResourceId import com.github.shadowsocks.utils.resolveResourceId
...@@ -65,9 +64,6 @@ import java.util.regex.PatternSyntaxException ...@@ -65,9 +64,6 @@ import java.util.regex.PatternSyntaxException
class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ActionMode.Callback { class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ActionMode.Callback {
companion object { companion object {
private const val REQUEST_CODE_ADD = 1
private const val REQUEST_CODE_EDIT = 2
private const val SELECTED_SUBNETS = "com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_SUBNETS" private const val SELECTED_SUBNETS = "com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_SUBNETS"
private const val SELECTED_HOSTNAMES = "com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_HOSTNAMES" private const val SELECTED_HOSTNAMES = "com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_HOSTNAMES"
private const val SELECTED_URLS = "com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_URLS" private const val SELECTED_URLS = "com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_URLS"
...@@ -95,8 +91,10 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ...@@ -95,8 +91,10 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
fun toAny() = if (isUrl) URL(item) else Subnet.fromString(item) ?: item fun toAny() = if (isUrl) URL(item) else Subnet.fromString(item) ?: item
} }
@Parcelize @Parcelize
data class AclEditResult(val edited: AclItem, val replacing: AclItem) : Parcelable data class AclArg(val item: AclItem? = null) : Parcelable
class AclRuleDialogFragment : AlertDialogFragment<AclItem, AclEditResult>(), @Parcelize
data class AclEditResult(val edited: AclItem?, val replacing: AclItem?) : Parcelable
class AclRuleDialogFragment : AlertDialogFragment<AclArg, AclEditResult>(),
TextWatcher, AdapterView.OnItemSelectedListener { TextWatcher, AdapterView.OnItemSelectedListener {
private lateinit var templateSelector: Spinner private lateinit var templateSelector: Spinner
private lateinit var editText: EditText private lateinit var editText: EditText
...@@ -111,8 +109,10 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ...@@ -111,8 +109,10 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
editText = view.findViewById(R.id.content) editText = view.findViewById(R.id.content)
inputLayout = view.findViewById(R.id.content_layout) inputLayout = view.findViewById(R.id.content_layout)
templateSelector.setSelection(Template.Generic.ordinal) templateSelector.setSelection(Template.Generic.ordinal)
editText.setText(arg.item) val arg = arg.item
editText.setText(arg?.item)
when { when {
arg == null -> { }
arg.isUrl -> templateSelector.setSelection(Template.Url.ordinal) arg.isUrl -> templateSelector.setSelection(Template.Url.ordinal)
Subnet.fromString(arg.item) == null -> { Subnet.fromString(arg.item) == null -> {
val match = domainPattern.find(arg.item) val match = domainPattern.find(arg.item)
...@@ -128,7 +128,7 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ...@@ -128,7 +128,7 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
setTitle(R.string.edit_rule) setTitle(R.string.edit_rule)
setPositiveButton(android.R.string.ok, listener) setPositiveButton(android.R.string.ok, listener)
setNegativeButton(android.R.string.cancel, null) setNegativeButton(android.R.string.cancel, null)
if (arg.item.isNotEmpty()) setNeutralButton(R.string.delete, listener) if (!arg?.item.isNullOrEmpty()) setNeutralButton(R.string.delete, listener)
setView(view) setView(view)
} }
...@@ -183,9 +183,9 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ...@@ -183,9 +183,9 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
.replace(".", "\\.").let { "(?:^|\\.)$it\$" }) .replace(".", "\\.").let { "(?:^|\\.)$it\$" })
Template.Url -> AclItem(text, true) Template.Url -> AclItem(text, true)
} }
}, arg) }, arg.item)
} }
DialogInterface.BUTTON_NEUTRAL -> AclEditResult(arg, arg) DialogInterface.BUTTON_NEUTRAL -> AclEditResult(null, arg.item)
else -> null else -> null
} }
...@@ -223,8 +223,10 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ...@@ -223,8 +223,10 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
} }
override fun onClick(v: View?) { override fun onClick(v: View?) {
if (selectedItems.isNotEmpty()) onLongClick(v) if (selectedItems.isNotEmpty()) onLongClick(v) else AclRuleDialogFragment().apply {
else AclRuleDialogFragment().withArg(AclItem(item)).show(this@CustomRulesFragment, REQUEST_CODE_EDIT) arg(AclArg(AclItem(item)))
key()
}.show(parentFragmentManager, null)
} }
override fun onLongClick(v: View?): Boolean { override fun onLongClick(v: View?): Boolean {
if (!selectedItems.add(item)) selectedItems.remove(item) // toggle if (!selectedItems.add(item)) selectedItems.remove(item) // toggle
...@@ -379,6 +381,14 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ...@@ -379,6 +381,14 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(view, ListHolderListener) ViewCompat.setOnApplyWindowInsetsListener(view, ListHolderListener)
AlertDialogFragment.setResultListener<AclRuleDialogFragment, AclEditResult>(this) { which, ret ->
val (edited, replacing) = ret ?: return@setResultListener
replacing?.toAny()?.let { item ->
adapter.remove(item)
if (which == DialogInterface.BUTTON_NEUTRAL) undoManager.remove(Pair(-1, item))
}
if (edited != null) adapter.add(edited.toAny())?.also { list.post { list.scrollToPosition(it) } }
}
if (savedInstanceState != null) { if (savedInstanceState != null) {
selectedItems.addAll(savedInstanceState.getStringArray(SELECTED_SUBNETS) selectedItems.addAll(savedInstanceState.getStringArray(SELECTED_SUBNETS)
?.mapNotNull { Subnet.fromString(it) } ?: listOf()) ?.mapNotNull { Subnet.fromString(it) } ?: listOf())
...@@ -442,7 +452,10 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ...@@ -442,7 +452,10 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
override fun onMenuItemClick(item: MenuItem): Boolean = when (item.itemId) { override fun onMenuItemClick(item: MenuItem): Boolean = when (item.itemId) {
R.id.action_manual_settings -> { R.id.action_manual_settings -> {
AclRuleDialogFragment().withArg(AclItem()).show(this, REQUEST_CODE_ADD) AclRuleDialogFragment().apply {
arg(AclArg(AclItem()))
key()
}.show(parentFragmentManager, null)
true true
} }
R.id.action_import_clipboard -> { R.id.action_import_clipboard -> {
...@@ -464,25 +477,6 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener, ...@@ -464,25 +477,6 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
else -> false else -> false
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val editing = when (requestCode) {
REQUEST_CODE_ADD -> false
REQUEST_CODE_EDIT -> true
else -> return super.onActivityResult(requestCode, resultCode, data)
}
val ret by lazy { AlertDialogFragment.getRet<AclEditResult>(data!!) }
when (resultCode) {
DialogInterface.BUTTON_POSITIVE -> {
if (editing) adapter.remove(ret.replacing.toAny())
adapter.add(ret.edited.toAny())?.also { list.post { list.scrollToPosition(it) } }
}
DialogInterface.BUTTON_NEUTRAL -> ret.replacing.toAny().let { item ->
adapter.remove(item)
undoManager.remove(Pair(-1, item))
}
}
}
override fun onDetach() { override fun onDetach() {
undoManager.flush() undoManager.flush()
mode?.finish() mode?.finish()
......
...@@ -41,7 +41,7 @@ import androidx.recyclerview.widget.RecyclerView ...@@ -41,7 +41,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.github.shadowsocks.MainActivity import com.github.shadowsocks.MainActivity
import com.github.shadowsocks.R import com.github.shadowsocks.R
import com.github.shadowsocks.ToolbarFragment import com.github.shadowsocks.ToolbarFragment
import com.github.shadowsocks.plugin.AlertDialogFragment import com.github.shadowsocks.plugin.fragment.AlertDialogFragment
import com.github.shadowsocks.utils.readableMessage import com.github.shadowsocks.utils.readableMessage
import com.github.shadowsocks.widget.ListHolderListener import com.github.shadowsocks.widget.ListHolderListener
import com.github.shadowsocks.widget.MainListListener import com.github.shadowsocks.widget.MainListListener
...@@ -53,18 +53,11 @@ import java.net.MalformedURLException ...@@ -53,18 +53,11 @@ import java.net.MalformedURLException
import java.net.URL import java.net.URL
class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
companion object {
private const val REQUEST_CODE_ADD = 1
private const val REQUEST_CODE_EDIT = 2
}
@Parcelize @Parcelize
data class SubItem(val item: String = "") : Parcelable { data class SubItem(val item: String? = null) : Parcelable
fun toURL() = URL(item)
}
@Parcelize @Parcelize
data class SubEditResult(val edited: SubItem, val replacing: SubItem) : Parcelable data class SubEditResult(val edited: String?, val replacing: String?) : Parcelable
class SubDialogFragment : AlertDialogFragment<SubItem, SubEditResult>(), class SubDialogFragment : AlertDialogFragment<SubItem, SubEditResult>(),
TextWatcher, AdapterView.OnItemSelectedListener { TextWatcher, AdapterView.OnItemSelectedListener {
...@@ -83,7 +76,7 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener ...@@ -83,7 +76,7 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
setTitle(R.string.edit_subscription) setTitle(R.string.edit_subscription)
setPositiveButton(android.R.string.ok, listener) setPositiveButton(android.R.string.ok, listener)
setNegativeButton(android.R.string.cancel, null) setNegativeButton(android.R.string.cancel, null)
if (arg.item.isNotEmpty()) setNeutralButton(R.string.delete, listener) if (!arg.item.isNullOrEmpty()) setNeutralButton(R.string.delete, listener)
setView(view) setView(view)
} }
...@@ -112,10 +105,8 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener ...@@ -112,10 +105,8 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
} }
override fun ret(which: Int) = when (which) { override fun ret(which: Int) = when (which) {
DialogInterface.BUTTON_POSITIVE -> { DialogInterface.BUTTON_POSITIVE -> SubEditResult(editText.text.toString(), arg.item)
SubEditResult(editText.text.toString().let { text -> SubItem(text) }, arg) DialogInterface.BUTTON_NEUTRAL -> SubEditResult(null, arg.item)
}
DialogInterface.BUTTON_NEUTRAL -> SubEditResult(arg, arg)
else -> null else -> null
} }
...@@ -141,8 +132,10 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener ...@@ -141,8 +132,10 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
} }
override fun onClick(v: View?) { override fun onClick(v: View?) {
SubDialogFragment().withArg(SubItem(item.toString())) SubDialogFragment().apply {
.show(this@SubscriptionFragment, REQUEST_CODE_EDIT) arg(SubItem(item.toString()))
key()
}.show(parentFragmentManager, null)
} }
} }
...@@ -186,14 +179,10 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener ...@@ -186,14 +179,10 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
apply() apply()
} }
fun remove(item: Any) { fun remove(item: URL) {
when (item) { notifyItemRemoved(subscription.urls.indexOf(item))
is URL -> { subscription.urls.remove(item)
notifyItemRemoved(subscription.urls.indexOf(item)) apply()
subscription.urls.remove(item)
apply()
}
}
} }
fun undo(actions: List<Pair<Int, Any>>) { fun undo(actions: List<Pair<Int, Any>>) {
...@@ -217,6 +206,15 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener ...@@ -217,6 +206,15 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(view, ListHolderListener) ViewCompat.setOnApplyWindowInsetsListener(view, ListHolderListener)
AlertDialogFragment.setResultListener<SubDialogFragment, SubEditResult>(this) { which, ret ->
val (edited, replacing) = ret ?: return@setResultListener
replacing?.let { item ->
val url = URL(item)
adapter.remove(url)
if (which == DialogInterface.BUTTON_NEUTRAL) undoManager.remove(-1 to url)
}
if (edited != null) adapter.add(URL(edited)).also { list.post { list.scrollToPosition(it) } }
}
toolbar.setTitle(R.string.subscriptions) toolbar.setTitle(R.string.subscriptions)
toolbar.inflateMenu(R.menu.subscription_menu) toolbar.inflateMenu(R.menu.subscription_menu)
toolbar.setOnMenuItemClickListener(this) toolbar.setOnMenuItemClickListener(this)
...@@ -250,7 +248,10 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener ...@@ -250,7 +248,10 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
override fun onMenuItemClick(item: MenuItem): Boolean = when (item.itemId) { override fun onMenuItemClick(item: MenuItem): Boolean = when (item.itemId) {
R.id.action_manual_settings -> { R.id.action_manual_settings -> {
SubDialogFragment().withArg(SubItem()).show(this, REQUEST_CODE_ADD) SubDialogFragment().apply {
arg(SubItem())
key()
}.show(parentFragmentManager, null)
true true
} }
R.id.action_update_subscription -> { R.id.action_update_subscription -> {
...@@ -261,25 +262,6 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener ...@@ -261,25 +262,6 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
else -> false else -> false
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val editing = when (requestCode) {
REQUEST_CODE_ADD -> false
REQUEST_CODE_EDIT -> true
else -> return super.onActivityResult(requestCode, resultCode, data)
}
val ret by lazy { AlertDialogFragment.getRet<SubEditResult>(data!!) }
when (resultCode) {
DialogInterface.BUTTON_POSITIVE -> {
if (editing) adapter.remove(ret.replacing.toURL())
adapter.add(ret.edited.toURL()).also { list.post { list.scrollToPosition(it) } }
}
DialogInterface.BUTTON_NEUTRAL -> ret.replacing.toURL().let { item ->
adapter.remove(item)
undoManager.remove(Pair(-1, item))
}
}
}
override fun onDetach() { override fun onDetach() {
undoManager.flush() undoManager.flush()
mode?.finish() mode?.finish()
......
* 2.0.1:
* Moved `AlertDialogFragment` and related utilities to `fragment` package, with support for Fragment Result API from AndroidX Fragment 1.3.
* Dependency updates:
- `androidx.fragment:fragment-ktx:1.3.2`;
- `com.google.android.material:material:1.3.0`;
- `org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32`.
* 2.0.0: * 2.0.0:
* Deprecated passing `-V` and `--fast-open` to plugin. * Deprecated passing `-V` and `--fast-open` to plugin.
Please find `__android_vpn` option passed via plugin options. Please find `__android_vpn` option passed via plugin options.
......
...@@ -26,5 +26,6 @@ dependencies { ...@@ -26,5 +26,6 @@ dependencies {
api("androidx.core:core-ktx:1.3.2") api("androidx.core:core-ktx:1.3.2")
// https://android-developers.googleblog.com/2019/07/android-q-beta-5-update.html // https://android-developers.googleblog.com/2019/07/android-q-beta-5-update.html
api("androidx.drawerlayout:drawerlayout:1.1.1") api("androidx.drawerlayout:drawerlayout:1.1.1")
api("androidx.fragment:fragment-ktx:1.3.2")
api("com.google.android.material:material:1.3.0") api("com.google.android.material:material:1.3.0")
} }
...@@ -32,6 +32,8 @@ import androidx.fragment.app.Fragment ...@@ -32,6 +32,8 @@ import androidx.fragment.app.Fragment
/** /**
* Based on: https://android.googlesource.com/platform/packages/apps/ExactCalculator/+/8c43f06/src/com/android/calculator2/AlertDialogFragment.java * Based on: https://android.googlesource.com/platform/packages/apps/ExactCalculator/+/8c43f06/src/com/android/calculator2/AlertDialogFragment.java
*/ */
@Suppress("DEPRECATION")
@Deprecated("Related APIs are deprecated in AndroidX", ReplaceWith("fragment.AlertDialogFragment"))
abstract class AlertDialogFragment<Arg : Parcelable, Ret : Parcelable> : abstract class AlertDialogFragment<Arg : Parcelable, Ret : Parcelable> :
AppCompatDialogFragment(), DialogInterface.OnClickListener { AppCompatDialogFragment(), DialogInterface.OnClickListener {
companion object { companion object {
......
...@@ -31,6 +31,7 @@ import kotlinx.parcelize.Parcelize ...@@ -31,6 +31,7 @@ import kotlinx.parcelize.Parcelize
class Empty : Parcelable class Empty : Parcelable
@JvmOverloads @JvmOverloads
@Deprecated("Moved to fragment package", ReplaceWith("fragment.showAllowingStateLoss"))
fun DialogFragment.showAllowingStateLoss(fragmentManager: FragmentManager, tag: String? = null) { fun DialogFragment.showAllowingStateLoss(fragmentManager: FragmentManager, tag: String? = null) {
if (!fragmentManager.isStateSaved) show(fragmentManager, tag) if (!fragmentManager.isStateSaved) show(fragmentManager, tag)
} }
/*******************************************************************************
* *
* Copyright (C) 2020 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2020 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
package com.github.shadowsocks.plugin.fragment
import android.app.Activity
import android.content.DialogInterface
import android.os.Bundle
import android.os.Parcelable
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.setFragmentResult
import androidx.fragment.app.setFragmentResultListener
/**
* Based on: https://android.googlesource.com/platform/packages/apps/ExactCalculator/+/8c43f06/src/com/android/calculator2/AlertDialogFragment.java
*/
abstract class AlertDialogFragment<Arg : Parcelable, Ret : Parcelable?> :
AppCompatDialogFragment(), DialogInterface.OnClickListener {
companion object {
private const val KEY_RESULT = "result"
private const val KEY_ARG = "arg"
private const val KEY_RET = "ret"
private const val KEY_WHICH = "which"
fun <Ret : Parcelable> setResultListener(fragment: Fragment, requestKey: String,
listener: (Int, Ret?) -> Unit) {
fragment.setFragmentResultListener(requestKey) { _, bundle ->
listener(bundle.getInt(KEY_WHICH, Activity.RESULT_CANCELED), bundle.getParcelable(KEY_RET))
}
}
inline fun <reified T : AlertDialogFragment<*, Ret>, Ret : Parcelable?> setResultListener(
fragment: Fragment, noinline listener: (Int, Ret?) -> Unit) =
setResultListener(fragment, T::class.java.name, listener)
}
protected abstract fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener)
private val resultKey get() = requireArguments().getString(KEY_RESULT)
protected val arg by lazy { requireArguments().getParcelable<Arg>(KEY_ARG)!! }
protected open fun ret(which: Int): Ret? = null
private fun args() = arguments ?: Bundle().also { arguments = it }
fun arg(arg: Arg) = args().putParcelable(KEY_ARG, arg)
fun key(resultKey: String = javaClass.name) = args().putString(KEY_RESULT, resultKey)
override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog =
AlertDialog.Builder(requireContext()).also { it.prepare(this) }.create()
override fun onClick(dialog: DialogInterface?, which: Int) {
setFragmentResult(resultKey ?: return, Bundle().apply {
putInt(KEY_WHICH, which)
putParcelable(KEY_RET, ret(which) ?: return@apply)
})
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
onClick(null, Activity.RESULT_CANCELED)
}
}
/*******************************************************************************
* *
* Copyright (C) 2020 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2020 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
@file:JvmName("Utils")
package com.github.shadowsocks.plugin.fragment
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
typealias Empty = com.github.shadowsocks.plugin.Empty
@JvmOverloads
fun DialogFragment.showAllowingStateLoss(fragmentManager: FragmentManager, tag: String? = null) {
if (!fragmentManager.isStateSaved) show(fragmentManager, tag)
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment