coroutines: use lifecycleScope where appropriate

There's still a bit of GlobalScope lingering around, which might be
removable.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2020-09-15 23:30:20 +02:00
parent d79cdb0d41
commit 49ac61304e
19 changed files with 66 additions and 82 deletions

View File

@ -10,16 +10,17 @@ buildscript {
coordinatorLayoutVersion = '1.1.0' coordinatorLayoutVersion = '1.1.0'
coreKtxVersion = '1.3.1' coreKtxVersion = '1.3.1'
coroutinesVersion = '1.3.9' coroutinesVersion = '1.3.9'
desugarVersion = '1.0.10'
eddsaVersion = '0.3.0' eddsaVersion = '0.3.0'
fragmentVersion = '1.2.5' fragmentVersion = '1.2.5'
jsr305Version = '3.0.2' jsr305Version = '3.0.2'
junitVersion = '4.13' junitVersion = '4.13'
kotlinVersion = '1.4.10' kotlinVersion = '1.4.10'
lifecycleRuntimeKtxVersion = '2.3.0-alpha07'
materialComponentsVersion = '22973ff10305397bfc72fed41684175fee8aeefb' materialComponentsVersion = '22973ff10305397bfc72fed41684175fee8aeefb'
mavenPluginVersion = '2.1' mavenPluginVersion = '2.1'
preferenceVersion = '1.1.1' preferenceVersion = '1.1.1'
zxingEmbeddedVersion = '3.6.0' zxingEmbeddedVersion = '3.6.0'
desugarVersion = '1.0.10'
groupName = 'com.wireguard.android' groupName = 'com.wireguard.android'
} }

View File

@ -66,6 +66,7 @@ dependencies {
implementation "androidx.databinding:databinding-runtime:$agpVersion" implementation "androidx.databinding:databinding-runtime:$agpVersion"
implementation "androidx.fragment:fragment-ktx:$fragmentVersion" implementation "androidx.fragment:fragment-ktx:$fragmentVersion"
implementation "androidx.preference:preference-ktx:$preferenceVersion" implementation "androidx.preference:preference-ktx:$preferenceVersion"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleRuntimeKtxVersion"
implementation "com.github.material-components:material-components-android:$materialComponentsVersion" implementation "com.github.material-components:material-components-android:$materialComponentsVersion"
implementation "com.journeyapps:zxing-android-embedded:$zxingEmbeddedVersion" implementation "com.journeyapps:zxing-android-embedded:$zxingEmbeddedVersion"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"

View File

@ -7,10 +7,10 @@ package com.wireguard.android.activity
import android.os.Bundle import android.os.Bundle
import androidx.databinding.CallbackRegistry import androidx.databinding.CallbackRegistry
import androidx.databinding.CallbackRegistry.NotifierCallback import androidx.databinding.CallbackRegistry.NotifierCallback
import androidx.lifecycle.lifecycleScope
import com.wireguard.android.Application import com.wireguard.android.Application
import com.wireguard.android.model.ObservableTunnel import com.wireguard.android.model.ObservableTunnel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/** /**
@ -39,7 +39,7 @@ abstract class BaseActivity : ThemeChangeAwareActivity() {
else -> null else -> null
} }
if (savedTunnelName != null) if (savedTunnelName != null)
GlobalScope.launch(Dispatchers.Main.immediate) { selectedTunnel = Application.getTunnelManager().getTunnels()[savedTunnelName] } lifecycleScope.launch(Dispatchers.Main.immediate) { selectedTunnel = Application.getTunnelManager().getTunnels()[savedTunnelName] }
// The selected tunnel must be set before the superclass method recreates fragments. // The selected tunnel must be set before the superclass method recreates fragments.
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@ -26,6 +26,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ShareCompat import androidx.core.app.ShareCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -40,10 +41,7 @@ import com.wireguard.android.widget.EdgeToEdge.setUpFAB
import com.wireguard.android.widget.EdgeToEdge.setUpRoot import com.wireguard.android.widget.EdgeToEdge.setUpRoot
import com.wireguard.android.widget.EdgeToEdge.setUpScrollingContent import com.wireguard.android.widget.EdgeToEdge.setUpScrollingContent
import com.wireguard.crypto.KeyPair import com.wireguard.crypto.KeyPair
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.BufferedReader import java.io.BufferedReader
@ -68,7 +66,6 @@ class LogViewerActivity : AppCompatActivity() {
private var rawLogLines = StringBuffer() private var rawLogLines = StringBuffer()
private var recyclerView: RecyclerView? = null private var recyclerView: RecyclerView? = null
private var saveButton: MenuItem? = null private var saveButton: MenuItem? = null
private val logStreamingScope = CoroutineScope(Dispatchers.IO)
private val year by lazy { private val year by lazy {
val yearFormatter: DateFormat = SimpleDateFormat("yyyy", Locale.US) val yearFormatter: DateFormat = SimpleDateFormat("yyyy", Locale.US)
yearFormatter.format(Date()) yearFormatter.format(Date())
@ -115,7 +112,7 @@ class LogViewerActivity : AppCompatActivity() {
addItemDecoration(DividerItemDecoration(context, LinearLayoutManager.VERTICAL)) addItemDecoration(DividerItemDecoration(context, LinearLayoutManager.VERTICAL))
} }
logStreamingScope.launch { streamingLog() } lifecycleScope.launch(Dispatchers.IO) { streamingLog() }
binding.shareFab.setOnClickListener { binding.shareFab.setOnClickListener {
revokeLastUri() revokeLastUri()
@ -136,7 +133,6 @@ class LogViewerActivity : AppCompatActivity() {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
logStreamingScope.cancel()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@ -159,7 +155,7 @@ class LogViewerActivity : AppCompatActivity() {
true true
} }
R.id.save_log -> { R.id.save_log -> {
GlobalScope.launch { saveLog() } lifecycleScope.launch(Dispatchers.IO) { saveLog() }
true true
} }
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)

View File

@ -12,6 +12,7 @@ import android.util.SparseArray
import android.view.MenuItem import android.view.MenuItem
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.wireguard.android.Application import com.wireguard.android.Application
@ -20,7 +21,6 @@ import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.util.AdminKnobs import com.wireguard.android.util.AdminKnobs
import com.wireguard.android.util.ModuleLoader import com.wireguard.android.util.ModuleLoader
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.ArrayList import java.util.ArrayList
@ -102,7 +102,7 @@ class SettingsActivity : ThemeChangeAwareActivity() {
preferenceManager.findPreference<Preference>("multiple_tunnels") preferenceManager.findPreference<Preference>("multiple_tunnels")
).filterNotNull() ).filterNotNull()
wgQuickOnlyPrefs.forEach { it.isVisible = false } wgQuickOnlyPrefs.forEach { it.isVisible = false }
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
if (Application.getBackend() is WgQuickBackend) { if (Application.getBackend() is WgQuickBackend) {
++preferenceScreen.initialExpandedChildrenCount ++preferenceScreen.initialExpandedChildrenCount
wgQuickOnlyPrefs.forEach { it.isVisible = true } wgQuickOnlyPrefs.forEach { it.isVisible = true }
@ -121,7 +121,7 @@ class SettingsActivity : ThemeChangeAwareActivity() {
moduleInstaller?.parent?.removePreference(moduleInstaller) moduleInstaller?.parent?.removePreference(moduleInstaller)
} else { } else {
kernelModuleDisabler?.parent?.removePreference(kernelModuleDisabler) kernelModuleDisabler?.parent?.removePreference(kernelModuleDisabler)
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
withContext(Dispatchers.IO) { Application.getRootShell().start() } withContext(Dispatchers.IO) { Application.getRootShell().start() }
moduleInstaller?.isVisible = true moduleInstaller?.isVisible = true

View File

@ -12,13 +12,13 @@ import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.wireguard.android.Application import com.wireguard.android.Application
import com.wireguard.android.QuickTileService import com.wireguard.android.QuickTileService
import com.wireguard.android.R import com.wireguard.android.R
import com.wireguard.android.backend.Tunnel import com.wireguard.android.backend.Tunnel
import com.wireguard.android.util.ErrorMessages import com.wireguard.android.util.ErrorMessages
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@RequiresApi(Build.VERSION_CODES.N) @RequiresApi(Build.VERSION_CODES.N)
@ -26,7 +26,7 @@ class TunnelToggleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val tunnel = Application.getTunnelManager().lastUsedTunnel ?: return val tunnel = Application.getTunnelManager().lastUsedTunnel ?: return
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
tunnel.setStateAsync(Tunnel.State.TOGGLE) tunnel.setStateAsync(Tunnel.State.TOGGLE)
} catch (e: Throwable) { } catch (e: Throwable) {

View File

@ -13,6 +13,7 @@ import androidx.appcompat.app.AlertDialog
import androidx.databinding.Observable import androidx.databinding.Observable
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.wireguard.android.BR import com.wireguard.android.BR
import com.wireguard.android.R import com.wireguard.android.R
@ -22,7 +23,6 @@ import com.wireguard.android.model.ApplicationData
import com.wireguard.android.util.ErrorMessages import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.requireTargetFragment import com.wireguard.android.util.requireTargetFragment
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -36,7 +36,7 @@ class AppListDialogFragment : DialogFragment() {
private fun loadData() { private fun loadData() {
val activity = activity ?: return val activity = activity ?: return
val pm = activity.packageManager val pm = activity.packageManager
GlobalScope.launch(Dispatchers.Default) { lifecycleScope.launch(Dispatchers.Default) {
try { try {
val applicationData: MutableList<ApplicationData> = ArrayList() val applicationData: MutableList<ApplicationData> = ArrayList()
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {

View File

@ -12,6 +12,7 @@ import android.widget.Toast
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.wireguard.android.Application import com.wireguard.android.Application
import com.wireguard.android.R import com.wireguard.android.R
@ -24,7 +25,6 @@ import com.wireguard.android.databinding.TunnelListItemBinding
import com.wireguard.android.model.ObservableTunnel import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.util.ErrorMessages import com.wireguard.android.util.ErrorMessages
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/** /**
@ -72,7 +72,7 @@ abstract class BaseFragment : Fragment(), OnSelectedTunnelChangedListener {
is TunnelListItemBinding -> binding.item is TunnelListItemBinding -> binding.item
else -> return else -> return
} ?: return } ?: return
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
if (Application.getBackend() is GoBackend) { if (Application.getBackend() is GoBackend) {
val intent = GoBackend.VpnService.prepare(view.context) val intent = GoBackend.VpnService.prepare(view.context)
if (intent != null) { if (intent != null) {
@ -87,7 +87,7 @@ abstract class BaseFragment : Fragment(), OnSelectedTunnelChangedListener {
} }
private fun setTunnelStateWithPermissionsResult(tunnel: ObservableTunnel, checked: Boolean) { private fun setTunnelStateWithPermissionsResult(tunnel: ObservableTunnel, checked: Boolean) {
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
tunnel.setStateAsync(Tunnel.State.of(checked)) tunnel.setStateAsync(Tunnel.State.of(checked))
} catch (e: Throwable) { } catch (e: Throwable) {

View File

@ -11,13 +11,13 @@ import android.os.Bundle
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.wireguard.android.Application import com.wireguard.android.Application
import com.wireguard.android.R import com.wireguard.android.R
import com.wireguard.android.databinding.ConfigNamingDialogFragmentBinding import com.wireguard.android.databinding.ConfigNamingDialogFragmentBinding
import com.wireguard.config.BadConfigException import com.wireguard.config.BadConfigException
import com.wireguard.config.Config import com.wireguard.config.Config
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.IOException import java.io.IOException
@ -31,7 +31,7 @@ class ConfigNamingDialogFragment : DialogFragment() {
private fun createTunnelAndDismiss() { private fun createTunnelAndDismiss() {
binding?.let { binding?.let {
val name = it.tunnelNameText.text.toString() val name = it.tunnelNameText.text.toString()
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
Application.getTunnelManager().create(name, config) Application.getTunnelManager().create(name, config)
dismiss() dismiss()

View File

@ -11,6 +11,7 @@ import android.view.MenuInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.lifecycle.lifecycleScope
import com.wireguard.android.R import com.wireguard.android.R
import com.wireguard.android.backend.Tunnel import com.wireguard.android.backend.Tunnel
import com.wireguard.android.databinding.TunnelDetailFragmentBinding import com.wireguard.android.databinding.TunnelDetailFragmentBinding
@ -19,7 +20,6 @@ import com.wireguard.android.model.ObservableTunnel
import com.wireguard.android.widget.EdgeToEdge.setUpRoot import com.wireguard.android.widget.EdgeToEdge.setUpRoot
import com.wireguard.android.widget.EdgeToEdge.setUpScrollingContent import com.wireguard.android.widget.EdgeToEdge.setUpScrollingContent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Timer import java.util.Timer
import java.util.TimerTask import java.util.TimerTask
@ -82,7 +82,7 @@ class TunnelDetailFragment : BaseFragment() {
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?) { override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?) {
binding ?: return binding ?: return
binding!!.tunnel = newTunnel binding!!.tunnel = newTunnel
if (newTunnel == null) binding!!.config = null else GlobalScope.launch(Dispatchers.Main.immediate) { if (newTunnel == null) binding!!.config = null else lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
binding!!.config = newTunnel.getConfigAsync() binding!!.config = newTunnel.getConfigAsync()
} catch (_: Throwable) { } catch (_: Throwable) {
@ -114,7 +114,7 @@ class TunnelDetailFragment : BaseFragment() {
val state = tunnel.state val state = tunnel.state
if (state != Tunnel.State.UP && lastState == state) return if (state != Tunnel.State.UP && lastState == state) return
lastState = state lastState = state
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
val statistics = tunnel.getStatisticsAsync() val statistics = tunnel.getStatisticsAsync()
for (i in 0 until binding!!.peersLayout.childCount) { for (i in 0 until binding!!.peersLayout.childCount) {

View File

@ -18,6 +18,7 @@ import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.EditText import android.widget.EditText
import android.widget.Toast import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.wireguard.android.Application import com.wireguard.android.Application
import com.wireguard.android.R import com.wireguard.android.R
@ -33,7 +34,6 @@ import com.wireguard.android.widget.EdgeToEdge.setUpRoot
import com.wireguard.android.widget.EdgeToEdge.setUpScrollingContent import com.wireguard.android.widget.EdgeToEdge.setUpScrollingContent
import com.wireguard.config.Config import com.wireguard.config.Config
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/** /**
@ -141,7 +141,7 @@ class TunnelEditorFragment : BaseFragment(), AppSelectionListener {
Snackbar.make(binding!!.mainContainer, error, Snackbar.LENGTH_LONG).show() Snackbar.make(binding!!.mainContainer, error, Snackbar.LENGTH_LONG).show()
return false return false
} }
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
when { when {
tunnel == null -> { tunnel == null -> {
Log.d(TAG, "Attempting to create new tunnel " + binding!!.name) Log.d(TAG, "Attempting to create new tunnel " + binding!!.name)
@ -205,7 +205,7 @@ class TunnelEditorFragment : BaseFragment(), AppSelectionListener {
binding!!.config = ConfigProxy() binding!!.config = ConfigProxy()
if (tunnel != null) { if (tunnel != null) {
binding!!.name = tunnel!!.name binding!!.name = tunnel!!.name
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
onConfigLoaded(tunnel!!.getConfigAsync()) onConfigLoaded(tunnel!!.getConfigAsync())
} catch (_: Throwable) { } catch (_: Throwable) {
@ -242,7 +242,7 @@ class TunnelEditorFragment : BaseFragment(), AppSelectionListener {
Log.d(TAG, message) Log.d(TAG, message)
// Now save the rest of configuration changes. // Now save the rest of configuration changes.
Log.d(TAG, "Attempting to save config of renamed tunnel " + tunnel!!.name) Log.d(TAG, "Attempting to save config of renamed tunnel " + tunnel!!.name)
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
renamedTunnel.setConfigAsync(newConfig) renamedTunnel.setConfigAsync(newConfig)
onConfigSaved(renamedTunnel, null) onConfigSaved(renamedTunnel, null)

View File

@ -21,6 +21,7 @@ import android.view.animation.Animation
import android.view.animation.AnimationUtils import android.view.animation.AnimationUtils
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.zxing.integration.android.IntentIntegrator import com.google.zxing.integration.android.IntentIntegrator
import com.wireguard.android.Application import com.wireguard.android.Application
@ -38,7 +39,6 @@ import com.wireguard.android.widget.MultiselectableRelativeLayout
import com.wireguard.config.Config import com.wireguard.config.Config
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
@ -74,7 +74,7 @@ class TunnelListFragment : BaseFragment() {
} }
private fun importTunnel(uri: Uri?) { private fun importTunnel(uri: Uri?) {
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val activity = activity val activity = activity
if (activity == null || uri == null) { if (activity == null || uri == null) {
@ -221,7 +221,7 @@ class TunnelListFragment : BaseFragment() {
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?) { override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?) {
binding ?: return binding ?: return
GlobalScope.launch(Dispatchers.Main) { lifecycleScope.launch(Dispatchers.Main) {
val tunnels = Application.getTunnelManager().getTunnels() val tunnels = Application.getTunnelManager().getTunnels()
if (newTunnel != null) viewForTunnel(newTunnel, tunnels).setSingleSelected(true) if (newTunnel != null) viewForTunnel(newTunnel, tunnels).setSingleSelected(true)
if (oldTunnel != null) viewForTunnel(oldTunnel, tunnels).setSingleSelected(false) if (oldTunnel != null) viewForTunnel(oldTunnel, tunnels).setSingleSelected(false)
@ -264,7 +264,7 @@ class TunnelListFragment : BaseFragment() {
super.onViewStateRestored(savedInstanceState) super.onViewStateRestored(savedInstanceState)
binding ?: return binding ?: return
binding!!.fragment = this binding!!.fragment = this
GlobalScope.launch(Dispatchers.Main.immediate) { binding!!.tunnels = Application.getTunnelManager().getTunnels() } lifecycleScope.launch(Dispatchers.Main.immediate) { binding!!.tunnels = Application.getTunnelManager().getTunnels() }
binding!!.rowConfigurationHandler = object : RowConfigurationHandler<TunnelListItemBinding, ObservableTunnel> { binding!!.rowConfigurationHandler = object : RowConfigurationHandler<TunnelListItemBinding, ObservableTunnel> {
override fun onConfigureRow(binding: TunnelListItemBinding, item: ObservableTunnel, position: Int) { override fun onConfigureRow(binding: TunnelListItemBinding, item: ObservableTunnel, position: Int) {
binding.fragment = this@TunnelListFragment binding.fragment = this@TunnelListFragment
@ -316,7 +316,7 @@ class TunnelListFragment : BaseFragment() {
scaleX = 1f scaleX = 1f
scaleY = 1f scaleY = 1f
} }
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
val tunnels = Application.getTunnelManager().getTunnels() val tunnels = Application.getTunnelManager().getTunnels()
val tunnelsToDelete = ArrayList<ObservableTunnel>() val tunnelsToDelete = ArrayList<ObservableTunnel>()
@ -332,7 +332,7 @@ class TunnelListFragment : BaseFragment() {
true true
} }
R.id.menu_action_select_all -> { R.id.menu_action_select_all -> {
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
val tunnels = Application.getTunnelManager().getTunnels() val tunnels = Application.getTunnelManager().getTunnels()
for (i in 0 until tunnels.size) { for (i in 0 until tunnels.size) {
setItemChecked(i, true) setItemChecked(i, true)

View File

@ -15,8 +15,8 @@ import com.wireguard.android.R
import com.wireguard.android.activity.SettingsActivity import com.wireguard.android.activity.SettingsActivity
import com.wireguard.android.backend.Tunnel import com.wireguard.android.backend.Tunnel
import com.wireguard.android.backend.WgQuickBackend import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.util.lifecycleScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
@ -28,7 +28,7 @@ class KernelModuleDisablerPreference(context: Context, attrs: AttributeSet?) : P
private var state = State.UNKNOWN private var state = State.UNKNOWN
init { init {
isVisible = false isVisible = false
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
setState(if (Application.getBackend() is WgQuickBackend) State.ENABLED else State.DISABLED) setState(if (Application.getBackend() is WgQuickBackend) State.ENABLED else State.DISABLED)
} }
} }
@ -46,7 +46,7 @@ class KernelModuleDisablerPreference(context: Context, attrs: AttributeSet?) : P
setState(State.DISABLING) setState(State.DISABLING)
Application.getSharedPreferences().edit().putBoolean("disable_kernel_module", true).commit() Application.getSharedPreferences().edit().putBoolean("disable_kernel_module", true).commit()
} }
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
val observableTunnels = Application.getTunnelManager().getTunnels() val observableTunnels = Application.getTunnelManager().getTunnels()
val downings = observableTunnels.map { async(SupervisorJob()) { it.setStateAsync(Tunnel.State.DOWN) } } val downings = observableTunnels.map { async(SupervisorJob()) { it.setStateAsync(Tunnel.State.DOWN) } }
try { try {

View File

@ -15,8 +15,8 @@ import com.wireguard.android.Application
import com.wireguard.android.R import com.wireguard.android.R
import com.wireguard.android.activity.SettingsActivity import com.wireguard.android.activity.SettingsActivity
import com.wireguard.android.util.ErrorMessages import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.lifecycleScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -30,21 +30,19 @@ class ModuleDownloaderPreference(context: Context, attrs: AttributeSet?) : Prefe
@SuppressLint("ApplySharedPref") @SuppressLint("ApplySharedPref")
override fun onClick() { override fun onClick() {
setState(State.WORKING) setState(State.WORKING)
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
when (withContext(Dispatchers.IO) { Application.getModuleLoader().download() }) { when (withContext(Dispatchers.IO) { Application.getModuleLoader().download() }) {
OsConstants.ENOENT -> setState(State.NOTFOUND) OsConstants.ENOENT -> setState(State.NOTFOUND)
OsConstants.EXIT_SUCCESS -> { OsConstants.EXIT_SUCCESS -> {
setState(State.SUCCESS) setState(State.SUCCESS)
Application.getSharedPreferences().edit().remove("disable_kernel_module").commit() Application.getSharedPreferences().edit().remove("disable_kernel_module").commit()
GlobalScope.launch(Dispatchers.Main.immediate) { withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { val restartIntent = Intent(context, SettingsActivity::class.java)
val restartIntent = Intent(context, SettingsActivity::class.java) restartIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
restartIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) Application.get().startActivity(restartIntent)
Application.get().startActivity(restartIntent) exitProcess(0)
exitProcess(0)
}
} }
} }
else -> setState(State.FAILURE) else -> setState(State.FAILURE)

View File

@ -10,8 +10,8 @@ import androidx.preference.Preference
import com.wireguard.android.Application import com.wireguard.android.Application
import com.wireguard.android.R import com.wireguard.android.R
import com.wireguard.android.util.ToolsInstaller import com.wireguard.android.util.ToolsInstaller
import com.wireguard.android.util.lifecycleScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -27,7 +27,7 @@ class ToolsInstallerPreference(context: Context, attrs: AttributeSet?) : Prefere
override fun onAttached() { override fun onAttached() {
super.onAttached() super.onAttached()
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
val state = withContext(Dispatchers.IO) { Application.getToolsInstaller().areInstalled() } val state = withContext(Dispatchers.IO) { Application.getToolsInstaller().areInstalled() }
when { when {
@ -45,7 +45,7 @@ class ToolsInstallerPreference(context: Context, attrs: AttributeSet?) : Prefere
override fun onClick() { override fun onClick() {
setState(State.WORKING) setState(State.WORKING)
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
try { try {
val result = withContext(Dispatchers.IO) { Application.getToolsInstaller().install() } val result = withContext(Dispatchers.IO) { Application.getToolsInstaller().install() }
when { when {

View File

@ -16,8 +16,8 @@ import com.wireguard.android.R
import com.wireguard.android.backend.Backend import com.wireguard.android.backend.Backend
import com.wireguard.android.backend.GoBackend import com.wireguard.android.backend.GoBackend
import com.wireguard.android.backend.WgQuickBackend import com.wireguard.android.backend.WgQuickBackend
import com.wireguard.android.util.lifecycleScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.Locale import java.util.Locale
@ -47,7 +47,7 @@ class VersionPreference(context: Context, attrs: AttributeSet?) : Preference(con
} }
init { init {
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
val backend = Application.getBackend() val backend = Application.getBackend()
versionSummary = getContext().getString(R.string.version_summary_checking, getBackendPrettyName(context, backend).toLowerCase(Locale.ENGLISH)) versionSummary = getContext().getString(R.string.version_summary_checking, getBackendPrettyName(context, backend).toLowerCase(Locale.ENGLISH))
notifyChanged() notifyChanged()

View File

@ -17,9 +17,9 @@ import com.wireguard.android.util.AdminKnobs
import com.wireguard.android.util.BiometricAuthenticator import com.wireguard.android.util.BiometricAuthenticator
import com.wireguard.android.util.DownloadsFileSaver import com.wireguard.android.util.DownloadsFileSaver
import com.wireguard.android.util.ErrorMessages import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.FragmentUtils import com.wireguard.android.util.activity
import com.wireguard.android.util.lifecycleScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
@ -35,7 +35,7 @@ import java.util.zip.ZipOutputStream
class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) { class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
private var exportedFilePath: String? = null private var exportedFilePath: String? = null
private fun exportZip() { private fun exportZip() {
GlobalScope.launch(Dispatchers.Main.immediate) { lifecycleScope.launch(Dispatchers.Main.immediate) {
val tunnels = Application.getTunnelManager().getTunnels() val tunnels = Application.getTunnelManager().getTunnels()
try { try {
exportedFilePath = withContext(Dispatchers.IO) { exportedFilePath = withContext(Dispatchers.IO) {
@ -64,7 +64,7 @@ class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference
val message = context.getString(R.string.zip_export_error, error) val message = context.getString(R.string.zip_export_error, error)
Log.e(TAG, message, e) Log.e(TAG, message, e)
Snackbar.make( Snackbar.make(
FragmentUtils.getPrefActivity(this@ZipExporterPreference).findViewById(android.R.id.content), activity.findViewById(android.R.id.content),
message, Snackbar.LENGTH_LONG).show() message, Snackbar.LENGTH_LONG).show()
isEnabled = true isEnabled = true
} }
@ -77,13 +77,12 @@ class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference
override fun onClick() { override fun onClick() {
if (AdminKnobs.disableConfigExport) return if (AdminKnobs.disableConfigExport) return
val prefActivity = FragmentUtils.getPrefActivity(this) val fragment = activity.supportFragmentManager.fragments.first()
val fragment = prefActivity.supportFragmentManager.fragments.first()
BiometricAuthenticator.authenticate(R.string.biometric_prompt_zip_exporter_title, fragment) { BiometricAuthenticator.authenticate(R.string.biometric_prompt_zip_exporter_title, fragment) {
when (it) { when (it) {
// When we have successful authentication, or when there is no biometric hardware available. // When we have successful authentication, or when there is no biometric hardware available.
is BiometricAuthenticator.Result.Success, is BiometricAuthenticator.Result.HardwareUnavailableOrDisabled -> { is BiometricAuthenticator.Result.Success, is BiometricAuthenticator.Result.HardwareUnavailableOrDisabled -> {
prefActivity.ensurePermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, grantResults -> activity.ensurePermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, grantResults ->
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
isEnabled = false isEnabled = false
exportZip() exportZip()
@ -92,7 +91,7 @@ class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference
} }
is BiometricAuthenticator.Result.Failure -> { is BiometricAuthenticator.Result.Failure -> {
Snackbar.make( Snackbar.make(
prefActivity.findViewById(android.R.id.content), activity.findViewById(android.R.id.content),
it.message, it.message,
Snackbar.LENGTH_SHORT Snackbar.LENGTH_SHORT
).show() ).show()

View File

@ -9,6 +9,10 @@ import android.content.Context
import android.util.TypedValue import android.util.TypedValue
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import com.wireguard.android.activity.SettingsActivity
import kotlinx.coroutines.CoroutineScope
fun Context.resolveAttribute(@AttrRes attrRes: Int): Int { fun Context.resolveAttribute(@AttrRes attrRes: Int): Int {
val typedValue = TypedValue() val typedValue = TypedValue()
@ -19,3 +23,9 @@ fun Context.resolveAttribute(@AttrRes attrRes: Int): Int {
fun Fragment.requireTargetFragment(): Fragment { fun Fragment.requireTargetFragment(): Fragment {
return requireNotNull(targetFragment) { "A target fragment should always be set for $this" } return requireNotNull(targetFragment) { "A target fragment should always be set for $this" }
} }
val Preference.activity: SettingsActivity
get() = if (this.context is SettingsActivity) this.context as SettingsActivity else throw IllegalStateException("Failed to resolve SettingsActivity")
val Preference.lifecycleScope: CoroutineScope
get() = this.activity.lifecycleScope

View File

@ -1,21 +0,0 @@
/*
* Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.util
import android.view.ContextThemeWrapper
import androidx.preference.Preference
import com.wireguard.android.activity.SettingsActivity
object FragmentUtils {
fun getPrefActivity(preference: Preference): SettingsActivity {
val context = preference.context
if (context is ContextThemeWrapper) {
if (context is SettingsActivity) {
return context
}
}
throw IllegalStateException("Failed to resolve SettingsActivity")
}
}