For #633 - Gives the user the ability to save their session
parent
46789f4c62
commit
416382c3ad
|
@ -23,6 +23,7 @@ import kotlinx.android.synthetic.main.fragment_home.view.*
|
|||
import mozilla.components.browser.menu.BrowserMenu
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.feature.session.bundling.SessionBundleStorage
|
||||
import org.mozilla.fenix.BrowsingModeManager
|
||||
import org.mozilla.fenix.DefaultThemeManager
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
|
@ -42,6 +43,12 @@ import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
|||
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
fun SessionBundleStorage.archive(sessionManager: SessionManager) {
|
||||
save(sessionManager.createSnapshot())
|
||||
sessionManager.removeAll()
|
||||
new()
|
||||
}
|
||||
|
||||
class HomeFragment : Fragment() {
|
||||
private val bus = ActionBusFactory.get(this)
|
||||
private var sessionObserver: SessionManager.Observer? = null
|
||||
|
@ -56,7 +63,9 @@ class HomeFragment : Fragment() {
|
|||
): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_home, container, false)
|
||||
val sessionManager = requireComponents.core.sessionManager
|
||||
tabsComponent = TabsComponent(view.homeContainer, bus, (activity as HomeActivity).browsingModeManager.isPrivate,
|
||||
tabsComponent = TabsComponent(
|
||||
view.homeContainer,
|
||||
bus,
|
||||
TabsState(sessionManager.sessions.map { it.toSessionViewState(it == sessionManager.selectedSession) })
|
||||
)
|
||||
sessionsComponent = SessionsComponent(view.homeContainer, bus)
|
||||
|
@ -76,7 +85,7 @@ class HomeFragment : Fragment() {
|
|||
|
||||
sessionsComponent.view.visibility = if ((activity as HomeActivity).browsingModeManager.isPrivate)
|
||||
View.GONE else View.VISIBLE
|
||||
tabsComponent.view.isNestedScrollingEnabled = false
|
||||
tabsComponent.tabList.isNestedScrollingEnabled = false
|
||||
sessionsComponent.view.isNestedScrollingEnabled = false
|
||||
|
||||
val bundles = requireComponents.core.sessionStorage.bundles(temporaryNumberOfSessions)
|
||||
|
@ -159,6 +168,9 @@ class HomeFragment : Fragment() {
|
|||
getAutoDisposeObservable<TabsAction>()
|
||||
.subscribe {
|
||||
when (it) {
|
||||
is TabsAction.Archive -> {
|
||||
requireComponents.core.sessionStorage.archive(requireComponents.core.sessionManager)
|
||||
}
|
||||
is TabsAction.Select -> {
|
||||
val session = requireComponents.core.sessionManager.findSessionById(it.sessionId)
|
||||
requireComponents.core.sessionManager.select(session!!)
|
||||
|
@ -177,6 +189,7 @@ class HomeFragment : Fragment() {
|
|||
.subscribe {
|
||||
when (it) {
|
||||
is SessionsAction.Select -> {
|
||||
requireComponents.core.sessionStorage.archive(requireComponents.core.sessionManager)
|
||||
it.archivedSession.bundle.restoreSnapshot(requireComponents.core.engine)?.apply {
|
||||
requireComponents.core.sessionManager.restore(this)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ package org.mozilla.fenix.home.tabs
|
|||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.component_tabs.view.*
|
||||
import mozilla.components.browser.session.Session
|
||||
import org.mozilla.fenix.mvi.Action
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
|
@ -16,7 +17,6 @@ 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>(
|
||||
|
@ -30,9 +30,9 @@ class TabsComponent(
|
|||
}
|
||||
}
|
||||
|
||||
override fun initView() = TabsUIView(container, actionEmitter, isPrivate, changesObservable)
|
||||
val view: RecyclerView
|
||||
get() = uiView.view as RecyclerView
|
||||
override fun initView() = TabsUIView(container, actionEmitter, changesObservable)
|
||||
val tabList: RecyclerView
|
||||
get() = uiView.view.tabs_list as RecyclerView
|
||||
|
||||
init {
|
||||
render(reducer)
|
||||
|
@ -47,6 +47,7 @@ fun Session.toSessionViewState(selected: Boolean): SessionViewState {
|
|||
}
|
||||
|
||||
sealed class TabsAction : Action {
|
||||
object Archive : TabsAction()
|
||||
data class Select(val sessionId: String) : TabsAction()
|
||||
data class Close(val sessionId: String) : TabsAction()
|
||||
}
|
||||
|
|
|
@ -8,16 +8,15 @@ import android.app.Activity
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.navigation.Navigation
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.functions.Consumer
|
||||
import kotlinx.android.synthetic.main.tab_list_header.view.*
|
||||
import kotlinx.android.synthetic.main.component_tabs.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.increaseTapArea
|
||||
import org.mozilla.fenix.home.HomeFragment
|
||||
|
@ -28,46 +27,57 @@ 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) {
|
||||
|
||||
private val header: ConstraintLayout = LayoutInflater.from(container.context)
|
||||
.inflate(R.layout.tab_list_header, container, true)
|
||||
.findViewById(R.id.tabs_header)
|
||||
|
||||
override val view: RecyclerView = LayoutInflater.from(container.context)
|
||||
override val view: View = LayoutInflater.from(container.context)
|
||||
.inflate(R.layout.component_tabs, container, true)
|
||||
.findViewById(R.id.tabs_list)
|
||||
|
||||
private val tabsAdapter = TabsAdapter(actionEmitter)
|
||||
|
||||
init {
|
||||
view.apply {
|
||||
view.tabs_list.apply {
|
||||
layoutManager = LinearLayoutManager(container.context)
|
||||
adapter = tabsAdapter
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
}
|
||||
header.add_tab_button.increaseTapArea(HomeFragment.addTabButtonIncreaseDps)
|
||||
header.add_tab_button.setOnClickListener {
|
||||
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null)
|
||||
Navigation.findNavController(it).navigate(directions)
|
||||
}
|
||||
header.tabs_overflow_button.increaseTapArea(HomeFragment.overflowButtonIncreaseDps)
|
||||
header.tabs_overflow_button.setOnClickListener {
|
||||
if (view.context as? Activity != null) {
|
||||
CurrentSessionBottomSheetFragment().show(
|
||||
(view.context as FragmentActivity).supportFragmentManager,
|
||||
overflowFragmentTag
|
||||
)
|
||||
view.apply {
|
||||
add_tab_button.increaseTapArea(HomeFragment.addTabButtonIncreaseDps)
|
||||
add_tab_button.setOnClickListener {
|
||||
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null)
|
||||
Navigation.findNavController(it).navigate(directions)
|
||||
}
|
||||
tabs_overflow_button.increaseTapArea(HomeFragment.overflowButtonIncreaseDps)
|
||||
tabs_overflow_button.setOnClickListener {
|
||||
if (view.context as? Activity != null) {
|
||||
CurrentSessionBottomSheetFragment().show(
|
||||
(view.context as FragmentActivity).supportFragmentManager,
|
||||
overflowFragmentTag
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
save_session_button_text.apply {
|
||||
val color = ContextCompat.getColor(context, R.color.photonWhite)
|
||||
val drawable = ContextCompat.getDrawable(context, R.drawable.ic_archive)
|
||||
drawable?.setTint(color)
|
||||
this.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null)
|
||||
}
|
||||
|
||||
save_session_button.setOnClickListener {
|
||||
actionEmitter.onNext(TabsAction.Archive)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateView() = Consumer<TabsState> {
|
||||
tabsAdapter.sessions = it.sessions
|
||||
header.visibility = if (it.sessions.isEmpty()) View.GONE else View.VISIBLE
|
||||
|
||||
(if (it.sessions.isEmpty()) View.GONE else View.VISIBLE).also { visibility ->
|
||||
view.tabs_header.visibility = visibility
|
||||
view.save_session_button.visibility = visibility
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -1,8 +1,82 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
<!-- 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/. -->
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tabs_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"/>
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/tabs_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/tabs_header_title"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:textColor="?attr/toolbarTextColor"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/add_tab_button"
|
||||
android:layout_width="@dimen/glyph_button_width"
|
||||
android:layout_height="@dimen/glyph_button_height"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/ic_new"
|
||||
android:tint="?attr/toolbarTextColor"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/tabs_overflow_button"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/tabs_overflow_button"
|
||||
android:layout_width="@dimen/glyph_button_width"
|
||||
android:layout_height="@dimen/glyph_button_height"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/ic_menu"
|
||||
android:tint="?attr/toolbarTextColor"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tabs_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/save_session_button"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="@drawable/button_background"
|
||||
android:backgroundTint="@color/photonInk70"
|
||||
android:layout_margin="16dp"
|
||||
android:padding="6dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<TextView
|
||||
android:id="@+id/save_session_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/session_save"
|
||||
android:textColor="@color/photonWhite"
|
||||
android:drawableStart="@drawable/ic_archive"
|
||||
android:drawablePadding="8dp"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:layout_gravity="center" />
|
||||
</FrameLayout>
|
||||
</merge>
|
|
@ -5,7 +5,7 @@
|
|||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/delete_history_button"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="@drawable/delete_history_background"
|
||||
android:background="@drawable/button_background"
|
||||
android:layout_margin="16dp"
|
||||
android:padding="6dp"
|
||||
android:clickable="true"
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
<?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/tabs_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/tabs_header_title"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:textColor="?attr/toolbarTextColor"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/add_tab_button"
|
||||
android:layout_width="@dimen/glyph_button_width"
|
||||
android:layout_height="@dimen/glyph_button_height"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/ic_new"
|
||||
android:tint="?attr/toolbarTextColor"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/tabs_overflow_button"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/tabs_overflow_button"
|
||||
android:layout_width="@dimen/glyph_button_width"
|
||||
android:layout_height="@dimen/glyph_button_height"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/ic_menu"
|
||||
android:tint="?attr/toolbarTextColor"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -165,6 +165,10 @@ people before profit. Our mission: keep the Internet open and accessible to all.
|
|||
<!-- Content description (not visible, for screen readers etc.): Title icon for current session menu -->
|
||||
<string name="current_session_image">Current session image</string>
|
||||
|
||||
|
||||
<!-- Text for the button to save a session -->
|
||||
<string name="session_save">Save Session</string>
|
||||
|
||||
<!-- Text for the button to delete a single session -->
|
||||
<string name="session_item_delete">Delete</string>
|
||||
|
||||
|
|
Loading…
Reference in New Issue