summaryrefslogtreecommitdiffhomepage
path: root/ui/src/main/java/com/wireguard
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2020-03-19 17:54:32 -0600
committerJason A. Donenfeld <Jason@zx2c4.com>2020-03-19 18:55:45 -0600
commit8451321a790d38f3a6932edc852cdda5c8469df9 (patch)
tree0f30e28b1ca9c7c458e56eb5817a8b042b566f6e /ui/src/main/java/com/wireguard
parent90050a00081dbbc43bddd828cb7d3a8014edc404 (diff)
preferences: rewrite in kotlin
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'ui/src/main/java/com/wireguard')
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/KernelModuleDisablerPreference.java94
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/KernelModuleDisablerPreference.kt63
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.java113
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.kt94
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/ModuleDownloaderPreference.java93
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/ModuleDownloaderPreference.kt69
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/ToolsInstallerPreference.java105
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/ToolsInstallerPreference.kt71
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/VersionPreference.java74
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/VersionPreference.kt57
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.java121
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt98
12 files changed, 452 insertions, 600 deletions
diff --git a/ui/src/main/java/com/wireguard/android/preference/KernelModuleDisablerPreference.java b/ui/src/main/java/com/wireguard/android/preference/KernelModuleDisablerPreference.java
deleted file mode 100644
index 885e48b3..00000000
--- a/ui/src/main/java/com/wireguard/android/preference/KernelModuleDisablerPreference.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright © 2020 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.preference;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.Intent;
-import android.util.AttributeSet;
-
-import com.wireguard.android.Application;
-import com.wireguard.android.R;
-import com.wireguard.android.activity.SettingsActivity;
-import com.wireguard.android.backend.Tunnel;
-import com.wireguard.android.backend.WgQuickBackend;
-import com.wireguard.util.NonNullForAll;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-import androidx.preference.Preference;
-import java9.util.concurrent.CompletableFuture;
-import java9.util.stream.Collectors;
-import java9.util.stream.StreamSupport;
-
-@NonNullForAll
-public class KernelModuleDisablerPreference extends Preference {
- private State state;
-
- public KernelModuleDisablerPreference(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- state = Application.getBackend() instanceof WgQuickBackend ? State.ENABLED : State.DISABLED;
- }
-
- @Override
- public CharSequence getSummary() {
- return getContext().getString(state.summaryResourceId);
- }
-
- @Override
- public CharSequence getTitle() {
- return getContext().getString(state.titleResourceId);
- }
-
- @SuppressLint("ApplySharedPref")
- @Override
- protected void onClick() {
- if (state == State.DISABLED) {
- setState(State.ENABLING);
- Application.getSharedPreferences().edit().putBoolean("disable_kernel_module", false).commit();
- } else if (state == State.ENABLED) {
- setState(State.DISABLING);
- Application.getSharedPreferences().edit().putBoolean("disable_kernel_module", true).commit();
- }
- Application.getAsyncWorker().runAsync(() -> Application.getTunnelManager().getTunnels().thenApply(observableTunnels -> {
- final Collection<CompletableFuture<Tunnel.State>> c = StreamSupport.stream(observableTunnels.values()).map(t -> t.setState(Tunnel.State.DOWN).toCompletableFuture()).collect(Collectors.toCollection(ArrayList::new));
- return CompletableFuture.allOf(c.toArray(new CompletableFuture[0])).thenRun(() -> {
- final Intent restartIntent = new Intent(getContext(), SettingsActivity.class);
- restartIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- Application.get().startActivity(restartIntent);
- System.exit(0);
- });
- }).join());
- }
-
- private void setState(final State state) {
- if (this.state == state)
- return;
- this.state = state;
- if (isEnabled() != state.shouldEnableView)
- setEnabled(state.shouldEnableView);
- notifyChanged();
- }
-
- private enum State {
- ENABLED(R.string.module_disabler_enabled_title, R.string.module_disabler_enabled_summary, true),
- DISABLED(R.string.module_disabler_disabled_title, R.string.module_disabler_disabled_summary, true),
- ENABLING(R.string.module_disabler_disabled_title, R.string.success_application_will_restart, false),
- DISABLING(R.string.module_disabler_enabled_title, R.string.success_application_will_restart, false);
-
- private final boolean shouldEnableView;
- private final int summaryResourceId;
- private final int titleResourceId;
-
- State(final int titleResourceId, final int summaryResourceId, final boolean shouldEnableView) {
- this.summaryResourceId = summaryResourceId;
- this.titleResourceId = titleResourceId;
- this.shouldEnableView = shouldEnableView;
- }
- }
-}
diff --git a/ui/src/main/java/com/wireguard/android/preference/KernelModuleDisablerPreference.kt b/ui/src/main/java/com/wireguard/android/preference/KernelModuleDisablerPreference.kt
new file mode 100644
index 00000000..0bb728df
--- /dev/null
+++ b/ui/src/main/java/com/wireguard/android/preference/KernelModuleDisablerPreference.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2020 WireGuard LLC. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package com.wireguard.android.preference
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.Intent
+import android.util.AttributeSet
+import androidx.preference.Preference
+import com.wireguard.android.Application
+import com.wireguard.android.R
+import com.wireguard.android.activity.SettingsActivity
+import com.wireguard.android.backend.Tunnel
+import com.wireguard.android.backend.WgQuickBackend
+import java9.util.concurrent.CompletableFuture
+import kotlin.system.exitProcess
+
+class KernelModuleDisablerPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
+ private var state = if (Application.getBackend() is WgQuickBackend) State.ENABLED else State.DISABLED
+
+ override fun getSummary() = context.getString(state.summaryResourceId)
+
+ override fun getTitle() = context.getString(state.titleResourceId)
+
+ @SuppressLint("ApplySharedPref")
+ override fun onClick() {
+ if (state == State.DISABLED) {
+ setState(State.ENABLING)
+ Application.getSharedPreferences().edit().putBoolean("disable_kernel_module", false).commit()
+ } else if (state == State.ENABLED) {
+ setState(State.DISABLING)
+ Application.getSharedPreferences().edit().putBoolean("disable_kernel_module", true).commit()
+ }
+ Application.getAsyncWorker().runAsync {
+ Application.getTunnelManager().tunnels.thenApply { observableTunnels ->
+ val downings = observableTunnels.values().map { it.setState(Tunnel.State.DOWN).toCompletableFuture() }.toTypedArray()
+ CompletableFuture.allOf(*downings).thenRun {
+ val restartIntent = Intent(context, SettingsActivity::class.java)
+ restartIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ Application.get().startActivity(restartIntent)
+ exitProcess(0)
+ }
+ }.join()
+ }
+ }
+
+ private fun setState(state: State) {
+ if (this.state == state) return
+ this.state = state
+ if (isEnabled != state.shouldEnableView) isEnabled = state.shouldEnableView
+ notifyChanged()
+ }
+
+ private enum class State(val titleResourceId: Int, val summaryResourceId: Int, val shouldEnableView: Boolean) {
+ ENABLED(R.string.module_disabler_enabled_title, R.string.module_disabler_enabled_summary, true),
+ DISABLED(R.string.module_disabler_disabled_title, R.string.module_disabler_disabled_summary, true),
+ ENABLING(R.string.module_disabler_disabled_title, R.string.success_application_will_restart, false),
+ DISABLING(R.string.module_disabler_enabled_title, R.string.success_application_will_restart, false);
+ }
+}
diff --git a/ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.java b/ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.java
deleted file mode 100644
index 32b3e33c..00000000
--- a/ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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 com.google.android.material.snackbar.Snackbar;
-import com.wireguard.android.Application;
-import com.wireguard.android.R;
-import com.wireguard.android.util.DownloadsFileSaver;
-import com.wireguard.android.util.DownloadsFileSaver.DownloadsFile;
-import com.wireguard.android.util.ErrorMessages;
-import com.wireguard.android.util.FragmentUtils;
-import com.wireguard.util.NonNullForAll;
-
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-
-import androidx.annotation.Nullable;
-import androidx.preference.Preference;
-
-/**
- * Preference implementing a button that asynchronously exports logs.
- */
-
-@NonNullForAll
-public class LogExporterPreference extends Preference {
- private static final String TAG = "WireGuard/" + LogExporterPreference.class.getSimpleName();
-
- @Nullable private String exportedFilePath;
-
- public LogExporterPreference(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- }
-
- private void exportLog() {
- Application.getAsyncWorker().supplyAsync(() -> {
- DownloadsFile outputFile = DownloadsFileSaver.save(getContext(), "wireguard-log.txt", "text/plain", true);
- try {
- final Process process = Runtime.getRuntime().exec(new String[]{
- "logcat", "-b", "all", "-d", "-v", "threadtime", "*:V"});
- try (final BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
- final BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
- String line;
- while ((line = stdout.readLine()) != null) {
- outputFile.getOutputStream().write(line.getBytes());
- outputFile.getOutputStream().write('\n');
- }
- outputFile.getOutputStream().close();
- stdout.close();
- if (process.waitFor() != 0) {
- final StringBuilder errors = new StringBuilder();
- errors.append(R.string.logcat_error);
- while ((line = stderr.readLine()) != null)
- errors.append(line);
- throw new Exception(errors.toString());
- }
- }
- } catch (final Exception e) {
- outputFile.delete();
- throw e;
- }
- return outputFile.getFileName();
- }).whenComplete(this::exportLogComplete);
- }
-
- private void exportLogComplete(final String filePath, @Nullable final Throwable throwable) {
- if (throwable != null) {
- final String error = ErrorMessages.get(throwable);
- final String message = getContext().getString(R.string.log_export_error, error);
- Log.e(TAG, message, throwable);
- Snackbar.make(
- FragmentUtils.getPrefActivity(this).findViewById(android.R.id.content),
- message, Snackbar.LENGTH_LONG).show();
- setEnabled(true);
- } else {
- exportedFilePath = filePath;
- notifyChanged();
- }
- }
-
- @Override
- public CharSequence getSummary() {
- return exportedFilePath == null ?
- getContext().getString(R.string.log_export_summary) :
- getContext().getString(R.string.log_export_success, exportedFilePath);
- }
-
- @Override
- public CharSequence getTitle() {
- return getContext().getString(R.string.log_export_title);
- }
-
- @Override
- protected void onClick() {
- FragmentUtils.getPrefActivity(this).ensurePermissions(
- new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
- (permissions, granted) -> {
- if (granted.length > 0 && granted[0] == PackageManager.PERMISSION_GRANTED) {
- setEnabled(false);
- exportLog();
- }
- });
- }
-
-}
diff --git a/ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.kt b/ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.kt
new file mode 100644
index 00000000..b4671309
--- /dev/null
+++ b/ui/src/main/java/com/wireguard/android/preference/LogExporterPreference.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.util.DownloadsFileSaver
+import com.wireguard.android.util.ErrorMessages
+import com.wireguard.android.util.FragmentUtils
+import java.io.BufferedReader
+import java.io.InputStreamReader
+
+/**
+ * Preference implementing a button that asynchronously exports logs.
+ */
+class LogExporterPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
+ private var exportedFilePath: String? = null
+ private fun exportLog() {
+ Application.getAsyncWorker().supplyAsync {
+ val outputFile = DownloadsFileSaver.save(context, "wireguard-log.txt", "text/plain", true)
+ try {
+ val process = Runtime.getRuntime().exec(arrayOf(
+ "logcat", "-b", "all", "-d", "-v", "threadtime", "*:V"))
+ BufferedReader(InputStreamReader(process.inputStream)).use { stdout ->
+ BufferedReader(InputStreamReader(process.errorStream)).use { stderr ->
+ while (true) {
+ val line = stdout.readLine() ?: break
+ outputFile.outputStream.write(line.toByteArray())
+ outputFile.outputStream.write('\n'.toInt())
+ }
+ outputFile.outputStream.close()
+ if (process.waitFor() != 0) {
+ val errors = StringBuilder()
+ errors.append(R.string.logcat_error)
+ while (true) {
+ val line = stderr.readLine() ?: break
+ errors.append(line)
+ }
+ throw Exception(errors.toString())
+ }
+ }
+ }
+ } catch (e: Exception) {
+ outputFile.delete()
+ throw e
+ }
+ outputFile.fileName
+ }.whenComplete(this::exportLogComplete)
+ }
+
+ private fun exportLogComplete(filePath: String, throwable: Throwable?) {
+ if (throwable != null) {
+ val error = ErrorMessages.get(throwable)
+ val message = context.getString(R.string.log_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.log_export_summary)
+ else
+ context.getString(R.string.log_export_success, exportedFilePath)
+
+ override fun getTitle() = context.getString(R.string.log_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
+ exportLog()
+ }
+ }
+ }
+
+ companion object {
+ private val TAG = "WireGuard/" + LogExporterPreference::class.java.simpleName
+ }
+}
diff --git a/ui/src/main/java/com/wireguard/android/preference/ModuleDownloaderPreference.java b/ui/src/main/java/com/wireguard/android/preference/ModuleDownloaderPreference.java
deleted file mode 100644
index 8bea5f8c..00000000
--- a/ui/src/main/java/com/wireguard/android/preference/ModuleDownloaderPreference.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright © 2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.preference;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.Intent;
-import android.system.OsConstants;
-import android.util.AttributeSet;
-import android.widget.Toast;
-
-import com.wireguard.android.Application;
-import com.wireguard.android.R;
-import com.wireguard.android.activity.SettingsActivity;
-import com.wireguard.android.util.ErrorMessages;
-import com.wireguard.util.NonNullForAll;
-
-import androidx.annotation.Nullable;
-import androidx.preference.Preference;
-
-@NonNullForAll
-public class ModuleDownloaderPreference extends Preference {
- private State state = State.INITIAL;
-
- public ModuleDownloaderPreference(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public CharSequence getSummary() {
- return getContext().getString(state.messageResourceId);
- }
-
- @Override
- public CharSequence getTitle() {
- return getContext().getString(R.string.module_installer_title);
- }
-
- @Override
- protected void onClick() {
- setState(State.WORKING);
- Application.getAsyncWorker().supplyAsync(Application.getModuleLoader()::download).whenComplete(this::onDownloadResult);
- }
-
- @SuppressLint("ApplySharedPref")
- private void onDownloadResult(final Integer result, @Nullable final Throwable throwable) {
- if (throwable != null) {
- setState(State.FAILURE);
- Toast.makeText(getContext(), ErrorMessages.get(throwable), Toast.LENGTH_LONG).show();
- } else if (result == OsConstants.ENOENT)
- setState(State.NOTFOUND);
- else if (result == OsConstants.EXIT_SUCCESS) {
- setState(State.SUCCESS);
- Application.getSharedPreferences().edit().remove("disable_kernel_module").commit();
- Application.getAsyncWorker().runAsync(() -> {
- final Intent restartIntent = new Intent(getContext(), SettingsActivity.class);
- restartIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- Application.get().startActivity(restartIntent);
- System.exit(0);
- });
- } else
- setState(State.FAILURE);
- }
-
- private void setState(final State state) {
- if (this.state == state)
- return;
- this.state = state;
- if (isEnabled() != state.shouldEnableView)
- setEnabled(state.shouldEnableView);
- notifyChanged();
- }
-
- private enum State {
- INITIAL(R.string.module_installer_initial, true),
- FAILURE(R.string.module_installer_error, true),
- WORKING(R.string.module_installer_working, false),
- SUCCESS(R.string.success_application_will_restart, false),
- NOTFOUND(R.string.module_installer_not_found, false);
-
- private final int messageResourceId;
- private final boolean shouldEnableView;
-
- State(final int messageResourceId, final boolean shouldEnableView) {
- this.messageResourceId = messageResourceId;
- this.shouldEnableView = shouldEnableView;
- }
- }
-}
diff --git a/ui/src/main/java/com/wireguard/android/preference/ModuleDownloaderPreference.kt b/ui/src/main/java/com/wireguard/android/preference/ModuleDownloaderPreference.kt
new file mode 100644
index 00000000..6ecebb95
--- /dev/null
+++ b/ui/src/main/java/com/wireguard/android/preference/ModuleDownloaderPreference.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2019 WireGuard LLC. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package com.wireguard.android.preference
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.Intent
+import android.system.OsConstants
+import android.util.AttributeSet
+import android.widget.Toast
+import androidx.preference.Preference
+import com.wireguard.android.Application
+import com.wireguard.android.R
+import com.wireguard.android.activity.SettingsActivity
+import com.wireguard.android.util.ErrorMessages
+import kotlin.system.exitProcess
+
+class ModuleDownloaderPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
+ private var state = State.INITIAL
+
+ override fun getSummary() = context.getString(state.messageResourceId)
+
+ override fun getTitle() = context.getString(R.string.module_installer_title)
+
+ override fun onClick() {
+ setState(State.WORKING)
+ Application.getAsyncWorker().supplyAsync(Application.getModuleLoader()::download).whenComplete(this::onDownloadResult)
+ }
+
+ @SuppressLint("ApplySharedPref")
+ private fun onDownloadResult(result: Int, throwable: Throwable?) {
+ when {
+ throwable != null -> {
+ setState(State.FAILURE)
+ Toast.makeText(context, ErrorMessages.get(throwable), Toast.LENGTH_LONG).show()
+ }
+ result == OsConstants.ENOENT -> setState(State.NOTFOUND)
+ result == OsConstants.EXIT_SUCCESS -> {
+ setState(State.SUCCESS)
+ Application.getSharedPreferences().edit().remove("disable_kernel_module").commit()
+ Application.getAsyncWorker().runAsync {
+ val restartIntent = Intent(context, SettingsActivity::class.java)
+ restartIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ Application.get().startActivity(restartIntent)
+ exitProcess(0)
+ }
+ }
+ else -> setState(State.FAILURE)
+ }
+ }
+
+ private fun setState(state: State) {
+ if (this.state == state) return
+ this.state = state
+ if (isEnabled != state.shouldEnableView) isEnabled = state.shouldEnableView
+ notifyChanged()
+ }
+
+ private enum class State(val messageResourceId: Int, val shouldEnableView: Boolean) {
+ INITIAL(R.string.module_installer_initial, true),
+ FAILURE(R.string.module_installer_error, true),
+ WORKING(R.string.module_installer_working, false),
+ SUCCESS(R.string.success_application_will_restart, false),
+ NOTFOUND(R.string.module_installer_not_found, false);
+ }
+}
diff --git a/ui/src/main/java/com/wireguard/android/preference/ToolsInstallerPreference.java b/ui/src/main/java/com/wireguard/android/preference/ToolsInstallerPreference.java
deleted file mode 100644
index 6219ed40..00000000
--- a/ui/src/main/java/com/wireguard/android/preference/ToolsInstallerPreference.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.preference;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-import com.wireguard.android.Application;
-import com.wireguard.android.R;
-import com.wireguard.android.util.ToolsInstaller;
-import com.wireguard.util.NonNullForAll;
-
-import androidx.annotation.Nullable;
-import androidx.preference.Preference;
-
-/**
- * Preference implementing a button that asynchronously runs {@code ToolsInstaller} and displays the
- * result as the preference summary.
- */
-
-@NonNullForAll
-public class ToolsInstallerPreference extends Preference {
- private State state = State.INITIAL;
-
- public ToolsInstallerPreference(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public CharSequence getSummary() {
- return getContext().getString(state.messageResourceId);
- }
-
- @Override
- public CharSequence getTitle() {
- return getContext().getString(R.string.tools_installer_title);
- }
-
- @Override
- public void onAttached() {
- super.onAttached();
- Application.getAsyncWorker().supplyAsync(Application.getToolsInstaller()::areInstalled).whenComplete(this::onCheckResult);
- }
-
- private void onCheckResult(final int state, @Nullable final Throwable throwable) {
- if (throwable != null || state == ToolsInstaller.ERROR)
- setState(State.INITIAL);
- else if ((state & ToolsInstaller.YES) == ToolsInstaller.YES)
- setState(State.ALREADY);
- else if ((state & (ToolsInstaller.MAGISK | ToolsInstaller.NO)) == (ToolsInstaller.MAGISK | ToolsInstaller.NO))
- setState(State.INITIAL_MAGISK);
- else if ((state & (ToolsInstaller.SYSTEM | ToolsInstaller.NO)) == (ToolsInstaller.SYSTEM | ToolsInstaller.NO))
- setState(State.INITIAL_SYSTEM);
- else
- setState(State.INITIAL);
- }
-
- @Override
- protected void onClick() {
- setState(State.WORKING);
- Application.getAsyncWorker().supplyAsync(Application.getToolsInstaller()::install).whenComplete(this::onInstallResult);
- }
-
- private void onInstallResult(final Integer result, @Nullable final Throwable throwable) {
- if (throwable != null)
- setState(State.FAILURE);
- else if ((result & (ToolsInstaller.YES | ToolsInstaller.MAGISK)) == (ToolsInstaller.YES | ToolsInstaller.MAGISK))
- setState(State.SUCCESS_MAGISK);
- else if ((result & (ToolsInstaller.YES | ToolsInstaller.SYSTEM)) == (ToolsInstaller.YES | ToolsInstaller.SYSTEM))
- setState(State.SUCCESS_SYSTEM);
- else
- setState(State.FAILURE);
- }
-
- private void setState(final State state) {
- if (this.state == state)
- return;
- this.state = state;
- if (isEnabled() != state.shouldEnableView)
- setEnabled(state.shouldEnableView);
- notifyChanged();
- }
-
- private enum State {
- INITIAL(R.string.tools_installer_initial, true),
- ALREADY(R.string.tools_installer_already, false),
- FAILURE(R.string.tools_installer_failure, true),
- WORKING(R.string.tools_installer_working, false),
- INITIAL_SYSTEM(R.string.tools_installer_initial_system, true),
- SUCCESS_SYSTEM(R.string.tools_installer_success_system, false),
- INITIAL_MAGISK(R.string.tools_installer_initial_magisk, true),
- SUCCESS_MAGISK(R.string.tools_installer_success_magisk, false);
-
- private final int messageResourceId;
- private final boolean shouldEnableView;
-
- State(final int messageResourceId, final boolean shouldEnableView) {
- this.messageResourceId = messageResourceId;
- this.shouldEnableView = shouldEnableView;
- }
- }
-}
diff --git a/ui/src/main/java/com/wireguard/android/preference/ToolsInstallerPreference.kt b/ui/src/main/java/com/wireguard/android/preference/ToolsInstallerPreference.kt
new file mode 100644
index 00000000..f7dd932d
--- /dev/null
+++ b/ui/src/main/java/com/wireguard/android/preference/ToolsInstallerPreference.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package com.wireguard.android.preference
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.preference.Preference
+import com.wireguard.android.Application
+import com.wireguard.android.R
+import com.wireguard.android.util.ToolsInstaller
+
+/**
+ * Preference implementing a button that asynchronously runs `ToolsInstaller` and displays the
+ * result as the preference summary.
+ */
+class ToolsInstallerPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
+ private var state = State.INITIAL
+
+ override fun getSummary() = context.getString(state.messageResourceId)
+
+ override fun getTitle() = context.getString(R.string.tools_installer_title)
+
+ override fun onAttached() {
+ super.onAttached()
+ Application.getAsyncWorker().supplyAsync(Application.getToolsInstaller()::areInstalled).whenComplete(this::onCheckResult)
+ }
+
+ private fun onCheckResult(state: Int, throwable: Throwable?) {
+ when {
+ throwable != null || state == ToolsInstaller.ERROR -> setState(State.INITIAL)
+ state and ToolsInstaller.YES == ToolsInstaller.YES -> setState(State.ALREADY)
+ state and (ToolsInstaller.MAGISK or ToolsInstaller.NO) == ToolsInstaller.MAGISK or ToolsInstaller.NO -> setState(State.INITIAL_MAGISK)
+ state and (ToolsInstaller.SYSTEM or ToolsInstaller.NO) == ToolsInstaller.SYSTEM or ToolsInstaller.NO -> setState(State.INITIAL_SYSTEM)
+ else -> setState(State.INITIAL)
+ }
+ }
+
+ override fun onClick() {
+ setState(State.WORKING)
+ Application.getAsyncWorker().supplyAsync { Application.getToolsInstaller().install() }.whenComplete { result: Int, throwable: Throwable? -> onInstallResult(result, throwable) }
+ }
+
+ private fun onInstallResult(result: Int, throwable: Throwable?) {
+ when {
+ throwable != null -> setState(State.FAILURE)
+ result and (ToolsInstaller.YES or ToolsInstaller.MAGISK) == ToolsInstaller.YES or ToolsInstaller.MAGISK -> setState(State.SUCCESS_MAGISK)
+ result and (ToolsInstaller.YES or ToolsInstaller.SYSTEM) == ToolsInstaller.YES or ToolsInstaller.SYSTEM -> setState(State.SUCCESS_SYSTEM)
+ else -> setState(State.FAILURE)
+ }
+ }
+
+ private fun setState(state: State) {
+ if (this.state == state) return
+ this.state = state
+ if (isEnabled != state.shouldEnableView) isEnabled = state.shouldEnableView
+ notifyChanged()
+ }
+
+ private enum class State(val messageResourceId: Int, val shouldEnableView: Boolean) {
+ INITIAL(R.string.tools_installer_initial, true),
+ ALREADY(R.string.tools_installer_already, false),
+ FAILURE(R.string.tools_installer_failure, true),
+ WORKING(R.string.tools_installer_working, false),
+ INITIAL_SYSTEM(R.string.tools_installer_initial_system, true),
+ SUCCESS_SYSTEM(R.string.tools_installer_success_system, false),
+ INITIAL_MAGISK(R.string.tools_installer_initial_magisk, true),
+ SUCCESS_MAGISK(R.string.tools_installer_success_magisk, false);
+ }
+}
diff --git a/ui/src/main/java/com/wireguard/android/preference/VersionPreference.java b/ui/src/main/java/com/wireguard/android/preference/VersionPreference.java
deleted file mode 100644
index b5c7c5e8..00000000
--- a/ui/src/main/java/com/wireguard/android/preference/VersionPreference.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.preference;
-
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.util.AttributeSet;
-
-import com.wireguard.android.Application;
-import com.wireguard.android.BuildConfig;
-import com.wireguard.android.R;
-import com.wireguard.android.backend.Backend;
-import com.wireguard.android.backend.GoBackend;
-import com.wireguard.android.backend.WgQuickBackend;
-import com.wireguard.util.NonNullForAll;
-
-import java.util.Locale;
-
-import androidx.annotation.Nullable;
-import androidx.preference.Preference;
-
-@NonNullForAll
-public class VersionPreference extends Preference {
- @Nullable private String versionSummary;
-
- public VersionPreference(final Context context, final AttributeSet attrs) {
- super(context, attrs);
-
- Application.getBackendAsync().thenAccept(backend -> {
- versionSummary = getContext().getString(R.string.version_summary_checking, getBackendPrettyName(context, backend).toLowerCase(Locale.ENGLISH));
- Application.getAsyncWorker().supplyAsync(backend::getVersion).whenComplete((version, exception) -> {
- versionSummary = exception == null
- ? getContext().getString(R.string.version_summary, getBackendPrettyName(context, backend), version)
- : getContext().getString(R.string.version_summary_unknown, getBackendPrettyName(context, backend).toLowerCase(Locale.ENGLISH));
- notifyChanged();
- });
- });
- }
-
- private static String getBackendPrettyName(final Context context, final Backend backend) {
- if (backend instanceof WgQuickBackend)
- return context.getString(R.string.type_name_kernel_module);
- if (backend instanceof GoBackend)
- return context.getString(R.string.type_name_go_userspace);
- return "";
- }
-
- @Nullable
- @Override
- public CharSequence getSummary() {
- return versionSummary;
- }
-
- @Override
- public CharSequence getTitle() {
- return getContext().getString(R.string.version_title, BuildConfig.VERSION_NAME);
- }
-
- @Override
- protected void onClick() {
- final Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setData(Uri.parse("https://www.wireguard.com/"));
- try {
- getContext().startActivity(intent);
- } catch (final ActivityNotFoundException ignored) {
- }
- }
-
-}
diff --git a/ui/src/main/java/com/wireguard/android/preference/VersionPreference.kt b/ui/src/main/java/com/wireguard/android/preference/VersionPreference.kt
new file mode 100644
index 00000000..6c5284ec
--- /dev/null
+++ b/ui/src/main/java/com/wireguard/android/preference/VersionPreference.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package com.wireguard.android.preference
+
+import android.content.ActivityNotFoundException
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.util.AttributeSet
+import androidx.preference.Preference
+import com.wireguard.android.Application
+import com.wireguard.android.BuildConfig
+import com.wireguard.android.R
+import com.wireguard.android.backend.Backend
+import com.wireguard.android.backend.GoBackend
+import com.wireguard.android.backend.WgQuickBackend
+import java.util.Locale
+
+class VersionPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs) {
+ private var versionSummary: String? = null
+
+ override fun getSummary() = versionSummary
+
+ override fun getTitle() = context.getString(R.string.version_title, BuildConfig.VERSION_NAME)
+
+ override fun onClick() {
+ val intent = Intent(Intent.ACTION_VIEW)
+ intent.data = Uri.parse("https://www.wireguard.com/")
+ try {
+ context.startActivity(intent)
+ } catch (_: ActivityNotFoundException) {
+ }
+ }
+
+ companion object {
+ private fun getBackendPrettyName(context: Context, backend: Backend) = when (backend) {
+ is WgQuickBackend -> context.getString(R.string.type_name_kernel_module)
+ is GoBackend -> context.getString(R.string.type_name_go_userspace)
+ else -> ""
+ }
+ }
+
+ init {
+ Application.getBackendAsync().thenAccept { backend: Backend ->
+ versionSummary = getContext().getString(R.string.version_summary_checking, getBackendPrettyName(context, backend).toLowerCase(Locale.ENGLISH))
+ Application.getAsyncWorker().supplyAsync(backend::getVersion).whenComplete { version, exception ->
+ versionSummary = if (exception == null)
+ getContext().getString(R.string.version_summary, getBackendPrettyName(context, backend), version)
+ else
+ getContext().getString(R.string.version_summary_unknown, getBackendPrettyName(context, backend).toLowerCase(Locale.ENGLISH))
+ notifyChanged()
+ }
+ }
+ }
+}
diff --git a/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.java b/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.java
deleted file mode 100644
index 0740787f..00000000
--- a/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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 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.DownloadsFileSaver.DownloadsFile;
-import com.wireguard.android.util.ErrorMessages;
-import com.wireguard.android.util.FragmentUtils;
-import com.wireguard.config.Config;
-import com.wireguard.util.NonNullForAll;
-
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
-import androidx.annotation.Nullable;
-import androidx.preference.Preference;
-import java9.util.concurrent.CompletableFuture;
-
-/**
- * Preference implementing a button that asynchronously exports config zips.
- */
-
-@NonNullForAll
-public class ZipExporterPreference extends Preference {
- private static final String TAG = "WireGuard/" + ZipExporterPreference.class.getSimpleName();
-
- @Nullable private String exportedFilePath;
-
- public ZipExporterPreference(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- }
-
- private void exportZip() {
- Application.getTunnelManager().getTunnels().thenAccept(this::exportZip);
- }
-
- private void exportZip(final List<ObservableTunnel> tunnels) {
- final List<CompletableFuture<Config>> futureConfigs = new ArrayList<>(tunnels.size());
- for (final ObservableTunnel tunnel : tunnels)
- futureConfigs.add(tunnel.getConfigAsync().toCompletableFuture());
- if (futureConfigs.isEmpty()) {
- exportZipComplete(null, new IllegalArgumentException(
- getContext().getString(R.string.no_tunnels_error)));
- return;
- }
- CompletableFuture.allOf(futureConfigs.toArray(new CompletableFuture[futureConfigs.size()]))
- .whenComplete((ignored1, exception) -> Application.getAsyncWorker().supplyAsync(() -> {
- if (exception != null)
- throw exception;
- DownloadsFile outputFile = DownloadsFileSaver.save(getContext(), "wireguard-export.zip", "application/zip", true);
- try (ZipOutputStream zip = new ZipOutputStream(outputFile.getOutputStream())) {
- for (int i = 0; i < futureConfigs.size(); ++i) {
- zip.putNextEntry(new ZipEntry(tunnels.get(i).getName() + ".conf"));
- zip.write(futureConfigs.get(i).getNow(null).
- toWgQuickString().getBytes(StandardCharsets.UTF_8));
- }
- zip.closeEntry();
- } catch (final Exception e) {
- outputFile.delete();
- throw e;
- }
- return outputFile.getFileName();
- }).whenComplete(this::exportZipComplete));
- }
-
- private void exportZipComplete(@Nullable final String filePath, @Nullable final Throwable throwable) {
- if (throwable != null) {
- final String error = ErrorMessages.get(throwable);
- final String message = getContext().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();
- setEnabled(true);
- } else {
- exportedFilePath = filePath;
- notifyChanged();
- }
- }
-
- @Override
- public CharSequence getSummary() {
- return exportedFilePath == null ?
- getContext().getString(R.string.zip_export_summary) :
- getContext().getString(R.string.zip_export_success, exportedFilePath);
- }
-
- @Override
- public CharSequence getTitle() {
- return getContext().getString(R.string.zip_export_title);
- }
-
- @Override
- protected void onClick() {
- FragmentUtils.getPrefActivity(this).ensurePermissions(
- new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
- (permissions, granted) -> {
- if (granted.length > 0 && granted[0] == PackageManager.PERMISSION_GRANTED) {
- setEnabled(false);
- exportZip();
- }
- });
- }
-
-}
diff --git a/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt b/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt
new file mode 100644
index 00000000..2a77f36c
--- /dev/null
+++ b/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt
@@ -0,0 +1,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
+ }
+}