1
0
Fork 0

For #633 - Gives the user the ability to save their session

master
Jeff Boek 2019-02-27 13:29:19 -08:00
parent 46789f4c62
commit 416382c3ad
8 changed files with 139 additions and 81 deletions

View File

@ -23,6 +23,7 @@ import kotlinx.android.synthetic.main.fragment_home.view.*
import mozilla.components.browser.menu.BrowserMenu import mozilla.components.browser.menu.BrowserMenu
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
import mozilla.components.feature.session.bundling.SessionBundleStorage
import org.mozilla.fenix.BrowsingModeManager import org.mozilla.fenix.BrowsingModeManager
import org.mozilla.fenix.DefaultThemeManager import org.mozilla.fenix.DefaultThemeManager
import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.HomeActivity
@ -42,6 +43,12 @@ import org.mozilla.fenix.mvi.getAutoDisposeObservable
import org.mozilla.fenix.mvi.getManagedEmitter import org.mozilla.fenix.mvi.getManagedEmitter
import kotlin.math.roundToInt import kotlin.math.roundToInt
fun SessionBundleStorage.archive(sessionManager: SessionManager) {
save(sessionManager.createSnapshot())
sessionManager.removeAll()
new()
}
class HomeFragment : Fragment() { class HomeFragment : Fragment() {
private val bus = ActionBusFactory.get(this) private val bus = ActionBusFactory.get(this)
private var sessionObserver: SessionManager.Observer? = null private var sessionObserver: SessionManager.Observer? = null
@ -56,7 +63,9 @@ class HomeFragment : Fragment() {
): View? { ): View? {
val view = inflater.inflate(R.layout.fragment_home, container, false) val view = inflater.inflate(R.layout.fragment_home, container, false)
val sessionManager = requireComponents.core.sessionManager 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) }) TabsState(sessionManager.sessions.map { it.toSessionViewState(it == sessionManager.selectedSession) })
) )
sessionsComponent = SessionsComponent(view.homeContainer, bus) sessionsComponent = SessionsComponent(view.homeContainer, bus)
@ -76,7 +85,7 @@ class HomeFragment : Fragment() {
sessionsComponent.view.visibility = if ((activity as HomeActivity).browsingModeManager.isPrivate) sessionsComponent.view.visibility = if ((activity as HomeActivity).browsingModeManager.isPrivate)
View.GONE else View.VISIBLE View.GONE else View.VISIBLE
tabsComponent.view.isNestedScrollingEnabled = false tabsComponent.tabList.isNestedScrollingEnabled = false
sessionsComponent.view.isNestedScrollingEnabled = false sessionsComponent.view.isNestedScrollingEnabled = false
val bundles = requireComponents.core.sessionStorage.bundles(temporaryNumberOfSessions) val bundles = requireComponents.core.sessionStorage.bundles(temporaryNumberOfSessions)
@ -159,6 +168,9 @@ class HomeFragment : Fragment() {
getAutoDisposeObservable<TabsAction>() getAutoDisposeObservable<TabsAction>()
.subscribe { .subscribe {
when (it) { when (it) {
is TabsAction.Archive -> {
requireComponents.core.sessionStorage.archive(requireComponents.core.sessionManager)
}
is TabsAction.Select -> { is TabsAction.Select -> {
val session = requireComponents.core.sessionManager.findSessionById(it.sessionId) val session = requireComponents.core.sessionManager.findSessionById(it.sessionId)
requireComponents.core.sessionManager.select(session!!) requireComponents.core.sessionManager.select(session!!)
@ -177,6 +189,7 @@ class HomeFragment : Fragment() {
.subscribe { .subscribe {
when (it) { when (it) {
is SessionsAction.Select -> { is SessionsAction.Select -> {
requireComponents.core.sessionStorage.archive(requireComponents.core.sessionManager)
it.archivedSession.bundle.restoreSnapshot(requireComponents.core.engine)?.apply { it.archivedSession.bundle.restoreSnapshot(requireComponents.core.engine)?.apply {
requireComponents.core.sessionManager.restore(this) requireComponents.core.sessionManager.restore(this)
} }

View File

@ -6,6 +6,7 @@ package org.mozilla.fenix.home.tabs
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.component_tabs.view.*
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import org.mozilla.fenix.mvi.Action import org.mozilla.fenix.mvi.Action
import org.mozilla.fenix.mvi.ActionBusFactory import org.mozilla.fenix.mvi.ActionBusFactory
@ -16,7 +17,6 @@ import org.mozilla.fenix.mvi.ViewState
class TabsComponent( class TabsComponent(
private val container: ViewGroup, private val container: ViewGroup,
bus: ActionBusFactory, bus: ActionBusFactory,
private val isPrivate: Boolean,
override var initialState: TabsState = TabsState(listOf()) override var initialState: TabsState = TabsState(listOf())
) : ) :
UIComponent<TabsState, TabsAction, TabsChange>( UIComponent<TabsState, TabsAction, TabsChange>(
@ -30,9 +30,9 @@ class TabsComponent(
} }
} }
override fun initView() = TabsUIView(container, actionEmitter, isPrivate, changesObservable) override fun initView() = TabsUIView(container, actionEmitter, changesObservable)
val view: RecyclerView val tabList: RecyclerView
get() = uiView.view as RecyclerView get() = uiView.view.tabs_list as RecyclerView
init { init {
render(reducer) render(reducer)
@ -47,6 +47,7 @@ fun Session.toSessionViewState(selected: Boolean): SessionViewState {
} }
sealed class TabsAction : Action { sealed class TabsAction : Action {
object Archive : TabsAction()
data class Select(val sessionId: String) : TabsAction() data class Select(val sessionId: String) : TabsAction()
data class Close(val sessionId: String) : TabsAction() data class Close(val sessionId: String) : TabsAction()
} }

View File

@ -8,16 +8,15 @@ import android.app.Activity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.navigation.Navigation import androidx.navigation.Navigation
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Observer import io.reactivex.Observer
import io.reactivex.functions.Consumer 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.R
import org.mozilla.fenix.ext.increaseTapArea import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.home.HomeFragment import org.mozilla.fenix.home.HomeFragment
@ -28,46 +27,57 @@ import org.mozilla.fenix.mvi.UIView
class TabsUIView( class TabsUIView(
container: ViewGroup, container: ViewGroup,
actionEmitter: Observer<TabsAction>, actionEmitter: Observer<TabsAction>,
isPrivate: Boolean,
changesObservable: Observable<TabsChange> changesObservable: Observable<TabsChange>
) : ) :
UIView<TabsState, TabsAction, TabsChange>(container, actionEmitter, changesObservable) { UIView<TabsState, TabsAction, TabsChange>(container, actionEmitter, changesObservable) {
private val header: ConstraintLayout = LayoutInflater.from(container.context) override val view: View = 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)
.inflate(R.layout.component_tabs, container, true) .inflate(R.layout.component_tabs, container, true)
.findViewById(R.id.tabs_list)
private val tabsAdapter = TabsAdapter(actionEmitter) private val tabsAdapter = TabsAdapter(actionEmitter)
init { init {
view.apply { view.tabs_list.apply {
layoutManager = LinearLayoutManager(container.context) layoutManager = LinearLayoutManager(container.context)
adapter = tabsAdapter adapter = tabsAdapter
itemAnimator = DefaultItemAnimator() itemAnimator = DefaultItemAnimator()
} }
header.add_tab_button.increaseTapArea(HomeFragment.addTabButtonIncreaseDps) view.apply {
header.add_tab_button.setOnClickListener { add_tab_button.increaseTapArea(HomeFragment.addTabButtonIncreaseDps)
val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null) add_tab_button.setOnClickListener {
Navigation.findNavController(it).navigate(directions) val directions = HomeFragmentDirections.actionHomeFragmentToSearchFragment(null)
} Navigation.findNavController(it).navigate(directions)
header.tabs_overflow_button.increaseTapArea(HomeFragment.overflowButtonIncreaseDps) }
header.tabs_overflow_button.setOnClickListener { tabs_overflow_button.increaseTapArea(HomeFragment.overflowButtonIncreaseDps)
if (view.context as? Activity != null) { tabs_overflow_button.setOnClickListener {
CurrentSessionBottomSheetFragment().show( if (view.context as? Activity != null) {
(view.context as FragmentActivity).supportFragmentManager, CurrentSessionBottomSheetFragment().show(
overflowFragmentTag (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> { override fun updateView() = Consumer<TabsState> {
tabsAdapter.sessions = it.sessions 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 { companion object {

View File

@ -1,8 +1,82 @@
<?xml version="1.0" encoding="utf-8"?> <?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" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tabs_list" xmlns:app="http://schemas.android.com/apk/res-auto">
android:layout_width="match_parent" <androidx.constraintlayout.widget.ConstraintLayout
android:layout_height="wrap_content" android:id="@+id/tabs_header"
android:layout_marginStart="16dp" android:layout_width="match_parent"
android:layout_marginEnd="16dp"/> 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>

View File

@ -5,7 +5,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/delete_history_button" android:id="@+id/delete_history_button"
android:foreground="?android:attr/selectableItemBackground" android:foreground="?android:attr/selectableItemBackground"
android:background="@drawable/delete_history_background" android:background="@drawable/button_background"
android:layout_margin="16dp" android:layout_margin="16dp"
android:padding="6dp" android:padding="6dp"
android:clickable="true" android:clickable="true"

View File

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

View File

@ -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 --> <!-- Content description (not visible, for screen readers etc.): Title icon for current session menu -->
<string name="current_session_image">Current session image</string> <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 --> <!-- Text for the button to delete a single session -->
<string name="session_item_delete">Delete</string> <string name="session_item_delete">Delete</string>