aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt')
-rw-r--r--app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt196
1 files changed, 196 insertions, 0 deletions
diff --git a/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt
new file mode 100644
index 0000000..68e008e
--- /dev/null
+++ b/app/src/main/java/org/pacien/tincapp/activities/StatusActivity.kt
@@ -0,0 +1,196 @@
1/*
2 * Tinc App, an Android binding and user interface for the tinc mesh VPN daemon
3 * Copyright (C) 2017-2018 Pacien TRAN-GIRARD
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19package org.pacien.tincapp.activities
20
21import android.content.Intent
22import android.os.Bundle
23import android.support.v4.widget.SwipeRefreshLayout
24import android.support.v7.app.AlertDialog
25import android.view.Menu
26import android.view.MenuItem
27import android.view.View
28import android.widget.AdapterView
29import android.widget.ArrayAdapter
30import android.widget.TextView
31import java8.util.concurrent.CompletableFuture
32import kotlinx.android.synthetic.main.base.*
33import kotlinx.android.synthetic.main.dialog_node_details.view.*
34import kotlinx.android.synthetic.main.fragment_list_view.*
35import kotlinx.android.synthetic.main.fragment_network_status_header.*
36import org.pacien.tincapp.R
37import org.pacien.tincapp.commands.Executor
38import org.pacien.tincapp.commands.Tinc
39import org.pacien.tincapp.data.VpnInterfaceConfiguration
40import org.pacien.tincapp.extensions.Android.setElements
41import org.pacien.tincapp.extensions.Android.setText
42import org.pacien.tincapp.intent.Actions
43import org.pacien.tincapp.intent.BroadcastMapper
44import org.pacien.tincapp.service.TincVpnService
45import java.util.*
46import kotlin.concurrent.timerTask
47
48/**
49 * @author pacien
50 */
51class StatusActivity : BaseActivity(), AdapterView.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener {
52 private val broadcastMapper = BroadcastMapper(mapOf(Actions.EVENT_DISCONNECTED to this::onVpnShutdown))
53 private var shutdownDialog: AlertDialog? = null
54 private var nodeListAdapter: ArrayAdapter<String>? = null
55 private var refreshTimer: Timer? = null
56 private var listNetworksAfterExit = true
57
58 override fun onCreate(savedInstanceState: Bundle?) {
59 super.onCreate(savedInstanceState)
60 nodeListAdapter = ArrayAdapter(this, R.layout.fragment_list_item)
61
62 layoutInflater.inflate(R.layout.fragment_list_view, main_content)
63 list_wrapper.setOnRefreshListener(this)
64 list.addHeaderView(layoutInflater.inflate(R.layout.fragment_network_status_header, list, false), null, false)
65 list.addFooterView(View(this), null, false)
66 list.onItemClickListener = this
67 list.adapter = nodeListAdapter
68
69 if (intent.action == Actions.ACTION_DISCONNECT) {
70 listNetworksAfterExit = false
71 stopVpn()
72 } else {
73 listNetworksAfterExit = true
74 }
75 }
76
77 override fun onCreateOptionsMenu(m: Menu): Boolean {
78 menuInflater.inflate(R.menu.menu_status, m)
79 return super.onCreateOptionsMenu(m)
80 }
81
82 override fun onDestroy() {
83 super.onDestroy()
84 nodeListAdapter = null
85 refreshTimer = null
86 }
87
88 override fun onStart() {
89 super.onStart()
90 refreshTimer = Timer(true)
91 refreshTimer?.schedule(timerTask { updateView() }, NOW, REFRESH_RATE)
92 writeNetworkInfo(TincVpnService.getCurrentInterfaceCfg() ?: VpnInterfaceConfiguration())
93 }
94
95 override fun onStop() {
96 refreshTimer?.cancel()
97 super.onStop()
98 }
99
100 override fun onResume() {
101 super.onResume()
102 broadcastMapper.register()
103 updateView()
104 handleRecentCrash()
105 }
106
107 override fun onPause() {
108 broadcastMapper.unregister()
109 super.onPause()
110 }
111
112 override fun onRefresh() {
113 refreshTimer?.schedule(timerTask { updateView() }, NOW)
114 }
115
116 override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) = when (view) {
117 is TextView -> showNodeInfo(view.text.toString())
118 else -> Unit
119 }
120
121 private fun onVpnShutdown() {
122 shutdownDialog?.dismiss()
123 if (listNetworksAfterExit) openStartActivity()
124 finish()
125 }
126
127 fun stopVpn(@Suppress("UNUSED_PARAMETER") i: MenuItem? = null) {
128 refreshTimer?.cancel()
129 list_wrapper.isRefreshing = false
130 shutdownDialog = showProgressDialog(R.string.message_disconnecting_vpn)
131 TincVpnService.disconnect()
132 }
133
134 fun openLogViewer(@Suppress("UNUSED_PARAMETER") i: MenuItem) =
135 startActivity(Intent(this, ViewLogActivity::class.java))
136
137 private fun writeNetworkInfo(cfg: VpnInterfaceConfiguration) {
138 text_network_name.text = TincVpnService.getCurrentNetName() ?: getString(R.string.value_none)
139 text_network_ip_addresses.setText(cfg.addresses.map { it.toSlashSeparated() })
140 text_network_routes.setText(cfg.routes.map { it.toSlashSeparated() })
141 text_network_dns_servers.setText(cfg.dnsServers)
142 text_network_search_domains.setText(cfg.searchDomains)
143 text_network_allow_bypass.text = getString(if (cfg.allowBypass) R.string.value_yes else R.string.value_no)
144 block_network_allowed_applications.visibility = if (cfg.allowedApplications.isNotEmpty()) View.VISIBLE else View.GONE
145 text_network_allowed_applications.setText(cfg.allowedApplications)
146 block_network_disallowed_applications.visibility = if (cfg.disallowedApplications.isNotEmpty()) View.VISIBLE else View.GONE
147 text_network_disallowed_applications.setText(cfg.disallowedApplications)
148 }
149
150 private fun writeNodeList(nodeList: List<String>) {
151 nodeListAdapter?.setElements(nodeList)
152 node_list_placeholder.visibility = View.GONE
153 list_wrapper.isRefreshing = false
154 }
155
156 private fun updateNodeList() {
157 getNodeNames().thenAccept { nodeList -> runOnUiThread { writeNodeList(nodeList) } }
158 }
159
160 private fun showNodeInfo(nodeName: String) {
161 val dialogTextView = layoutInflater.inflate(R.layout.dialog_node_details, main_content, false)
162
163 runOnUiThread {
164 AlertDialog.Builder(this)
165 .setTitle(R.string.title_node_info)
166 .setView(dialogTextView)
167 .setPositiveButton(R.string.action_close) { _, _ -> Unit }
168 .show()
169 }
170
171 TincVpnService.getCurrentNetName()?.let { netName ->
172 Tinc.info(netName, nodeName).thenAccept { nodeInfo ->
173 runOnUiThread { dialogTextView.dialog_node_details.text = nodeInfo }
174 }
175 }
176 }
177
178 private fun updateView() = when {
179 TincVpnService.isConnected() -> updateNodeList()
180 else -> openStartActivity()
181 }
182
183 private fun openStartActivity() {
184 startActivity(Intent(this, StartActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))
185 finish()
186 }
187
188 companion object {
189 private const val REFRESH_RATE = 5000L
190 private const val NOW = 0L
191
192 fun getNodeNames(): CompletableFuture<List<String>> = TincVpnService.getCurrentNetName()?.let { netName ->
193 Tinc.dumpNodes(netName).thenApply<List<String>> { list -> list.map { it.substringBefore(' ') } }
194 } ?: Executor.supplyAsyncTask<List<String>> { emptyList() }
195 }
196}