Skip to content
Snippets Groups Projects
Commit 6344fbbb authored by Matthew Finkel's avatar Matthew Finkel
Browse files

Bug 40028: Implement Tor connect and logger screens

parent aa09aebf
Branches
No related tags found
1 merge request!39Bug 40050 01
Showing
with 1063 additions and 6 deletions
......@@ -253,7 +253,8 @@ class HomeFragment : Fragment() {
handleSwipedItemDeletionCancel = ::handleSwipedItemDeletionCancel,
handleTorBootstrapConnect = ::handleTorBootstrapConnect,
cancelTorBootstrap = ::cancelTorBootstrap,
initiateTorBootstrap = ::initiateTorBootstrap
initiateTorBootstrap = ::initiateTorBootstrap,
openTorNetworkSettings = ::openTorNetworkSettings
)
)
......@@ -1062,6 +1063,9 @@ class HomeFragment : Fragment() {
requireComponents.torController.initiateTorBootstrap(lifecycleScope, withDebugLogging)
}
private fun openTorNetworkSettings() {
}
companion object {
const val ALL_NORMAL_TABS = "all_normal"
const val ALL_PRIVATE_TABS = "all_private"
......
......@@ -21,6 +21,7 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionHeaderViewHol
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoCollectionsMessageViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.PrivateBrowsingDescriptionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TorBootstrapPagerViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSitePagerViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingAutomaticSignInViewHolder
......@@ -60,6 +61,8 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
object PrivateBrowsingDescription : AdapterItem(PrivateBrowsingDescriptionViewHolder.LAYOUT_ID)
object NoCollectionsMessage : AdapterItem(NoCollectionsMessageViewHolder.LAYOUT_ID)
object TorBootstrap : AdapterItem(TorBootstrapPagerViewHolder.LAYOUT_ID)
object CollectionHeader : AdapterItem(CollectionHeaderViewHolder.LAYOUT_ID)
data class CollectionItem(
val collection: TabCollection,
......@@ -152,6 +155,11 @@ class SessionControlAdapter(
view,
interactor
)
TorBootstrapPagerViewHolder.LAYOUT_ID -> TorBootstrapPagerViewHolder(
view,
components,
interactor
)
NoCollectionsMessageViewHolder.LAYOUT_ID ->
NoCollectionsMessageViewHolder(
view,
......
......@@ -156,9 +156,9 @@ interface SessionControlController {
fun handleTorBootstrapConnectClicked()
/**
* @see [TorBootstrapInteractor.onTorBootstrapConnectingClicked]
* @see [TorBootstrapInteractor.onTorStopBootstrapping]
*/
fun handleTorBootstrapConnectingClicked()
fun handleTorStopBootstrapping()
/**
* @see [TorBootstrapInteractor.onTorStartBootstrapping]
......@@ -169,6 +169,11 @@ interface SessionControlController {
* @see [TorBootstrapInteractor.onTorStartDebugBootstrapping]
*/
fun handleTorStartDebugBootstrapping()
/**
* @see [TorBootstrapInteractor.onTorBootstrapNetworkSettingsClicked]
*/
fun handleTorNetworkSettingsClicked()
}
@Suppress("TooManyFunctions", "LargeClass")
......@@ -196,7 +201,8 @@ class DefaultSessionControlController(
private val handleSwipedItemDeletionCancel: () -> Unit,
private val handleTorBootstrapConnect: () -> Unit,
private val initiateTorBootstrap: (Boolean) -> Unit,
private val cancelTorBootstrap: () -> Unit
private val cancelTorBootstrap: () -> Unit,
private val openTorNetworkSettings: () -> Unit
) : SessionControlController {
override fun handleCollectionAddTabTapped(collection: TabCollection) {
......@@ -462,7 +468,7 @@ class DefaultSessionControlController(
handleTorBootstrapConnect()
}
override fun handleTorBootstrapConnectingClicked() {
override fun handleTorStopBootstrapping() {
cancelTorBootstrap()
}
......@@ -473,4 +479,8 @@ class DefaultSessionControlController(
override fun handleTorStartDebugBootstrapping() {
initiateTorBootstrap(true)
}
override fun handleTorNetworkSettingsClicked() {
openTorNetworkSettings()
}
}
......@@ -144,6 +144,34 @@ interface TipInteractor {
fun onCloseTip(tip: Tip)
}
interface TorBootstrapInteractor {
/**
* Initiates Tor bootstrapping. Called when a user clicks on the "Connect" button.
*/
fun onTorBootstrapConnectClicked()
/**
* Initiates Tor bootstrapping. Called when a user clicks on the "Connect" button.
*/
fun onTorStartBootstrapping()
/**
* Stop Tor bootstrapping. Called when a user clicks on the "settings" cog/button.
*/
fun onTorStopBootstrapping()
/**
* Initiates Tor bootstrapping with debug logging. Called when bootstrapping fails with
* the control.txt file not existing.
*/
fun onTorStartDebugBootstrapping()
/**
* Open Tor Network Settings preference screen
*/
fun onTorBootstrapNetworkSettingsClicked()
}
/**
* Interface for top site related actions in the [SessionControlInteractor].
*/
......@@ -181,7 +209,7 @@ interface TopSiteInteractor {
class SessionControlInteractor(
private val controller: SessionControlController
) : CollectionInteractor, OnboardingInteractor, TopSiteInteractor, TipInteractor,
TabSessionInteractor, ToolbarInteractor {
TabSessionInteractor, ToolbarInteractor, TorBootstrapInteractor {
override fun onCollectionAddTabTapped(collection: TabCollection) {
controller.handleCollectionAddTabTapped(collection)
}
......@@ -265,4 +293,24 @@ class SessionControlInteractor(
override fun onRemoveCollectionsPlaceholder() {
controller.handleRemoveCollectionsPlaceholder()
}
override fun onTorBootstrapConnectClicked() {
controller.handleTorBootstrapConnectClicked()
}
override fun onTorStopBootstrapping() {
controller.handleTorStopBootstrapping()
}
override fun onTorStartBootstrapping() {
controller.handleTorStartBootstrapping()
}
override fun onTorStartDebugBootstrapping() {
controller.handleTorStartDebugBootstrapping()
}
override fun onTorBootstrapNetworkSettingsClicked() {
controller.handleTorNetworkSettingsClicked()
}
}
......@@ -68,6 +68,8 @@ private fun showCollections(
private fun privateModeAdapterItems() = listOf(AdapterItem.PrivateBrowsingDescription)
private fun bootstrapAdapterItems() = listOf(AdapterItem.TorBootstrap)
private fun onboardingAdapterItems(onboardingState: OnboardingState): List<AdapterItem> {
val items: MutableList<AdapterItem> = mutableListOf(AdapterItem.OnboardingHeader)
......@@ -117,6 +119,7 @@ private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) {
)
is Mode.Private -> privateModeAdapterItems()
is Mode.Onboarding -> onboardingAdapterItems(mode.state)
is Mode.Bootstrap -> bootstrapAdapterItems()
}
private fun collectionTabItems(collection: TabCollection) =
......
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.View
import androidx.appcompat.widget.SwitchCompat
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.tor_bootstrap_connect.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.tor.TorEvents
import org.mozilla.fenix.tor.bootstrap.TorQuickStart
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.home.sessioncontrol.TorBootstrapInteractor
class TorBootstrapConnectViewHolder(
private val view: View,
private val components: Components,
private val interactor: TorBootstrapInteractor
) : RecyclerView.ViewHolder(view), TorEvents {
init {
val torQuickStart = TorQuickStart(view.context)
setQuickStartDescription(view, torQuickStart)
with(view.quick_start_toggle as SwitchCompat) {
setOnCheckedChangeListener { _, isChecked ->
torQuickStart.setQuickStartTor(isChecked)
setQuickStartDescription(view, torQuickStart)
}
isChecked = torQuickStart.quickStartTor()
}
with(view.tor_bootstrap_network_settings_button) {
setOnClickListener {
interactor.onTorStopBootstrapping()
interactor.onTorBootstrapNetworkSettingsClicked()
with(view.tor_bootstrap_progress) {
visibility = View.INVISIBLE
}
with(view.tor_bootstrap_connect_button) {
visibility = View.VISIBLE
}
}
}
with(view.tor_bootstrap_connect_button) {
setOnClickListener {
interactor.onTorBootstrapConnectClicked()
interactor.onTorStartBootstrapping()
visibility = View.INVISIBLE
with(view.tor_bootstrap_progress) {
visibility = View.VISIBLE
}
}
}
components.torController.registerTorListener(this)
}
private fun setQuickStartDescription(view: View, torQuickStart: TorQuickStart) {
val resources = view.context.resources
val appName = resources.getString(R.string.app_name)
if (torQuickStart.quickStartTor()) {
view.tor_bootstrap_quick_start_description.text = resources.getString(
R.string.tor_bootstrap_quick_start_enabled, appName
)
} else {
view.tor_bootstrap_quick_start_description.text = resources.getString(
R.string.tor_bootstrap_quick_start_disabled
)
}
}
@SuppressWarnings("EmptyFunctionBlock")
override fun onTorConnecting() {
}
override fun onTorConnected() {
components.torController.unregisterTorListener(this)
}
@SuppressWarnings("EmptyFunctionBlock")
override fun onTorStopped() {
}
override fun onTorStatusUpdate(entry: String?, status: String?) {
if (entry == null) return
view.tor_bootstrap_status_message.text = entry
if (entry.startsWith(BOOTSTRAPPED_PREFIX)) {
val percentIdx = entry.indexOf("%")
val percent = entry.substring(
BOOTSTRAPPED_PREFIX.length,
percentIdx
)
with(view.tor_bootstrap_progress) {
progress = percent.toInt()
}
}
}
companion object {
const val LAYOUT_ID = R.layout.tor_bootstrap_connect
const val BOOTSTRAPPED_PREFIX = "NOTICE: Bootstrapped "
}
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.text.method.ScrollingMovementMethod
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.tor_bootstrap_logger.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.tor.TorEvents
import org.mozilla.fenix.components.Components
class TorBootstrapLoggerViewHolder(
private val view: View,
private val components: Components
) : RecyclerView.ViewHolder(view), TorEvents {
private var entries = mutableListOf<String>()
init {
components.torController.registerTorListener(this)
val currentEntries = components.torController.logEntries
.filter { it.first != null }
.filter { !(it.first!!.startsWith("Circuit") && it.second == "ON") }
// Keep synchronized with format in onTorStatusUpdate
.flatMap { listOf("(${it.second}) '${it.first}'") }
val entriesLen = currentEntries.size
val subListOffset = if (entriesLen > MAX_NEW_ENTRIES) MAX_NEW_ENTRIES else entriesLen
entries = currentEntries.subList((entriesLen - subListOffset), entriesLen) as MutableList<String>
val initLog = "---------------" + view.resources.getString(R.string.tor_initializing_log) + "---------------"
entries.add(0, initLog)
with(view.tor_bootstrap_log_entries) {
movementMethod = ScrollingMovementMethod()
text = formatLogEntries(entries)
}
}
private fun formatLogEntries(entries: List<String>) = entries.joinToString("\n")
@SuppressWarnings("EmptyFunctionBlock")
override fun onTorConnecting() {
}
override fun onTorConnected() {
components.torController.unregisterTorListener(this)
}
@SuppressWarnings("EmptyFunctionBlock")
override fun onTorStopped() {
}
override fun onTorStatusUpdate(entry: String?, status: String?) {
if (status == null || entry == null) return
if (status == "ON" && entry.startsWith("Circuit")) return
if (entries.size > MAX_LINES) {
entries = entries.drop(1) as MutableList<String>
}
entries.add("($status) '$entry'")
view.tor_bootstrap_log_entries.text = formatLogEntries(entries)
}
companion object {
const val LAYOUT_ID = R.layout.tor_bootstrap_logger
const val MAX_NEW_ENTRIES = 24
const val MAX_LINES = 25
}
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.tor_bootstrap_pager.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.home.sessioncontrol.TorBootstrapInteractor
import org.mozilla.fenix.home.sessioncontrol.viewholders.torbootstrap.BootstrapPagerAdapter
class TorBootstrapPagerViewHolder(
view: View,
components: Components,
interactor: TorBootstrapInteractor
) : RecyclerView.ViewHolder(view) {
private val bootstrapPagerAdapter = BootstrapPagerAdapter(components, interactor)
init {
view.bootstrap_pager.apply {
adapter = bootstrapPagerAdapter
}
}
companion object {
const val LAYOUT_ID = R.layout.tor_bootstrap_pager
}
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.home.sessioncontrol.viewholders.torbootstrap
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.home.sessioncontrol.TorBootstrapInteractor
import org.mozilla.fenix.home.sessioncontrol.viewholders.TorBootstrapConnectViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.TorBootstrapLoggerViewHolder
class BootstrapPagerAdapter(
private val components: Components,
private val interactor: TorBootstrapInteractor
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == BOOTSTRAP_UI_PAGE_TYPE) {
val viewDVH = LayoutInflater.from(parent.context)
.inflate(TorBootstrapConnectViewHolder.LAYOUT_ID, parent, false)
return TorBootstrapConnectViewHolder(viewDVH, components, interactor)
} else {
val viewLVH = LayoutInflater.from(parent.context)
.inflate(TorBootstrapLoggerViewHolder.LAYOUT_ID, parent, false)
return TorBootstrapLoggerViewHolder(viewLVH, components)
}
}
@SuppressWarnings("EmptyFunctionBlock")
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
}
override fun getItemViewType(position: Int): Int = position
override fun getItemCount(): Int = BOOTSTRAP_PAGE_COUNT
companion object {
const val BOOTSTRAP_UI_PAGE_TYPE = 0
const val BOOTSTRAP_LOG_PAGE_TYPE = 1
const val BOOTSTRAP_PAGE_COUNT = 2
}
}
app/src/main/res/drawable/connectoncropped.png

44.9 KiB

<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="302dp"
android:height="263dp"
android:viewportWidth="302"
android:viewportHeight="263">
<path
android:pathData="M279.49,222.64l-94.22,-0l-0,-79.61l94.22,-0z"
android:strokeWidth="1"
android:fillType="nonZero"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:startY="203.70642"
android:startX="231.9"
android:endY="160.04314"
android:endX="232.90999"
android:type="linear">
<item android:offset="0.08" android:color="#FF7E4696"/>
<item android:offset="0.39" android:color="#9B7E4696"/>
<item android:offset="0.85" android:color="#007E4696"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M112.74,217.05l-102.32,-0l-0,-102.32l102.32,-0z"
android:strokeWidth="1"
android:fillType="nonZero"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:startY="203.36"
android:startX="60.96"
android:endY="124.990005"
android:endX="62.249996"
android:type="linear">
<item android:offset="0.08" android:color="#FF00D9B5"/>
<item android:offset="0.3" android:color="#BA00D9B5"/>
<item android:offset="0.8" android:color="#1100D9B5"/>
<item android:offset="0.85" android:color="#0000D9B5"/>
</gradient>
</aapt:attr>
</path>
<path
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"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#65318E"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M60.55,8.87h120.41v108.53h-120.41z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M60.55,8.87h120.41v108.53h-120.41z"
android:strokeWidth="1"
android:fillType="nonZero"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:startY="-56.010002"
android:startX="120.75"
android:endY="120.520004"
android:endX="120.75"
android:type="linear">
<item android:offset="0.08" android:color="#FF00D9B5"/>
<item android:offset="0.12" android:color="#F700D9B5"/>
<item android:offset="0.19" android:color="#E200D9B5"/>
<item android:offset="0.26" android:color="#BF00D9B5"/>
<item android:offset="0.35" android:color="#8E00D9B5"/>
<item android:offset="0.44" android:color="#5100D9B5"/>
<item android:offset="0.54" android:color="#0700D9B5"/>
<item android:offset="0.54" android:color="#0000D9B5"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M77.5,120.83h134.33v10.38h-134.33z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#65318E"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M52.47,100.25l0,10.29l25.03,20.67l0,-10.38z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#65318E"
android:strokeColor="#F0D4FD"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M60.33,33.17h43.89v37.87h-43.89z"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#65318E"
android:fillType="nonZero"/>
<path
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"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M66.88,89.57h105.71v76.37h-105.71z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M177.06,170.61l-115.63,0l21.37,17.75l115.64,0z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#65318E"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M82.8,188.36h115.11v8.96h-115.11z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M61.43,170.61l0,7.04l21.37,19.67l0,-8.96z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
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"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M79.42,101.91h105.71v76.37h-105.71z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M189.73,182.95l-115.77,0l21.37,17.75l115.77,0z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#65318E"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M95.33,200.7h115.38v8.96h-115.38z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M73.96,182.95l0,6.78l21.37,19.93l0,-8.96z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
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"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M91.95,114.25h105.71v76.37h-105.71z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M202.4,195.29l-115.9,0l21.37,17.75l115.9,0z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#65318E"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M107.87,213.04h115.64v8.96h-115.64z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M86.5,195.29l0,6.51l21.37,20.2l0,-8.96z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
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"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M104.49,126.59h105.71v76.37h-105.71z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M215.06,207.63l-116.03,0l21.37,17.75l116.03,0z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#65318E"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M120.4,225.38h115.9v8.96h-115.9z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M99.03,207.63l0,6.25l21.37,20.46l0,-8.96z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
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"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M117.02,138.93h105.71v76.37h-105.71z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M227.73,219.97l-116.16,0l21.37,17.75l116.16,0z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#65318E"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M132.94,237.72h116.16v8.96h-116.16z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M111.57,219.97l0,5.99l21.37,19.72l0,-7.96z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
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"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M134.36,153.64h105.71v76.37h-105.71z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M245.06,234.67l-116.16,0l21.37,17.76l116.16,0z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#65318E"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M150.27,252.43h116.16v8.96h-116.16z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M60.33,33.89h41.01v38.59h-41.01z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#65318E"
android:strokeColor="#65318E"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M67.24,33.89h34.1v30.04h-34.1z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#65318E"
android:strokeColor="#65318E"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
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"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M277.38,150.95l-126.76,0l23.62,19.62l126.76,0z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M174.24,170.57h126.76v9.9h-126.76z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M150.62,150.95l0,9.81l23.62,19.71l0,-9.9z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#490260"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M157.37,61.41h116.09v80.79h-116.09z"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#65318E"
android:fillType="nonZero"/>
<path
android:pathData="M157.37,61.41h116.09v80.79h-116.09z"
android:strokeWidth="1"
android:fillType="nonZero"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:startY="18.45"
android:startX="215.42"
android:endY="112.96"
android:endX="215.42"
android:type="linear">
<item android:offset="0.08" android:color="#FFF0D4FD"/>
<item android:offset="0.27" android:color="#C4F0D4FD"/>
<item android:offset="0.7" android:color="#33F0D4FD"/>
<item android:offset="0.85" android:color="#00F0D4FD"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M157.37,61.41h1.72v80.79h-1.72z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#490260"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
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"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M9.05,34.34h106.66v76.63h-106.66z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M121.75,119.63l-120.75,0l22.5,18.69l120.74,0z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M23.5,138.32h120.75v9.43h-120.75z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#00D9B5"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M1,119.63l0,9.34l22.5,18.78l0,-9.43z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#490260"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M9.82,35.75h104.98v74.01h-104.98z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M9.82,35.75h104.98v74.01h-104.98z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
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"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M69.28,35.43h46.09v53.39h-46.09z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#F0D4FD"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
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"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#490260"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
<path
android:pathData="M132.94,246.68l17.33,14.71l0,-8.96l-17.33,-14.71z"
android:strokeLineJoin="round"
android:strokeWidth="1.76"
android:fillColor="#FFFFFF"
android:strokeColor="#490260"
android:fillType="nonZero"
android:strokeLineCap="round"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- Used for rounding the corners of a button -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#03DAC5" />
<corners android:radius="4dp" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<gradient
android:angle="225"
android:startColor="#FF7329A4"
android:centerColor="#FF3A3274"
android:endColor="#FF3A3274"
android:type="linear" />
</shape>
</item>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp" >
<ImageView
android:id="@+id/tor_bootstrap_network_settings_button"
app:srcCompat="@drawable/ic_settings"
android:scaleType="fitCenter"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="20dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/tor_bootstrap_image"
app:srcCompat="@drawable/ic_tor_connect_computer_graphic"
android:scaleType="fitCenter"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="80dp"
android:layout_marginBottom="10dp"
android:layout_centerHorizontal="true"
android:layout_below="@id/tor_bootstrap_network_settings_button"
android:layout_above="@id/tor_bootstrap_connect_button"
android:gravity="center"
tools:ignore="ContentDescription" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/quick_start_toggle"
android:visibility="gone"
android:checked="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/tor_bootstrap_connect_button" />
<TextView
android:id="@+id/quick_start_label"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/library_item_icon_margin_horizontal"
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
android:layout_above="@id/tor_bootstrap_quick_start_description"
android:layout_toEndOf="@id/quick_start_toggle"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:clickable="false"
android:textAppearance="@style/ListItemTextStyle"
android:textColor="#EEEEEE"
android:text="@string/tor_bootstrap_quick_start_label"
android:textSize="18sp" />
<TextView
android:id="@+id/tor_bootstrap_quick_start_description"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/library_item_icon_margin_horizontal"
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:clickable="false"
android:layout_marginBottom="20dp"
android:textAppearance="@style/ListItemTextStyle"
android:layout_toEndOf="@id/quick_start_toggle"
android:layout_above="@id/tor_bootstrap_connect_button"
android:textColor="#FFBBBBBB"
android:text="@string/tor_bootstrap_quick_start_disabled"
android:lines="3"
android:textSize="18sp" />
<Button
android:id="@+id/tor_bootstrap_connect_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="160dp"
android:height="36dp"
android:layout_marginBottom="10dp"
android:layout_centerHorizontal="true"
android:layout_above="@id/tor_bootstrap_status_message"
android:gravity="center|center_vertical"
android:text="@string/tor_bootstrap_connect"
android:fontFamily="Roboto-Medium"
android:textColor="#FF000000"
android:textSize="18sp"
android:lineSpacingMultiplier="0.89"
android:background="@drawable/rounded_corners" />
<TextView
android:id="@+id/tor_bootstrap_status_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:layout_centerHorizontal="true"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:gravity="center"
android:lines="3"
android:layout_above="@id/tor_bootstrap_swipe_log" />
<TextView
android:id="@+id/tor_bootstrap_swipe_log"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:width="360dp"
android:height="24dp"
android:layout_marginBottom="15dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:gravity="center"
android:textSize="14sp"
android:textColor="#FFFFFFFF"
android:fontFamily="Roboto-Regular"
android:lineSpacingMultiplier="1.71"
android:text="@string/tor_bootstrap_swipe_for_logs"
android:layout_above="@id/tor_bootstrap_progress" />
<ProgressBar
android:id="@+id/tor_bootstrap_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="3dp"
android:visibility="invisible"
android:layout_alignParentBottom="true" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tor_bootstrap_log_entries"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom"
android:textColor="@android:color/white"
android:fontFamily="RobotoMono-Regular"
android:textSize="12sp"
android:textIsSelectable="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="20dp" />
</FrameLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<com.google.android.material.appbar.AppBarLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="?torBootstrapBackground">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/bootstrap_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.google.android.material.appbar.AppBarLayout>
......@@ -29,6 +29,7 @@
<!-- Misc -->
<attr name="homeBackground" format="reference"/>
<attr name="torBootstrapBackground" format="reference"/>
<attr name="bottomBarBackground" format="reference"/>
<attr name="bottomBarBackgroundTop" format="reference"/>
<attr name="privateBrowsingButtonBackground" format="reference" />
......
......@@ -252,6 +252,7 @@
<!-- Drawables -->
<item name="fenixLogo">@drawable/ic_logo_wordmark_private</item>
<item name="homeBackground">@drawable/private_home_background_gradient</item>
<item name="torBootstrapBackground">@drawable/tor_bootstrap_background_gradient</item>
<item name="bottomBarBackground">@drawable/private_home_bottom_bar_background_gradient</item>
<item name="bottomBarBackgroundTop">@drawable/private_home_bottom_bar_background_gradient_top</item>
<item name="privateBrowsingButtonBackground">@color/primary_text_private_theme</item>
......
......@@ -8,4 +8,13 @@
<string name="preferences_donate">Donate to The Tor Project</string>
<!-- Preference for allowing screenshots to be taken in the app-->
<string name="preferences_allow_screenshots">Allow screenshots</string>
<string name="tor_bootstrap_connect">Connect</string>
<string name="tor_bootstrap_connecting">Connecting</string>
<string name="tor_bootstrap_connecting_failed">Connecting Failed</string>
<string name="tor_bootstrap_quick_start_label">Quick Start</string>
<string name="tor_bootstrap_quick_start_disabled">Enable Quick Start to connect automatically in the future</string>
<string name="tor_bootstrap_quick_start_enabled">%s will connect automatically to the Tor Network in the future</string>
<string name="tor_bootstrap_swipe_for_logs">Swipe to the left to see Tor logs</string>
<string name="tor_initializing_log">Initializing Tor Log</string>
</resources>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment