From bf352297ece79b9b5fc1cf17cac80b6a7ae1f4af Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 1 Jun 2020 18:52:40 -0400 Subject: [PATCH] Use new fragment result API --- .../mygod/vpnhotspot/AlertDialogFragment.kt | 35 ++++++---- .../java/be/mygod/vpnhotspot/MainActivity.kt | 1 - .../vpnhotspot/client/ClientsFragment.kt | 14 ++-- .../vpnhotspot/manage/RepeaterManager.kt | 39 ++++++++--- .../vpnhotspot/manage/TetheringFragment.kt | 64 ++++++------------- 5 files changed, 82 insertions(+), 71 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/AlertDialogFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/AlertDialogFragment.kt index 03249de7..c8dee139 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/AlertDialogFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/AlertDialogFragment.kt @@ -7,8 +7,10 @@ import android.os.Bundle import android.os.Parcelable import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatDialogFragment +import androidx.core.os.bundleOf import androidx.fragment.app.Fragment -import be.mygod.vpnhotspot.util.showAllowingStateLoss +import androidx.fragment.app.setFragmentResult +import androidx.fragment.app.setFragmentResultListener import kotlinx.android.parcel.Parcelize /** @@ -17,33 +19,44 @@ import kotlinx.android.parcel.Parcelize abstract class AlertDialogFragment : AppCompatDialogFragment(), DialogInterface.OnClickListener { companion object { + private const val KEY_RESULT = "result" private const val KEY_ARG = "arg" private const val KEY_RET = "ret" - fun getRet(data: Intent) = data.getParcelableExtra(KEY_RET)!! + private const val KEY_WHICH = "which" + + fun 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 , 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(KEY_ARG)!! } protected open val ret: Ret? get() = null - fun withArg(arg: Arg) = apply { arguments = Bundle().apply { putParcelable(KEY_ARG, arg) } } + + 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) { - targetFragment?.onActivityResult(targetRequestCode, which, ret?.let { - Intent().replaceExtras(Bundle().apply { putParcelable(KEY_RET, it) }) + setFragmentResult(resultKey ?: return, Bundle().apply { + putInt(KEY_WHICH, which) + putParcelable(KEY_RET, ret ?: return@apply) }) } override fun onDismiss(dialog: DialogInterface) { super.onDismiss(dialog) - targetFragment?.onActivityResult(targetRequestCode, Activity.RESULT_CANCELED, null) - } - - fun show(target: Fragment, requestCode: Int = 0, tag: String = javaClass.simpleName) { - setTargetFragment(target, requestCode) - showAllowingStateLoss(target.parentFragmentManager, tag) + setFragmentResult(resultKey ?: return, bundleOf(KEY_WHICH to Activity.RESULT_CANCELED)) } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt b/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt index 5cd29511..65a2810d 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/MainActivity.kt @@ -1,6 +1,5 @@ package be.mygod.vpnhotspot -import android.content.Intent import android.os.Bundle import android.view.MenuItem import androidx.activity.viewModels diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt index 0058e4b3..a9b29746 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt @@ -39,6 +39,7 @@ import be.mygod.vpnhotspot.room.AppDatabase import be.mygod.vpnhotspot.room.ClientStats import be.mygod.vpnhotspot.room.TrafficRecord import be.mygod.vpnhotspot.util.SpanFormatter +import be.mygod.vpnhotspot.util.showAllowingStateLoss import be.mygod.vpnhotspot.util.toPluralInt import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.android.parcel.Parcelize @@ -129,8 +130,9 @@ class ClientsFragment : Fragment() { return when (item?.itemId) { R.id.nickname -> { val client = binding.client ?: return false - NicknameDialogFragment().withArg(NicknameArg(client.mac, client.nickname)) - .show(this@ClientsFragment) + NicknameDialogFragment().apply { + arg(NicknameArg(client.mac, client.nickname)) + }.showAllowingStateLoss(parentFragmentManager) true } R.id.block, R.id.unblock -> { @@ -152,10 +154,10 @@ class ClientsFragment : Fragment() { binding.client?.let { client -> viewLifecycleOwner.lifecycleScope.launchWhenCreated { withContext(Dispatchers.Unconfined) { - StatsDialogFragment().withArg(StatsArg( - client.title.value ?: return@withContext, - AppDatabase.instance.trafficRecordDao.queryStats(client.mac.addr) - )).show(this@ClientsFragment) + StatsDialogFragment().apply { + arg(StatsArg(client.title.value ?: return@withContext, + AppDatabase.instance.trafficRecordDao.queryStats(client.mac.addr))) + }.showAllowingStateLoss(parentFragmentManager) } } } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt index 9a640de8..8d02af14 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/RepeaterManager.kt @@ -22,15 +22,19 @@ import androidx.databinding.BaseObservable import androidx.databinding.Bindable import androidx.fragment.app.viewModels import androidx.lifecycle.ViewModel +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import be.mygod.vpnhotspot.* import be.mygod.vpnhotspot.databinding.ListitemRepeaterBinding import be.mygod.vpnhotspot.net.wifi.configuration.* import be.mygod.vpnhotspot.util.ServiceForegroundConnector import be.mygod.vpnhotspot.util.formatAddresses +import be.mygod.vpnhotspot.util.showAllowingStateLoss import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.android.parcel.Parcelize import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber import java.net.NetworkInterface @@ -91,7 +95,9 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic } fun wps() { - if (binder?.active == true) WpsDialogFragment().show(parent, TetheringFragment.REPEATER_WPS) + if (binder?.active == true) WpsDialogFragment().apply { + key() + }.showAllowingStateLoss(parent.parentFragmentManager) } } @@ -119,6 +125,26 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic init { ServiceForegroundConnector(parent, this, RepeaterService::class) + AlertDialogFragment.setResultListener(parent, javaClass.name) { which, ret -> + if (which == DialogInterface.BUTTON_POSITIVE) GlobalScope.launch(Dispatchers.Main.immediate) { + updateConfiguration(ret!!.configuration) + } + } + AlertDialogFragment.setResultListener(parent) { which, ret -> + when (which) { + DialogInterface.BUTTON_POSITIVE -> binder!!.startWps(ret!!.pin) + DialogInterface.BUTTON_NEUTRAL -> binder!!.startWps(null) + } + } + } + + fun configure() = parent.viewLifecycleOwner.lifecycleScope.launchWhenCreated { + getConfiguration()?.let { config -> + WifiApDialogFragment().apply { + arg(WifiApDialogFragment.Arg(config, p2pMode = true)) + key(this@RepeaterManager.javaClass.name) + }.showAllowingStateLoss(parent.parentFragmentManager) + } } override val type get() = VIEW_TYPE_REPEATER @@ -146,15 +172,8 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic data.onStatusChanged() } - fun onWpsResult(which: Int, data: Intent?) { - when (which) { - DialogInterface.BUTTON_POSITIVE -> binder!!.startWps(AlertDialogFragment.getRet(data!!).pin) - DialogInterface.BUTTON_NEUTRAL -> binder!!.startWps(null) - } - } - @MainThread - suspend fun getConfiguration(): WifiConfiguration? { + private suspend fun getConfiguration(): WifiConfiguration? { if (RepeaterService.safeMode) { val networkName = RepeaterService.networkName val passphrase = RepeaterService.passphrase @@ -191,7 +210,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic SmartSnackbar.make(R.string.repeater_configure_failure).show() return null } - suspend fun updateConfiguration(config: WifiConfiguration) { + private suspend fun updateConfiguration(config: WifiConfiguration) { if (RepeaterService.safeMode) { RepeaterService.networkName = config.SSID RepeaterService.passphrase = config.preSharedKey diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt index 8d122445..d3eb1267 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/manage/TetheringFragment.kt @@ -31,23 +31,15 @@ import be.mygod.vpnhotspot.net.wifi.configuration.WifiApDialogFragment import be.mygod.vpnhotspot.util.ServiceForegroundConnector import be.mygod.vpnhotspot.util.broadcastReceiver import be.mygod.vpnhotspot.util.isNotGone +import be.mygod.vpnhotspot.util.showAllowingStateLoss import be.mygod.vpnhotspot.widget.SmartSnackbar import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch import timber.log.Timber import java.lang.reflect.InvocationTargetException import java.net.NetworkInterface import java.net.SocketException class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener { - companion object { - const val REPEATER_WPS = 3 - const val CONFIGURE_REPEATER = 2 - const val CONFIGURE_AP = 4 - } - inner class ManagerAdapter : ListAdapter(Manager) { internal val repeaterManager by lazy { RepeaterManager(this@TetheringFragment) } @get:RequiresApi(26) @@ -168,25 +160,22 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick true } R.id.configuration_repeater -> { - viewLifecycleOwner.lifecycleScope.launchWhenCreated { - adapter.repeaterManager.getConfiguration()?.let { config -> - WifiApDialogFragment().withArg(WifiApDialogFragment.Arg(config, p2pMode = true)).show( - this@TetheringFragment, CONFIGURE_REPEATER) - } - } + adapter.repeaterManager.configure() true } R.id.configuration_temp_hotspot -> { - WifiApDialogFragment().withArg(WifiApDialogFragment.Arg( - adapter.localOnlyHotspotManager.binder?.configuration ?: return false, - readOnly = true - )).show(this, 0) // read-only, no callback needed + WifiApDialogFragment().apply { + arg(WifiApDialogFragment.Arg(adapter.localOnlyHotspotManager.binder?.configuration ?: return false, + readOnly = true)) + // no need for callback + }.showAllowingStateLoss(parentFragmentManager) true } R.id.configuration_ap -> try { - WifiApDialogFragment().withArg(WifiApDialogFragment.Arg( - WifiApManager.configuration - )).show(this, CONFIGURE_AP) + WifiApDialogFragment().apply { + arg(WifiApDialogFragment.Arg(WifiApManager.configuration)) + key() + }.showAllowingStateLoss(parentFragmentManager) true } catch (e: InvocationTargetException) { if (e.targetException !is SecurityException) Timber.w(e) @@ -198,6 +187,16 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + AlertDialogFragment.setResultListener(this) { which, ret -> + if (which == DialogInterface.BUTTON_POSITIVE) try { + WifiApManager.configuration = ret!!.configuration + } catch (e: IllegalArgumentException) { + Timber.d(e) + SmartSnackbar.make(R.string.configuration_rejected).show() + } catch (e: InvocationTargetException) { + SmartSnackbar.make(e.targetException).show() + } + } binding = FragmentTetheringBinding.inflate(inflater, container, false) binding.interfaces.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) binding.interfaces.itemAnimator = DefaultItemAnimator() @@ -224,27 +223,6 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick if (Build.VERSION.SDK_INT >= 27) ManageBar.Data.notifyChange() } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - val configuration by lazy { AlertDialogFragment.getRet(data!!).configuration } - when (requestCode) { - REPEATER_WPS -> adapter.repeaterManager.onWpsResult(resultCode, data) - CONFIGURE_REPEATER -> if (resultCode == DialogInterface.BUTTON_POSITIVE) { - GlobalScope.launch(Dispatchers.Main.immediate) { - adapter.repeaterManager.updateConfiguration(configuration) - } - } - CONFIGURE_AP -> if (resultCode == DialogInterface.BUTTON_POSITIVE) try { - WifiApManager.configuration = configuration - } catch (e: IllegalArgumentException) { - Timber.d(e) - SmartSnackbar.make(R.string.configuration_rejected).show() - } catch (e: InvocationTargetException) { - SmartSnackbar.make(e.targetException).show() - } - else -> super.onActivityResult(requestCode, resultCode, data) - } - } - override fun onServiceConnected(name: ComponentName?, service: IBinder?) { binder = service as TetheringService.Binder service.routingsChanged[this] = {