Dan Ballard pushed to branch firefox-android-115.2.1-13.5-1 at The Tor Project / Applications / firefox-android
Commits:
-
2095a229
by clairehurst at 2024-04-17T00:27:30+00:00
4 changed files:
- + fenix/app/src/main/java/org/mozilla/fenix/tor/TorLogsComposeFragment.kt
- − fenix/app/src/main/java/org/mozilla/fenix/tor/TorLogsFragment.kt
- + fenix/app/src/main/java/org/mozilla/fenix/tor/TorLogsViewModel.kt
- fenix/app/src/main/res/navigation/nav_graph.xml
Changes:
1 | +/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2 | + * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
4 | + |
|
5 | +package org.mozilla.fenix.tor
|
|
6 | + |
|
7 | +import android.os.Bundle
|
|
8 | +import android.view.LayoutInflater
|
|
9 | +import android.view.View
|
|
10 | +import android.view.ViewGroup
|
|
11 | +import androidx.compose.foundation.layout.Column
|
|
12 | +import androidx.compose.foundation.layout.fillMaxSize
|
|
13 | +import androidx.compose.foundation.layout.fillMaxWidth
|
|
14 | +import androidx.compose.foundation.layout.padding
|
|
15 | +import androidx.compose.foundation.rememberScrollState
|
|
16 | +import androidx.compose.foundation.text.selection.DisableSelection
|
|
17 | +import androidx.compose.foundation.text.selection.SelectionContainer
|
|
18 | +import androidx.compose.foundation.verticalScroll
|
|
19 | +import androidx.compose.material.Text
|
|
20 | +import androidx.compose.runtime.Composable
|
|
21 | +import androidx.compose.runtime.Stable
|
|
22 | +import androidx.compose.ui.Modifier
|
|
23 | +import androidx.compose.ui.platform.ComposeView
|
|
24 | +import androidx.compose.ui.unit.dp
|
|
25 | +import androidx.fragment.app.Fragment
|
|
26 | +import androidx.fragment.app.viewModels
|
|
27 | +import mozilla.components.ui.colors.PhotonColors
|
|
28 | + |
|
29 | +class TorLogsComposeFragment : Fragment() {
|
|
30 | + private val viewModel: TorLogsViewModel by viewModels()
|
|
31 | + |
|
32 | + override fun onCreateView(
|
|
33 | + inflater: LayoutInflater,
|
|
34 | + container: ViewGroup?,
|
|
35 | + savedInstanceState: Bundle?,
|
|
36 | + ): View {
|
|
37 | + return ComposeView(requireContext()).apply {
|
|
38 | + setContent {
|
|
39 | + SelectionContainer {
|
|
40 | + Column(
|
|
41 | + // Column instead of LazyColumn so that you can select all the logs, and not just one "screen" at a time
|
|
42 | + // The logs won't be too big so loading them all instead of just whats visible shouldn't be a big deal
|
|
43 | + modifier = Modifier
|
|
44 | + .fillMaxSize()
|
|
45 | + .verticalScroll(state = rememberScrollState(), reverseScrolling = true),
|
|
46 | + ) {
|
|
47 | + for (log in viewModel.torLogs) {
|
|
48 | + LogRow(log = log)
|
|
49 | + }
|
|
50 | + }
|
|
51 | + }
|
|
52 | + }
|
|
53 | + }
|
|
54 | + }
|
|
55 | +}
|
|
56 | + |
|
57 | +@Composable
|
|
58 | +@Stable
|
|
59 | +fun LogRow(log: TorLog, modifier: Modifier = Modifier) {
|
|
60 | + Column(
|
|
61 | + modifier
|
|
62 | + .fillMaxWidth()
|
|
63 | + .padding(
|
|
64 | + start = 16.dp,
|
|
65 | + end = 16.dp,
|
|
66 | + bottom = 16.dp,
|
|
67 | + ),
|
|
68 | + ) {
|
|
69 | + DisableSelection {
|
|
70 | + Text(
|
|
71 | + text = log.timestamp.toString(),
|
|
72 | + color = PhotonColors.LightGrey40,
|
|
73 | + modifier = modifier
|
|
74 | + .padding(bottom = 4.dp),
|
|
75 | + )
|
|
76 | + }
|
|
77 | + Text(
|
|
78 | + text = log.text,
|
|
79 | + color = PhotonColors.LightGrey05,
|
|
80 | + )
|
|
81 | + }
|
|
82 | +} |
1 | -/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2 | - * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3 | - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
4 | - |
|
5 | -package org.mozilla.fenix.tor
|
|
6 | - |
|
7 | -import android.os.Bundle
|
|
8 | -import android.text.method.ScrollingMovementMethod
|
|
9 | -import android.view.LayoutInflater
|
|
10 | -import android.view.View
|
|
11 | -import android.view.ViewGroup
|
|
12 | -import androidx.fragment.app.Fragment
|
|
13 | -import org.mozilla.fenix.R
|
|
14 | -import org.mozilla.fenix.components.Components
|
|
15 | -import org.mozilla.fenix.databinding.TorBootstrapLoggerBinding
|
|
16 | -import org.mozilla.fenix.ext.requireComponents
|
|
17 | -import org.mozilla.fenix.tor.view.TorBootstrapLoggerViewHolder
|
|
18 | - |
|
19 | -class TorLogsFragment : Fragment(), TorLogs {
|
|
20 | - |
|
21 | - private var entries = mutableListOf<String>()
|
|
22 | - internal var _binding: TorBootstrapLoggerBinding? = null
|
|
23 | - private val binding get() = _binding!!
|
|
24 | - |
|
25 | - private var _components: Components? = null
|
|
26 | - private val components get() = _components!!
|
|
27 | - |
|
28 | - override fun onCreateView(
|
|
29 | - inflater: LayoutInflater,
|
|
30 | - container: ViewGroup?,
|
|
31 | - savedInstanceState: Bundle?,
|
|
32 | - ): View {
|
|
33 | - _binding = TorBootstrapLoggerBinding.inflate(inflater)
|
|
34 | - _components = requireComponents
|
|
35 | - |
|
36 | - components.torController.registerTorLogListener(this)
|
|
37 | - |
|
38 | - val currentEntries = components.torController.logEntries.filter { it.second != null }
|
|
39 | - .filter { !(it.second!!.startsWith("Circuit") && it.first == "ON") }
|
|
40 | - // Keep synchronized with format in onTorStatusUpdate
|
|
41 | - .flatMap { listOf("(${it.first}) '${it.second}'") }
|
|
42 | - val entriesLen = currentEntries.size
|
|
43 | - val subListOffset =
|
|
44 | - if (entriesLen > TorBootstrapLoggerViewHolder.MAX_NEW_ENTRIES) TorBootstrapLoggerViewHolder.MAX_NEW_ENTRIES else entriesLen
|
|
45 | - entries =
|
|
46 | - currentEntries.subList((entriesLen - subListOffset), entriesLen) as MutableList<String>
|
|
47 | - val initLog =
|
|
48 | - "---------------" + getString(R.string.tor_initializing_log) + "---------------"
|
|
49 | - entries.add(0, initLog)
|
|
50 | - |
|
51 | - with(binding.torBootstrapLogEntries) {
|
|
52 | - movementMethod = ScrollingMovementMethod()
|
|
53 | - text = formatLogEntries(entries)
|
|
54 | - }
|
|
55 | - |
|
56 | - |
|
57 | - return binding.root
|
|
58 | - }
|
|
59 | - |
|
60 | - // TODO on destroy unregiuster
|
|
61 | - |
|
62 | - private fun formatLogEntries(entries: List<String>) = entries.joinToString("\n")
|
|
63 | - |
|
64 | - override fun onLog(type: String?, message: String?) {
|
|
65 | - if (message == null || type == null) return
|
|
66 | - if (type == "ON" && type.startsWith("Circuit")) return
|
|
67 | - |
|
68 | - if (entries.size > TorBootstrapLoggerViewHolder.MAX_LINES) {
|
|
69 | - entries = entries.drop(1) as MutableList<String>
|
|
70 | - }
|
|
71 | - entries.add("($type) '$message'")
|
|
72 | - |
|
73 | - binding.torBootstrapLogEntries.text = formatLogEntries(entries)
|
|
74 | - }
|
|
75 | - |
|
76 | - override fun onStop() {
|
|
77 | - super.onStop()
|
|
78 | - components.torController.unregisterTorLogListener(this)
|
|
79 | - }
|
|
80 | - |
|
81 | -} |
1 | +/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2 | + * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
4 | + |
|
5 | +package org.mozilla.fenix.tor
|
|
6 | + |
|
7 | +import android.app.Application
|
|
8 | +import android.content.ClipData
|
|
9 | +import android.content.ClipboardManager
|
|
10 | +import android.content.Context
|
|
11 | +import android.os.Build
|
|
12 | +import android.widget.Toast
|
|
13 | +import androidx.compose.runtime.Stable
|
|
14 | +import androidx.lifecycle.AndroidViewModel
|
|
15 | +import org.mozilla.fenix.R
|
|
16 | +import org.mozilla.fenix.ext.components
|
|
17 | +import java.sql.Timestamp
|
|
18 | + |
|
19 | +class TorLogsViewModel(application: Application) : AndroidViewModel(application), TorLogs {
|
|
20 | + private val torController = application.components.torController
|
|
21 | + private val clipboardManager =
|
|
22 | + application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
23 | + |
|
24 | + val torLogs: MutableList<TorLog> = mutableListOf(
|
|
25 | + TorLog(
|
|
26 | + "---------------" + application.getString(R.string.tor_initializing_log) + "---------------",
|
|
27 | + ),
|
|
28 | + )
|
|
29 | + |
|
30 | + init {
|
|
31 | + setupClipboardListener()
|
|
32 | + torController.registerTorLogListener(this)
|
|
33 | + val currentEntries = torController.logEntries.filter { it.second != null }
|
|
34 | + .filter { !(it.second!!.startsWith("Circuit") && it.first == "ON") }
|
|
35 | + // Keep synchronized with format in onTorStatusUpdate
|
|
36 | + .flatMap { listOf(TorLog("[${it.first}] ${it.second}")) }
|
|
37 | + torLogs.addAll(currentEntries)
|
|
38 | + }
|
|
39 | + |
|
40 | + override fun onLog(type: String?, message: String?) {
|
|
41 | + if (message == null || type == null) return
|
|
42 | + if (type == "ON" && type.startsWith("Circuit")) return
|
|
43 | + |
|
44 | + torLogs.add(TorLog("[$type] $message"))
|
|
45 | + }
|
|
46 | + |
|
47 | + override fun onCleared() {
|
|
48 | + super.onCleared()
|
|
49 | + torController.unregisterTorLogListener(this)
|
|
50 | + }
|
|
51 | + |
|
52 | + private fun setupClipboardListener() {
|
|
53 | + clipboardManager.addPrimaryClipChangedListener {
|
|
54 | + // Only show a toast for Android 12 and lower.
|
|
55 | + // https://developer.android.com/develop/ui/views/touch-and-input/copy-paste#duplicate-notifications
|
|
56 | + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
|
|
57 | + Toast.makeText(
|
|
58 | + getApplication<Application>().applicationContext,
|
|
59 | + getApplication<Application>().getString(R.string.toast_copy_link_to_clipboard), // "Copied to clipboard" already translated
|
|
60 | + Toast.LENGTH_SHORT,
|
|
61 | + ).show()
|
|
62 | + }
|
|
63 | + }
|
|
64 | + }
|
|
65 | + |
|
66 | + fun copyAllLogsToClipboard() { // TODO add kebab menu in top right corner which includes option to "Copy all logs"
|
|
67 | + clipboardManager.setPrimaryClip(
|
|
68 | + ClipData.newPlainText(
|
|
69 | + "Copied Text",
|
|
70 | + getAllTorLogs(),
|
|
71 | + ),
|
|
72 | + )
|
|
73 | + }
|
|
74 | + |
|
75 | + private fun getAllTorLogs(): String {
|
|
76 | + var ret = ""
|
|
77 | + for (log in torLogs) {
|
|
78 | + ret += log.text + '\n'
|
|
79 | + }
|
|
80 | + return ret
|
|
81 | + }
|
|
82 | +}
|
|
83 | + |
|
84 | +@Stable
|
|
85 | +data class TorLog(
|
|
86 | + val text: String,
|
|
87 | + val timestamp: Timestamp = Timestamp(System.currentTimeMillis()),
|
|
88 | +) |
... | ... | @@ -976,8 +976,7 @@ |
976 | 976 | <fragment
|
977 | 977 | android:id="@+id/torBridgeConfigFragment"
|
978 | 978 | android:name="org.mozilla.fenix.settings.TorBridgeConfigFragment"
|
979 | - android:label="@string/preferences_tor_network_settings_bridge_config"
|
|
980 | - tools:layout="@layout/fragment_tor_bridge_config" />
|
|
979 | + android:label="@string/preferences_tor_network_settings_bridge_config" />
|
|
981 | 980 | <fragment
|
982 | 981 | android:id="@+id/torBetaConnectionFeaturesFragment"
|
983 | 982 | android:name="org.mozilla.fenix.tor.TorBetaConnectionFeaturesFragment"
|
... | ... | @@ -985,9 +984,8 @@ |
985 | 984 | tools:layout="@layout/tor_network_settings_beta_connection_features" />
|
986 | 985 | <fragment
|
987 | 986 | android:id="@+id/torLogsFragment"
|
988 | - android:name="org.mozilla.fenix.tor.TorLogsFragment"
|
|
989 | - android:label="Tor Logs"
|
|
990 | - tools:layout="@layout/tor_bootstrap_logger" />
|
|
987 | + android:name="org.mozilla.fenix.tor.TorLogsComposeFragment"
|
|
988 | + android:label="@string/preferences_tor_logs" />
|
|
991 | 989 | |
992 | 990 | <fragment
|
993 | 991 | android:id="@+id/trackingProtectionFragment"
|