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> |