aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/src/main/java/com/wireguard/android/fragment
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2020-09-14 19:46:49 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2020-09-15 12:30:15 +0200
commitbab70ab51ecc02c2e8afd1843cdd4d90ae9cc257 (patch)
treebd7117473f42dc6211d9aad4c78cbdddeb851b3e /ui/src/main/java/com/wireguard/android/fragment
parentcoroutines: convert low-hanging fruits (diff)
downloadwireguard-android-bab70ab51ecc02c2e8afd1843cdd4d90ae9cc257.tar.xz
wireguard-android-bab70ab51ecc02c2e8afd1843cdd4d90ae9cc257.zip
coroutines: convert the rest
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'ui/src/main/java/com/wireguard/android/fragment')
-rw-r--r--ui/src/main/java/com/wireguard/android/fragment/AppListDialogFragment.kt11
-rw-r--r--ui/src/main/java/com/wireguard/android/fragment/BaseFragment.kt39
-rw-r--r--ui/src/main/java/com/wireguard/android/fragment/ConfigNamingDialogFragment.kt14
-rw-r--r--ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt46
-rw-r--r--ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt68
-rw-r--r--ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt197
6 files changed, 211 insertions, 164 deletions
diff --git a/ui/src/main/java/com/wireguard/android/fragment/AppListDialogFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/AppListDialogFragment.kt
index 1a29c5e6..966ba7d1 100644
--- a/ui/src/main/java/com/wireguard/android/fragment/AppListDialogFragment.kt
+++ b/ui/src/main/java/com/wireguard/android/fragment/AppListDialogFragment.kt
@@ -14,7 +14,6 @@ import androidx.databinding.Observable
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import com.google.android.material.tabs.TabLayout
-import com.wireguard.android.Application
import com.wireguard.android.BR
import com.wireguard.android.R
import com.wireguard.android.databinding.AppListDialogFragmentBinding
@@ -22,8 +21,8 @@ import com.wireguard.android.databinding.ObservableKeyedArrayList
import com.wireguard.android.model.ApplicationData
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.requireTargetFragment
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -37,7 +36,7 @@ class AppListDialogFragment : DialogFragment() {
private fun loadData() {
val activity = activity ?: return
val pm = activity.packageManager
- CoroutineScope(Dispatchers.Default).launch {
+ GlobalScope.launch(Dispatchers.Default) {
try {
val applicationData: MutableList<ApplicationData> = ArrayList()
withContext(Dispatchers.IO) {
@@ -57,12 +56,12 @@ class AppListDialogFragment : DialogFragment() {
}
}
applicationData.sortWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name })
- withContext(Dispatchers.Main) {
+ withContext(Dispatchers.Main.immediate) {
appData.clear()
appData.addAll(applicationData)
}
- } catch (e: Exception) {
- withContext(Dispatchers.Main) {
+ } catch (e: Throwable) {
+ withContext(Dispatchers.Main.immediate) {
val error = ErrorMessages[e]
val message = activity.getString(R.string.error_fetching_apps, error)
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
diff --git a/ui/src/main/java/com/wireguard/android/fragment/BaseFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/BaseFragment.kt
index 82802623..997c2221 100644
--- a/ui/src/main/java/com/wireguard/android/fragment/BaseFragment.kt
+++ b/ui/src/main/java/com/wireguard/android/fragment/BaseFragment.kt
@@ -17,13 +17,15 @@ import com.wireguard.android.Application
import com.wireguard.android.R
import com.wireguard.android.activity.BaseActivity
import com.wireguard.android.activity.BaseActivity.OnSelectedTunnelChangedListener
-import com.wireguard.android.backend.Backend
import com.wireguard.android.backend.GoBackend
import com.wireguard.android.backend.Tunnel
import com.wireguard.android.databinding.TunnelDetailFragmentBinding
import com.wireguard.android.databinding.TunnelListItemBinding
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.util.ErrorMessages
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
/**
* Base class for fragments that need to know the currently-selected tunnel. Only does anything when
@@ -70,14 +72,14 @@ abstract class BaseFragment : Fragment(), OnSelectedTunnelChangedListener {
is TunnelListItemBinding -> binding.item
else -> return
} ?: return
- Application.getBackendAsync().thenAccept { backend: Backend? ->
- if (backend is GoBackend) {
+ GlobalScope.launch(Dispatchers.Main.immediate) {
+ if (Application.getBackend() is GoBackend) {
val intent = GoBackend.VpnService.prepare(view.context)
if (intent != null) {
pendingTunnel = tunnel
pendingTunnelUp = checked
startActivityForResult(intent, REQUEST_CODE_VPN_PERMISSION)
- return@thenAccept
+ return@launch
}
}
setTunnelStateWithPermissionsResult(tunnel, checked)
@@ -85,19 +87,22 @@ abstract class BaseFragment : Fragment(), OnSelectedTunnelChangedListener {
}
private fun setTunnelStateWithPermissionsResult(tunnel: ObservableTunnel, checked: Boolean) {
- tunnel.setStateAsync(Tunnel.State.of(checked)).whenComplete { _, throwable ->
- if (throwable == null) return@whenComplete
- val error = ErrorMessages[throwable]
- val messageResId = if (checked) R.string.error_up else R.string.error_down
- val message = requireContext().getString(messageResId, error)
- val view = view
- if (view != null)
- Snackbar.make(view, message, Snackbar.LENGTH_LONG)
- .setAnchorView(view.findViewById<View>(R.id.create_fab))
- .show()
- else
- Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
- Log.e(TAG, message, throwable)
+ GlobalScope.launch(Dispatchers.Main.immediate) {
+ try {
+ tunnel.setStateAsync(Tunnel.State.of(checked))
+ } catch (e: Throwable) {
+ val error = ErrorMessages[e]
+ val messageResId = if (checked) R.string.error_up else R.string.error_down
+ val message = requireContext().getString(messageResId, error)
+ val view = view
+ if (view != null)
+ Snackbar.make(view, message, Snackbar.LENGTH_LONG)
+ .setAnchorView(view.findViewById<View>(R.id.create_fab))
+ .show()
+ else
+ Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show()
+ Log.e(TAG, message, e)
+ }
}
}
diff --git a/ui/src/main/java/com/wireguard/android/fragment/ConfigNamingDialogFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/ConfigNamingDialogFragment.kt
index d1b01944..12406df2 100644
--- a/ui/src/main/java/com/wireguard/android/fragment/ConfigNamingDialogFragment.kt
+++ b/ui/src/main/java/com/wireguard/android/fragment/ConfigNamingDialogFragment.kt
@@ -16,6 +16,9 @@ import com.wireguard.android.R
import com.wireguard.android.databinding.ConfigNamingDialogFragmentBinding
import com.wireguard.config.BadConfigException
import com.wireguard.config.Config
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
import java.io.ByteArrayInputStream
import java.io.IOException
import java.nio.charset.StandardCharsets
@@ -28,11 +31,12 @@ class ConfigNamingDialogFragment : DialogFragment() {
private fun createTunnelAndDismiss() {
binding?.let {
val name = it.tunnelNameText.text.toString()
- Application.getTunnelManager().create(name, config).whenComplete { tunnel, throwable ->
- if (tunnel != null) {
+ GlobalScope.launch(Dispatchers.Main.immediate) {
+ try {
+ Application.getTunnelManager().create(name, config)
dismiss()
- } else {
- it.tunnelNameTextLayout.error = throwable.message
+ } catch (e: Throwable) {
+ it.tunnelNameTextLayout.error = e.message
}
}
}
@@ -49,7 +53,7 @@ class ConfigNamingDialogFragment : DialogFragment() {
val configBytes = configText!!.toByteArray(StandardCharsets.UTF_8)
config = try {
Config.parse(ByteArrayInputStream(configBytes))
- } catch (e: Exception) {
+ } catch (e: Throwable) {
when (e) {
is BadConfigException, is IOException -> throw IllegalArgumentException("Invalid config passed to ${javaClass.simpleName}", e)
else -> throw e
diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt
index 2b5a4ba6..e81b4e6d 100644
--- a/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt
+++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt
@@ -18,6 +18,9 @@ import com.wireguard.android.databinding.TunnelDetailPeerBinding
import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.widget.EdgeToEdge.setUpRoot
import com.wireguard.android.widget.EdgeToEdge.setUpScrollingContent
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
import java.util.Timer
import java.util.TimerTask
@@ -79,7 +82,13 @@ class TunnelDetailFragment : BaseFragment() {
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?) {
binding ?: return
binding!!.tunnel = newTunnel
- if (newTunnel == null) binding!!.config = null else newTunnel.configAsync.thenAccept { config -> binding!!.config = config }
+ if (newTunnel == null) binding!!.config = null else GlobalScope.launch(Dispatchers.Main.immediate) {
+ try {
+ binding!!.config = newTunnel.getConfigAsync()
+ } catch (_: Throwable) {
+ binding!!.config = null
+ }
+ }
lastState = Tunnel.State.TOGGLE
updateStats()
}
@@ -105,30 +114,31 @@ class TunnelDetailFragment : BaseFragment() {
val state = tunnel.state
if (state != Tunnel.State.UP && lastState == state) return
lastState = state
- tunnel.statisticsAsync.whenComplete { statistics, throwable ->
- if (throwable != null) {
+ GlobalScope.launch(Dispatchers.Main.immediate) {
+ try {
+ val statistics = tunnel.getStatisticsAsync()
for (i in 0 until binding!!.peersLayout.childCount) {
val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding!!.peersLayout.getChildAt(i))
?: continue
- peer.transferLabel.visibility = View.GONE
- peer.transferText.visibility = View.GONE
+ val publicKey = peer.item!!.publicKey
+ val rx = statistics.peerRx(publicKey)
+ val tx = statistics.peerTx(publicKey)
+ if (rx == 0L && tx == 0L) {
+ peer.transferLabel.visibility = View.GONE
+ peer.transferText.visibility = View.GONE
+ continue
+ }
+ peer.transferText.text = requireContext().getString(R.string.transfer_rx_tx, formatBytes(rx), formatBytes(tx))
+ peer.transferLabel.visibility = View.VISIBLE
+ peer.transferText.visibility = View.VISIBLE
}
- return@whenComplete
- }
- for (i in 0 until binding!!.peersLayout.childCount) {
- val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding!!.peersLayout.getChildAt(i))
- ?: continue
- val publicKey = peer.item!!.publicKey
- val rx = statistics.peerRx(publicKey)
- val tx = statistics.peerTx(publicKey)
- if (rx == 0L && tx == 0L) {
+ } catch (e: Throwable) {
+ for (i in 0 until binding!!.peersLayout.childCount) {
+ val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding!!.peersLayout.getChildAt(i))
+ ?: continue
peer.transferLabel.visibility = View.GONE
peer.transferText.visibility = View.GONE
- continue
}
- peer.transferText.text = requireContext().getString(R.string.transfer_rx_tx, formatBytes(rx), formatBytes(tx))
- peer.transferLabel.visibility = View.VISIBLE
- peer.transferText.visibility = View.VISIBLE
}
}
}
diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt
index cf39d052..5b556bc2 100644
--- a/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt
+++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt
@@ -25,13 +25,16 @@ import com.wireguard.android.backend.Tunnel
import com.wireguard.android.databinding.TunnelEditorFragmentBinding
import com.wireguard.android.fragment.AppListDialogFragment.AppSelectionListener
import com.wireguard.android.model.ObservableTunnel
-import com.wireguard.android.util.BiometricAuthenticator
import com.wireguard.android.util.AdminKnobs
+import com.wireguard.android.util.BiometricAuthenticator
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.viewmodel.ConfigProxy
import com.wireguard.android.widget.EdgeToEdge.setUpRoot
import com.wireguard.android.widget.EdgeToEdge.setUpScrollingContent
import com.wireguard.config.Config
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
/**
* Fragment for editing a WireGuard configuration.
@@ -130,7 +133,7 @@ class TunnelEditorFragment : BaseFragment(), AppSelectionListener {
binding ?: return false
val newConfig = try {
binding!!.config!!.resolve()
- } catch (e: Exception) {
+ } catch (e: Throwable) {
val error = ErrorMessages[e]
val tunnelName = if (tunnel == null) binding!!.name else tunnel!!.name
val message = getString(R.string.config_save_error, tunnelName, error)
@@ -138,20 +141,35 @@ class TunnelEditorFragment : BaseFragment(), AppSelectionListener {
Snackbar.make(binding!!.mainContainer, error, Snackbar.LENGTH_LONG).show()
return false
}
- when {
- tunnel == null -> {
- Log.d(TAG, "Attempting to create new tunnel " + binding!!.name)
- val manager = Application.getTunnelManager()
- manager.create(binding!!.name!!, newConfig).whenComplete(this::onTunnelCreated)
- }
- tunnel!!.name != binding!!.name -> {
- Log.d(TAG, "Attempting to rename tunnel to " + binding!!.name)
- tunnel!!.setNameAsync(binding!!.name!!).whenComplete { _, t -> onTunnelRenamed(tunnel!!, newConfig, t) }
- }
- else -> {
- Log.d(TAG, "Attempting to save config of " + tunnel!!.name)
- tunnel!!.setConfigAsync(newConfig)
- .whenComplete { _, t -> onConfigSaved(tunnel!!, t) }
+ GlobalScope.launch(Dispatchers.Main.immediate) {
+ when {
+ tunnel == null -> {
+ Log.d(TAG, "Attempting to create new tunnel " + binding!!.name)
+ val manager = Application.getTunnelManager()
+ try {
+ onTunnelCreated(manager.create(binding!!.name!!, newConfig), null)
+ } catch (e: Throwable) {
+ onTunnelCreated(null, e)
+ }
+ }
+ tunnel!!.name != binding!!.name -> {
+ Log.d(TAG, "Attempting to rename tunnel to " + binding!!.name)
+ try {
+ tunnel!!.setNameAsync(binding!!.name!!)
+ onTunnelRenamed(tunnel!!, newConfig, null)
+ } catch (e: Throwable) {
+ onTunnelRenamed(tunnel!!, newConfig, e)
+ }
+ }
+ else -> {
+ Log.d(TAG, "Attempting to save config of " + tunnel!!.name)
+ try {
+ tunnel!!.setConfigAsync(newConfig)
+ onConfigSaved(tunnel!!, null)
+ } catch (e: Throwable) {
+ onConfigSaved(tunnel!!, e)
+ }
+ }
}
}
return true
@@ -187,13 +205,18 @@ class TunnelEditorFragment : BaseFragment(), AppSelectionListener {
binding!!.config = ConfigProxy()
if (tunnel != null) {
binding!!.name = tunnel!!.name
- tunnel!!.configAsync.thenAccept(this::onConfigLoaded)
+ GlobalScope.launch(Dispatchers.Main.immediate) {
+ try {
+ onConfigLoaded(tunnel!!.getConfigAsync())
+ } catch (_: Throwable) {
+ }
+ }
} else {
binding!!.name = ""
}
}
- private fun onTunnelCreated(newTunnel: ObservableTunnel, throwable: Throwable?) {
+ private fun onTunnelCreated(newTunnel: ObservableTunnel?, throwable: Throwable?) {
val message: String
if (throwable == null) {
tunnel = newTunnel
@@ -219,7 +242,14 @@ class TunnelEditorFragment : BaseFragment(), AppSelectionListener {
Log.d(TAG, message)
// Now save the rest of configuration changes.
Log.d(TAG, "Attempting to save config of renamed tunnel " + tunnel!!.name)
- renamedTunnel.setConfigAsync(newConfig).whenComplete { _, t -> onConfigSaved(renamedTunnel, t) }
+ GlobalScope.launch(Dispatchers.Main.immediate) {
+ try {
+ renamedTunnel.setConfigAsync(newConfig)
+ onConfigSaved(renamedTunnel, null)
+ } catch (e: Throwable) {
+ onConfigSaved(renamedTunnel, e)
+ }
+ }
} else {
val error = ErrorMessages[throwable]
message = getString(R.string.tunnel_rename_error, error)
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 7af5e06b..3250db65 100644
--- a/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt
+++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelListFragment.kt
@@ -36,7 +36,14 @@ import com.wireguard.android.widget.EdgeToEdge.setUpRoot
import com.wireguard.android.widget.EdgeToEdge.setUpScrollingContent
import com.wireguard.android.widget.MultiselectableRelativeLayout
import com.wireguard.config.Config
-import java9.util.concurrent.CompletableFuture
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import java.io.BufferedReader
import java.io.ByteArrayInputStream
import java.io.InputStreamReader
@@ -61,108 +68,96 @@ class TunnelListFragment : BaseFragment() {
// Config text is valid, now create the tunnel…
newInstance(configText).show(parentFragmentManager, null)
- } catch (e: Exception) {
+ } catch (e: Throwable) {
onTunnelImportFinished(emptyList(), listOf<Throwable>(e))
}
}
private fun importTunnel(uri: Uri?) {
- val activity = activity
- if (activity == null || uri == null) {
- return
- }
- val contentResolver = activity.contentResolver
-
- val futureTunnels = ArrayList<CompletableFuture<ObservableTunnel>>()
- val throwables = ArrayList<Throwable>()
- Application.getAsyncWorker().supplyAsync {
- val columns = arrayOf(OpenableColumns.DISPLAY_NAME)
- var name = ""
- contentResolver.query(uri, columns, null, null, null)?.use { cursor ->
- if (cursor.moveToFirst() && !cursor.isNull(0)) {
- name = cursor.getString(0)
+ GlobalScope.launch(Dispatchers.Main.immediate) {
+ withContext(Dispatchers.IO) {
+ val activity = activity
+ if (activity == null || uri == null) {
+ return@withContext
}
- }
- if (name.isEmpty()) {
- name = Uri.decode(uri.lastPathSegment)
- }
- var idx = name.lastIndexOf('/')
- if (idx >= 0) {
- require(idx < name.length - 1) { resources.getString(R.string.illegal_filename_error, name) }
- name = name.substring(idx + 1)
- }
- val isZip = name.toLowerCase(Locale.ROOT).endsWith(".zip")
- if (name.toLowerCase(Locale.ROOT).endsWith(".conf")) {
- name = name.substring(0, name.length - ".conf".length)
- } else {
- require(isZip) { resources.getString(R.string.bad_extension_error) }
- }
+ val contentResolver = activity.contentResolver
+ val futureTunnels = ArrayList<Deferred<ObservableTunnel>>()
+ val throwables = ArrayList<Throwable>()
+ try {
+ val columns = arrayOf(OpenableColumns.DISPLAY_NAME)
+ var name = ""
+ contentResolver.query(uri, columns, null, null, null)?.use { cursor ->
+ if (cursor.moveToFirst() && !cursor.isNull(0)) {
+ name = cursor.getString(0)
+ }
+ }
+ if (name.isEmpty()) {
+ name = Uri.decode(uri.lastPathSegment)
+ }
+ var idx = name.lastIndexOf('/')
+ if (idx >= 0) {
+ require(idx < name.length - 1) { resources.getString(R.string.illegal_filename_error, name) }
+ name = name.substring(idx + 1)
+ }
+ val isZip = name.toLowerCase(Locale.ROOT).endsWith(".zip")
+ if (name.toLowerCase(Locale.ROOT).endsWith(".conf")) {
+ name = name.substring(0, name.length - ".conf".length)
+ } else {
+ require(isZip) { resources.getString(R.string.bad_extension_error) }
+ }
- if (isZip) {
- ZipInputStream(contentResolver.openInputStream(uri)).use { zip ->
- val reader = BufferedReader(InputStreamReader(zip, StandardCharsets.UTF_8))
- var entry: ZipEntry?
- while (true) {
- entry = zip.nextEntry ?: break
- name = entry.name
- idx = name.lastIndexOf('/')
- if (idx >= 0) {
- if (idx >= name.length - 1) {
- continue
+ if (isZip) {
+ ZipInputStream(contentResolver.openInputStream(uri)).use { zip ->
+ val reader = BufferedReader(InputStreamReader(zip, StandardCharsets.UTF_8))
+ var entry: ZipEntry?
+ while (true) {
+ entry = zip.nextEntry ?: break
+ name = entry.name
+ idx = name.lastIndexOf('/')
+ if (idx >= 0) {
+ if (idx >= name.length - 1) {
+ continue
+ }
+ name = name.substring(name.lastIndexOf('/') + 1)
+ }
+ if (name.toLowerCase(Locale.ROOT).endsWith(".conf")) {
+ name = name.substring(0, name.length - ".conf".length)
+ } else {
+ continue
+ }
+ try {
+ Config.parse(reader)
+ } catch (e: Throwable) {
+ throwables.add(e)
+ null
+ }?.let {
+ val nameCopy = name
+ futureTunnels.add(async(SupervisorJob()) { Application.getTunnelManager().create(nameCopy, it) })
+ }
}
- name = name.substring(name.lastIndexOf('/') + 1)
}
- if (name.toLowerCase(Locale.ROOT).endsWith(".conf")) {
- name = name.substring(0, name.length - ".conf".length)
+ } else {
+ futureTunnels.add(async(SupervisorJob()) { Application.getTunnelManager().create(name, Config.parse(contentResolver.openInputStream(uri)!!)) })
+ }
+
+ if (futureTunnels.isEmpty()) {
+ if (throwables.size == 1) {
+ throw throwables[0]
} else {
- continue
- }
- try {
- Config.parse(reader)
- } catch (e: Exception) {
- throwables.add(e)
- null
- }?.let {
- futureTunnels.add(Application.getTunnelManager().create(name, it).toCompletableFuture())
+ require(throwables.isNotEmpty()) { resources.getString(R.string.no_configs_error) }
}
}
- }
- } else {
- futureTunnels.add(
- Application.getTunnelManager().create(
- name,
- Config.parse(contentResolver.openInputStream(uri)!!)
- ).toCompletableFuture()
- )
- }
-
- if (futureTunnels.isEmpty()) {
- if (throwables.size == 1) {
- throw throwables[0]
- } else {
- require(throwables.isNotEmpty()) { resources.getString(R.string.no_configs_error) }
- }
- }
- CompletableFuture.allOf(*futureTunnels.toTypedArray())
- }.whenComplete { future, exception ->
- if (exception != null) {
- onTunnelImportFinished(emptyList(), listOf(exception))
- } else {
- future.whenComplete { _, _ ->
- val tunnels = mutableListOf<ObservableTunnel>()
- for (futureTunnel in futureTunnels) {
- val tunnel: ObservableTunnel? = try {
- futureTunnel.getNow(null)
- } catch (e: Exception) {
+ val tunnels = futureTunnels.mapNotNull {
+ try {
+ it.await()
+ } catch (e: Throwable) {
throwables.add(e)
null
}
-
- if (tunnel != null) {
- tunnels.add(tunnel)
- }
}
- onTunnelImportFinished(tunnels, throwables)
+ withContext(Dispatchers.Main.immediate) { onTunnelImportFinished(tunnels, throwables) }
+ } catch (e: Throwable) {
+ withContext(Dispatchers.Main.immediate) { onTunnelImportFinished(emptyList(), listOf(e)) }
}
}
}
@@ -226,7 +221,8 @@ class TunnelListFragment : BaseFragment() {
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?) {
binding ?: return
- Application.getTunnelManager().tunnels.thenAccept { tunnels ->
+ GlobalScope.launch(Dispatchers.Main.immediate) {
+ val tunnels = Application.getTunnelManager().getTunnels()
if (newTunnel != null) viewForTunnel(newTunnel, tunnels).setSingleSelected(true)
if (oldTunnel != null) viewForTunnel(oldTunnel, tunnels).setSingleSelected(false)
}
@@ -268,11 +264,10 @@ class TunnelListFragment : BaseFragment() {
super.onViewStateRestored(savedInstanceState)
binding ?: return
binding!!.fragment = this
- Application.getTunnelManager().tunnels.thenAccept { tunnels -> binding!!.tunnels = tunnels }
- val parent = this
+ GlobalScope.launch(Dispatchers.Main.immediate) { binding!!.tunnels = Application.getTunnelManager().getTunnels() }
binding!!.rowConfigurationHandler = object : RowConfigurationHandler<TunnelListItemBinding, ObservableTunnel> {
override fun onConfigureRow(binding: TunnelListItemBinding, item: ObservableTunnel, position: Int) {
- binding.fragment = parent
+ binding.fragment = this@TunnelListFragment
binding.root.setOnClickListener {
if (actionMode == null) {
selectedTunnel = item
@@ -321,20 +316,24 @@ class TunnelListFragment : BaseFragment() {
scaleX = 1f
scaleY = 1f
}
- Application.getTunnelManager().tunnels.thenAccept { tunnels ->
- val tunnelsToDelete = ArrayList<ObservableTunnel>()
- for (position in copyCheckedItems) tunnelsToDelete.add(tunnels[position])
- val futures = tunnelsToDelete.map { it.delete().toCompletableFuture() }.toTypedArray()
- CompletableFuture.allOf(*futures)
- .thenApply { futures.size }
- .whenComplete(this@TunnelListFragment::onTunnelDeletionFinished)
+ GlobalScope.launch(Dispatchers.Main.immediate) {
+ try {
+ val tunnels = Application.getTunnelManager().getTunnels()
+ val tunnelsToDelete = ArrayList<ObservableTunnel>()
+ for (position in copyCheckedItems) tunnelsToDelete.add(tunnels[position])
+ val futures = tunnelsToDelete.map { async(SupervisorJob()) { it.deleteAsync() } }
+ onTunnelDeletionFinished(futures.awaitAll().size, null)
+ } catch (e: Throwable) {
+ onTunnelDeletionFinished(0, e)
+ }
}
checkedItems.clear()
mode.finish()
true
}
R.id.menu_action_select_all -> {
- Application.getTunnelManager().tunnels.thenAccept { tunnels ->
+ GlobalScope.launch(Dispatchers.Main.immediate) {
+ val tunnels = Application.getTunnelManager().getTunnels()
for (i in 0 until tunnels.size) {
setItemChecked(i, true)
}