1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
/*
* Copyright © 2017-2019 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.DownloadsFileSaver
import com.wireguard.android.util.ErrorMessages
import com.wireguard.android.util.FragmentUtils
import com.wireguard.android.util.ObservableSortedKeyedList
import com.wireguard.config.Config
import java9.util.concurrent.CompletableFuture
import java.nio.charset.StandardCharsets
import java.util.ArrayList
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
/**
* Preference implementing a button that asynchronously exports config zips.
*/
class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
private var exportedFilePath: String? = null
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()
}
} catch (e: Exception) {
outputFile.delete()
throw e
}
outputFile.fileName
}.whenComplete(this::exportZipComplete)
}
}
private fun exportZipComplete(filePath: String?, throwable: Throwable?) {
if (throwable != null) {
val error = ErrorMessages.get(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()
}
}
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() {
FragmentUtils.getPrefActivity(this).ensurePermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, granted ->
if (granted.isNotEmpty() && granted[0] == PackageManager.PERMISSION_GRANTED) {
isEnabled = false
exportZip()
}
}
}
companion object {
private val TAG = "WireGuard/" + ZipExporterPreference::class.java.simpleName
}
}
|