diff options
Diffstat (limited to 'app/src/main/java/org')
9 files changed, 314 insertions, 230 deletions
diff --git a/app/src/main/java/org/pacien/tincapp/activities/LaunchActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/LaunchActivity.kt deleted file mode 100644 index 0179040..0000000 --- a/app/src/main/java/org/pacien/tincapp/activities/LaunchActivity.kt +++ /dev/null | |||
@@ -1,98 +0,0 @@ | |||
1 | package org.pacien.tincapp.activities | ||
2 | |||
3 | import android.annotation.SuppressLint | ||
4 | import android.app.Activity | ||
5 | import android.content.Intent | ||
6 | import android.net.Uri | ||
7 | import android.net.VpnService | ||
8 | import android.os.Bundle | ||
9 | import android.support.v7.app.AlertDialog | ||
10 | import android.support.v7.app.AppCompatActivity | ||
11 | import kotlinx.android.synthetic.main.dialog_decrypt_keys.view.* | ||
12 | import org.pacien.tincapp.R | ||
13 | import org.pacien.tincapp.commands.TincApp | ||
14 | import org.pacien.tincapp.context.App | ||
15 | import org.pacien.tincapp.intent.action.ACTION_CONNECT | ||
16 | import org.pacien.tincapp.intent.action.ACTION_DISCONNECT | ||
17 | import org.pacien.tincapp.intent.action.TINC_SCHEME | ||
18 | import org.pacien.tincapp.service.TincVpnService | ||
19 | import org.pacien.tincapp.utils.PemUtils | ||
20 | import java.io.FileNotFoundException | ||
21 | |||
22 | /** | ||
23 | * @author pacien | ||
24 | */ | ||
25 | class LaunchActivity : AppCompatActivity() { | ||
26 | |||
27 | override fun onCreate(savedInstanceState: Bundle?) { | ||
28 | super.onCreate(savedInstanceState) | ||
29 | |||
30 | when (intent.action) { | ||
31 | ACTION_CONNECT -> requestPerm() | ||
32 | ACTION_DISCONNECT -> disconnect() | ||
33 | } | ||
34 | } | ||
35 | |||
36 | override fun onActivityResult(request: Int, result: Int, data: Intent?) { | ||
37 | if (request == PERMISSION_REQUEST_CODE && result == Activity.RESULT_OK) askPassphrase() | ||
38 | } | ||
39 | |||
40 | private fun requestPerm() = VpnService.prepare(this).let { | ||
41 | if (it != null) | ||
42 | startActivityForResult(it, PERMISSION_REQUEST_CODE) | ||
43 | else | ||
44 | onActivityResult(PERMISSION_REQUEST_CODE, Activity.RESULT_OK, null) | ||
45 | } | ||
46 | |||
47 | @SuppressLint("InflateParams") | ||
48 | private fun askPassphrase() { | ||
49 | val netName = intent.data.schemeSpecificPart | ||
50 | |||
51 | if (needPassphrase(netName) && intent.data.fragment == null) { | ||
52 | val dialog = layoutInflater.inflate(R.layout.dialog_decrypt_keys, null, false) | ||
53 | AlertDialog.Builder(this) | ||
54 | .setTitle(R.string.title_unlock_private_keys).setView(dialog) | ||
55 | .setPositiveButton(R.string.action_unlock) { _, _ -> connect(netName, dialog.passphrase.text.toString()) } | ||
56 | .setNegativeButton(R.string.action_cancel, { _, _ -> finish() }) | ||
57 | .show() | ||
58 | } else { | ||
59 | connect(netName, intent.data.fragment) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | private fun needPassphrase(netName: String) = try { | ||
64 | TincApp.listPrivateKeys(netName).filter { it.exists() }.any { PemUtils.isEncrypted(PemUtils.read(it)) } | ||
65 | } catch (e: FileNotFoundException) { | ||
66 | false | ||
67 | } | ||
68 | |||
69 | private fun connect(netName: String, passphrase: String? = null) { | ||
70 | TincVpnService.startVpn(netName, passphrase) | ||
71 | finish() | ||
72 | } | ||
73 | |||
74 | private fun disconnect() { | ||
75 | TincVpnService.stopVpn() | ||
76 | finish() | ||
77 | } | ||
78 | |||
79 | companion object { | ||
80 | |||
81 | private val PERMISSION_REQUEST_CODE = 0 | ||
82 | |||
83 | fun connect(netName: String, passphrase: String? = null) { | ||
84 | App.getContext().startActivity(Intent(App.getContext(), LaunchActivity::class.java) | ||
85 | .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) | ||
86 | .setAction(ACTION_CONNECT) | ||
87 | .setData(Uri.Builder().scheme(TINC_SCHEME).opaquePart(netName).fragment(passphrase).build())) | ||
88 | } | ||
89 | |||
90 | fun disconnect() { | ||
91 | App.getContext().startActivity(Intent(App.getContext(), LaunchActivity::class.java) | ||
92 | .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) | ||
93 | .setAction(ACTION_DISCONNECT)) | ||
94 | } | ||
95 | |||
96 | } | ||
97 | |||
98 | } | ||
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 719bbc1..9fa5e44 100644 --- a/app/src/main/java/org/pacien/tincapp/activities/StartActivity.kt +++ b/app/src/main/java/org/pacien/tincapp/activities/StartActivity.kt | |||
@@ -1,8 +1,13 @@ | |||
1 | package org.pacien.tincapp.activities | 1 | package org.pacien.tincapp.activities |
2 | 2 | ||
3 | import android.app.Activity | ||
4 | import android.app.ProgressDialog | ||
3 | import android.content.Intent | 5 | import android.content.Intent |
6 | import android.content.IntentFilter | ||
7 | import android.net.VpnService | ||
4 | import android.os.Bundle | 8 | import android.os.Bundle |
5 | import android.support.v4.widget.SwipeRefreshLayout | 9 | import android.support.v4.widget.SwipeRefreshLayout |
10 | import android.support.v7.app.AlertDialog | ||
6 | import android.view.Menu | 11 | import android.view.Menu |
7 | import android.view.MenuItem | 12 | import android.view.MenuItem |
8 | import android.view.View | 13 | import android.view.View |
@@ -10,29 +15,117 @@ import android.widget.AdapterView | |||
10 | import android.widget.ArrayAdapter | 15 | import android.widget.ArrayAdapter |
11 | import android.widget.TextView | 16 | import android.widget.TextView |
12 | import kotlinx.android.synthetic.main.base.* | 17 | import kotlinx.android.synthetic.main.base.* |
18 | import kotlinx.android.synthetic.main.dialog_decrypt_keys.view.* | ||
13 | import kotlinx.android.synthetic.main.fragment_list_view.* | 19 | import kotlinx.android.synthetic.main.fragment_list_view.* |
14 | import kotlinx.android.synthetic.main.fragment_network_list_header.* | 20 | import kotlinx.android.synthetic.main.fragment_network_list_header.* |
15 | import org.pacien.tincapp.R | 21 | import org.pacien.tincapp.R |
16 | import org.pacien.tincapp.context.AppPaths | 22 | import org.pacien.tincapp.context.AppPaths |
17 | import org.pacien.tincapp.extensions.Android.setElements | 23 | import org.pacien.tincapp.extensions.Android.setElements |
24 | import org.pacien.tincapp.intent.Actions | ||
25 | import org.pacien.tincapp.intent.SimpleBroadcastReceiver | ||
18 | import org.pacien.tincapp.service.TincVpnService | 26 | import org.pacien.tincapp.service.TincVpnService |
27 | import org.pacien.tincapp.utils.TincKeyring | ||
19 | 28 | ||
20 | /** | 29 | /** |
21 | * @author pacien | 30 | * @author pacien |
22 | */ | 31 | */ |
23 | class StartActivity : BaseActivity(), AdapterView.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener { | 32 | class StartActivity : BaseActivity() { |
33 | companion object { | ||
34 | private const val PERMISSION_REQUEST = 0 | ||
35 | } | ||
36 | |||
37 | private val networkList = object : AdapterView.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener { | ||
38 | private var networkListAdapter: ArrayAdapter<String>? = null | ||
39 | |||
40 | fun init() { | ||
41 | networkListAdapter = ArrayAdapter(this@StartActivity, R.layout.fragment_list_item) | ||
42 | layoutInflater.inflate(R.layout.fragment_list_view, main_content) | ||
43 | list_wrapper.setOnRefreshListener(this) | ||
44 | list.addHeaderView(layoutInflater.inflate(R.layout.fragment_network_list_header, list, false), null, false) | ||
45 | list.addFooterView(View(this@StartActivity), null, false) | ||
46 | list.adapter = networkListAdapter | ||
47 | list.onItemClickListener = this | ||
48 | } | ||
49 | |||
50 | fun destroy() { | ||
51 | networkListAdapter = null | ||
52 | } | ||
53 | |||
54 | override fun onRefresh() { | ||
55 | val networks = AppPaths.confDir()?.list()?.toList() ?: emptyList() | ||
56 | runOnUiThread { | ||
57 | networkListAdapter?.setElements(networks) | ||
58 | setPlaceholderVisibility() | ||
59 | list_wrapper.isRefreshing = false | ||
60 | } | ||
61 | } | ||
62 | |||
63 | override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { | ||
64 | connectionStarter.tryStart(netName = (view as TextView).text.toString(), displayStatus = true) | ||
65 | } | ||
66 | |||
67 | private fun setPlaceholderVisibility() = if (networkListAdapter?.isEmpty != false) { | ||
68 | network_list_placeholder.text = getListPlaceholderText() | ||
69 | network_list_placeholder.visibility = View.VISIBLE | ||
70 | } else { | ||
71 | network_list_placeholder.visibility = View.GONE | ||
72 | } | ||
73 | |||
74 | private fun getListPlaceholderText() = if (!AppPaths.storageAvailable()) { | ||
75 | getText(R.string.message_storage_unavailable) | ||
76 | } else { | ||
77 | getText(R.string.message_no_network_configuration_found) | ||
78 | } | ||
79 | } | ||
80 | |||
81 | private val connectionStarter = object { | ||
82 | private var netName: String? = null | ||
83 | private var passphrase: String? = null |