From 0a7f8f489625d90b8969f112d80059d6757d5e8d Mon Sep 17 00:00:00 2001 From: pacien Date: Thu, 9 Aug 2018 00:09:39 +0200 Subject: Refactor configuration activity --- app/build.gradle | 5 + app/src/main/AndroidManifest.xml | 2 +- .../org/pacien/tincapp/activities/BaseActivity.kt | 16 ++- .../pacien/tincapp/activities/ConfigureActivity.kt | 145 --------------------- .../org/pacien/tincapp/activities/StartActivity.kt | 4 +- .../pacien/tincapp/activities/StatusActivity.kt | 3 +- .../tincapp/activities/common/ProgressModal.kt | 49 +++++++ .../activities/configure/ConfigureActivity.kt | 35 +++++ .../activities/configure/PathInfoFragment.kt | 40 ++++++ .../tincapp/activities/configure/ToolsFragment.kt | 53 ++++++++ .../configure/tools/ConfigurationTool.kt | 64 +++++++++ .../tools/EncryptDecryptPrivateKeysTool.kt | 49 +++++++ .../configure/tools/GenerateConfigTool.kt | 51 ++++++++ .../activities/configure/tools/JoinNetworkTool.kt | 83 ++++++++++++ .../java/org/pacien/tincapp/context/AppPaths.kt | 12 +- .../java/org/pacien/tincapp/utils/ProgressModal.kt | 49 ------- app/src/main/res/layout/configure_activity.xml | 53 ++++++++ ...configure_tools_dialog_encrypt_decrypt_keys.xml | 51 ++++++++ .../configure_tools_dialog_network_generate.xml | 51 ++++++++ .../layout/configure_tools_dialog_network_join.xml | 78 +++++++++++ .../main/res/layout/configure_tools_fragment.xml | 73 +++++++++++ .../layout/configure_tools_path_info_fragment.xml | 74 +++++++++++ .../res/layout/dialog_encrypt_decrypt_keys.xml | 51 -------- .../main/res/layout/dialog_network_generate.xml | 51 -------- app/src/main/res/layout/dialog_network_join.xml | 66 ---------- app/src/main/res/layout/page_configure.xml | 121 ----------------- app/src/main/res/values-ja/strings.xml | 2 +- app/src/main/res/values-nb-rNO/strings.xml | 2 +- app/src/main/res/values-zh-rCN/strings.xml | 48 +++---- app/src/main/res/values-zh-rHK/strings.xml | 48 +++---- app/src/main/res/values-zh-rTW/strings.xml | 48 +++---- app/src/main/res/values/strings.xml | 55 ++++---- 32 files changed, 936 insertions(+), 596 deletions(-) delete mode 100644 app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt create mode 100644 app/src/main/java/org/pacien/tincapp/activities/common/ProgressModal.kt create mode 100644 app/src/main/java/org/pacien/tincapp/activities/configure/ConfigureActivity.kt create mode 100644 app/src/main/java/org/pacien/tincapp/activities/configure/PathInfoFragment.kt create mode 100644 app/src/main/java/org/pacien/tincapp/activities/configure/ToolsFragment.kt create mode 100644 app/src/main/java/org/pacien/tincapp/activities/configure/tools/ConfigurationTool.kt create mode 100644 app/src/main/java/org/pacien/tincapp/activities/configure/tools/EncryptDecryptPrivateKeysTool.kt create mode 100644 app/src/main/java/org/pacien/tincapp/activities/configure/tools/GenerateConfigTool.kt create mode 100644 app/src/main/java/org/pacien/tincapp/activities/configure/tools/JoinNetworkTool.kt delete mode 100644 app/src/main/java/org/pacien/tincapp/utils/ProgressModal.kt create mode 100644 app/src/main/res/layout/configure_activity.xml create mode 100644 app/src/main/res/layout/configure_tools_dialog_encrypt_decrypt_keys.xml create mode 100644 app/src/main/res/layout/configure_tools_dialog_network_generate.xml create mode 100644 app/src/main/res/layout/configure_tools_dialog_network_join.xml create mode 100644 app/src/main/res/layout/configure_tools_fragment.xml create mode 100644 app/src/main/res/layout/configure_tools_path_info_fragment.xml delete mode 100644 app/src/main/res/layout/dialog_encrypt_decrypt_keys.xml delete mode 100644 app/src/main/res/layout/dialog_network_generate.xml delete mode 100644 app/src/main/res/layout/dialog_network_join.xml delete mode 100644 app/src/main/res/layout/page_configure.xml diff --git a/app/build.gradle b/app/build.gradle index ade3cba..d475102 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,6 +19,7 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' apply plugin: 'com.github.triplet.play' def keystorePropertiesFile = rootProject.file("keystore.properties") @@ -63,6 +64,10 @@ android { path 'CMakeLists.txt' } } + + dataBinding { + enabled = true + } } dependencies { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 517097e..ed9ddf0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,7 +49,7 @@ diff --git a/app/src/main/java/org/pacien/tincapp/activities/BaseActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/BaseActivity.kt index 4dc2381..af71544 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/BaseActivity.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/BaseActivity.kt @@ -19,19 +19,18 @@ package org.pacien.tincapp.activities import android.os.Bundle +import android.support.annotation.LayoutRes import android.support.annotation.StringRes import android.support.design.widget.Snackbar import android.support.v7.app.AlertDialog import android.support.v7.app.AppCompatActivity -import android.view.Menu -import android.view.MenuItem +import android.view.* import kotlinx.android.synthetic.main.base.* import org.pacien.tincapp.R import org.pacien.tincapp.context.App import org.pacien.tincapp.context.AppInfo import org.pacien.tincapp.context.AppPaths import org.pacien.tincapp.context.CrashRecorder -import org.pacien.tincapp.utils.ProgressModal /** * @author pacien @@ -105,10 +104,13 @@ abstract class BaseActivity : AppCompatActivity() { .show() } - protected fun notify(@StringRes msg: Int) = Snackbar.make(activity_base, msg, Snackbar.LENGTH_LONG).show() - protected fun notify(msg: String) = Snackbar.make(activity_base, msg, Snackbar.LENGTH_LONG).show() - protected fun showProgressDialog(@StringRes msg: Int): AlertDialog = ProgressModal.show(this, getString(msg)) - protected fun showErrorDialog(msg: String): AlertDialog = AlertDialog.Builder(this) + fun inflate(@LayoutRes layout: Int) = layoutInflater.inflate(layout, main_content, false)!! + fun inflate(inflateFunc: (LayoutInflater, ViewGroup, Boolean) -> View) = inflateFunc(layoutInflater, main_content, false) + + fun notify(@StringRes msg: Int) = Snackbar.make(activity_base, msg, Snackbar.LENGTH_LONG).show() + fun notify(msg: String) = Snackbar.make(activity_base, msg, Snackbar.LENGTH_LONG).show() + + fun showErrorDialog(msg: String): AlertDialog = AlertDialog.Builder(this) .setTitle(R.string.title_error).setMessage(msg) .setPositiveButton(R.string.action_close) { _, _ -> Unit }.show() } diff --git a/app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt deleted file mode 100644 index 2aab304..0000000 --- a/app/src/main/java/org/pacien/tincapp/activities/ConfigureActivity.kt +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon - * Copyright (C) 2017-2018 Pacien TRAN-GIRARD - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.pacien.tincapp.activities - -import android.content.Intent -import android.os.Bundle -import android.support.annotation.StringRes -import android.support.v7.app.AlertDialog -import android.view.View -import com.google.zxing.integration.android.IntentIntegrator -import com.google.zxing.integration.android.IntentResult -import java8.util.concurrent.CompletableFuture -import kotlinx.android.synthetic.main.base.* -import kotlinx.android.synthetic.main.dialog_encrypt_decrypt_keys.view.* -import kotlinx.android.synthetic.main.dialog_network_generate.view.* -import kotlinx.android.synthetic.main.dialog_network_join.view.* -import kotlinx.android.synthetic.main.page_configure.* -import org.pacien.tincapp.R -import org.pacien.tincapp.commands.Tinc -import org.pacien.tincapp.commands.TincApp -import org.pacien.tincapp.context.AppPaths -import org.pacien.tincapp.extensions.Java.exceptionallyAccept -import java.util.regex.Pattern - -/** - * @author pacien - */ -class ConfigureActivity : BaseActivity() { - companion object { - private val NETWORK_NAME_PATTERN = Pattern.compile("^[^\\x00/]*$") - } - - private var joinDialog: View? = null - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - - IntentIntegrator.parseActivityResult(requestCode, resultCode, data) - ?.let(IntentResult::getContents) - ?.let(String::trim) - ?.let { joinDialog?.invitation_url?.setText(it) } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - supportActionBar!!.setDisplayHomeAsUpEnabled(true) - layoutInflater.inflate(R.layout.page_configure, main_content) - writeContent() - } - - fun scanCode(@Suppress("UNUSED_PARAMETER") v: View) { - IntentIntegrator(this).initiateScan() - } - - fun openGenerateConfDialog(@Suppress("UNUSED_PARAMETER") v: View) { - val genDialog = layoutInflater.inflate(R.layout.dialog_network_generate, main_content, false) - AlertDialog.Builder(this).setTitle(R.string.title_new_network).setView(genDialog) - .setPositiveButton(R.string.action_create) { _, _ -> - generateConf( - genDialog.new_net_name.text.toString(), - genDialog.new_node_name.text.toString(), - genDialog.new_passphrase.text.toString()) - }.setNegativeButton(R.string.action_cancel) { _, _ -> Unit }.show() - } - - fun openJoinNetworkDialog(@Suppress("UNUSED_PARAMETER") v: View) { - joinDialog = layoutInflater.inflate(R.layout.dialog_network_join, main_content, false) - AlertDialog.Builder(this).setTitle(R.string.title_join_network).setView(joinDialog) - .setPositiveButton(R.string.action_join) { _, _ -> - joinNetwork( - joinDialog!!.net_name.text.toString(), - joinDialog!!.invitation_url.text.toString(), - joinDialog!!.join_passphrase.text.toString()) - }.setNegativeButton(R.string.action_cancel) { _, _ -> Unit }.show() - } - - fun openEncryptDecryptPrivateKeyDialog(@Suppress("UNUSED_PARAMETER") v: View) { - val encryptDecryptDialog = layoutInflater.inflate(R.layout.dialog_encrypt_decrypt_keys, main_content, false) - AlertDialog.Builder(this).setTitle(R.string.title_private_keys_encryption).setView(encryptDecryptDialog) - .setPositiveButton(R.string.action_apply) { _, _ -> - encryptDecryptPrivateKeys( - encryptDecryptDialog!!.enc_dec_net_name.text.toString(), - encryptDecryptDialog.enc_dec_current_passphrase.text.toString(), - encryptDecryptDialog.enc_dec_new_passphrase.text.toString()) - }.setNegativeButton(R.string.action_cancel) { _, _ -> Unit }.show() - } - - private fun writeContent() { - text_configuration_directory.text = AppPaths.confDir().absolutePath - text_log_directory.text = AppPaths.cacheDir().absolutePath - text_tinc_binary.text = AppPaths.tinc().absolutePath - } - - private fun generateConf(netName: String, nodeName: String, passphrase: String? = null) = execAction( - R.string.message_generating_configuration, - validateNetName(netName) - .thenCompose { Tinc.init(netName, nodeName) } - .thenCompose { TincApp.removeScripts(netName) } - .thenCompose { TincApp.generateIfaceCfgTemplate(netName) } - .thenCompose { TincApp.setPassphrase(netName, newPassphrase = passphrase) }) - - private fun joinNetwork(netName: String, url: String, passphrase: String? = null) = execAction( - R.string.message_joining_network, - validateNetName(netName) - .thenCompose { Tinc.join(netName, url) } - .thenCompose { TincApp.removeScripts(netName) } - .thenCompose { TincApp.generateIfaceCfg(netName) } - .thenCompose { TincApp.setPassphrase(netName, newPassphrase = passphrase) }) - - private fun encryptDecryptPrivateKeys(netName: String, currentPassphrase: String, newPassphrase: String) = execAction( - R.string.message_encrypting_decrypting_private_keys, - validateNetName(netName) - .thenCompose { TincApp.setPassphrase(netName, currentPassphrase, newPassphrase) }) - - private fun execAction(@StringRes label: Int, action: CompletableFuture) { - showProgressDialog(label).let { progressDialog -> - action - .whenComplete { _, _ -> progressDialog.dismiss() } - .thenAccept { notify(R.string.message_network_configuration_written) } - .exceptionallyAccept { runOnUiThread { showErrorDialog(it.cause!!.localizedMessage) } } - } - } - - private fun validateNetName(netName: String): CompletableFuture = - if (NETWORK_NAME_PATTERN.matcher(netName).matches()) - CompletableFuture.completedFuture(Unit) - else - CompletableFuture.failedFuture(IllegalArgumentException(resources.getString(R.string.message_invalid_network_name))) -} diff --git a/app/src/main/java/org/pacien/tincapp/activities/StartActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/StartActivity.kt index 50e002f..70302e5 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/StartActivity.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/StartActivity.kt @@ -35,6 +35,8 @@ import kotlinx.android.synthetic.main.dialog_decrypt_keys.view.* import kotlinx.android.synthetic.main.fragment_list_view.* import kotlinx.android.synthetic.main.fragment_network_list_header.* import org.pacien.tincapp.R +import org.pacien.tincapp.activities.common.ProgressModal +import org.pacien.tincapp.activities.configure.ConfigureActivity import org.pacien.tincapp.context.AppPaths import org.pacien.tincapp.extensions.Android.setElements import org.pacien.tincapp.intent.Actions @@ -127,7 +129,7 @@ class StartActivity : BaseActivity() { } private fun startVpn(netName: String, passphrase: String? = null) { - connectDialog = showProgressDialog(R.string.message_starting_vpn) + connectDialog = ProgressModal.show(this@StartActivity, getString(R.string.message_starting_vpn)) TincVpnService.connect(netName, passphrase) } } diff --git a/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt index 68e008e..88ce222 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt @@ -34,6 +34,7 @@ import kotlinx.android.synthetic.main.dialog_node_details.view.* import kotlinx.android.synthetic.main.fragment_list_view.* import kotlinx.android.synthetic.main.fragment_network_status_header.* import org.pacien.tincapp.R +import org.pacien.tincapp.activities.common.ProgressModal import org.pacien.tincapp.commands.Executor import org.pacien.tincapp.commands.Tinc import org.pacien.tincapp.data.VpnInterfaceConfiguration @@ -127,7 +128,7 @@ class StatusActivity : BaseActivity(), AdapterView.OnItemClickListener, SwipeRef fun stopVpn(@Suppress("UNUSED_PARAMETER") i: MenuItem? = null) { refreshTimer?.cancel() list_wrapper.isRefreshing = false - shutdownDialog = showProgressDialog(R.string.message_disconnecting_vpn) + shutdownDialog = ProgressModal.show(this, getString(R.string.message_disconnecting_vpn)) TincVpnService.disconnect() } diff --git a/app/src/main/java/org/pacien/tincapp/activities/common/ProgressModal.kt b/app/src/main/java/org/pacien/tincapp/activities/common/ProgressModal.kt new file mode 100644 index 0000000..d4310a1 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/activities/common/ProgressModal.kt @@ -0,0 +1,49 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.activities.common + +import android.annotation.SuppressLint +import android.app.Activity +import android.support.v7.app.AlertDialog +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import org.pacien.tincapp.R + +/** + * An indefinite progress dialog replacing the deprecated `android.app.ProgressDialog`. + * + * @author pacien + */ +object ProgressModal { + fun show(context: Activity, text: String): AlertDialog { + return AlertDialog.Builder(context) + .setView(newDialogView(context.layoutInflater, text)) + .setCancelable(false) + .show() + } + + @SuppressLint("InflateParams") + private fun newDialogView(inflater: LayoutInflater, text: String): View { + val view = inflater.inflate(R.layout.common_progress_dialog, null) + val textView: TextView = view.findViewById(R.id.common_progress_dialog_text) + textView.text = text + return view + } +} diff --git a/app/src/main/java/org/pacien/tincapp/activities/configure/ConfigureActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/configure/ConfigureActivity.kt new file mode 100644 index 0000000..d154ff7 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/activities/configure/ConfigureActivity.kt @@ -0,0 +1,35 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.activities.configure + +import android.os.Bundle +import kotlinx.android.synthetic.main.base.* +import org.pacien.tincapp.R +import org.pacien.tincapp.activities.BaseActivity + +/** + * @author pacien + */ +class ConfigureActivity : BaseActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + supportActionBar!!.setDisplayHomeAsUpEnabled(true) + layoutInflater.inflate(R.layout.configure_activity, main_content) + } +} diff --git a/app/src/main/java/org/pacien/tincapp/activities/configure/PathInfoFragment.kt b/app/src/main/java/org/pacien/tincapp/activities/configure/PathInfoFragment.kt new file mode 100644 index 0000000..dae4413 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/activities/configure/PathInfoFragment.kt @@ -0,0 +1,40 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.activities.configure + +import android.app.Fragment +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import org.pacien.tincapp.context.AppPaths +import org.pacien.tincapp.databinding.ConfigureToolsPathInfoFragmentBinding + +/** + * @author pacien + */ +class PathInfoFragment : Fragment() { + private val appPaths = AppPaths + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + val binding = ConfigureToolsPathInfoFragmentBinding.inflate(inflater, container, false) + binding.appPaths = appPaths + return binding.root + } +} diff --git a/app/src/main/java/org/pacien/tincapp/activities/configure/ToolsFragment.kt b/app/src/main/java/org/pacien/tincapp/activities/configure/ToolsFragment.kt new file mode 100644 index 0000000..0ad2f19 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/activities/configure/ToolsFragment.kt @@ -0,0 +1,53 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.activities.configure + +import android.app.Fragment +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import org.pacien.tincapp.activities.BaseActivity +import org.pacien.tincapp.activities.configure.tools.EncryptDecryptPrivateKeysTool +import org.pacien.tincapp.activities.configure.tools.GenerateConfigTool +import org.pacien.tincapp.activities.configure.tools.JoinNetworkTool +import org.pacien.tincapp.databinding.ConfigureToolsFragmentBinding + +/** + * @author pacien + */ +class ToolsFragment : Fragment() { + private val parentActivity by lazy { activity as BaseActivity } + private val generateConfigTool by lazy { GenerateConfigTool(parentActivity) } + private val joinNetworkTool by lazy { JoinNetworkTool(this, parentActivity) } + private val encryptDecryptPrivateKeysTool by lazy { EncryptDecryptPrivateKeysTool(parentActivity) } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + val binding = ConfigureToolsFragmentBinding.inflate(inflater, container, false) + binding.generateConfigAction = generateConfigTool::openGenerateConfDialog + binding.joinNetworkAction = joinNetworkTool::openJoinNetworkDialog + binding.encryptDecryptPrivateKeysAction = encryptDecryptPrivateKeysTool::openEncryptDecryptPrivateKeyDialog + return binding.root + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + joinNetworkTool.onActivityResult(requestCode, resultCode, data) + } +} diff --git a/app/src/main/java/org/pacien/tincapp/activities/configure/tools/ConfigurationTool.kt b/app/src/main/java/org/pacien/tincapp/activities/configure/tools/ConfigurationTool.kt new file mode 100644 index 0000000..41d1f55 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/activities/configure/tools/ConfigurationTool.kt @@ -0,0 +1,64 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.activities.configure.tools + +import android.app.AlertDialog +import android.support.annotation.LayoutRes +import android.support.annotation.StringRes +import android.view.View +import java8.util.concurrent.CompletableFuture +import org.pacien.tincapp.R +import org.pacien.tincapp.activities.BaseActivity +import org.pacien.tincapp.activities.common.ProgressModal +import org.pacien.tincapp.extensions.Java.exceptionallyAccept +import java.util.regex.Pattern + +/** + * @author pacien + */ +abstract class ConfigurationTool(private val parentActivity: BaseActivity) { + private val networkNamePattern by lazy { Pattern.compile("^[^\\x00/]*$")!! } + + protected fun showDialog(@LayoutRes layout: Int, @StringRes title: Int, @StringRes applyButton: Int, applyAction: (View) -> Unit) = + showDialog(parentActivity.inflate(layout), title, applyButton, applyAction) + + protected fun showDialog(view: View, @StringRes title: Int, @StringRes applyButton: Int, applyAction: (View) -> Unit) { + AlertDialog.Builder(parentActivity) + .setTitle(title) + .setView(view) + .setPositiveButton(applyButton) { _, _ -> applyAction(view) } + .setNegativeButton(R.string.action_cancel) { _, _ -> Unit } + .show() + } + + protected fun execAction(@StringRes label: Int, action: CompletableFuture) { + ProgressModal.show(parentActivity, parentActivity.getString(label)).let { progressDialog -> + action + .whenComplete { _, _ -> progressDialog.dismiss() } + .thenAccept { parentActivity.notify(R.string.message_network_configuration_written) } + .exceptionallyAccept { parentActivity.runOnUiThread { parentActivity.showErrorDialog(it.cause!!.localizedMessage) } } + } + } + + protected fun validateNetName(netName: String): CompletableFuture = + if (networkNamePattern.matcher(netName).matches()) + CompletableFuture.completedFuture(Unit) + else + CompletableFuture.failedFuture(IllegalArgumentException(parentActivity.getString(R.string.message_invalid_network_name))) +} diff --git a/app/src/main/java/org/pacien/tincapp/activities/configure/tools/EncryptDecryptPrivateKeysTool.kt b/app/src/main/java/org/pacien/tincapp/activities/configure/tools/EncryptDecryptPrivateKeysTool.kt new file mode 100644 index 0000000..cd55111 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/activities/configure/tools/EncryptDecryptPrivateKeysTool.kt @@ -0,0 +1,49 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.activities.configure.tools + +import kotlinx.android.synthetic.main.configure_tools_dialog_encrypt_decrypt_keys.view.* +import org.pacien.tincapp.R +import org.pacien.tincapp.activities.BaseActivity +import org.pacien.tincapp.commands.TincApp + +/** + * @author pacien + */ +class EncryptDecryptPrivateKeysTool(parentActivity: BaseActivity) : ConfigurationTool(parentActivity) { + fun openEncryptDecryptPrivateKeyDialog() = + showDialog( + R.layout.configure_tools_dialog_encrypt_decrypt_keys, + R.string.configure_tools_private_keys_encryption_title, + R.string.configure_tools_private_keys_encryption_action + ) { dialog -> + encryptDecryptPrivateKeys( + dialog.enc_dec_net_name.text.toString(), + dialog.enc_dec_current_passphrase.text.toString(), + dialog.enc_dec_new_passphrase.text.toString() + ) + } + + private fun encryptDecryptPrivateKeys(netName: String, currentPassphrase: String, newPassphrase: String) = + execAction( + R.string.configure_tools_private_keys_encryption_encrypting, + validateNetName(netName) + .thenCompose { TincApp.setPassphrase(netName, currentPassphrase, newPassphrase) } + ) +} diff --git a/app/src/main/java/org/pacien/tincapp/activities/configure/tools/GenerateConfigTool.kt b/app/src/main/java/org/pacien/tincapp/activities/configure/tools/GenerateConfigTool.kt new file mode 100644 index 0000000..2f0ef49 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/activities/configure/tools/GenerateConfigTool.kt @@ -0,0 +1,51 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.activities.configure.tools + +import kotlinx.android.synthetic.main.configure_tools_dialog_network_generate.view.* +import org.pacien.tincapp.R +import org.pacien.tincapp.activities.BaseActivity +import org.pacien.tincapp.commands.Tinc +import org.pacien.tincapp.commands.TincApp + +/** + * @author pacien + */ +class GenerateConfigTool(parentActivity: BaseActivity) : ConfigurationTool(parentActivity) { + fun openGenerateConfDialog() = + showDialog( + R.layout.configure_tools_dialog_network_generate, + R.string.configure_tools_generate_config_title, + R.string.configure_tools_generate_config_action + ) { dialog -> + generateConf( + dialog.new_net_name.text.toString(), + dialog.new_node_name.text.toString(), + dialog.new_passphrase.text.toString() + ) + } + + private fun generateConf(netName: String, nodeName: String, passphrase: String? = null) = execAction( + R.string.configure_tools_generate_config_generating, + validateNetName(netName) + .thenCompose { Tinc.init(netName, nodeName) } + .thenCompose { TincApp.removeScripts(netName) } + .thenCompose { TincApp.generateIfaceCfgTemplate(netName) } + .thenCompose { TincApp.setPassphrase(netName, newPassphrase = passphrase) }) +} diff --git a/app/src/main/java/org/pacien/tincapp/activities/configure/tools/JoinNetworkTool.kt b/app/src/main/java/org/pacien/tincapp/activities/configure/tools/JoinNetworkTool.kt new file mode 100644 index 0000000..82a4380 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/activities/configure/tools/JoinNetworkTool.kt @@ -0,0 +1,83 @@ +/* + * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon + * Copyright (C) 2017-2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.pacien.tincapp.activities.configure.tools + +import android.app.Fragment +import android.content.Intent +import android.view.View +import com.google.zxing.integration.android.IntentIntegrator +import com.google.zxing.integration.android.IntentResult +import kotlinx.android.synthetic.main.configure_tools_dialog_network_join.view.* +import org.pacien.tincapp.R +import org.pacien.tincapp.activities.BaseActivity +import org.pacien.tincapp.commands.Tinc +import org.pacien.tincapp.commands.TincApp +import org.pacien.tincapp.databinding.ConfigureToolsDialogNetworkJoinBinding + +/** + * @author pacien + */ +class JoinNetworkTool(parentFragment: Fragment, private val parentActivity: BaseActivity) : ConfigurationTool(parentActivity) { + private val scanner by lazy { IntentIntegrator(parentFragment) } + private var joinDialog: View? = null + + fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + IntentIntegrator.parseActivityResult(requestCode, resultCode, data) + ?.let(IntentResult::getContents) + ?.let(String::trim) + ?.let { joinDialog?.invitation_url?.setText(it) } + } + + fun openJoinNetworkDialog() = + makeJoinDialog().let { newDialog -> + joinDialog = newDialog + showDialog( + newDialog, + R.string.configure_tools_join_network_title, + R.string.configure_tools_join_network_action + ) { dialog -> + joinNetwork( + dialog.net_name.text.toString(), + dialog.invitation_url.text.toString(), + dialog.join_passphrase.text.toString() + ) + } + } + + private fun makeJoinDialog() = + parentActivity.inflate { inflater, parent, attachToRoot -> + ConfigureToolsDialogNetworkJoinBinding.inflate(inflater, parent, attachToRoot) + .apply { scanAction = this@JoinNetworkTool::scanCode } + .root + } + + private fun scanCode() { + scanner.initiateScan() + } + + private fun joinNetwork(netName: String, url: String, passphrase: String? = null) = + execAction( + R.string.configure_tools_join_network_joining, + validateNetName(netName) + .thenCompose { Tinc.join(netName, url) } + .thenCompose { TincApp.removeScripts(netName) } + .thenCompose { TincApp.generateIfaceCfg(netName) } + .thenCompose { TincApp.setPassphrase(netName, newPassphrase = passphrase) } + ) +} diff --git a/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt b/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt index 0b85565..1efb7cf 100644 --- a/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt +++ b/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt @@ -43,13 +43,15 @@ object AppPaths { private const val NET_DEFAULT_ED25519_PRIVATE_KEY_FILE = "ed25519_key.priv" private const val NET_DEFAULT_RSA_PRIVATE_KEY_FILE = "rsa_key.priv" + private val context by lazy { App.getContext() } + fun storageAvailable() = Environment.getExternalStorageState().let { it == Environment.MEDIA_MOUNTED && it != Environment.MEDIA_MOUNTED_READ_ONLY } - private fun internalCacheDir() = App.getContext().cacheDir!! - fun cacheDir() = App.getContext().externalCacheDir!! - fun confDir() = App.getContext().getExternalFilesDir(null)!! - private fun binDir() = File(App.getContext().applicationInfo.nativeLibraryDir) + private fun internalCacheDir() = context.cacheDir!! + fun cacheDir() = context.externalCacheDir!! + fun confDir() = context.getExternalFilesDir(null)!! + private fun binDir() = File(context.applicationInfo.nativeLibraryDir) fun confDir(netName: String) = File(confDir(), netName) fun hostsDir(netName: String) = File(confDir(netName), NET_HOSTS_DIR) @@ -57,7 +59,7 @@ object AppPaths { fun tincConfFile(netName: String) = File(confDir(netName), NET_TINC_CONF_FILE) fun invitationFile(netName: String) = File(confDir(netName), NET_INVITATION_FILE) fun logFile(netName: String) = File(cacheDir(), String.format(LOGFILE_FORMAT, netName)) - fun pidFile(netName: String) = File(App.getContext().cacheDir, String.format(PIDFILE_FORMAT, netName)) + fun pidFile(netName: String) = File(context.cacheDir, String.format(PIDFILE_FORMAT, netName)) fun appLogFile() = File(cacheDir(), APPLOG_FILE) fun crashFlagFile() = File(internalCacheDir(), CRASHFLAG_FILE) diff --git a/app/src/main/java/org/pacien/tincapp/utils/ProgressModal.kt b/app/src/main/java/org/pacien/tincapp/utils/ProgressModal.kt deleted file mode 100644 index 53eb651..0000000 --- a/app/src/main/java/org/pacien/tincapp/utils/ProgressModal.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon - * Copyright (C) 2017-2018 Pacien TRAN-GIRARD - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.pacien.tincapp.utils - -import android.annotation.SuppressLint -import android.app.Activity -import android.support.v7.app.AlertDialog -import android.view.LayoutInflater -import android.view.View -import android.widget.TextView -import org.pacien.tincapp.R - -/** - * An indefinite progress dialog replacing the deprecated `android.app.ProgressDialog`. - * - * @author pacien - */ -object ProgressModal { - fun show(context: Activity, text: String): AlertDialog { - return AlertDialog.Builder(context) - .setView(newDialogView(context.layoutInflater, text)) - .setCancelable(false) - .show() - } - - @SuppressLint("InflateParams") - private fun newDialogView(inflater: LayoutInflater, text: String): View { - val view = inflater.inflate(R.layout.common_progress_dialog, null) - val textView: TextView = view.findViewById(R.id.common_progress_dialog_text) - textView.text = text - return view - } -} diff --git a/app/src/main/res/layout/configure_activity.xml b/app/src/main/res/layout/configure_activity.xml new file mode 100644 index 0000000..6097384 --- /dev/null +++ b/app/src/main/res/layout/configure_activity.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/configure_tools_dialog_encrypt_decrypt_keys.xml b/app/src/main/res/layout/configure_tools_dialog_encrypt_decrypt_keys.xml new file mode 100644 index 0000000..1e8e64a --- /dev/null +++ b/app/src/main/res/layout/configure_tools_dialog_encrypt_decrypt_keys.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/configure_tools_dialog_network_generate.xml b/app/src/main/res/layout/configure_tools_dialog_network_generate.xml new file mode 100644 index 0000000..614cc16 --- /dev/null +++ b/app/src/main/res/layout/configure_tools_dialog_network_generate.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/configure_tools_dialog_network_join.xml b/app/src/main/res/layout/configure_tools_dialog_network_join.xml new file mode 100644 index 0000000..9171284 --- /dev/null +++ b/app/src/main/res/layout/configure_tools_dialog_network_join.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/configure_tools_fragment.xml b/app/src/main/res/layout/configure_tools_fragment.xml new file mode 100644 index 0000000..bb67b45 --- /dev/null +++ b/app/src/main/res/layout/configure_tools_fragment.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/configure_tools_path_info_fragment.xml b/app/src/main/res/layout/configure_tools_path_info_fragment.xml new file mode 100644 index 0000000..376fa3d --- /dev/null +++ b/app/src/main/res/layout/configure_tools_path_info_fragment.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_encrypt_decrypt_keys.xml b/app/src/main/res/layout/dialog_encrypt_decrypt_keys.xml deleted file mode 100644 index 47433e4..0000000 --- a/app/src/main/res/layout/dialog_encrypt_decrypt_keys.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_network_generate.xml b/app/src/main/res/layout/dialog_network_generate.xml deleted file mode 100644 index 5e3d42c..0000000 --- a/app/src/main/res/layout/dialog_network_generate.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_network_join.xml b/app/src/main/res/layout/dialog_network_join.xml deleted file mode 100644 index 65d772d..0000000 --- a/app/src/main/res/layout/dialog_network_join.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/page_configure.xml b/app/src/main/res/layout/page_configure.xml deleted file mode 100644 index c86ff76..0000000 --- a/app/src/main/res/layout/page_configure.xml +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -