Dan Ballard pushed to branch firefox-android-115.2.1-13.5-1 at The Tor Project / Applications / firefox-android
Commits:
-
cf78d7f5
by clairehurst at 2024-05-13T14:35:02-07:00
18 changed files:
- fenix/app/src/main/AndroidManifest.xml
- fenix/app/src/main/java/org/mozilla/fenix/FenixApplication.kt
- fenix/app/src/main/java/org/mozilla/fenix/components/Components.kt
- − fenix/app/src/main/java/org/mozilla/fenix/tor/TorBootstrapFragment.kt
- fenix/app/src/main/java/org/mozilla/fenix/tor/TorController.kt
- − fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerTAS.kt
- − fenix/app/src/main/java/org/mozilla/fenix/tor/controller/TorBootstrapController.kt
- − fenix/app/src/main/java/org/mozilla/fenix/tor/interactor/TorBootstrapInteractor.kt
- − fenix/app/src/main/java/org/mozilla/fenix/tor/view/TorBootstrapAdapter.kt
- − fenix/app/src/main/java/org/mozilla/fenix/tor/view/TorBootstrapConnectViewHolder.kt
- − fenix/app/src/main/java/org/mozilla/fenix/tor/view/TorBootstrapLoggerViewHolder.kt
- − fenix/app/src/main/java/org/mozilla/fenix/tor/view/TorBootstrapPagerAdapter.kt
- − fenix/app/src/main/java/org/mozilla/fenix/tor/view/TorBootstrapPagerViewHolder.kt
- − fenix/app/src/main/java/org/mozilla/fenix/tor/view/TorBootstrapView.kt
- − fenix/app/src/main/res/drawable/ic_tor_connect_computer_graphic.xml
- − fenix/app/src/main/res/layout/tor_bootstrap_connect.xml
- − fenix/app/src/main/res/layout/tor_bootstrap_logger.xml
- − fenix/app/src/main/res/layout/tor_bootstrap_pager.xml
Changes:
... | ... | @@ -367,12 +367,6 @@ |
367 | 367 | tools:node="remove" />
|
368 | 368 | </provider>
|
369 | 369 | <!-- Define Orbotservice's TorService -->
|
370 | - <service
|
|
371 | - android:name="org.torproject.android.service.TorService"
|
|
372 | - android:enabled="true"
|
|
373 | - android:exported="false"
|
|
374 | - android:stopWithTask="true">
|
|
375 | - </service>
|
|
376 | 370 | </application>
|
377 | 371 | |
378 | 372 | </manifest> |
... | ... | @@ -103,7 +103,6 @@ import org.mozilla.fenix.utils.Settings.Companion.TOP_SITES_PROVIDER_MAX_THRESHO |
103 | 103 | import org.mozilla.fenix.wallpapers.Wallpaper
|
104 | 104 | import java.util.UUID
|
105 | 105 | import java.util.concurrent.TimeUnit
|
106 | -import org.torproject.android.service.util.Prefs
|
|
107 | 106 | |
108 | 107 | /**
|
109 | 108 | *The main application class for Fenix. Records data to measure initialization performance.
|
... | ... | @@ -234,8 +233,8 @@ open class FenixApplication : LocaleAwareApplication(), Provider { |
234 | 233 | Log.addSink(FenixLogSink(logsDebug = Config.channel.isDebug, AndroidLogSink()))
|
235 | 234 | }
|
236 | 235 | |
237 | - @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage
|
|
238 | - open fun setupInMainProcessOnly() {
|
|
236 | + @VisibleForTesting
|
|
237 | + protected open fun setupInMainProcessOnly() {
|
|
239 | 238 | ProfilerMarkerFactProcessor.create { components.core.engine.profiler }.register()
|
240 | 239 | |
241 | 240 | run {
|
... | ... | @@ -273,11 +272,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider { |
273 | 272 | if (!megazordSetup.isCompleted) {
|
274 | 273 | runBlockingIncrement { megazordSetup.await() }
|
275 | 274 | }
|
276 | - |
|
277 | - GlobalScope.launch(Dispatchers.IO) {
|
|
278 | - // Give TAS the base Context
|
|
279 | - Prefs.setContext(applicationContext)
|
|
280 | - }
|
|
281 | 275 | }
|
282 | 276 | |
283 | 277 | setupLeakCanary()
|
... | ... | @@ -44,7 +44,6 @@ import org.mozilla.fenix.perf.StartupStateProvider |
44 | 44 | import org.mozilla.fenix.perf.StrictModeManager
|
45 | 45 | import org.mozilla.fenix.perf.lazyMonitored
|
46 | 46 | import org.mozilla.fenix.tor.TorControllerGV
|
47 | -import org.mozilla.fenix.tor.TorControllerTAS
|
|
48 | 47 | import org.mozilla.fenix.utils.ClipboardHandler
|
49 | 48 | import org.mozilla.fenix.utils.Settings
|
50 | 49 | import org.mozilla.fenix.wifi.WifiConnectionMonitor
|
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.core.view.children
|
|
12 | -import androidx.fragment.app.Fragment
|
|
13 | -import androidx.lifecycle.lifecycleScope
|
|
14 | -import org.mozilla.fenix.BuildConfig
|
|
15 | -import org.mozilla.fenix.databinding.FragmentHomeBinding
|
|
16 | -import org.mozilla.fenix.ext.requireComponents
|
|
17 | -import org.mozilla.fenix.tor.interactor.DefaultTorBootstrapInteractor
|
|
18 | -import org.mozilla.fenix.tor.interactor.TorBootstrapInteractor
|
|
19 | -import androidx.navigation.fragment.findNavController
|
|
20 | -import com.google.android.material.appbar.AppBarLayout
|
|
21 | -import org.mozilla.fenix.HomeActivity
|
|
22 | -import org.mozilla.fenix.R
|
|
23 | -import org.mozilla.fenix.ext.components
|
|
24 | -import org.mozilla.fenix.ext.hideToolbar
|
|
25 | -import org.mozilla.fenix.ext.settings
|
|
26 | -import org.mozilla.fenix.tor.controller.DefaultTorBootstrapController
|
|
27 | -import org.mozilla.fenix.tor.view.TorBootstrapView
|
|
28 | - |
|
29 | - |
|
30 | -@Suppress("TooManyFunctions", "LargeClass")
|
|
31 | -class TorBootstrapFragment : Fragment() {
|
|
32 | - |
|
33 | - internal var _binding: FragmentHomeBinding? = null
|
|
34 | - private val binding get() = _binding!!
|
|
35 | - |
|
36 | - |
|
37 | - private var torBootstrapView: TorBootstrapView? = null
|
|
38 | - |
|
39 | - private var _torBootstrapInteractor: TorBootstrapInteractor? = null
|
|
40 | - private val torBootstrapInteractor: TorBootstrapInteractor
|
|
41 | - get() = _torBootstrapInteractor!!
|
|
42 | - |
|
43 | - private lateinit var torBootstrapStatus: TorBootstrapStatus
|
|
44 | - |
|
45 | - |
|
46 | - @Suppress("LongMethod")
|
|
47 | - override fun onCreateView(
|
|
48 | - inflater: LayoutInflater,
|
|
49 | - container: ViewGroup?,
|
|
50 | - savedInstanceState: Bundle?,
|
|
51 | - ): View {
|
|
52 | - _binding = FragmentHomeBinding.inflate(inflater, container, false)
|
|
53 | - val components = requireComponents
|
|
54 | - |
|
55 | - torBootstrapStatus = TorBootstrapStatus(
|
|
56 | - !BuildConfig.DISABLE_TOR,
|
|
57 | - components.torController,
|
|
58 | - ::dispatchModeChanges
|
|
59 | - )
|
|
60 | - |
|
61 | - if (!torBootstrapStatus.isBootstrapping()) {
|
|
62 | - openHome()
|
|
63 | - }
|
|
64 | - |
|
65 | - // Was _sessionControlInteractor
|
|
66 | - _torBootstrapInteractor = DefaultTorBootstrapInteractor(
|
|
67 | - controller = DefaultTorBootstrapController(
|
|
68 | - handleTorBootstrapConnect = ::handleTorBootstrapConnect,
|
|
69 | - cancelTorBootstrap = ::cancelTorBootstrap,
|
|
70 | - initiateTorBootstrap = ::initiateTorBootstrap,
|
|
71 | - openTorNetworkSettings = ::openTorNetworkSettings
|
|
72 | - ),
|
|
73 | - )
|
|
74 | - |
|
75 | - torBootstrapView = TorBootstrapView(
|
|
76 | - containerView = binding.sessionControlRecyclerView,
|
|
77 | - viewLifecycleOwner = viewLifecycleOwner,
|
|
78 | - interactor = torBootstrapInteractor,
|
|
79 | - )
|
|
80 | - |
|
81 | - adjustHomeFragmentView()
|
|
82 | - updateSessionControlView()
|
|
83 | - showSessionControlView()
|
|
84 | - |
|
85 | - return binding.root
|
|
86 | - }
|
|
87 | - |
|
88 | - private fun updateSessionControlView() {
|
|
89 | - torBootstrapView?.update(requireContext().components.appStore.state)
|
|
90 | - }
|
|
91 | - |
|
92 | - // This function should be paired with showSessionControlView()
|
|
93 | - private fun adjustHomeFragmentView() {
|
|
94 | - binding.sessionControlRecyclerView.apply {
|
|
95 | - visibility = View.INVISIBLE
|
|
96 | - }
|
|
97 | - |
|
98 | - binding.sessionControlRecyclerView.apply {
|
|
99 | - setPadding(0, 0, 0, 0)
|
|
100 | - (layoutParams as ViewGroup.MarginLayoutParams).setMargins(0, 0, 0, 0)
|
|
101 | - }
|
|
102 | - |
|
103 | - binding.homeAppBar.apply {
|
|
104 | - visibility = View.GONE
|
|
105 | - |
|
106 | - // Reset this as SCROLL in case it was previously set as NO_SCROLL after bootstrap
|
|
107 | - children.forEach {
|
|
108 | - (it.layoutParams as AppBarLayout.LayoutParams).scrollFlags =
|
|
109 | - AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
|
|
110 | - }
|
|
111 | - }
|
|
112 | - binding.onionPatternImage.apply {
|
|
113 | - visibility = View.GONE
|
|
114 | - }
|
|
115 | - binding.toolbarLayout.apply {
|
|
116 | - visibility = View.GONE
|
|
117 | - }
|
|
118 | - }
|
|
119 | - |
|
120 | - // This function should be paired with adjustHomeFragmentView()
|
|
121 | - private fun showSessionControlView() {
|
|
122 | - binding.sessionControlRecyclerView.apply {
|
|
123 | - visibility = View.VISIBLE
|
|
124 | - }
|
|
125 | - }
|
|
126 | - |
|
127 | - private fun dispatchModeChanges(isBootstrapping: Boolean) {
|
|
128 | - //requireComponents.appStore.dispatch(AppAction.ModeChange(mode))
|
|
129 | - if (!isBootstrapping) {
|
|
130 | - openHome()
|
|
131 | - } else {
|
|
132 | - adjustHomeFragmentView()
|
|
133 | - updateSessionControlView()
|
|
134 | - showSessionControlView()
|
|
135 | - }
|
|
136 | - }
|
|
137 | - |
|
138 | - override fun onStop() {
|
|
139 | - super.onStop()
|
|
140 | - torBootstrapStatus.unregisterTorListener()
|
|
141 | - }
|
|
142 | - |
|
143 | - override fun onResume() {
|
|
144 | - super.onResume()
|
|
145 | - |
|
146 | - torBootstrapStatus.registerTorListener()
|
|
147 | - |
|
148 | - // fenix#40176: Ensure the Home fragment is rendered correctly when we resume.
|
|
149 | - val isBootstraping = torBootstrapStatus.isBootstrapping()
|
|
150 | - |
|
151 | - if (!isBootstraping) {
|
|
152 | - openHome()
|
|
153 | - }
|
|
154 | - |
|
155 | - adjustHomeFragmentView()
|
|
156 | - updateSessionControlView()
|
|
157 | - showSessionControlView()
|
|
158 | - |
|
159 | - hideToolbar()
|
|
160 | - |
|
161 | - // Whenever a tab is selected its last access timestamp is automatically updated by A-C.
|
|
162 | - // However, in the case of resuming the app to the home fragment, we already have an
|
|
163 | - // existing selected tab, but its last access timestamp is outdated. No action is
|
|
164 | - // triggered to cause an automatic update on warm start (no tab selection occurs). So we
|
|
165 | - // update it manually here.
|
|
166 | - requireComponents.useCases.sessionUseCases.updateLastAccess()
|
|
167 | - (requireActivity() as HomeActivity).navigateToHome()
|
|
168 | - }
|
|
169 | - |
|
170 | - private fun handleTorBootstrapConnect() {
|
|
171 | - requireComponents.torController.onTorConnecting()
|
|
172 | - }
|
|
173 | - |
|
174 | - private fun cancelTorBootstrap() {
|
|
175 | - requireComponents.torController.stopTor()
|
|
176 | - }
|
|
177 | - |
|
178 | - private fun initiateTorBootstrap(withDebugLogging: Boolean = false) {
|
|
179 | - requireComponents.torController.initiateTorBootstrap(lifecycleScope, withDebugLogging)
|
|
180 | - }
|
|
181 | - |
|
182 | - private fun openTorNetworkSettings() {
|
|
183 | - val directions =
|
|
184 | - TorBootstrapFragmentDirections.actionTorbootstrapFragmentToSettingsFragment(
|
|
185 | - requireContext().getString(R.string.pref_key_connection)
|
|
186 | - )
|
|
187 | - findNavController().navigate(directions)
|
|
188 | - }
|
|
189 | - |
|
190 | - private fun openHome() {
|
|
191 | - val directions =
|
|
192 | - TorBootstrapFragmentDirections
|
|
193 | - .actionStartupHome()
|
|
194 | - findNavController().navigate(directions)
|
|
195 | - }
|
|
196 | - |
|
197 | -} |
... | ... | @@ -29,25 +29,6 @@ internal enum class TorStatus(val status: String) { |
29 | 29 | ON("ON"),
|
30 | 30 | STOPPING("STOPPING"),
|
31 | 31 | UNKNOWN("UNKNOWN");
|
32 | - |
|
33 | - companion object {
|
|
34 | - fun fromString(status: String): TorStatus {
|
|
35 | - return when (status) {
|
|
36 | - "ON" -> ON
|
|
37 | - "STARTING" -> STARTING
|
|
38 | - "STOPPING" -> STOPPING
|
|
39 | - "OFF" -> OFF
|
|
40 | - else -> UNKNOWN
|
|
41 | - }
|
|
42 | - }
|
|
43 | - }
|
|
44 | - |
|
45 | - fun isOff() = this == OFF
|
|
46 | - fun isOn() = this == ON
|
|
47 | - fun isStarting() = this == STARTING
|
|
48 | - fun isStarted() = ((this == STARTING) || (this == ON))
|
|
49 | - fun isStopping() = this == STOPPING
|
|
50 | - fun isUnknown() = this == UNKNOWN
|
|
51 | 32 | }
|
52 | 33 | |
53 | 34 | interface TorController: TorEvents {
|
1 | -package org.mozilla.fenix.tor
|
|
2 | - |
|
3 | -import android.content.BroadcastReceiver
|
|
4 | -import android.content.Context
|
|
5 | -import android.content.Intent
|
|
6 | -import android.content.IntentFilter
|
|
7 | -import androidx.lifecycle.LifecycleCoroutineScope
|
|
8 | -import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
|
9 | -import kotlinx.coroutines.channels.Channel
|
|
10 | -import kotlinx.coroutines.launch
|
|
11 | -import kotlinx.coroutines.withTimeoutOrNull
|
|
12 | -import org.mozilla.fenix.BuildConfig
|
|
13 | -import org.torproject.android.service.TorService
|
|
14 | -import org.torproject.android.service.TorServiceConstants
|
|
15 | -import org.torproject.android.service.util.Prefs
|
|
16 | - |
|
17 | -@SuppressWarnings("TooManyFunctions")
|
|
18 | -class TorControllerTAS (private val context: Context): TorController {
|
|
19 | - private val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context)
|
|
20 | - private val entries = mutableListOf<Pair<String?, String?>>()
|
|
21 | - override val logEntries get() = entries
|
|
22 | - override var quickstart: Boolean = false // Stub, is never used
|
|
23 | - |
|
24 | - private var torListeners = mutableListOf<TorEvents>()
|
|
25 | - |
|
26 | - private var pendingRegisterChangeList = mutableListOf<Pair<TorEvents, Boolean>>()
|
|
27 | - private var lockTorListenersMutation = false
|
|
28 | - |
|
29 | - private var lastKnownStatus = TorStatus.OFF
|
|
30 | - private var wasTorBootstrapped = false
|
|
31 | - private var isTorRestarting = false
|
|
32 | - |
|
33 | - // This may be a lie
|
|
34 | - private var isTorBootstrapped = false
|
|
35 | - get() = ((lastKnownStatus == TorStatus.ON) && wasTorBootstrapped)
|
|
36 | - |
|
37 | - val isDebugLoggingEnabled get() =
|
|
38 | - context
|
|
39 | - .getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE)
|
|
40 | - .getBoolean("pref_enable_logging", false)
|
|
41 | - |
|
42 | - override val isStarting get() = lastKnownStatus.isStarting()
|
|
43 | - override val isRestarting get() = isTorRestarting
|
|
44 | - override val isBootstrapped get() = isTorBootstrapped
|
|
45 | - override val isConnected get() = (lastKnownStatus.isStarted() && !isTorRestarting)
|
|
46 | - |
|
47 | - override var bridgesEnabled: Boolean
|
|
48 | - get() = Prefs.bridgesEnabled()
|
|
49 | - set(value) { Prefs.putBridgesEnabled(value) }
|
|
50 | - |
|
51 | - override var bridgeTransport: TorBridgeTransportConfig
|
|
52 | - get() {
|
|
53 | - return TorBridgeTransportConfigUtil.getStringToBridgeTransport(
|
|
54 | - Prefs.getBridgesList()
|
|
55 | - )
|
|
56 | - }
|
|
57 | - set(value) {
|
|
58 | - if (value == TorBridgeTransportConfig.USER_PROVIDED) {
|
|
59 | - // Don't set the pref when the value is USER_PROVIDED because
|
|
60 | - // "user_provided" is not a valid bridge or transport type.
|
|
61 | - // This call should be followed by setting userProvidedBridges.
|
|
62 | - return
|
|
63 | - }
|
|
64 | - Prefs.setBridgesList(value.transportName)
|
|
65 | - }
|
|
66 | - |
|
67 | - override var userProvidedBridges: String?
|
|
68 | - get() {
|
|
69 | - val bridges = Prefs.getBridgesList()
|
|
70 | - val bridgeType =
|
|
71 | - TorBridgeTransportConfigUtil.getStringToBridgeTransport(bridges)
|
|
72 | - return when (bridgeType) {
|
|
73 | - TorBridgeTransportConfig.USER_PROVIDED -> bridges
|
|
74 | - else -> null
|
|
75 | - }
|
|
76 | - }
|
|
77 | - set(value) {
|
|
78 | - Prefs.setBridgesList(value)
|
|
79 | - }
|
|
80 | - |
|
81 | - override fun start() {
|
|
82 | - // Register receiver
|
|
83 | - lbm.registerReceiver(
|
|
84 | - persistentBroadcastReceiver,
|
|
85 | - IntentFilter(TorServiceConstants.ACTION_STATUS)
|
|
86 | - )
|
|
87 | - lbm.registerReceiver(
|
|
88 | - persistentBroadcastReceiver,
|
|
89 | - IntentFilter(TorServiceConstants.LOCAL_ACTION_LOG)
|
|
90 | - )
|
|
91 | - }
|
|
92 | - |
|
93 | - override fun stop() {
|
|
94 | - lbm.unregisterReceiver(persistentBroadcastReceiver)
|
|
95 | - }
|
|
96 | - |
|
97 | - private val persistentBroadcastReceiver = object : BroadcastReceiver() {
|
|
98 | - override fun onReceive(context: Context, intent: Intent) {
|
|
99 | - if (intent.action == null ||
|
|
100 | - (intent.action != TorServiceConstants.ACTION_STATUS &&
|
|
101 | - intent.action != TorServiceConstants.LOCAL_ACTION_LOG)
|
|
102 | - ) {
|
|
103 | - return
|
|
104 | - }
|
|
105 | - val action = intent.action
|
|
106 | - |
|
107 | - val logentry: String?
|
|
108 | - val status: String?
|
|
109 | - if (action == TorServiceConstants.LOCAL_ACTION_LOG) {
|
|
110 | - logentry = intent.getExtras()
|
|
111 | - ?.getCharSequence(TorServiceConstants.LOCAL_EXTRA_LOG) as? String?
|
|
112 | - } else {
|
|
113 | - logentry = null
|
|
114 | - }
|
|
115 | - |
|
116 | - status = intent.getExtras()
|
|
117 | - ?.getCharSequence(TorServiceConstants.EXTRA_STATUS) as? String?
|
|
118 | - |
|
119 | - if (logentry == null && status == null) {
|
|
120 | - return
|
|
121 | - }
|
|
122 | - |
|
123 | - onTorStatusUpdate(logentry, status)
|
|
124 | - |
|
125 | - if (status == null) {
|
|
126 | - return
|
|
127 | - }
|
|
128 | - |
|
129 | - val newStatus = TorStatus.fromString(status)
|
|
130 | - |
|
131 | - if (newStatus.isUnknown() && wasTorBootstrapped) {
|
|
132 | - stopTor()
|
|
133 | - }
|
|
134 | - |
|
135 | - entries.add(Pair(logentry, status))
|
|
136 | - |
|
137 | - if (logentry != null && logentry.contains(TorServiceConstants.TOR_CONTROL_PORT_MSG_BOOTSTRAP_DONE)) {
|
|
138 | - wasTorBootstrapped = true
|
|
139 | - onTorConnected()
|
|
140 | - }
|
|
141 | - |
|
142 | - if (lastKnownStatus.isStopping() && newStatus.isOff()) {
|
|
143 | - if (isTorRestarting) {
|
|
144 | - initiateTorBootstrap()
|
|
145 | - } else {
|
|
146 | - onTorStopped()
|
|
147 | - }
|
|
148 | - }
|
|
149 | - |
|
150 | - if (lastKnownStatus.isOff() && newStatus.isStarting()) {
|
|
151 | - isTorRestarting = false
|
|
152 | - }
|
|
153 | - |
|
154 | - lastKnownStatus = newStatus
|
|
155 | - }
|
|
156 | - }
|
|
157 | - |
|
158 | - override fun onTorConnecting() {
|
|
159 | - lockTorListenersMutation = true
|
|
160 | - torListeners.forEach { it.onTorConnecting() }
|
|
161 | - lockTorListenersMutation = false
|
|
162 | - |
|
163 | - handlePendingRegistrationChanges()
|
|
164 | - }
|
|
165 | - |
|
166 | - override fun onTorConnected() {
|
|
167 | - lockTorListenersMutation = true
|
|
168 | - torListeners.forEach { it.onTorConnected() }
|
|
169 | - lockTorListenersMutation = false
|
|
170 | - |
|
171 | - handlePendingRegistrationChanges()
|
|
172 | - }
|
|
173 | - |
|
174 | - override fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?) {
|
|
175 | - lockTorListenersMutation = true
|
|
176 | - torListeners.forEach { it.onTorStatusUpdate(entry, status) }
|
|
177 | - lockTorListenersMutation = false
|
|
178 | - |
|
179 | - handlePendingRegistrationChanges()
|
|
180 | - }
|
|
181 | - |
|
182 | - override fun onTorStopped() {
|
|
183 | - lockTorListenersMutation = true
|
|
184 | - torListeners.forEach { it.onTorStopped() }
|
|
185 | - lockTorListenersMutation = false
|
|
186 | - |
|
187 | - handlePendingRegistrationChanges()
|
|
188 | - }
|
|
189 | - |
|
190 | - override fun registerTorListener(l: TorEvents) {
|
|
191 | - if (torListeners.contains(l)) {
|
|
192 | - return
|
|
193 | - }
|
|
194 | - |
|
195 | - if (lockTorListenersMutation) {
|
|
196 | - pendingRegisterChangeList.add(Pair(l, true))
|
|
197 | - } else {
|
|
198 | - torListeners.add(l)
|
|
199 | - }
|
|
200 | - }
|
|
201 | - |
|
202 | - override fun unregisterTorListener(l: TorEvents) {
|
|
203 | - if (!torListeners.contains(l)) {
|
|
204 | - return
|
|
205 | - }
|
|
206 | - |
|
207 | - if (lockTorListenersMutation) {
|
|
208 | - pendingRegisterChangeList.add(Pair(l, false))
|
|
209 | - } else {
|
|
210 | - torListeners.remove(l)
|
|
211 | - }
|
|
212 | - }
|
|
213 | - |
|
214 | - override fun registerTorLogListener(l: TorLogs) {}
|
|
215 | - override fun unregisterTorLogListener(l: TorLogs) {}
|
|
216 | - |
|
217 | - private fun handlePendingRegistrationChanges() {
|
|
218 | - pendingRegisterChangeList.forEach {
|
|
219 | - if (it.second) {
|
|
220 | - registerTorListener(it.first)
|
|
221 | - } else {
|
|
222 | - unregisterTorListener(it.first)
|
|
223 | - }
|
|
224 | - }
|
|
225 | - |
|
226 | - pendingRegisterChangeList.clear()
|
|
227 | - }
|
|
228 | - |
|
229 | - /**
|
|
230 | - * Receive the current Tor status.
|
|
231 | - *
|
|
232 | - * Send a request for the current status and receive the response.
|
|
233 | - * Returns true if Tor is running, false otherwise.
|
|
234 | - *
|
|
235 | - */
|
|
236 | - private suspend fun checkTorIsStarted(): Boolean {
|
|
237 | - val channel = Channel<Boolean>()
|
|
238 | - |
|
239 | - // Register receiver
|
|
240 | - val lbm: LocalBroadcastManager = LocalBroadcastManager.getInstance(context)
|
|
241 | - val localBroadcastReceiver = object : BroadcastReceiver() {
|
|
242 | - override fun onReceive(context: Context, intent: Intent) {
|
|
243 | - val action = intent.action ?: return
|
|
244 | - // We only want ACTION_STATUS messages
|
|
245 | - if (action != TorServiceConstants.ACTION_STATUS) {
|
|
246 | - return
|
|
247 | - }
|
|
248 | - // The current status has the EXTRA_STATUS key
|
|
249 | - val currentStatus =
|
|
250 | - intent.getStringExtra(TorServiceConstants.EXTRA_STATUS)
|
|
251 | - channel.trySend(currentStatus === TorServiceConstants.STATUS_ON)
|
|
252 | - }
|
|
253 | - }
|
|
254 | - lbm.registerReceiver(
|
|
255 | - localBroadcastReceiver,
|
|
256 | - IntentFilter(TorServiceConstants.ACTION_STATUS)
|
|
257 | - )
|
|
258 | - |
|
259 | - // Request service status
|
|
260 | - sendServiceAction(TorServiceConstants.ACTION_STATUS)
|
|
261 | - |
|
262 | - // Wait for response and unregister receiver
|
|
263 | - var torIsStarted = false
|
|
264 | - withTimeoutOrNull(torServiceResponseTimeout) {
|
|
265 | - torIsStarted = channel.receive()
|
|
266 | - }
|
|
267 | - lbm.unregisterReceiver(localBroadcastReceiver)
|
|
268 | - return torIsStarted
|
|
269 | - }
|
|
270 | - |
|
271 | - override fun initiateTorBootstrap(lifecycleScope: LifecycleCoroutineScope?, withDebugLogging: Boolean) {
|
|
272 | - if (BuildConfig.DISABLE_TOR) {
|
|
273 | - return
|
|
274 | - }
|
|
275 | - |
|
276 | - context.getSharedPreferences("org.torproject.android_preferences", Context.MODE_PRIVATE)
|
|
277 | - .edit().putBoolean("pref_enable_logging", withDebugLogging).apply()
|
|
278 | - |
|
279 | - if (lifecycleScope == null) {
|
|
280 | - sendServiceAction(TorServiceConstants.ACTION_START)
|
|
281 | - } else {
|
|
282 | - lifecycleScope.launch {
|
|
283 | - val torNeedsStart = !checkTorIsStarted()
|
|
284 | - if (torNeedsStart) {
|
|
285 | - sendServiceAction(TorServiceConstants.ACTION_START)
|
|
286 | - }
|
|
287 | - }
|
|
288 | - }
|
|
289 | - }
|
|
290 | - |
|
291 | - override fun stopTor() {
|
|
292 | - if (BuildConfig.DISABLE_TOR) {
|
|
293 | - return
|
|
294 | - }
|
|
295 | - |
|
296 | - val torService = Intent(context, TorService::class.java)
|
|
297 | - context.stopService(torService)
|
|
298 | - }
|
|
299 | - |
|
300 | - override fun setTorStopped() {
|
|
301 | - lastKnownStatus = TorStatus.OFF
|
|
302 | - onTorStopped()
|
|
303 | - }
|
|
304 | - |
|
305 | - override fun restartTor() {
|
|
306 | - // tor-android-service doesn't dynamically update the torrc file,
|
|
307 | - // and it doesn't use SETCONF, so we completely restart the service.
|
|
308 | - // However, don't restart if we aren't started and we weren't
|
|
309 | - // previously started.
|
|
310 | - if (!lastKnownStatus.isStarted() && !wasTorBootstrapped) {
|
|
311 | - return
|
|
312 | - }
|
|
313 | - |
|
314 | - if (!lastKnownStatus.isStarted() && wasTorBootstrapped) {
|
|
315 | - // If we aren't started, but we were previously bootstrapped,
|
|
316 | - // then we handle a "restart" request as a "start" restart
|
|
317 | - initiateTorBootstrap()
|
|
318 | - } else {
|
|
319 | - // |isTorRestarting| tracks the state of restart. When we receive an |OFF| state
|
|
320 | - // from TorService in persistentBroadcastReceiver::onReceive we restart the Tor
|
|
321 | - // service.
|
|
322 | - isTorRestarting = true
|
|
323 | - stopTor()
|
|
324 | - }
|
|
325 | - }
|
|
326 | - |
|
327 | - private fun sendServiceAction(action: String) {
|
|
328 | - val torServiceStatus = Intent(context, TorService::class.java)
|
|
329 | - torServiceStatus.action = action
|
|
330 | - context.startService(torServiceStatus)
|
|
331 | - }
|
|
332 | - |
|
333 | - companion object {
|
|
334 | - const val torServiceResponseTimeout = 5000L
|
|
335 | - }
|
|
336 | - |
|
337 | - // Compat with TorControlGV Stubs
|
|
338 | - |
|
339 | - override fun getLastErrorState() : TorError? {
|
|
340 | - return null;
|
|
341 | - }
|
|
342 | -} |
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.controller
|
|
6 | - |
|
7 | -import org.mozilla.fenix.tor.interactor.TorBootstrapInteractor
|
|
8 | - |
|
9 | -interface TorBootstrapController {
|
|
10 | - /**
|
|
11 | - * @see [TorBootstrapInteractor.onTorBootstrapConnectClicked]
|
|
12 | - */
|
|
13 | - fun handleTorBootstrapConnectClicked()
|
|
14 | - |
|
15 | - /**
|
|
16 | - * @see [TorBootstrapInteractor.onTorStopBootstrapping]
|
|
17 | - */
|
|
18 | - fun handleTorStopBootstrapping()
|
|
19 | - |
|
20 | - /**
|
|
21 | - * @see [TorBootstrapInteractor.onTorStartBootstrapping]
|
|
22 | - */
|
|
23 | - fun handleTorStartBootstrapping()
|
|
24 | - |
|
25 | - /**
|
|
26 | - * @see [TorBootstrapInteractor.onTorStartDebugBootstrapping]
|
|
27 | - */
|
|
28 | - fun handleTorStartDebugBootstrapping()
|
|
29 | - |
|
30 | - /**
|
|
31 | - * @see [TorBootstrapInteractor.onTorBootstrapNetworkSettingsClicked]
|
|
32 | - */
|
|
33 | - fun handleTorNetworkSettingsClicked()
|
|
34 | - |
|
35 | - |
|
36 | -}
|
|
37 | - |
|
38 | -class DefaultTorBootstrapController(
|
|
39 | - private val handleTorBootstrapConnect: () -> Unit,
|
|
40 | - private val initiateTorBootstrap: (Boolean) -> Unit,
|
|
41 | - private val cancelTorBootstrap: () -> Unit,
|
|
42 | - private val openTorNetworkSettings: () -> Unit
|
|
43 | -) : TorBootstrapController {
|
|
44 | - override fun handleTorBootstrapConnectClicked() {
|
|
45 | - handleTorBootstrapConnect()
|
|
46 | - }
|
|
47 | - |
|
48 | - override fun handleTorStopBootstrapping() {
|
|
49 | - cancelTorBootstrap()
|
|
50 | - }
|
|
51 | - |
|
52 | - override fun handleTorStartBootstrapping() {
|
|
53 | - initiateTorBootstrap(false)
|
|
54 | - }
|
|
55 | - |
|
56 | - override fun handleTorStartDebugBootstrapping() {
|
|
57 | - initiateTorBootstrap(true)
|
|
58 | - }
|
|
59 | - |
|
60 | - override fun handleTorNetworkSettingsClicked() {
|
|
61 | - openTorNetworkSettings()
|
|
62 | - }
|
|
63 | -} |
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.interactor
|
|
6 | - |
|
7 | -import org.mozilla.fenix.tor.controller.TorBootstrapController
|
|
8 | - |
|
9 | -interface TorBootstrapInteractor {
|
|
10 | - /**
|
|
11 | - * Initiates Tor bootstrapping. Called when a user clicks on the "Connect" button.
|
|
12 | - */
|
|
13 | - fun onTorBootstrapConnectClicked()
|
|
14 | - |
|
15 | - /**
|
|
16 | - * Initiates Tor bootstrapping. Called when a user clicks on the "Connect" button.
|
|
17 | - */
|
|
18 | - fun onTorStartBootstrapping()
|
|
19 | - |
|
20 | - /**
|
|
21 | - * Stop Tor bootstrapping. Called when a user clicks on the "settings" cog/button.
|
|
22 | - */
|
|
23 | - fun onTorStopBootstrapping()
|
|
24 | - |
|
25 | - /**
|
|
26 | - * Initiates Tor bootstrapping with debug logging. Called when bootstrapping fails with
|
|
27 | - * the control.txt file not existing.
|
|
28 | - */
|
|
29 | - fun onTorStartDebugBootstrapping()
|
|
30 | - |
|
31 | - /**
|
|
32 | - * Open Tor Network Settings preference screen
|
|
33 | - */
|
|
34 | - fun onTorBootstrapNetworkSettingsClicked()
|
|
35 | -}
|
|
36 | - |
|
37 | -class DefaultTorBootstrapInteractor(
|
|
38 | - private val controller: TorBootstrapController,
|
|
39 | -) : TorBootstrapInteractor {
|
|
40 | - |
|
41 | - override fun onTorBootstrapConnectClicked() {
|
|
42 | - controller.handleTorBootstrapConnectClicked()
|
|
43 | - }
|
|
44 | - |
|
45 | - override fun onTorStopBootstrapping() {
|
|
46 | - controller.handleTorStopBootstrapping()
|
|
47 | - }
|
|
48 | - |
|
49 | - override fun onTorStartBootstrapping() {
|
|
50 | - controller.handleTorStartBootstrapping()
|
|
51 | - }
|
|
52 | - |
|
53 | - override fun onTorStartDebugBootstrapping() {
|
|
54 | - controller.handleTorStartDebugBootstrapping()
|
|
55 | - }
|
|
56 | - |
|
57 | - override fun onTorBootstrapNetworkSettingsClicked() {
|
|
58 | - controller.handleTorNetworkSettingsClicked()
|
|
59 | - }
|
|
60 | -} |
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.view
|
|
6 | - |
|
7 | -import android.view.LayoutInflater
|
|
8 | -import android.view.ViewGroup
|
|
9 | -import androidx.annotation.LayoutRes
|
|
10 | -import androidx.lifecycle.LifecycleOwner
|
|
11 | -import androidx.recyclerview.widget.DiffUtil
|
|
12 | -import androidx.recyclerview.widget.ListAdapter
|
|
13 | -import androidx.recyclerview.widget.RecyclerView
|
|
14 | -import org.mozilla.fenix.components.Components
|
|
15 | -import org.mozilla.fenix.home.topsites.TopSitePagerViewHolder
|
|
16 | -import org.mozilla.fenix.tor.interactor.TorBootstrapInteractor
|
|
17 | - |
|
18 | -sealed class AdapterItem(@LayoutRes val viewType: Int) {
|
|
19 | - object TorBootstrap : AdapterItem(TorBootstrapPagerViewHolder.LAYOUT_ID)
|
|
20 | - |
|
21 | - |
|
22 | - open fun sameAs(other: AdapterItem) = this::class == other::class
|
|
23 | - open fun getChangePayload(newItem: AdapterItem): Any? = null
|
|
24 | - open fun contentsSameAs(other: AdapterItem) = this::class == other::class
|
|
25 | - |
|
26 | -}
|
|
27 | - |
|
28 | -class AdapterItemDiffCallback : DiffUtil.ItemCallback<AdapterItem>() {
|
|
29 | - override fun areItemsTheSame(oldItem: AdapterItem, newItem: AdapterItem) =
|
|
30 | - oldItem.sameAs(newItem)
|
|
31 | - |
|
32 | - @Suppress("DiffUtilEquals")
|
|
33 | - override fun areContentsTheSame(oldItem: AdapterItem, newItem: AdapterItem) =
|
|
34 | - oldItem.contentsSameAs(newItem)
|
|
35 | - |
|
36 | - override fun getChangePayload(oldItem: AdapterItem, newItem: AdapterItem): Any? {
|
|
37 | - return oldItem.getChangePayload(newItem) ?: return super.getChangePayload(oldItem, newItem)
|
|
38 | - }
|
|
39 | -}
|
|
40 | - |
|
41 | - |
|
42 | - |
|
43 | -class TorBootstrapAdapter(
|
|
44 | - private val interactor: TorBootstrapInteractor,
|
|
45 | - private val viewLifecycleOwner: LifecycleOwner,
|
|
46 | - private val components: Components,
|
|
47 | -) : ListAdapter<AdapterItem, RecyclerView.ViewHolder>(AdapterItemDiffCallback()) {
|
|
48 | - |
|
49 | - // This method triggers the ComplexMethod lint error when in fact it's quite simple.
|
|
50 | - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
|
51 | - val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
|
52 | - return when (viewType) {
|
|
53 | - TorBootstrapPagerViewHolder.LAYOUT_ID -> TorBootstrapPagerViewHolder(
|
|
54 | - view,
|
|
55 | - components,
|
|
56 | - interactor
|
|
57 | - )
|
|
58 | - else -> throw IllegalStateException()
|
|
59 | - }
|
|
60 | - }
|
|
61 | - |
|
62 | - override fun getItemViewType(position: Int) = getItem(position).viewType
|
|
63 | - |
|
64 | - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList<Any>) {
|
|
65 | - if (payloads.isEmpty()) {
|
|
66 | - onBindViewHolder(holder, position)
|
|
67 | - } else {
|
|
68 | - when (holder) {
|
|
69 | - is TopSitePagerViewHolder -> {
|
|
70 | - if (payloads[0] is org.mozilla.fenix.home.sessioncontrol.AdapterItem.TopSitePagerPayload) {
|
|
71 | - val payload = payloads[0] as org.mozilla.fenix.home.sessioncontrol.AdapterItem.TopSitePagerPayload
|
|
72 | - holder.update(payload)
|
|
73 | - }
|
|
74 | - }
|
|
75 | - }
|
|
76 | - }
|
|
77 | - }
|
|
78 | - |
|
79 | - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
|
80 | - // no-op. This ViewHolder receives the HomeStore as argument and will observe that
|
|
81 | - // without the need for us to manually update from here the data to be displayed.
|
|
82 | - }
|
|
83 | -} |
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.view
|
|
6 | - |
|
7 | -import android.view.View
|
|
8 | -import androidx.recyclerview.widget.RecyclerView
|
|
9 | -import org.mozilla.fenix.R
|
|
10 | -import org.mozilla.fenix.components.Components
|
|
11 | -import org.mozilla.fenix.databinding.TorBootstrapConnectBinding
|
|
12 | -import org.mozilla.fenix.tor.TorEvents
|
|
13 | -import org.mozilla.fenix.tor.interactor.TorBootstrapInteractor
|
|
14 | - |
|
15 | -class TorBootstrapConnectViewHolder(
|
|
16 | - private val view: View,
|
|
17 | - private val components: Components,
|
|
18 | - private val interactor: TorBootstrapInteractor
|
|
19 | -) : RecyclerView.ViewHolder(view), TorEvents {
|
|
20 | - |
|
21 | - var binding: TorBootstrapConnectBinding
|
|
22 | - |
|
23 | - init {
|
|
24 | - binding = TorBootstrapConnectBinding.bind(view)
|
|
25 | - |
|
26 | - with(binding.torBootstrapNetworkSettingsButton) {
|
|
27 | - setOnClickListener {
|
|
28 | - interactor.onTorStopBootstrapping()
|
|
29 | - interactor.onTorBootstrapNetworkSettingsClicked()
|
|
30 | - |
|
31 | - with(binding.torBootstrapProgress) {
|
|
32 | - visibility = View.INVISIBLE
|
|
33 | - }
|
|
34 | - |
|
35 | - with(binding.torBootstrapConnectButton) {
|
|
36 | - visibility = View.VISIBLE
|
|
37 | - }
|
|
38 | - }
|
|
39 | - }
|
|
40 | - |
|
41 | - with(binding.torBootstrapConnectButton) {
|
|
42 | - setOnClickListener {
|
|
43 | - interactor.onTorBootstrapConnectClicked()
|
|
44 | - interactor.onTorStartBootstrapping()
|
|
45 | - |
|
46 | - visibility = View.INVISIBLE
|
|
47 | - |
|
48 | - with(binding.torBootstrapProgress) {
|
|
49 | - visibility = View.VISIBLE
|
|
50 | - }
|
|
51 | - }
|
|
52 | - }
|
|
53 | - |
|
54 | - components.torController.registerTorListener(this)
|
|
55 | - }
|
|
56 | - |
|
57 | - @SuppressWarnings("EmptyFunctionBlock")
|
|
58 | - override fun onTorConnecting() {
|
|
59 | - }
|
|
60 | - |
|
61 | - override fun onTorConnected() {
|
|
62 | - components.torController.unregisterTorListener(this)
|
|
63 | - }
|
|
64 | - |
|
65 | - @SuppressWarnings("EmptyFunctionBlock")
|
|
66 | - override fun onTorStopped() {
|
|
67 | - }
|
|
68 | - |
|
69 | - override fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?) {
|
|
70 | - if (entry == null) return
|
|
71 | - |
|
72 | - binding.torBootstrapStatusMessage.text = entry
|
|
73 | - if (entry.startsWith(BOOTSTRAPPED_PREFIX)) {
|
|
74 | - val percentIdx = entry.indexOf("%")
|
|
75 | - val percent = entry.substring(
|
|
76 | - BOOTSTRAPPED_PREFIX.length,
|
|
77 | - percentIdx
|
|
78 | - )
|
|
79 | - with(binding.torBootstrapProgress) {
|
|
80 | - this.progress = percent.toInt()
|
|
81 | - }
|
|
82 | - }
|
|
83 | - }
|
|
84 | - |
|
85 | - companion object {
|
|
86 | - const val LAYOUT_ID = R.layout.tor_bootstrap_connect
|
|
87 | - const val BOOTSTRAPPED_PREFIX = "NOTICE: Bootstrapped "
|
|
88 | - }
|
|
89 | -} |
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.view
|
|
6 | - |
|
7 | -import android.text.method.ScrollingMovementMethod
|
|
8 | -import android.view.View
|
|
9 | -import androidx.recyclerview.widget.RecyclerView
|
|
10 | -import org.mozilla.fenix.R
|
|
11 | -import org.mozilla.fenix.components.Components
|
|
12 | -import org.mozilla.fenix.databinding.TorBootstrapLoggerBinding
|
|
13 | -import org.mozilla.fenix.tor.TorEvents
|
|
14 | - |
|
15 | -class TorBootstrapLoggerViewHolder(
|
|
16 | - private val view: View,
|
|
17 | - private val components: Components
|
|
18 | - ) : RecyclerView.ViewHolder(view), TorEvents {
|
|
19 | - |
|
20 | - private var entries = mutableListOf<String>()
|
|
21 | - private var binding: TorBootstrapLoggerBinding
|
|
22 | - |
|
23 | - init {
|
|
24 | - binding = TorBootstrapLoggerBinding.bind(view)
|
|
25 | - components.torController.registerTorListener(this)
|
|
26 | - |
|
27 | - val currentEntries = components.torController.logEntries
|
|
28 | - .filter { it.first != null }
|
|
29 | - .filter { !(it.first!!.startsWith("Circuit") && it.second == "ON") }
|
|
30 | - // Keep synchronized with format in onTorStatusUpdate
|
|
31 | - .flatMap { listOf("(${it.second}) '${it.first}'") }
|
|
32 | - val entriesLen = currentEntries.size
|
|
33 | - val subListOffset = if (entriesLen > MAX_NEW_ENTRIES) MAX_NEW_ENTRIES else entriesLen
|
|
34 | - entries = currentEntries.subList((entriesLen - subListOffset), entriesLen) as MutableList<String>
|
|
35 | - val initLog = "---------------" + view.resources.getString(R.string.tor_initializing_log) + "---------------"
|
|
36 | - entries.add(0, initLog)
|
|
37 | - |
|
38 | - with(binding.torBootstrapLogEntries) {
|
|
39 | - movementMethod = ScrollingMovementMethod()
|
|
40 | - text = formatLogEntries(entries)
|
|
41 | - }
|
|
42 | - }
|
|
43 | - |
|
44 | - private fun formatLogEntries(entries: List<String>) = entries.joinToString("\n")
|
|
45 | - |
|
46 | - @SuppressWarnings("EmptyFunctionBlock")
|
|
47 | - override fun onTorConnecting() {
|
|
48 | - }
|
|
49 | - |
|
50 | - override fun onTorConnected() {
|
|
51 | - components.torController.unregisterTorListener(this)
|
|
52 | - }
|
|
53 | - |
|
54 | - @SuppressWarnings("EmptyFunctionBlock")
|
|
55 | - override fun onTorStopped() {
|
|
56 | - }
|
|
57 | - |
|
58 | - override fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?) {
|
|
59 | - if (status == null || entry == null) return
|
|
60 | - if (status == "ON" && entry.startsWith("Circuit")) return
|
|
61 | - |
|
62 | - if (entries.size > MAX_LINES) {
|
|
63 | - entries = entries.drop(1) as MutableList<String>
|
|
64 | - }
|
|
65 | - entries.add("($status) '$entry'")
|
|
66 | - |
|
67 | - binding.torBootstrapLogEntries.text = formatLogEntries(entries)
|
|
68 | - }
|
|
69 | - |
|
70 | - companion object {
|
|
71 | - const val LAYOUT_ID = R.layout.tor_bootstrap_logger
|
|
72 | - const val MAX_NEW_ENTRIES = 24
|
|
73 | - const val MAX_LINES = 25
|
|
74 | - }
|
|
75 | - |
|
76 | -} |
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.view
|
|
6 | - |
|
7 | -import android.view.LayoutInflater
|
|
8 | -import android.view.ViewGroup
|
|
9 | -import androidx.recyclerview.widget.RecyclerView
|
|
10 | -import org.mozilla.fenix.components.Components
|
|
11 | -import org.mozilla.fenix.tor.interactor.TorBootstrapInteractor
|
|
12 | - |
|
13 | -class TorBootstrapPagerAdapter(
|
|
14 | - private val components: Components,
|
|
15 | - private val interactor: TorBootstrapInteractor
|
|
16 | -) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
|
17 | - |
|
18 | - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
|
19 | - if (viewType == BOOTSTRAP_UI_PAGE_TYPE) {
|
|
20 | - val viewDVH = LayoutInflater.from(parent.context)
|
|
21 | - .inflate(TorBootstrapConnectViewHolder.LAYOUT_ID, parent, false)
|
|
22 | - return TorBootstrapConnectViewHolder(viewDVH, components, interactor)
|
|
23 | - } else {
|
|
24 | - val viewLVH = LayoutInflater.from(parent.context)
|
|
25 | - .inflate(TorBootstrapLoggerViewHolder.LAYOUT_ID, parent, false)
|
|
26 | - return TorBootstrapLoggerViewHolder(viewLVH, components)
|
|
27 | - }
|
|
28 | - }
|
|
29 | - |
|
30 | - @SuppressWarnings("EmptyFunctionBlock")
|
|
31 | - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
|
32 | - }
|
|
33 | - |
|
34 | - override fun getItemViewType(position: Int): Int = position
|
|
35 | - |
|
36 | - override fun getItemCount(): Int = BOOTSTRAP_PAGE_COUNT
|
|
37 | - |
|
38 | - companion object {
|
|
39 | - const val BOOTSTRAP_UI_PAGE_TYPE = 0
|
|
40 | - const val BOOTSTRAP_LOG_PAGE_TYPE = 1
|
|
41 | - const val BOOTSTRAP_PAGE_COUNT = 2
|
|
42 | - }
|
|
43 | -} |
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.view
|
|
6 | - |
|
7 | -import android.view.View
|
|
8 | -import androidx.recyclerview.widget.RecyclerView
|
|
9 | -import org.mozilla.fenix.R
|
|
10 | -import org.mozilla.fenix.components.Components
|
|
11 | -import org.mozilla.fenix.databinding.TorBootstrapPagerBinding
|
|
12 | -import org.mozilla.fenix.tor.interactor.TorBootstrapInteractor
|
|
13 | - |
|
14 | -class TorBootstrapPagerViewHolder(
|
|
15 | - view: View,
|
|
16 | - components: Components,
|
|
17 | - interactor: TorBootstrapInteractor
|
|
18 | - ) : RecyclerView.ViewHolder(view) {
|
|
19 | - |
|
20 | - private val bootstrapPagerAdapter = TorBootstrapPagerAdapter(components, interactor)
|
|
21 | - |
|
22 | - init {
|
|
23 | - val binding = TorBootstrapPagerBinding.bind(view)
|
|
24 | - binding.bootstrapPager.apply {
|
|
25 | - adapter = bootstrapPagerAdapter
|
|
26 | - }
|
|
27 | - }
|
|
28 | - |
|
29 | - companion object {
|
|
30 | - const val LAYOUT_ID = R.layout.tor_bootstrap_pager
|
|
31 | - }
|
|
32 | -} |
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.view
|
|
6 | - |
|
7 | -import androidx.lifecycle.LifecycleOwner
|
|
8 | -import androidx.recyclerview.widget.LinearLayoutManager
|
|
9 | -import androidx.recyclerview.widget.RecyclerView
|
|
10 | -import org.mozilla.fenix.components.appstate.AppState
|
|
11 | -import org.mozilla.fenix.ext.components
|
|
12 | -import org.mozilla.fenix.tor.interactor.TorBootstrapInteractor
|
|
13 | - |
|
14 | - |
|
15 | -class TorBootstrapView(
|
|
16 | - containerView: RecyclerView,
|
|
17 | - viewLifecycleOwner: LifecycleOwner,
|
|
18 | - interactor: TorBootstrapInteractor,
|
|
19 | -) {
|
|
20 | - |
|
21 | - val view: RecyclerView = containerView //as RecyclerView
|
|
22 | - |
|
23 | - private fun bootstrapAdapterItems() = listOf(AdapterItem.TorBootstrap)
|
|
24 | - |
|
25 | - private val torBootstrapAdapter = TorBootstrapAdapter(
|
|
26 | - interactor,
|
|
27 | - viewLifecycleOwner,
|
|
28 | - containerView.context.components,
|
|
29 | - )
|
|
30 | - |
|
31 | - //private val torBootstrapAdapter =
|
|
32 | - // TorBootstrapAdapter(interactor, containerView.context.components)
|
|
33 | - //private val torBootstrapAdapter = TorBootstrapPagerAdapter(containerView.context.components, interactor)
|
|
34 | - |
|
35 | - init {
|
|
36 | - containerView.apply {
|
|
37 | - adapter = torBootstrapAdapter
|
|
38 | - layoutManager = LinearLayoutManager(containerView.context)
|
|
39 | - }
|
|
40 | - }
|
|
41 | - |
|
42 | - private fun AppState.toAdapterList(): List<AdapterItem> {
|
|
43 | - return bootstrapAdapterItems()
|
|
44 | - }
|
|
45 | - |
|
46 | - fun update(state: AppState) {
|
|
47 | - torBootstrapAdapter.submitList(state.toAdapterList())
|
|
48 | - }
|
|
49 | -} |
1 | -<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2 | - xmlns:aapt="http://schemas.android.com/aapt"
|
|
3 | - android:width="302dp"
|
|
4 | - android:height="263dp"
|
|
5 | - android:viewportWidth="302"
|
|
6 | - android:viewportHeight="263">
|
|
7 | - <path
|
|
8 | - android:pathData="M279.49,222.64l-94.22,-0l-0,-79.61l94.22,-0z"
|
|
9 | - android:strokeWidth="1"
|
|
10 | - android:fillType="nonZero"
|
|
11 | - android:strokeColor="#00000000">
|
|
12 | - <aapt:attr name="android:fillColor">
|
|
13 | - <gradient
|
|
14 | - android:startY="203.70642"
|
|
15 | - android:startX="231.9"
|
|
16 | - android:endY="160.04314"
|
|
17 | - android:endX="232.90999"
|
|
18 | - android:type="linear">
|
|
19 | - <item android:offset="0.08" android:color="#FF7E4696"/>
|
|
20 | - <item android:offset="0.39" android:color="#9B7E4696"/>
|
|
21 | - <item android:offset="0.85" android:color="#007E4696"/>
|
|
22 | - </gradient>
|
|
23 | - </aapt:attr>
|
|
24 | - </path>
|
|
25 | - <path
|
|
26 | - android:pathData="M112.74,217.05l-102.32,-0l-0,-102.32l102.32,-0z"
|
|
27 | - android:strokeWidth="1"
|
|
28 | - android:fillType="nonZero"
|
|
29 | - android:strokeColor="#00000000">
|
|
30 | - <aapt:attr name="android:fillColor">
|
|
31 | - <gradient
|
|
32 | - android:startY="203.36"
|
|
33 | - android:startX="60.96"
|
|
34 | - android:endY="124.990005"
|
|
35 | - android:endX="62.249996"
|
|
36 | - android:type="linear">
|
|
37 | - <item android:offset="0.08" android:color="#FF00D9B5"/>
|
|
38 | - <item android:offset="0.3" android:color="#BA00D9B5"/>
|
|
39 | - <item android:offset="0.8" android:color="#1100D9B5"/>
|
|
40 | - <item android:offset="0.85" android:color="#0000D9B5"/>
|
|
41 | - </gradient>
|
|
42 | - </aapt:attr>
|
|
43 | - </path>
|
|
44 | - <path
|
|
45 | - android:pathData="M58,1L183.49,1C186.526,1.016 188.984,3.474 189,6.51L189,100.25L52.47,100.25L52.47,6.51C52.486,3.466 54.956,1.005 58,1Z"
|
|
46 | - android:strokeLineJoin="round"
|
|
47 | - android:strokeWidth="1.76"
|
|
48 | - android:fillColor="#F0D4FD"
|
|
49 | - android:strokeColor="#65318E"
|
|
50 | - android:fillType="nonZero"
|
|
51 | - android:strokeLineCap="round"/>
|
|
52 | - <path
|
|
53 | - android:pathData="M60.55,8.87h120.41v108.53h-120.41z"
|
|
54 | - android:strokeLineJoin="round"
|
|
55 | - android:strokeWidth="1.76"
|
|
56 | - android:fillColor="#FFFFFF"
|
|
57 | - android:strokeColor="#490260"
|
|
58 | - android:fillType="nonZero"
|
|
59 | - android:strokeLineCap="round"/>
|
|
60 | - <path
|
|
61 | - android:pathData="M60.55,8.87h120.41v108.53h-120.41z"
|
|
62 | - android:strokeWidth="1"
|
|
63 | - android:fillType="nonZero"
|
|
64 | - android:strokeColor="#00000000">
|
|
65 | - <aapt:attr name="android:fillColor">
|
|
66 | - <gradient
|
|
67 | - android:startY="-56.010002"
|
|
68 | - android:startX="120.75"
|
|
69 | - android:endY="120.520004"
|
|
70 | - android:endX="120.75"
|
|
71 | - android:type="linear">
|
|
72 | - <item android:offset="0.08" android:color="#FF00D9B5"/>
|
|
73 | - <item android:offset="0.12" android:color="#F700D9B5"/>
|
|
74 | - <item android:offset="0.19" android:color="#E200D9B5"/>
|
|
75 | - <item android:offset="0.26" android:color="#BF00D9B5"/>
|
|
76 | - <item android:offset="0.35" android:color="#8E00D9B5"/>
|
|
77 | - <item android:offset="0.44" android:color="#5100D9B5"/>
|
|
78 | - <item android:offset="0.54" android:color="#0700D9B5"/>
|
|
79 | - <item android:offset="0.54" android:color="#0000D9B5"/>
|
|
80 | - </gradient>
|
|
81 | - </aapt:attr>
|
|
82 | - </path>
|
|
83 | - <path
|
|
84 | - android:pathData="M77.5,120.83h134.33v10.38h-134.33z"
|
|
85 | - android:strokeLineJoin="round"
|
|
86 | - android:strokeWidth="1.76"
|
|
87 | - android:fillColor="#F0D4FD"
|
|
88 | - android:strokeColor="#65318E"
|
|
89 | - android:fillType="nonZero"
|
|
90 | - android:strokeLineCap="round"/>
|
|
91 | - <path
|
|
92 | - android:pathData="M52.47,100.25l0,10.29l25.03,20.67l0,-10.38z"
|
|
93 | - android:strokeLineJoin="round"
|
|
94 | - android:strokeWidth="1.76"
|
|
95 | - android:fillColor="#65318E"
|
|
96 | - android:strokeColor="#F0D4FD"
|
|
97 | - android:fillType="nonZero"
|
|
98 | - android:strokeLineCap="round"/>
|
|
99 | - <path
|
|
100 | - android:pathData="M60.33,33.17h43.89v37.87h-43.89z"
|
|
101 | - android:strokeWidth="1.76"
|
|
102 | - android:fillColor="#FFFFFF"
|
|
103 | - android:strokeColor="#65318E"
|
|
104 | - android:fillType="nonZero"/>
|
|
105 | - <path
|
|
106 | - android:pathData="M178.05,170.61L61.43,170.61L61.43,87C61.43,85.895 62.325,85 63.43,85L176,85C177.105,85 178,85.895 178,87L178.05,170.61Z"
|
|
107 | - android:strokeLineJoin="round"
|
|
108 | - android:strokeWidth="1.76"
|
|
109 | - android:fillColor="#00D9B5"
|
|
110 | - android:strokeColor="#490260"
|
|
111 | - android:fillType="nonZero"
|
|
112 | - android:strokeLineCap="round"/>
|
|
113 | - <path
|
|
114 | - android:pathData="M66.88,89.57h105.71v76.37h-105.71z"
|
|
115 | - android:strokeLineJoin="round"
|
|
116 | - android:strokeWidth="1.76"
|
|
117 | - android:fillColor="#F0D4FD"
|
|
118 | - android:strokeColor="#490260"
|
|
119 | - android:fillType="nonZero"
|
|
120 | - android:strokeLineCap="round"/>
|
|
121 | - <path
|
|
122 | - android:pathData="M177.06,170.61l-115.63,0l21.37,17.75l115.64,0z"
|
|
123 | - android:strokeLineJoin="round"
|
|
124 | - android:strokeWidth="1.76"
|
|
125 | - android:fillColor="#65318E"
|
|
126 | - android:strokeColor="#490260"
|
|
127 | - android:fillType="nonZero"
|
|
128 | - android:strokeLineCap="round"/>
|
|
129 | - <path
|
|
130 | - android:pathData="M82.8,188.36h115.11v8.96h-115.11z"
|
|
131 | - android:strokeLineJoin="round"
|
|
132 | - android:strokeWidth="1.76"
|
|
133 | - android:fillColor="#00D9B5"
|
|
134 | - android:strokeColor="#490260"
|
|
135 | - android:fillType="nonZero"
|
|
136 | - android:strokeLineCap="round"/>
|
|
137 | - <path
|
|
138 | - android:pathData="M61.43,170.61l0,7.04l21.37,19.67l0,-8.96z"
|
|
139 | - android:strokeLineJoin="round"
|
|
140 | - android:strokeWidth="1.76"
|
|
141 | - android:fillColor="#FFFFFF"
|
|
142 | - android:strokeColor="#490260"
|
|
143 | - android:fillType="nonZero"
|
|
144 | - android:strokeLineCap="round"/>
|
|
145 | - <path
|
|
146 | - android:pathData="M190.59,183L74,183L74,99.29C74.027,98.177 74.937,97.29 76.05,97.29L188.54,97.29C189.653,97.29 190.563,98.177 190.59,99.29L190.59,183Z"
|
|
147 | - android:strokeLineJoin="round"
|
|
148 | - android:strokeWidth="1.76"
|
|
149 | - android:fillColor="#00D9B5"
|
|
150 | - android:strokeColor="#490260"
|
|
151 | - android:fillType="nonZero"
|
|
152 | - android:strokeLineCap="round"/>
|
|
153 | - <path
|
|
154 | - android:pathData="M79.42,101.91h105.71v76.37h-105.71z"
|
|
155 | - android:strokeLineJoin="round"
|
|
156 | - android:strokeWidth="1.76"
|
|
157 | - android:fillColor="#F0D4FD"
|
|
158 | - android:strokeColor="#490260"
|
|
159 | - android:fillType="nonZero"
|
|
160 | - android:strokeLineCap="round"/>
|
|
161 | - <path
|
|
162 | - android:pathData="M189.73,182.95l-115.77,0l21.37,17.75l115.77,0z"
|
|
163 | - android:strokeLineJoin="round"
|
|
164 | - android:strokeWidth="1.76"
|
|
165 | - android:fillColor="#65318E"
|
|
166 | - android:strokeColor="#490260"
|
|
167 | - android:fillType="nonZero"
|
|
168 | - android:strokeLineCap="round"/>
|
|
169 | - <path
|
|
170 | - android:pathData="M95.33,200.7h115.38v8.96h-115.38z"
|
|
171 | - android:strokeLineJoin="round"
|
|
172 | - android:strokeWidth="1.76"
|
|
173 | - android:fillColor="#00D9B5"
|
|
174 | - android:strokeColor="#490260"
|
|
175 | - android:fillType="nonZero"
|
|
176 | - android:strokeLineCap="round"/>
|
|
177 | - <path
|
|
178 | - android:pathData="M73.96,182.95l0,6.78l21.37,19.93l0,-8.96z"
|
|
179 | - android:strokeLineJoin="round"
|
|
180 | - android:strokeWidth="1.76"
|
|
181 | - android:fillColor="#FFFFFF"
|
|
182 | - android:strokeColor="#490260"
|
|
183 | - android:fillType="nonZero"
|
|
184 | - android:strokeLineCap="round"/>
|
|
185 | - <path
|
|
186 | - android:pathData="M203.12,195.29L86.5,195.29L86.5,111.63C86.5,110.525 87.395,109.63 88.5,109.63L201.08,109.63C202.185,109.63 203.08,110.525 203.08,111.63L203.12,195.29Z"
|
|
187 | - android:strokeLineJoin="round"
|
|
188 | - android:strokeWidth="1.76"
|
|
189 | - android:fillColor="#00D9B5"
|
|
190 | - android:strokeColor="#490260"
|
|
191 | - android:fillType="nonZero"
|
|
192 | - android:strokeLineCap="round"/>
|
|
193 | - <path
|
|
194 | - android:pathData="M91.95,114.25h105.71v76.37h-105.71z"
|
|
195 | - android:strokeLineJoin="round"
|
|
196 | - android:strokeWidth="1.76"
|
|
197 | - android:fillColor="#F0D4FD"
|
|
198 | - android:strokeColor="#490260"
|
|
199 | - android:fillType="nonZero"
|
|
200 | - android:strokeLineCap="round"/>
|
|
201 | - <path
|
|
202 | - android:pathData="M202.4,195.29l-115.9,0l21.37,17.75l115.9,0z"
|
|
203 | - android:strokeLineJoin="round"
|
|
204 | - android:strokeWidth="1.76"
|
|
205 | - android:fillColor="#65318E"
|
|
206 | - android:strokeColor="#490260"
|
|
207 | - android:fillType="nonZero"
|
|
208 | - android:strokeLineCap="round"/>
|
|
209 | - <path
|
|
210 | - android:pathData="M107.87,213.04h115.64v8.96h-115.64z"
|
|
211 | - android:strokeLineJoin="round"
|
|
212 | - android:strokeWidth="1.76"
|
|
213 | - android:fillColor="#00D9B5"
|
|
214 | - android:strokeColor="#490260"
|
|
215 | - android:fillType="nonZero"
|
|
216 | - android:strokeLineCap="round"/>
|
|
217 | - <path
|
|
218 | - android:pathData="M86.5,195.29l0,6.51l21.37,20.2l0,-8.96z"
|
|
219 | - android:strokeLineJoin="round"
|
|
220 | - android:strokeWidth="1.76"
|
|
221 | - android:fillColor="#FFFFFF"
|
|
222 | - android:strokeColor="#490260"
|
|
223 | - android:fillType="nonZero"
|
|
224 | - android:strokeLineCap="round"/>
|
|
225 | - <path
|
|
226 | - android:pathData="M215.66,207.63L99,207.63L99,124C99,122.895 99.895,122 101,122L213.61,122C214.715,122 215.61,122.895 215.61,124L215.66,207.63Z"
|
|
227 | - android:strokeLineJoin="round"
|
|
228 | - android:strokeWidth="1.76"
|
|
229 | - android:fillColor="#00D9B5"
|
|
230 | - android:strokeColor="#490260"
|
|
231 | - android:fillType="nonZero"
|
|
232 | - android:strokeLineCap="round"/>
|
|
233 | - <path
|
|
234 | - android:pathData="M104.49,126.59h105.71v76.37h-105.71z"
|
|
235 | - android:strokeLineJoin="round"
|
|
236 | - android:strokeWidth="1.76"
|
|
237 | - android:fillColor="#F0D4FD"
|
|
238 | - android:strokeColor="#490260"
|
|
239 | - android:fillType="nonZero"
|
|
240 | - android:strokeLineCap="round"/>
|
|
241 | - <path
|
|
242 | - android:pathData="M215.06,207.63l-116.03,0l21.37,17.75l116.03,0z"
|
|
243 | - android:strokeLineJoin="round"
|
|
244 | - android:strokeWidth="1.76"
|
|
245 | - android:fillColor="#65318E"
|
|
246 | - android:strokeColor="#490260"
|
|
247 | - android:fillType="nonZero"
|
|
248 | - android:strokeLineCap="round"/>
|
|
249 | - <path
|
|
250 | - android:pathData="M120.4,225.38h115.9v8.96h-115.9z"
|
|
251 | - android:strokeLineJoin="round"
|
|
252 | - android:strokeWidth="1.76"
|
|
253 | - android:fillColor="#00D9B5"
|
|
254 | - android:strokeColor="#490260"
|
|
255 | - android:fillType="nonZero"
|
|
256 | - android:strokeLineCap="round"/>
|
|
257 | - <path
|
|
258 | - android:pathData="M99.03,207.63l0,6.25l21.37,20.46l0,-8.96z"
|
|
259 | - android:strokeLineJoin="round"
|
|
260 | - android:strokeWidth="1.76"
|
|
261 | - android:fillColor="#FFFFFF"
|
|
262 | - android:strokeColor="#490260"
|
|
263 | - android:fillType="nonZero"
|
|
264 | - android:strokeLineCap="round"/>
|
|
265 | - <path
|
|
266 | - android:pathData="M228.19,220L111.57,220L111.57,136.31C111.57,135.205 112.465,134.31 113.57,134.31L226.15,134.31C227.255,134.31 228.15,135.205 228.15,136.31L228.19,220Z"
|
|
267 | - android:strokeLineJoin="round"
|
|
268 | - android:strokeWidth="1.76"
|
|
269 | - android:fillColor="#00D9B5"
|
|
270 | - android:strokeColor="#490260"
|
|
271 | - android:fillType="nonZero"
|
|
272 | - android:strokeLineCap="round"/>
|
|
273 | - <path
|
|
274 | - android:pathData="M117.02,138.93h105.71v76.37h-105.71z"
|
|
275 | - android:strokeLineJoin="round"
|
|
276 | - android:strokeWidth="1.76"
|
|
277 | - android:fillColor="#F0D4FD"
|
|
278 | - android:strokeColor="#490260"
|
|
279 | - android:fillType="nonZero"
|
|
280 | - android:strokeLineCap="round"/>
|
|
281 | - <path
|
|
282 | - android:pathData="M227.73,219.97l-116.16,0l21.37,17.75l116.16,0z"
|
|
283 | - android:strokeLineJoin="round"
|
|
284 | - android:strokeWidth="1.76"
|
|
285 | - android:fillColor="#65318E"
|
|
286 | - android:strokeColor="#490260"
|
|
287 | - android:fillType="nonZero"
|
|
288 | - android:strokeLineCap="round"/>
|
|
289 | - <path
|
|
290 | - android:pathData="M132.94,237.72h116.16v8.96h-116.16z"
|
|
291 | - android:strokeLineJoin="round"
|
|
292 | - android:strokeWidth="1.76"
|
|
293 | - android:fillColor="#00D9B5"
|
|
294 | - android:strokeColor="#490260"
|
|
295 | - android:fillType="nonZero"
|
|
296 | - android:strokeLineCap="round"/>
|
|
297 | - <path
|
|
298 | - android:pathData="M111.57,219.97l0,5.99l21.37,19.72l0,-7.96z"
|
|
299 | - android:strokeLineJoin="round"
|
|
300 | - android:strokeWidth="1.76"
|
|
301 | - android:fillColor="#FFFFFF"
|
|
302 | - android:strokeColor="#490260"
|
|
303 | - android:fillType="nonZero"
|
|
304 | - android:strokeLineCap="round"/>
|
|
305 | - <path
|
|
306 | - android:pathData="M245.52,234.67L128.9,234.67L128.9,151C128.9,149.895 129.795,149 130.9,149L243.48,149C244.585,149 245.48,149.895 245.48,151L245.52,234.67Z"
|
|
307 | - android:strokeLineJoin="round"
|
|
308 | - android:strokeWidth="1.76"
|
|
309 | - android:fillColor="#00D9B5"
|
|
310 | - android:strokeColor="#490260"
|
|
311 | - android:fillType="nonZero"
|
|
312 | - android:strokeLineCap="round"/>
|
|
313 | - <path
|
|
314 | - android:pathData="M134.36,153.64h105.71v76.37h-105.71z"
|
|
315 | - android:strokeLineJoin="round"
|
|
316 | - android:strokeWidth="1.76"
|
|
317 | - android:fillColor="#F0D4FD"
|
|
318 | - android:strokeColor="#490260"
|
|
319 | - android:fillType="nonZero"
|
|
320 | - android:strokeLineCap="round"/>
|
|
321 | - <path
|
|
322 | - android:pathData="M245.06,234.67l-116.16,0l21.37,17.76l116.16,0z"
|
|
323 | - android:strokeLineJoin="round"
|
|
324 | - android:strokeWidth="1.76"
|
|
325 | - android:fillColor="#65318E"
|
|
326 | - android:strokeColor="#490260"
|
|
327 | - android:fillType="nonZero"
|
|
328 | - android:strokeLineCap="round"/>
|
|
329 | - <path
|
|
330 | - android:pathData="M150.27,252.43h116.16v8.96h-116.16z"
|
|
331 | - android:strokeLineJoin="round"
|
|
332 | - android:strokeWidth="1.76"
|
|
333 | - android:fillColor="#00D9B5"
|
|
334 | - android:strokeColor="#490260"
|
|
335 | - android:fillType="nonZero"
|
|
336 | - android:strokeLineCap="round"/>
|
|
337 | - <path
|
|
338 | - android:pathData="M60.33,33.89h41.01v38.59h-41.01z"
|
|
339 | - android:strokeLineJoin="round"
|
|
340 | - android:strokeWidth="1.76"
|
|
341 | - android:fillColor="#65318E"
|
|
342 | - android:strokeColor="#65318E"
|
|
343 | - android:fillType="nonZero"
|
|
344 | - android:strokeLineCap="round"/>
|
|
345 | - <path
|
|
346 | - android:pathData="M67.24,33.89h34.1v30.04h-34.1z"
|
|
347 | - android:strokeLineJoin="round"
|
|
348 | - android:strokeWidth="1.76"
|
|
349 | - android:fillColor="#65318E"
|
|
350 | - android:strokeColor="#65318E"
|
|
351 | - android:fillType="nonZero"
|
|
352 | - android:strokeLineCap="round"/>
|
|
353 | - <path
|
|
354 | - android:pathData="M150.62,58.81L150.62,151L279.49,151L279.49,58.81C279.49,55.01 276.41,51.93 272.61,51.93L157.5,51.93C153.7,51.93 150.62,55.01 150.62,58.81ZM273.46,142.2L156.65,142.2L156.65,61.41L273.46,61.41L273.46,142.2Z"
|
|
355 | - android:strokeLineJoin="round"
|
|
356 | - android:strokeWidth="1.76"
|
|
357 | - android:fillColor="#00D9B5"
|
|
358 | - android:strokeColor="#490260"
|
|
359 | - android:fillType="nonZero"
|
|
360 | - android:strokeLineCap="round"/>
|
|
361 | - <path
|
|
362 | - android:pathData="M277.38,150.95l-126.76,0l23.62,19.62l126.76,0z"
|
|
363 | - android:strokeLineJoin="round"
|
|
364 | - android:strokeWidth="1.76"
|
|
365 | - android:fillColor="#F0D4FD"
|
|
366 | - android:strokeColor="#490260"
|
|
367 | - android:fillType="nonZero"
|
|
368 | - android:strokeLineCap="round"/>
|
|
369 | - <path
|
|
370 | - android:pathData="M174.24,170.57h126.76v9.9h-126.76z"
|
|
371 | - android:strokeLineJoin="round"
|
|
372 | - android:strokeWidth="1.76"
|
|
373 | - android:fillColor="#00D9B5"
|
|
374 | - android:strokeColor="#490260"
|
|
375 | - android:fillType="nonZero"
|
|
376 | - android:strokeLineCap="round"/>
|
|
377 | - <path
|
|
378 | - android:pathData="M150.62,150.95l0,9.81l23.62,19.71l0,-9.9z"
|
|
379 | - android:strokeLineJoin="round"
|
|
380 | - android:strokeWidth="1.76"
|
|
381 | - android:fillColor="#490260"
|
|
382 | - android:strokeColor="#490260"
|
|
383 | - android:fillType="nonZero"
|
|
384 | - android:strokeLineCap="round"/>
|
|
385 | - <path
|
|
386 | - android:pathData="M157.37,61.41h116.09v80.79h-116.09z"
|
|
387 | - android:strokeWidth="1.76"
|
|
388 | - android:fillColor="#FFFFFF"
|
|
389 | - android:strokeColor="#65318E"
|
|
390 | - android:fillType="nonZero"/>
|
|
391 | - <path
|
|
392 | - android:pathData="M157.37,61.41h116.09v80.79h-116.09z"
|
|
393 | - android:strokeWidth="1"
|
|
394 | - android:fillType="nonZero"
|
|
395 | - android:strokeColor="#00000000">
|
|
396 | - <aapt:attr name="android:fillColor">
|
|
397 | - <gradient
|
|
398 | - android:startY="18.45"
|
|
399 | - android:startX="215.42"
|
|
400 | - android:endY="112.96"
|
|
401 | - android:endX="215.42"
|
|
402 | - android:type="linear">
|
|
403 | - <item android:offset="0.08" android:color="#FFF0D4FD"/>
|
|
404 | - <item android:offset="0.27" android:color="#C4F0D4FD"/>
|
|
405 | - <item android:offset="0.7" android:color="#33F0D4FD"/>
|
|
406 | - <item android:offset="0.85" android:color="#00F0D4FD"/>
|
|
407 | - </gradient>
|
|
408 | - </aapt:attr>
|
|
409 | - </path>
|
|
410 | - <path
|
|
411 | - android:pathData="M157.37,61.41h1.72v80.79h-1.72z"
|
|
412 | - android:strokeLineJoin="round"
|
|
413 | - android:strokeWidth="1.76"
|
|
414 | - android:fillColor="#490260"
|
|
415 | - android:strokeColor="#490260"
|
|
416 | - android:fillType="nonZero"
|
|
417 | - android:strokeLineCap="round"/>
|
|
418 | - <path
|
|
419 | - android:pathData="M5.78,27.13L119,27.13C121.64,27.13 123.78,29.27 123.78,31.91L123.78,119.64L1,119.64L1,31.9C1.006,29.264 3.144,27.13 5.78,27.13Z"
|
|
420 | - android:strokeLineJoin="round"
|
|
421 | - android:strokeWidth="1.76"
|
|
422 | - android:fillColor="#00D9B5"
|
|
423 | - android:strokeColor="#490260"
|
|
424 | - android:fillType="nonZero"
|
|
425 | - android:strokeLineCap="round"/>
|
|
426 | - <path
|
|
427 | - android:pathData="M9.05,34.34h106.66v76.63h-106.66z"
|
|
428 | - android:strokeLineJoin="round"
|
|
429 | - android:strokeWidth="1.76"
|
|
430 | - android:fillColor="#FFFFFF"
|
|
431 | - android:strokeColor="#490260"
|
|
432 | - android:fillType="nonZero"
|
|
433 | - android:strokeLineCap="round"/>
|
|
434 | - <path
|
|
435 | - android:pathData="M121.75,119.63l-120.75,0l22.5,18.69l120.74,0z"
|
|
436 | - android:strokeLineJoin="round"
|
|
437 | - android:strokeWidth="1.76"
|
|
438 | - android:fillColor="#F0D4FD"
|
|
439 | - android:strokeColor="#490260"
|
|
440 | - android:fillType="nonZero"
|
|
441 | - android:strokeLineCap="round"/>
|
|
442 | - <path
|
|
443 | - android:pathData="M23.5,138.32h120.75v9.43h-120.75z"
|
|
444 | - android:strokeLineJoin="round"
|
|
445 | - android:strokeWidth="1.76"
|
|
446 | - android:fillColor="#00D9B5"
|
|
447 | - android:strokeColor="#490260"
|
|
448 | - android:fillType="nonZero"
|
|
449 | - android:strokeLineCap="round"/>
|
|
450 | - <path
|
|
451 | - android:pathData="M1,119.63l0,9.34l22.5,18.78l0,-9.43z"
|
|
452 | - android:strokeLineJoin="round"
|
|
453 | - android:strokeWidth="1.76"
|
|
454 | - android:fillColor="#490260"
|
|
455 | - android:strokeColor="#490260"
|
|
456 | - android:fillType="nonZero"
|
|
457 | - android:strokeLineCap="round"/>
|
|
458 | - <path
|
|
459 | - android:pathData="M9.82,35.75h104.98v74.01h-104.98z"
|
|
460 | - android:strokeLineJoin="round"
|
|
461 | - android:strokeWidth="1.76"
|
|
462 | - android:fillColor="#FFFFFF"
|
|
463 | - android:strokeColor="#490260"
|
|
464 | - android:fillType="nonZero"
|
|
465 | - android:strokeLineCap="round"/>
|
|
466 | - <path
|
|
467 | - android:pathData="M9.82,35.75h104.98v74.01h-104.98z"
|
|
468 | - android:strokeLineJoin="round"
|
|
469 | - android:strokeWidth="1.76"
|
|
470 | - android:fillColor="#FFFFFF"
|
|
471 | - android:strokeColor="#490260"
|
|
472 | - android:fillType="nonZero"
|
|
473 | - android:strokeLineCap="round"/>
|
|
474 | - <path
|
|
475 | - android:pathData="M58.1,35.75L115.36,35.75L115.36,102.15L65.3,102.15C61.341,102.156 58.122,98.959 58.1,95L58.1,35.75Z"
|
|
476 | - android:strokeLineJoin="round"
|
|
477 | - android:strokeWidth="1.76"
|
|
478 | - android:fillColor="#F0D4FD"
|
|
479 | - android:strokeColor="#490260"
|
|
480 | - android:fillType="nonZero"
|
|
481 | - android:strokeLineCap="round"/>
|
|
482 | - <path
|
|
483 | - android:pathData="M69.28,35.43h46.09v53.39h-46.09z"
|
|
484 | - android:strokeLineJoin="round"
|
|
485 | - android:strokeWidth="1.76"
|
|
486 | - android:fillColor="#F0D4FD"
|
|
487 | - android:strokeColor="#490260"
|
|
488 | - android:fillType="nonZero"
|
|
489 | - android:strokeLineCap="round"/>
|
|
490 | - <path
|
|
491 | - android:pathData="M9.05,110.21L9.05,34.34L12.25,34.34L12.25,107C12.253,107.85 11.917,108.667 11.316,109.269C10.716,109.871 9.9,110.21 9.05,110.21Z"
|
|
492 | - android:strokeLineJoin="round"
|
|
493 | - android:strokeWidth="1.76"
|
|
494 | - android:fillColor="#490260"
|
|
495 | - android:strokeColor="#490260"
|
|
496 | - android:fillType="nonZero"
|
|
497 | - android:strokeLineCap="round"/>
|
|
498 | - <path
|
|
499 | - android:pathData="M132.94,246.68l17.33,14.71l0,-8.96l-17.33,-14.71z"
|
|
500 | - android:strokeLineJoin="round"
|
|
501 | - android:strokeWidth="1.76"
|
|
502 | - android:fillColor="#FFFFFF"
|
|
503 | - android:strokeColor="#490260"
|
|
504 | - android:fillType="nonZero"
|
|
505 | - android:strokeLineCap="round"/>
|
|
506 | -</vector> |
1 | -<?xml version="1.0" encoding="utf-8"?>
|
|
2 | -<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
3 | - - License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4 | - - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
5 | -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
6 | - xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
7 | - xmlns:tools="http://schemas.android.com/tools"
|
|
8 | - android:layout_width="match_parent"
|
|
9 | - android:layout_height="match_parent"
|
|
10 | - android:layout_gravity="bottom"
|
|
11 | - android:layout_marginLeft="16dp"
|
|
12 | - android:layout_marginRight="16dp" >
|
|
13 | - |
|
14 | - <ImageView
|
|
15 | - android:id="@+id/tor_bootstrap_network_settings_button"
|
|
16 | - app:srcCompat="@drawable/mozac_ic_settings"
|
|
17 | - android:scaleType="fitCenter"
|
|
18 | - android:layout_height="wrap_content"
|
|
19 | - android:layout_width="wrap_content"
|
|
20 | - android:layout_marginTop="20dp"
|
|
21 | - android:layout_alignParentTop="true"
|
|
22 | - android:layout_alignParentEnd="true"
|
|
23 | - tools:ignore="ContentDescription" />
|
|
24 | - |
|
25 | - <ImageView
|
|
26 | - android:id="@+id/tor_bootstrap_image"
|
|
27 | - app:srcCompat="@drawable/ic_tor_connect_computer_graphic"
|
|
28 | - android:scaleType="fitCenter"
|
|
29 | - android:layout_height="wrap_content"
|
|
30 | - android:layout_width="wrap_content"
|
|
31 | - android:layout_marginTop="80dp"
|
|
32 | - android:layout_marginBottom="10dp"
|
|
33 | - android:layout_centerHorizontal="true"
|
|
34 | - android:layout_below="@id/tor_bootstrap_network_settings_button"
|
|
35 | - android:layout_above="@id/tor_bootstrap_connect_button"
|
|
36 | - android:gravity="center"
|
|
37 | - tools:ignore="ContentDescription" />
|
|
38 | - |
|
39 | - <androidx.appcompat.widget.SwitchCompat
|
|
40 | - android:id="@+id/quick_start_toggle"
|
|
41 | - android:visibility="gone"
|
|
42 | - android:checked="false"
|
|
43 | - android:layout_width="wrap_content"
|
|
44 | - android:layout_height="wrap_content"
|
|
45 | - android:layout_above="@id/tor_bootstrap_connect_button" />
|
|
46 | - |
|
47 | - <Button
|
|
48 | - android:id="@+id/tor_bootstrap_connect_button"
|
|
49 | - android:layout_width="wrap_content"
|
|
50 | - android:layout_height="wrap_content"
|
|
51 | - android:width="160dp"
|
|
52 | - android:height="36dp"
|
|
53 | - android:layout_marginBottom="10dp"
|
|
54 | - android:layout_centerHorizontal="true"
|
|
55 | - android:layout_above="@id/tor_bootstrap_status_message"
|
|
56 | - android:gravity="center|center_vertical"
|
|
57 | - android:text="@string/tor_bootstrap_connect"
|
|
58 | - android:fontFamily="Roboto-Medium"
|
|
59 | - android:textColor="#FF000000"
|
|
60 | - android:textSize="18sp"
|
|
61 | - android:lineSpacingMultiplier="0.89"
|
|
62 | - android:background="@drawable/rounded_corners" />
|
|
63 | - |
|
64 | - <TextView
|
|
65 | - android:id="@+id/tor_bootstrap_status_message"
|
|
66 | - android:layout_width="wrap_content"
|
|
67 | - android:layout_height="wrap_content"
|
|
68 | - android:layout_marginBottom="32dp"
|
|
69 | - android:layout_centerHorizontal="true"
|
|
70 | - android:paddingStart="8dp"
|
|
71 | - android:paddingEnd="8dp"
|
|
72 | - android:gravity="center"
|
|
73 | - android:lines="3"
|
|
74 | - android:layout_above="@id/tor_bootstrap_swipe_log" />
|
|
75 | - |
|
76 | - <TextView
|
|
77 | - android:id="@+id/tor_bootstrap_swipe_log"
|
|
78 | - android:layout_width="match_parent"
|
|
79 | - android:layout_height="wrap_content"
|
|
80 | - android:width="360dp"
|
|
81 | - android:height="24dp"
|
|
82 | - android:layout_marginBottom="15dp"
|
|
83 | - android:layout_centerHorizontal="true"
|
|
84 | - android:layout_alignParentBottom="true"
|
|
85 | - android:gravity="center"
|
|
86 | - android:textSize="14sp"
|
|
87 | - android:textColor="#FFFFFFFF"
|
|
88 | - android:fontFamily="Roboto-Regular"
|
|
89 | - android:lineSpacingMultiplier="1.71"
|
|
90 | - android:text="@string/tor_bootstrap_swipe_for_logs"
|
|
91 | - android:layout_above="@id/tor_bootstrap_progress" />
|
|
92 | - |
|
93 | - <ProgressBar
|
|
94 | - android:id="@+id/tor_bootstrap_progress"
|
|
95 | - style="?android:attr/progressBarStyleHorizontal"
|
|
96 | - android:layout_width="match_parent"
|
|
97 | - android:layout_height="3dp"
|
|
98 | - android:visibility="invisible"
|
|
99 | - android:layout_alignParentBottom="true" />
|
|
100 | -</RelativeLayout> |
1 | -<?xml version="1.0" encoding="utf-8"?>
|
|
2 | -<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
3 | - - License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4 | - - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
5 | -<FrameLayout
|
|
6 | - xmlns:android="http://schemas.android.com/apk/res/android"
|
|
7 | - xmlns:tools="http://schemas.android.com/tools"
|
|
8 | - android:layout_width="match_parent"
|
|
9 | - android:layout_height="match_parent">
|
|
10 | - <TextView
|
|
11 | - android:id="@+id/tor_bootstrap_log_entries"
|
|
12 | - android:layout_width="match_parent"
|
|
13 | - android:layout_height="match_parent"
|
|
14 | - android:gravity="bottom"
|
|
15 | - android:textColor="@android:color/white"
|
|
16 | - android:fontFamily="RobotoMono-Regular"
|
|
17 | - android:textSize="12sp"
|
|
18 | - android:textIsSelectable="true"
|
|
19 | - android:layout_marginLeft="20dp"
|
|
20 | - android:layout_marginRight="20dp"
|
|
21 | - android:layout_marginBottom="20dp" />
|
|
22 | -</FrameLayout> |
1 | -<?xml version="1.0" encoding="utf-8"?>
|
|
2 | -<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
3 | - - License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4 | - - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
5 | - |
|
6 | -<com.google.android.material.appbar.AppBarLayout
|
|
7 | - xmlns:android="http://schemas.android.com/apk/res/android"
|
|
8 | - xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
9 | - android:layout_width="match_parent"
|
|
10 | - android:layout_height="match_parent"
|
|
11 | - android:orientation="vertical"
|
|
12 | - android:background="?torBootstrapBackground">
|
|
13 | - |
|
14 | - <androidx.viewpager2.widget.ViewPager2
|
|
15 | - android:id="@+id/bootstrap_pager"
|
|
16 | - android:layout_width="match_parent"
|
|
17 | - android:layout_height="match_parent" />
|
|
18 | -</com.google.android.material.appbar.AppBarLayout> |