1
0
Fork 0

Closes #347: Adds private browsing logic (#506)

* For #347: Adds private browsing logic

*  For #347: Adds private session explainer

* Adds persistence
master
Sawyer Blatz 2019-02-15 09:31:03 -08:00 committed by GitHub
parent fe74df8b7f
commit e5fe80f928
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 244 additions and 53 deletions

View File

@ -0,0 +1,51 @@
/* 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
import android.preference.PreferenceManager
interface BrowsingModeManager {
enum class Mode {
Normal, Private
}
}
var temporaryModeStorage: BrowsingModeManager.Mode? = null
class DefaultBrowsingModeManager(private val homeActivity: HomeActivity) : BrowsingModeManager {
val isPrivate: Boolean
get() = mode == BrowsingModeManager.Mode.Private
var mode: BrowsingModeManager.Mode
get() = temporaryModeStorage!!
set(value) {
temporaryModeStorage = value
updateTheme(value)
setPreference()
}
private fun updateTheme(mode: BrowsingModeManager.Mode) {
homeActivity.themeManager.apply {
val newTheme = when (mode) {
BrowsingModeManager.Mode.Normal -> ThemeManager.Theme.Light
BrowsingModeManager.Mode.Private -> ThemeManager.Theme.Private
}
setTheme(newTheme)
}
}
private fun setPreference() {
PreferenceManager.getDefaultSharedPreferences(homeActivity)
.edit().putBoolean(homeActivity.getString(R.string.pref_key_private_mode), isPrivate).apply()
}
init {
if (temporaryModeStorage == null) {
mode = when (PreferenceManager.getDefaultSharedPreferences(homeActivity)
.getBoolean(homeActivity.getString(R.string.pref_key_private_mode), false)) {
true -> BrowsingModeManager.Mode.Private
false -> BrowsingModeManager.Mode.Normal
}
}
}
}

View File

@ -29,8 +29,12 @@ open class HomeActivity : AppCompatActivity() {
}
}
lateinit var browsingModeManager: DefaultBrowsingModeManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
browsingModeManager = DefaultBrowsingModeManager(this)
setContentView(R.layout.activity_home)
setTheme(themeManager.currentTheme)
@ -85,7 +89,7 @@ open class HomeActivity : AppCompatActivity() {
private fun openToBrowser() {
val sessionId = SafeIntent(intent).getStringExtra(IntentProcessor.ACTIVE_SESSION_ID)
val host = supportFragmentManager.findFragmentById(R.id.container) as NavHostFragment
val directions = NavGraphDirections.actionGlobalBrowser(sessionId)
val directions = NavGraphDirections.actionGlobalBrowser(sessionId, browsingModeManager.isPrivate)
host.navController.navigate(directions)
}

View File

@ -8,7 +8,6 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.preference.PreferenceManager
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
@ -31,6 +30,7 @@ import mozilla.components.feature.session.SessionFeature
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import org.mozilla.fenix.BrowsingModeManager
import org.mozilla.fenix.DefaultThemeManager
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
@ -56,6 +56,7 @@ class BrowserFragment : Fragment(), BackHandler {
private val findInPageIntegration = ViewBoundFeatureWrapper<FindInPageIntegration>()
private val customTabsToolbarFeature = ViewBoundFeatureWrapper<CustomTabsToolbarFeature>()
private val toolbarIntegration = ViewBoundFeatureWrapper<ToolbarIntegration>()
private var isPrivate = false
var sessionId: String? = null
override fun onCreateView(
@ -63,12 +64,16 @@ class BrowserFragment : Fragment(), BackHandler {
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
require(arguments != null)
sessionId = BrowserFragmentArgs.fromBundle(arguments!!).sessionId
isPrivate = BrowserFragmentArgs.fromBundle(arguments!!).isPrivateTab
val view = inflater.inflate(R.layout.fragment_browser, container, false)
toolbarComponent = ToolbarComponent(
view.browserLayout,
ActionBusFactory.get(this), sessionId,
isPrivate,
SearchState("", isEditing = false)
)
@ -105,7 +110,8 @@ class BrowserFragment : Fragment(), BackHandler {
when (it) {
is SearchAction.ToolbarTapped -> Navigation.findNavController(toolbar)
.navigate(BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
requireComponents.core.sessionManager.selectedSession?.id
requireComponents.core.sessionManager.selectedSession?.id,
(activity as HomeActivity).browsingModeManager.isPrivate
))
is SearchAction.ToolbarMenuItemTapped -> handleToolbarItemInteraction(it)
}
@ -224,12 +230,11 @@ class BrowserFragment : Fragment(), BackHandler {
ToolbarMenu.Item.Share -> requireComponents.core.sessionManager
.selectedSession?.url?.apply { requireContext().share(this) }
ToolbarMenu.Item.NewPrivateTab -> {
PreferenceManager.getDefaultSharedPreferences(context)
.edit().putBoolean(
context!!.getString(R.string.pref_key_private_mode),
!PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context!!.getString(R.string.pref_key_private_mode), false)
).apply()
val directions = BrowserFragmentDirections
.actionBrowserFragmentToSearchFragment(requireComponents.core.sessionManager.selectedSession?.id,
(activity as HomeActivity).browsingModeManager.isPrivate)
Navigation.findNavController(view!!).navigate(directions)
(activity as HomeActivity).browsingModeManager.mode = BrowsingModeManager.Mode.Private
}
ToolbarMenu.Item.FindInPage -> FindInPageIntegration.launch?.invoke()
ToolbarMenu.Item.ReportIssue -> requireComponents.core.sessionManager
@ -241,9 +246,11 @@ class BrowserFragment : Fragment(), BackHandler {
// TODO Help
}
ToolbarMenu.Item.NewTab -> {
val directions =
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(null)
val directions = BrowserFragmentDirections
.actionBrowserFragmentToSearchFragment(null,
(activity as HomeActivity).browsingModeManager.isPrivate)
Navigation.findNavController(view!!).navigate(directions)
(activity as HomeActivity).browsingModeManager.mode = BrowsingModeManager.Mode.Normal
}
}
}

View File

@ -21,6 +21,7 @@ class ToolbarComponent(
private val container: ViewGroup,
bus: ActionBusFactory,
private val sessionId: String?,
private val isPrivate: Boolean,
override var initialState: SearchState = SearchState("", false)
) :
UIComponent<SearchState, SearchAction, SearchChange>(
@ -34,7 +35,7 @@ class ToolbarComponent(
}
}
override fun initView() = ToolbarUIView(sessionId, container, actionEmitter, changesObservable)
override fun initView() = ToolbarUIView(sessionId, isPrivate, container, actionEmitter, changesObservable)
init {
render(reducer)
applyTheme()

View File

@ -9,6 +9,7 @@ import android.graphics.PorterDuff
import androidx.core.content.ContextCompat
import androidx.navigation.Navigation
import mozilla.components.browser.domains.autocomplete.DomainAutocompleteProvider
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.session.runWithSession
import mozilla.components.browser.toolbar.BrowserToolbar
@ -28,7 +29,8 @@ class ToolbarIntegration(
domainAutocompleteProvider: DomainAutocompleteProvider,
historyStorage: HistoryStorage,
sessionManager: SessionManager,
sessionId: String? = null
sessionId: String? = null,
isPrivate: Boolean
) : LifecycleAwareFeature {
init {
toolbar.setMenuBuilder(toolbarMenu.menuBuilder)
@ -68,10 +70,15 @@ class ToolbarIntegration(
toolbar,
context.components.core.sessionManager,
if (sessionId == null) {
context.components.useCases.tabsUseCases.addTab
if (isPrivate) {
context.components.useCases.tabsUseCases.addPrivateTab
} else {
context.components.useCases.tabsUseCases.addTab
}
} else context.components.useCases.sessionUseCases.loadUrl,
{ searchTerms -> if (sessionId == null) {
context.components.useCases.searchUseCases.newTabSearch.invoke(searchTerms)
context.components.useCases.searchUseCases.newTabSearch
.invoke(searchTerms, Session.Source.USER_ENTERED, true, isPrivate)
} else context.components.useCases.searchUseCases.defaultSearch.invoke(searchTerms) },
sessionId
)

View File

@ -19,6 +19,7 @@ import org.mozilla.fenix.mvi.UIView
class ToolbarUIView(
sessionId: String?,
isPrivate: Boolean,
container: ViewGroup,
actionEmitter: Observer<SearchAction>,
changesObservable: Observable<SearchChange>
@ -72,7 +73,8 @@ class ToolbarUIView(
ShippedDomainsProvider().also { it.initialize(this) },
components.core.historyStorage,
components.core.sessionManager,
sessionId
sessionId,
isPrivate
)
}
}

View File

@ -21,9 +21,9 @@ import mozilla.components.browser.menu.BrowserMenu
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import org.mozilla.fenix.DefaultThemeManager
import org.mozilla.fenix.BrowsingModeManager
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.ThemeManager
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.home.sessions.SessionsComponent
import org.mozilla.fenix.home.tabs.TabsAction
@ -47,8 +47,9 @@ class HomeFragment : Fragment() {
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_home, container, false)
TabsComponent(view.homeLayout, bus, TabsState(requireComponents.core.sessionManager.sessions))
SessionsComponent(view.homeLayout, bus)
TabsComponent(view.homeLayout, bus, (activity as HomeActivity).browsingModeManager.isPrivate,
TabsState(requireComponents.core.sessionManager.sessions))
SessionsComponent(view.homeLayout, bus, (activity as HomeActivity).browsingModeManager.isPrivate)
layoutComponents(view)
ActionBusFactory.get(this).logMergedObservables()
val activity = activity as HomeActivity
@ -68,7 +69,8 @@ class HomeFragment : Fragment() {
when (it) {
is TabsAction.Select -> {
requireComponents.core.sessionManager.select(it.session)
val directions = HomeFragmentDirections.actionHomeFragmentToBrowserFragment(it.session.id)
val directions = HomeFragmentDirections.actionHomeFragmentToBrowserFragment(it.session.id,
(activity as HomeActivity).browsingModeManager.isPrivate)
Navigation.findNavController(view).navigate(directions)
}
is TabsAction.Close -> {
@ -90,8 +92,9 @@ class HomeFragment : Fragment() {
view.toolbar.setCompoundDrawablesWithIntrinsicBounds(searchIcon, null, null, null)
val roundToInt = (toolbarPaddingDp * Resources.getSystem().displayMetrics.density).roundToInt()
view.toolbar.compoundDrawablePadding = roundToInt
view.toolbar.setOnClickListener {
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null)
view.toolbar.setOnClickListener { it ->
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null,
(activity as HomeActivity).browsingModeManager.isPrivate)
Navigation.findNavController(it).navigate(directions)
}
@ -129,14 +132,10 @@ class HomeFragment : Fragment() {
.isPrivate()
privateBrowsingButton.setOnClickListener {
// When we build out private mode we will want to handle this logic elsewhere.
(requireActivity() as HomeActivity).themeManager.apply {
val newTheme = when (this.currentTheme) {
ThemeManager.Theme.Light -> ThemeManager.Theme.Private
ThemeManager.Theme.Private -> ThemeManager.Theme.Light
}
setTheme(newTheme)
val browsingModeManager = (activity as HomeActivity).browsingModeManager
browsingModeManager.mode = when (browsingModeManager.mode) {
BrowsingModeManager.Mode.Normal -> BrowsingModeManager.Mode.Private
BrowsingModeManager.Mode.Private -> BrowsingModeManager.Mode.Normal
}
}
}
@ -144,6 +143,7 @@ class HomeFragment : Fragment() {
override fun onResume() {
super.onResume()
sessionObserver = subscribeToSessions()
sessionObserver?.onSessionsRestored()
}
override fun onPause() {
@ -170,31 +170,36 @@ class HomeFragment : Fragment() {
override fun onSessionAdded(session: Session) {
super.onSessionAdded(session)
getManagedEmitter<TabsChange>().onNext(
TabsChange.Changed(requireComponents.core.sessionManager.sessions))
TabsChange.Changed(requireComponents.core.sessionManager.sessions
.filter { (activity as HomeActivity).browsingModeManager.isPrivate == it.private }))
}
override fun onSessionRemoved(session: Session) {
super.onSessionRemoved(session)
getManagedEmitter<TabsChange>().onNext(
TabsChange.Changed(requireComponents.core.sessionManager.sessions))
TabsChange.Changed(requireComponents.core.sessionManager.sessions
.filter { (activity as HomeActivity).browsingModeManager.isPrivate == it.private }))
}
override fun onSessionSelected(session: Session) {
super.onSessionSelected(session)
getManagedEmitter<TabsChange>().onNext(
TabsChange.Changed(requireComponents.core.sessionManager.sessions))
TabsChange.Changed(requireComponents.core.sessionManager.sessions
.filter { (activity as HomeActivity).browsingModeManager.isPrivate == it.private }))
}
override fun onSessionsRestored() {
super.onSessionsRestored()
getManagedEmitter<TabsChange>().onNext(
TabsChange.Changed(requireComponents.core.sessionManager.sessions))
TabsChange.Changed(requireComponents.core.sessionManager.sessions
.filter { (activity as HomeActivity).browsingModeManager.isPrivate == it.private }))
}
override fun onAllSessionsRemoved() {
super.onAllSessionsRemoved()
getManagedEmitter<TabsChange>().onNext(
TabsChange.Changed(requireComponents.core.sessionManager.sessions))
TabsChange.Changed(requireComponents.core.sessionManager.sessions
.filter { (activity as HomeActivity).browsingModeManager.isPrivate == it.private }))
}
}
requireComponents.core.sessionManager.register(observer)

View File

@ -4,6 +4,10 @@
package org.mozilla.fenix.home.sessions
import android.content.Context
import android.text.SpannableString
import android.text.style.ClickableSpan
import android.text.style.ForegroundColorSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -12,38 +16,70 @@ import androidx.recyclerview.widget.RecyclerView
import org.mozilla.fenix.R
class SessionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var isPrivate = false
var context: Context? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return when (viewType) {
HeaderViewHolder.LAYOUT_ID -> HeaderViewHolder(view)
EmptyListViewHolder.LAYOUT_ID -> EmptyListViewHolder(view)
PrivateEmptyListViewHolder.LAYOUT_ID -> PrivateEmptyListViewHolder(view)
else -> EmptyListViewHolder(view)
}
}
override fun getItemViewType(position: Int) = when (position) {
0 -> HeaderViewHolder.LAYOUT_ID
1 -> EmptyListViewHolder.LAYOUT_ID
1 -> if (isPrivate) PrivateEmptyListViewHolder.LAYOUT_ID else EmptyListViewHolder.LAYOUT_ID
else -> -1
}
override fun getItemCount(): Int = 2
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is HeaderViewHolder) {
holder.headerText.text = "Today"
when (holder) {
is HeaderViewHolder -> if (isPrivate) {
holder.headerText.text = "Private Session"
} else {
holder.headerText.text = "Today"
}
is PrivateEmptyListViewHolder -> {
// Format the description text to include a hyperlink
val descriptionText = String
.format(holder.description.text.toString(), System.getProperty("line.separator"))
val linkStartIndex = descriptionText.indexOf("\n\n") + 2
val linkAction = object : ClickableSpan() {
override fun onClick(widget: View?) {
// TODO Go to SUMO page
}
}
val textWithLink = SpannableString(descriptionText).apply {
setSpan(linkAction, linkStartIndex, descriptionText.length, 0)
val colorSpan = ForegroundColorSpan(holder.description.currentTextColor)
setSpan(colorSpan, linkStartIndex, descriptionText.length, 0)
}
holder.description.text = textWithLink
}
}
}
private class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
val headerText = view.findViewById<TextView>(R.id.header_text)
companion object {
const val LAYOUT_ID = R.layout.session_list_header
}
}
private class PrivateEmptyListViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
val description = view.findViewById<TextView>(R.id.session_description)
companion object {
const val LAYOUT_ID = R.layout.session_list_empty_private
}
}
private class EmptyListViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
companion object {
const val LAYOUT_ID = R.layout.session_list_empty

View File

@ -15,6 +15,7 @@ import org.mozilla.fenix.mvi.ViewState
class SessionsComponent(
private val container: ViewGroup,
bus: ActionBusFactory,
private val isPrivate: Boolean,
override var initialState: SessionsState = SessionsState(emptyList())
) :
UIComponent<SessionsState, SessionsAction, SessionsChange>(
@ -28,19 +29,19 @@ class SessionsComponent(
}
}
override fun initView() = SessionsUIView(container, actionEmitter, changesObservable)
override fun initView() = SessionsUIView(container, actionEmitter, isPrivate, changesObservable)
init {
render(reducer)
}
}
data class SessionsState(val sessions: List<Session>) : ViewState
data class SessionsState(val sessions: List<Session>, val isPrivate: Boolean = false) : ViewState
sealed class SessionsAction : Action {
object Select : SessionsAction()
}
sealed class SessionsChange : Change {
object Changed : SessionsChange()
data class Changed(val isPrivate: Boolean) : SessionsChange()
}

View File

@ -17,6 +17,7 @@ import org.mozilla.fenix.mvi.UIView
class SessionsUIView(
container: ViewGroup,
actionEmitter: Observer<SessionsAction>,
isPrivate: Boolean,
changesObservable: Observable<SessionsChange>
) :
UIView<SessionsState, SessionsAction, SessionsChange>(container, actionEmitter, changesObservable) {
@ -30,6 +31,8 @@ class SessionsUIView(
init {
view.apply {
layoutManager = LinearLayoutManager(container.context)
sessionsAdapter.isPrivate = isPrivate
sessionsAdapter.context = context
adapter = sessionsAdapter
}
}

View File

@ -15,6 +15,7 @@ import org.mozilla.fenix.mvi.ViewState
class TabsComponent(
private val container: ViewGroup,
bus: ActionBusFactory,
private val isPrivate: Boolean,
override var initialState: TabsState = TabsState(listOf())
) :
UIComponent<TabsState, TabsAction, TabsChange>(
@ -28,7 +29,7 @@ class TabsComponent(
}
}
override fun initView() = TabsUIView(container, actionEmitter, changesObservable)
override fun initView() = TabsUIView(container, actionEmitter, isPrivate, changesObservable)
init {
render(reducer)

View File

@ -28,6 +28,7 @@ import org.mozilla.fenix.mvi.UIView
class TabsUIView(
container: ViewGroup,
actionEmitter: Observer<TabsAction>,
isPrivate: Boolean,
changesObservable: Observable<TabsChange>
) :
UIView<TabsState, TabsAction, TabsChange>(container, actionEmitter, changesObservable) {
@ -50,7 +51,8 @@ class TabsUIView(
}
header.add_tab_button.increaseTapArea(HomeFragment.addTabButtonIncreaseDps)
header.add_tab_button.setOnClickListener {
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null)
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null,
isPrivate)
Navigation.findNavController(it).navigate(directions)
}
header.tabs_overflow_button.increaseTapArea(HomeFragment.overflowButtonIncreaseDps)

View File

@ -21,6 +21,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.mvi.ActionBusFactory
@ -57,7 +58,8 @@ class HistoryFragment : Fragment(), CoroutineScope {
if (it is HistoryAction.Select) {
Navigation.findNavController(requireActivity(), R.id.container).apply {
navigate(
HistoryFragmentDirections.actionGlobalBrowser(null),
HistoryFragmentDirections.actionGlobalBrowser(null,
(activity as HomeActivity).browsingModeManager.isPrivate),
NavOptions.Builder().setPopUpTo(R.id.homeFragment, false).build()
)
}

View File

@ -13,6 +13,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.fragment_search.view.*
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.getManagedEmitter
@ -36,11 +37,13 @@ class SearchFragment : Fragment() {
savedInstanceState: Bundle?
): View? {
val sessionId = SearchFragmentArgs.fromBundle(arguments!!).sessionId
val isPrivate = SearchFragmentArgs.fromBundle(arguments!!).isPrivateTab
val view = inflater.inflate(R.layout.fragment_search, container, false)
toolbarComponent = ToolbarComponent(
view.toolbar_wrapper,
ActionBusFactory.get(this),
sessionId,
isPrivate,
SearchState("", isEditing = true)
)
awesomeBarComponent = AwesomeBarComponent(
@ -83,7 +86,9 @@ class SearchFragment : Fragment() {
private fun transitionToBrowser() {
val sessionId = SearchFragmentArgs.fromBundle(arguments!!).sessionId
val directions = SearchFragmentDirections.actionSearchFragmentToBrowserFragment(sessionId)
val directions = SearchFragmentDirections.actionSearchFragmentToBrowserFragment(sessionId,
(activity as HomeActivity).browsingModeManager.isPrivate)
Navigation.findNavController(view!!.search_layout).navigate(directions)
}
}

View File

@ -88,7 +88,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
Navigation.findNavController(it)
.navigate(
SettingsFragmentDirections.actionGlobalBrowser(
null
null, false
)
)
}

View File

@ -0,0 +1,11 @@
<?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/. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
<solid android:color="@color/photonGrey40" />
<corners
android:bottomLeftRadius="7dp"
android:bottomRightRadius="7dp" />
</shape>

View File

@ -0,0 +1,13 @@
<?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/. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="2dp"
android:color="?attr/sessionBorderColor"/>
<padding android:left="1dp" android:top="1dp" android:right="1dp" android:bottom="1dp"/>
<corners android:radius="8dp"/>
</shape>

View File

@ -0,0 +1,24 @@
<?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/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/private_no_sessions"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/session_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:ellipsize="none"
android:gravity="center_vertical"
android:scrollHorizontally="false"
android:text="@string/private_browsing_explanation"
android:textColor="@color/off_white"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -5,8 +5,8 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/header_text"
android:layout_marginBottom="8dp"
android:textSize="12dp"
android:textColor="@color/session_list_header"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:textColor="?attr/toolbarTextColor"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</TextView>

View File

@ -16,7 +16,7 @@
android:layout_height="wrap_content"
android:text="@string/tabs_header_title"
android:textSize="24sp"
android:textColor="@android:color/black"
android:textColor="?attr/toolbarTextColor"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
@ -25,6 +25,7 @@
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:src="@drawable/ic_new"
android:tint="?attr/toolbarTextColor"
android:baselineAlignBottom="true"
app:layout_constraintBaseline_toBaselineOf="@id/header_text"
app:layout_constraintEnd_toStartOf="@id/tabs_overflow_button"/>
@ -33,6 +34,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:tint="?attr/toolbarTextColor"
android:src="@drawable/ic_menu"
android:baselineAlignBottom="true"
app:layout_constraintBaseline_toBaselineOf="@id/header_text"

View File

@ -8,9 +8,11 @@
android:layout_marginBottom="8dp"
android:padding="10dp"
android:elevation="5dp"
app:cardCornerRadius="10dp">
app:cardCornerRadius="10dp"
android:clipChildren="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:background="@drawable/session_border"
android:id="@+id/item_tab"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -50,7 +52,10 @@
<ImageView android:layout_width="0dp"
android:layout_height="100dp"
android:src="@color/photonBlue40"
android:layout_marginStart="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
android:background="@drawable/session_background"
app:layout_constraintTop_toBottomOf="@id/text_url"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"

View File

@ -38,6 +38,7 @@
app:destination="@id/browserFragment"
app:popUpTo="@id/homeFragment" />
<argument android:name="session_id" app:argType="string" app:nullable="true"/>
<argument android:name="is_private_tab" app:argType="boolean"/>
</fragment>
<fragment
@ -57,6 +58,9 @@
android:name="session_id"
app:argType="string"
app:nullable="true" />
<argument
android:name="is_private_tab"
app:argType="boolean"/>
<action
android:id="@+id/action_browserFragment_to_settingsFragment"
app:destination="@id/settingsFragment" />

View File

@ -23,6 +23,7 @@
<attr name="toolbarTextColor" format="reference"/>
<attr name="navigationBarColorHome" format="reference"/>
<attr name="homeDividerColor" format="reference" />
<attr name="sessionBorderColor" format="reference"/>
<!-- Search fragment -->
<attr name="searchBackground" format="reference"/>

View File

@ -21,6 +21,7 @@
<color name="off_white">#f9f9fa</color>
<color name="url_box_view">#E9E9ED</color>
<color name="icons">#20233E</color>
<color name="session_border_color">#2f26c1</color>
<color name="session_list_empty_bg">#1A665BFD</color>
<color name="session_list_empty_fg">#544CD9</color>

View File

@ -21,7 +21,8 @@
<!-- Explanation for private browsing displayed to users on home view when they first enable private mode -->
<string name="private_browsing_explanation">Fenix clears your search and browsing history when you close all
Private Sessions tabs. While this doesn\'t make you anonymous to websites or your internet service provider,
it makes it easier to keep what you do online private from anyone else who uses this device.</string>
it makes it easier to keep what you do online private from anyone else who uses this device.\n\nCommon myths
about private browsing</string>
<!-- Delete session button to erase your history in a private session -->
<string name="private_browsing_delete_session">Delete Session</string>

View File

@ -24,6 +24,7 @@
<item name="toolbarWrapperBackground">@drawable/home_search_background_light</item>
<item name="toolbarTextColor">@color/search_text</item>
<item name="homeDividerColor">@color/photonGrey30</item>
<item name="sessionBorderColor">@color/session_border_color</item>
<!-- Search fragment colors -->
<item name="searchBackground">@color/off_white</item>
@ -64,6 +65,7 @@
<item name="toolbarWrapperBackground">@drawable/home_search_background_private</item>
<item name="toolbarTextColor">@color/off_white</item>
<item name="homeDividerColor">@color/search_private_background</item>
<item name="sessionBorderColor">@color/private_browsing_primary</item>
<!-- Search fragment colors -->
<item name="searchBackground">@color/private_browsing_bottom_gradient</item>