aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/org/pacien/tincapp/activities/ViewLogActivity.kt
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/org/pacien/tincapp/activities/ViewLogActivity.kt')
-rw-r--r--app/src/main/java/org/pacien/tincapp/activities/ViewLogActivity.kt164
1 files changed, 164 insertions, 0 deletions
diff --git a/app/src/main/java/org/pacien/tincapp/activities/ViewLogActivity.kt b/app/src/main/java/org/pacien/tincapp/activities/ViewLogActivity.kt
new file mode 100644
index 0000000..f3f7e24
--- /dev/null
+++ b/app/src/main/java/org/pacien/tincapp/activities/ViewLogActivity.kt
@@ -0,0 +1,164 @@
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.view.Menu
24import android.view.MenuItem
25import android.view.View
26import android.widget.ScrollView
27import kotlinx.android.synthetic.main.base.*
28import kotlinx.android.synthetic.main.page_viewlog.*
29import org.pacien.tincapp.R
30import org.pacien.tincapp.commands.Executor
31import org.pacien.tincapp.commands.Tinc
32import org.pacien.tincapp.service.TincVpnService
33import java.util.*
34import kotlin.concurrent.timer
35
36/**
37 * @author pacien
38 */
39class ViewLogActivity : BaseActivity() {
40 companion object {
41 private const val LOG_LINES = 250
42 private const val LOG_LEVEL = 5
43 private const val NEW_LINE = "\n"
44 private const val SPACED_NEW_LINE = "\n\n"
45 private const val UPDATE_INTERVAL = 250L // ms
46 private const val MIME_TYPE = "text/plain"
47 }
48
49 private val log = LinkedList<String>()
50 private var logUpdateTimer: Timer? = null
51 private var logger: Process? = null
52 private var toggleButton: MenuItem? = null
53
54 override fun onCreate(savedInstanceState: Bundle?) {
55 super.onCreate(savedInstanceState)
56 supportActionBar!!.setDisplayHomeAsUpEnabled(true)
57 layoutInflater.inflate(R.layout.page_viewlog, main_content)
58 toggleLogging(true)
59 }
60
61 override fun onCreateOptionsMenu(m: Menu): Boolean {
62 menuInflater.inflate(R.menu.menu_viewlog, m)
63 toggleButton = m.findItem(R.id.log_viewer_action_toggle)
64 return super.onCreateOptionsMenu(m)
65 }
66
67 override fun onSupportNavigateUp(): Boolean {
68 finish()
69 return true
70 }
71
72 override fun onDestroy() {
73 toggleLogging(false)
74 super.onDestroy()
75 }
76
77 fun share(@Suppress("UNUSED_PARAMETER") menuItem: MenuItem) {
78 synchronized(this) {
79 val logFragment = log.joinToString(NEW_LINE)
80 val shareIntent = Intent(Intent.ACTION_SEND)
81 .setType(MIME_TYPE)
82 .putExtra(Intent.EXTRA_TEXT, logFragment)
83
84 startActivity(Intent.createChooser(shareIntent, resources.getString(R.string.menu_share_log)))
85 }
86 }
87
88 fun toggleLogging(@Suppress("UNUSED_PARAMETER") menuItem: MenuItem) = toggleLogging(logger == null)
89
90 private fun toggleLogging(enable: Boolean) {
91 if (enable) {
92 disableUserScroll()
93 toggleButton?.setIcon(R.drawable.ic_pause_circle_outline_primary_24dp)
94 startLogging()
95 } else {
96 enableUserScroll()
97 toggleButton?.setIcon(R.drawable.ic_pause_circle_filled_primary_24dp)
98 stopLogging()
99 }
100 }
101
102 private fun startLogging(level: Int = LOG_LEVEL) {
103 appendLog(resources.getString(R.string.message_log_level_set, level))
104
105 TincVpnService.getCurrentNetName()?.let { netName ->
106 Tinc.log(netName, level).let { process ->
107 logger = process
108 Executor.runAsyncTask { captureLog(process) }
109 }
110 logUpdateTimer = timer(period = UPDATE_INTERVAL, action = { printLog() })
111 } ?: run {
112 appendLog(resources.getString(R.string.message_no_daemon))
113 toggleLogging(false)
114 }
115 }
116
117 private fun stopLogging() {
118 logger?.destroy()
119 logger = null
120 logUpdateTimer?.cancel()
121 logUpdateTimer?.purge()
122 logUpdateTimer = null
123 appendLog(resources.getString(R.string.message_log_paused))
124 printLog()
125 }
126
127 private fun captureLog(logger: Process) {
128 logger.inputStream?.use { inputStream ->
129 inputStream.bufferedReader().useLines { lines ->
130 lines.forEach { appendLog(it) }
131 }
132 }
133 }
134
135 private fun appendLog(line: String) = synchronized(this) {
136 if (log.size >= LOG_LINES) log.removeFirst()
137 log.addLast(line)
138 }
139
140 private fun printLog() = synchronized(this) {
141 log.joinToString(SPACED_NEW_LINE).let {
142 logview_text.post {
143 logview_text.text = it
144 logview_frame.post { logview_frame.fullScroll(View.FOCUS_DOWN) }
145 }
146 }
147 }
148
149 private fun enableUserScroll() {
150 logview_text.setTextIsSelectable(true)
151 logview_frame.setState(true)
152 }
153
154 private fun disableUserScroll() {
155 logview_text.setTextIsSelectable(false)
156 logview_frame.setState(false)
157 }
158
159 private fun ScrollView.setState(enabled: Boolean) {
160 if (enabled) setOnTouchListener(null) else setOnTouchListener { _, _ -> true }
161 logview_frame.isSmoothScrollingEnabled = enabled
162 logview_frame.isVerticalScrollBarEnabled = enabled
163 }
164}