From 938399d881aa6b365be131ffb3a517d64be427bb Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Sat, 26 Sep 2020 13:34:20 +0200 Subject: ui: queue up tunnel mutating on activity scope instead of fragment scope Fragment scopes get cancelled when the fragment goes away, but we don't actually want to cancel an in-flight transition in that case. Also, before when the fragment would cancel, there'd be an exception, and the exception handler would call Fragment::getString, which in turn called requireContext, which caused an exception. Work around this by using the `activity ?: Application.get()` idiom to always have a context for strings and toasts. Signed-off-by: Jason A. Donenfeld --- .../android/fragment/TunnelListFragment.kt | 30 ++++++++++++++-------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt') diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt index 9c9d0ecd..3fbc3451 100644 --- a/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt +++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt @@ -15,6 +15,7 @@ import android.view.View import android.view.ViewGroup import android.view.animation.Animation import android.view.animation.AnimationUtils +import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode @@ -46,9 +47,10 @@ class TunnelListFragment : BaseFragment() { private var actionMode: ActionMode? = null private var binding: TunnelListFragmentBinding? = null private val tunnelFileImportResultLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { data -> - lifecycleScope.launch { - if (data == null) return@launch - val contentResolver = activity?.contentResolver ?: return@launch + if (data == null) return@registerForActivityResult + val activity = activity ?: return@registerForActivityResult + val contentResolver = activity.contentResolver ?: return@registerForActivityResult + activity.lifecycleScope.launch { TunnelImporter.importTunnel(contentResolver, data) { showSnackbar(it) } } } @@ -56,7 +58,9 @@ class TunnelListFragment : BaseFragment() { private val qrImportResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> val qrCode = IntentIntegrator.parseActivityResult(result.resultCode, result.data)?.contents ?: return@registerForActivityResult - lifecycleScope.launch { TunnelImporter.importTunnel(parentFragmentManager, qrCode) { showSnackbar(it) } } + val activity = activity ?: return@registerForActivityResult + val fragManager = parentFragmentManager + activity.lifecycleScope.launch { TunnelImporter.importTunnel(fragManager, qrCode) { showSnackbar(it) } } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -121,11 +125,12 @@ class TunnelListFragment : BaseFragment() { private fun onTunnelDeletionFinished(count: Int, throwable: Throwable?) { val message: String + val ctx = activity ?: Application.get() if (throwable == null) { - message = resources.getQuantityString(R.plurals.delete_success, count, count) + message = ctx.resources.getQuantityString(R.plurals.delete_success, count, count) } else { val error = ErrorMessages[throwable] - message = resources.getQuantityString(R.plurals.delete_error, count, count, error) + message = ctx.resources.getQuantityString(R.plurals.delete_error, count, count, error) Log.e(TAG, message, throwable) } showSnackbar(message) @@ -159,11 +164,13 @@ class TunnelListFragment : BaseFragment() { } private fun showSnackbar(message: CharSequence) { - binding?.let { - Snackbar.make(it.mainContainer, message, Snackbar.LENGTH_LONG) - .setAnchorView(it.createFab) + val binding = binding + if (binding != null) + Snackbar.make(binding.mainContainer, message, Snackbar.LENGTH_LONG) + .setAnchorView(binding.createFab) .show() - } + else + Toast.makeText(activity ?: Application.get(), message, Toast.LENGTH_SHORT).show() } private fun viewForTunnel(tunnel: ObservableTunnel, tunnels: List<*>): MultiselectableRelativeLayout? { @@ -181,13 +188,14 @@ class TunnelListFragment : BaseFragment() { override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { return when (item.itemId) { R.id.menu_action_delete -> { + val activity = activity ?: return true val copyCheckedItems = HashSet(checkedItems) binding?.createFab?.apply { visibility = View.VISIBLE scaleX = 1f scaleY = 1f } - lifecycleScope.launch { + activity.lifecycleScope.launch { try { val tunnels = Application.getTunnelManager().getTunnels() val tunnelsToDelete = ArrayList() -- cgit v1.2.3-59-g8ed1b