aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt')
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt119
1 files changed, 60 insertions, 59 deletions
diff --git a/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt b/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt
index cdd25134..52701157 100644
--- a/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt
+++ b/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt
@@ -1,24 +1,28 @@
/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
+ * Copyright © 2017-2025 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.preference
-import android.Manifest
import android.content.Context
-import android.content.pm.PackageManager
import android.util.AttributeSet
import android.util.Log
import androidx.preference.Preference
import com.google.android.material.snackbar.Snackbar
import com.wireguard.android.Application
import com.wireguard.android.R
-import com.wireguard.android.model.ObservableTunnel
+import com.wireguard.android.util.AdminKnobs
import com.wireguard.android.util.BiometricAuthenticator
import com.wireguard.android.util.DownloadsFileSaver
import com.wireguard.android.util.ErrorMessages
-import com.wireguard.android.util.FragmentUtils
-import java9.util.concurrent.CompletableFuture
+import com.wireguard.android.util.activity
+import com.wireguard.android.util.lifecycleScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import java.nio.charset.StandardCharsets
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
@@ -28,80 +32,77 @@ import java.util.zip.ZipOutputStream
*/
class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
private var exportedFilePath: String? = null
+ private val downloadsFileSaver = DownloadsFileSaver(activity)
private fun exportZip() {
- Application.getTunnelManager().tunnels.thenAccept(this::exportZip)
- }
-
- private fun exportZip(tunnels: List<ObservableTunnel>) {
- val futureConfigs = tunnels.map { it.configAsync.toCompletableFuture() }.toTypedArray()
- if (futureConfigs.isEmpty()) {
- exportZipComplete(null, IllegalArgumentException(
- context.getString(R.string.no_tunnels_error)))
- return
- }
- CompletableFuture.allOf(*futureConfigs)
- .whenComplete { _, exception ->
- Application.getAsyncWorker().supplyAsync {
- if (exception != null) throw exception
- val outputFile = DownloadsFileSaver.save(context, "wireguard-export.zip", "application/zip", true)
- try {
- ZipOutputStream(outputFile.outputStream).use { zip ->
- for (i in futureConfigs.indices) {
- zip.putNextEntry(ZipEntry(tunnels[i].name + ".conf"))
- zip.write(futureConfigs[i].getNow(null)!!.toWgQuickString().toByteArray(StandardCharsets.UTF_8))
- }
- zip.closeEntry()
+ lifecycleScope.launch {
+ val tunnels = Application.getTunnelManager().getTunnels()
+ try {
+ exportedFilePath = withContext(Dispatchers.IO) {
+ val configs = tunnels.map { async(SupervisorJob()) { it.getConfigAsync() } }.awaitAll()
+ if (configs.isEmpty()) {
+ throw IllegalArgumentException(context.getString(R.string.no_tunnels_error))
+ }
+ val outputFile = downloadsFileSaver.save("wireguard-export.zip", "application/zip", true)
+ if (outputFile == null) {
+ withContext(Dispatchers.Main.immediate) {
+ isEnabled = true
+ }
+ return@withContext null
+ }
+ try {
+ ZipOutputStream(outputFile.outputStream).use { zip ->
+ for (i in configs.indices) {
+ zip.putNextEntry(ZipEntry(tunnels[i].name + ".conf"))
+ zip.write(configs[i].toWgQuickString().toByteArray(StandardCharsets.UTF_8))
}
- } catch (e: Exception) {
- outputFile.delete()
- throw e
+ zip.closeEntry()
}
- outputFile.fileName
- }.whenComplete(this::exportZipComplete)
+ } catch (e: Throwable) {
+ outputFile.delete()
+ throw e
+ }
+ outputFile.fileName
}
- }
-
- private fun exportZipComplete(filePath: String?, throwable: Throwable?) {
- if (throwable != null) {
- val error = ErrorMessages[throwable]
- val message = context.getString(R.string.zip_export_error, error)
- Log.e(TAG, message, throwable)
- Snackbar.make(
- FragmentUtils.getPrefActivity(this).findViewById(android.R.id.content),
- message, Snackbar.LENGTH_LONG).show()
- isEnabled = true
- } else {
- exportedFilePath = filePath
- notifyChanged()
+ notifyChanged()
+ } catch (e: Throwable) {
+ val error = ErrorMessages[e]
+ val message = context.getString(R.string.zip_export_error, error)
+ Log.e(TAG, message, e)
+ Snackbar.make(
+ activity.findViewById(android.R.id.content),
+ message, Snackbar.LENGTH_LONG
+ ).show()
+ isEnabled = true
+ }
}
}
- override fun getSummary() = if (exportedFilePath == null) context.getString(R.string.zip_export_summary) else context.getString(R.string.zip_export_success, exportedFilePath)
+ override fun getSummary() =
+ if (exportedFilePath == null) context.getString(R.string.zip_export_summary) else context.getString(R.string.zip_export_success, exportedFilePath)
override fun getTitle() = context.getString(R.string.zip_export_title)
override fun onClick() {
- val prefActivity = FragmentUtils.getPrefActivity(this)
- val fragment = prefActivity.supportFragmentManager.fragments.first()
+ if (AdminKnobs.disableConfigExport) return
+ val fragment = activity.supportFragmentManager.fragments.first()
BiometricAuthenticator.authenticate(R.string.biometric_prompt_zip_exporter_title, fragment) {
when (it) {
// When we have successful authentication, or when there is no biometric hardware available.
is BiometricAuthenticator.Result.Success, is BiometricAuthenticator.Result.HardwareUnavailableOrDisabled -> {
- prefActivity.ensurePermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, grantResults ->
- if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- isEnabled = false
- exportZip()
- }
- }
+ isEnabled = false
+ exportZip()
}
+
is BiometricAuthenticator.Result.Failure -> {
Snackbar.make(
- prefActivity.findViewById(android.R.id.content),
- it.message,
- Snackbar.LENGTH_SHORT
+ activity.findViewById(android.R.id.content),
+ it.message,
+ Snackbar.LENGTH_SHORT
).show()
}
+
+ is BiometricAuthenticator.Result.Cancelled -> {}
}
}
}