* For #2205: Adds TabCollectionStorage * For #1578: Adds delete to TabCollectionmaster
parent
892a4b7bf4
commit
72d29c2a43
|
@ -309,6 +309,7 @@ dependencies {
|
||||||
implementation Deps.mozilla_feature_session_bundling
|
implementation Deps.mozilla_feature_session_bundling
|
||||||
implementation Deps.mozilla_feature_site_permissions
|
implementation Deps.mozilla_feature_site_permissions
|
||||||
implementation Deps.mozilla_feature_readerview
|
implementation Deps.mozilla_feature_readerview
|
||||||
|
implementation Deps.mozilla_feature_tab_collections
|
||||||
|
|
||||||
implementation Deps.mozilla_service_firefox_accounts
|
implementation Deps.mozilla_service_firefox_accounts
|
||||||
implementation Deps.mozilla_service_fretboard
|
implementation Deps.mozilla_service_fretboard
|
||||||
|
|
|
@ -687,7 +687,7 @@ class BrowserFragment : Fragment(), BackHandler, CoroutineScope {
|
||||||
launch {
|
launch {
|
||||||
val host = session.url.toUri()?.host
|
val host = session.url.toUri()?.host
|
||||||
val sitePermissions: SitePermissions? = host?.let {
|
val sitePermissions: SitePermissions? = host?.let {
|
||||||
val storage = requireContext().components.storage
|
val storage = requireContext().components.core.permissionStorage
|
||||||
storage.findSitePermissionsBy(it)
|
storage.findSitePermissionsBy(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,20 +14,26 @@ import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.android.synthetic.main.fragment_create_collection.view.*
|
import kotlinx.android.synthetic.main.fragment_create_collection.view.*
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.mozilla.fenix.FenixViewModelProvider
|
import org.mozilla.fenix.FenixViewModelProvider
|
||||||
|
import mozilla.components.browser.session.Session
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.components.FenixSnackbar
|
import org.mozilla.fenix.components.FenixSnackbar
|
||||||
import org.mozilla.fenix.ext.getRootView
|
import org.mozilla.fenix.ext.getRootView
|
||||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
import org.mozilla.fenix.ext.requireComponents
|
||||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||||
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
||||||
import org.mozilla.fenix.mvi.getManagedEmitter
|
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||||
import java.util.Random
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class CreateCollectionFragment : DialogFragment() {
|
class CreateCollectionFragment : DialogFragment(), CoroutineScope {
|
||||||
// Temporary callback. In the future we will just directly add the collection to the core session manager.
|
|
||||||
var onCollectionSaved: ((TabCollection) -> Unit)? = null
|
|
||||||
private lateinit var collectionCreationComponent: CollectionCreationComponent
|
private lateinit var collectionCreationComponent: CollectionCreationComponent
|
||||||
|
private lateinit var job: Job
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = Dispatchers.Main + job
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -40,6 +46,7 @@ class CreateCollectionFragment : DialogFragment() {
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
|
job = Job()
|
||||||
val view = inflater.inflate(R.layout.fragment_create_collection, container, false)
|
val view = inflater.inflate(R.layout.fragment_create_collection, container, false)
|
||||||
|
|
||||||
val viewModel = activity?.run {
|
val viewModel = activity?.run {
|
||||||
|
@ -76,6 +83,12 @@ class CreateCollectionFragment : DialogFragment() {
|
||||||
subscribeToActions()
|
subscribeToActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("ComplexMethod")
|
||||||
private fun subscribeToActions() {
|
private fun subscribeToActions() {
|
||||||
getAutoDisposeObservable<CollectionCreationAction>().subscribe {
|
getAutoDisposeObservable<CollectionCreationAction>().subscribe {
|
||||||
when (it) {
|
when (it) {
|
||||||
|
@ -97,8 +110,17 @@ class CreateCollectionFragment : DialogFragment() {
|
||||||
is CollectionCreationAction.SaveCollectionName -> {
|
is CollectionCreationAction.SaveCollectionName -> {
|
||||||
showSavedSnackbar(it.tabs.size)
|
showSavedSnackbar(it.tabs.size)
|
||||||
dismiss()
|
dismiss()
|
||||||
val newCollection = TabCollection(Random().nextInt(), it.name, it.tabs.toMutableList())
|
|
||||||
onCollectionSaved?.invoke(newCollection)
|
val sessionBundle = mutableListOf<Session>()
|
||||||
|
it.tabs.forEach {
|
||||||
|
requireComponents.core.sessionManager.findSessionById(it.sessionId)?.let { session ->
|
||||||
|
sessionBundle.add(session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
launch(Dispatchers.IO) {
|
||||||
|
requireComponents.core.tabCollectionStorage.createCollection(it.name, sessionBundle)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,5 +19,4 @@ class Components(private val context: Context) {
|
||||||
val useCases by lazy { UseCases(context, core.sessionManager, search.searchEngineManager) }
|
val useCases by lazy { UseCases(context, core.sessionManager, search.searchEngineManager) }
|
||||||
val utils by lazy { Utilities(context, core.sessionManager, useCases.sessionUseCases, useCases.searchUseCases) }
|
val utils by lazy { Utilities(context, core.sessionManager, useCases.sessionUseCases, useCases.searchUseCases) }
|
||||||
val analytics by lazy { Analytics(context) }
|
val analytics by lazy { Analytics(context) }
|
||||||
val storage by lazy { Storage(context) }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,8 +127,11 @@ class Core(private val context: Context) {
|
||||||
*/
|
*/
|
||||||
val historyStorage by lazy { PlacesHistoryStorage(context) }
|
val historyStorage by lazy { PlacesHistoryStorage(context) }
|
||||||
|
|
||||||
val bookmarksStorage
|
val bookmarksStorage by lazy { PlacesBookmarksStorage(context) }
|
||||||
by lazy { PlacesBookmarksStorage(context) }
|
|
||||||
|
val tabCollectionStorage by lazy { TabCollectionStorage(context, sessionManager) }
|
||||||
|
|
||||||
|
val permissionStorage by lazy { PermissionStorage(context) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a [TrackingProtectionPolicy] based on current preferences.
|
* Constructs a [TrackingProtectionPolicy] based on current preferences.
|
||||||
|
|
|
@ -12,7 +12,7 @@ import mozilla.components.feature.sitepermissions.SitePermissionsStorage
|
||||||
import org.mozilla.fenix.test.Mockable
|
import org.mozilla.fenix.test.Mockable
|
||||||
|
|
||||||
@Mockable
|
@Mockable
|
||||||
class Storage(private val context: Context) {
|
class PermissionStorage(private val context: Context) {
|
||||||
|
|
||||||
private val permissionsStorage by lazy {
|
private val permissionsStorage by lazy {
|
||||||
SitePermissionsStorage(context)
|
SitePermissionsStorage(context)
|
|
@ -0,0 +1,55 @@
|
||||||
|
/* 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.components
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.paging.DataSource
|
||||||
|
import mozilla.components.browser.session.Session
|
||||||
|
import mozilla.components.browser.session.SessionManager
|
||||||
|
import mozilla.components.feature.tab.collections.Tab
|
||||||
|
import mozilla.components.feature.tab.collections.TabCollection
|
||||||
|
import mozilla.components.feature.tab.collections.TabCollectionStorage
|
||||||
|
import org.mozilla.fenix.test.Mockable
|
||||||
|
|
||||||
|
@Mockable
|
||||||
|
class TabCollectionStorage(private val context: Context, private val sessionManager: SessionManager) {
|
||||||
|
|
||||||
|
private val collectionStorage by lazy {
|
||||||
|
TabCollectionStorage(context, sessionManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createCollection(title: String, sessions: List<Session>) {
|
||||||
|
collectionStorage.createCollection(title, sessions)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addTabsToCollection(tabCollection: TabCollection, sessions: List<Session>) {
|
||||||
|
collectionStorage.addTabsToCollection(tabCollection, sessions)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCollections(limit: Int = 20): LiveData<List<TabCollection>> {
|
||||||
|
return collectionStorage.getCollections(limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCollectionsPaged(): DataSource.Factory<Int, TabCollection> {
|
||||||
|
return collectionStorage.getCollectionsPaged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeCollection(tabCollection: TabCollection) {
|
||||||
|
collectionStorage.removeCollection(tabCollection)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeTabFromCollection(tabCollection: TabCollection, tab: Tab) {
|
||||||
|
if (tabCollection.tabs.size == 1) {
|
||||||
|
removeCollection(tabCollection)
|
||||||
|
} else {
|
||||||
|
collectionStorage.removeTabFromCollection(tabCollection, tab)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun renameCollection(tabCollection: TabCollection, title: String) {
|
||||||
|
collectionStorage.renameCollection(tabCollection, title)
|
||||||
|
}
|
||||||
|
}
|
|
@ -164,7 +164,7 @@ class DefaultToolbarMenu(
|
||||||
|
|
||||||
BrowserMenuImageText(
|
BrowserMenuImageText(
|
||||||
context.getString(R.string.browser_menu_save_to_collection),
|
context.getString(R.string.browser_menu_save_to_collection),
|
||||||
R.drawable.ic_archive,
|
R.drawable.ic_tab_collection,
|
||||||
DefaultThemeManager.resolveAttribute(R.attr.primaryText, context)
|
DefaultThemeManager.resolveAttribute(R.attr.primaryText, context)
|
||||||
) {
|
) {
|
||||||
onItemTapped.invoke(ToolbarMenu.Item.SaveToCollection)
|
onItemTapped.invoke(ToolbarMenu.Item.SaveToCollection)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID
|
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import androidx.navigation.Navigation
|
import androidx.navigation.Navigation
|
||||||
import kotlinx.android.synthetic.main.fragment_home.*
|
import kotlinx.android.synthetic.main.fragment_home.*
|
||||||
|
@ -68,6 +69,8 @@ import kotlin.math.roundToInt
|
||||||
class HomeFragment : Fragment(), CoroutineScope {
|
class HomeFragment : Fragment(), CoroutineScope {
|
||||||
private val bus = ActionBusFactory.get(this)
|
private val bus = ActionBusFactory.get(this)
|
||||||
private var sessionObserver: SessionManager.Observer? = null
|
private var sessionObserver: SessionManager.Observer? = null
|
||||||
|
private var tabCollectionObserver: Observer<List<TabCollection>>? = null
|
||||||
|
|
||||||
private var homeMenu: HomeMenu? = null
|
private var homeMenu: HomeMenu? = null
|
||||||
|
|
||||||
var deleteSessionJob: (suspend () -> Unit)? = null
|
var deleteSessionJob: (suspend () -> Unit)? = null
|
||||||
|
@ -79,9 +82,6 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||||
override val coroutineContext: CoroutineContext
|
override val coroutineContext: CoroutineContext
|
||||||
get() = Dispatchers.Main + job
|
get() = Dispatchers.Main + job
|
||||||
|
|
||||||
// TODO Remove this stub when we have the a-c version!
|
|
||||||
var storedCollections = mutableListOf<TabCollection>()
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
|
@ -99,7 +99,7 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||||
this,
|
this,
|
||||||
SessionControlViewModel::class.java
|
SessionControlViewModel::class.java
|
||||||
) {
|
) {
|
||||||
SessionControlViewModel(SessionControlState(listOf(), listOf(), mode))
|
SessionControlViewModel(SessionControlState(listOf(), setOf(), listOf(), mode))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -190,13 +190,6 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||||
) ?: arrayListOf()).toList()
|
) ?: arrayListOf()).toList()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
getManagedEmitter<SessionControlChange>().onNext(
|
|
||||||
SessionControlChange.CollectionsChange(
|
|
||||||
(savedInstanceState.getParcelableArrayList<TabCollection>(
|
|
||||||
KEY_COLLECTIONS
|
|
||||||
) ?: arrayListOf()).toList()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,12 +223,17 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||||
|
|
||||||
emitSessionChanges()
|
emitSessionChanges()
|
||||||
sessionObserver = subscribeToSessions()
|
sessionObserver = subscribeToSessions()
|
||||||
|
tabCollectionObserver = subscribeToTabCollections()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
sessionObserver?.let {
|
sessionObserver?.let {
|
||||||
requireComponents.core.sessionManager.unregister(it)
|
requireComponents.core.sessionManager.unregister(it)
|
||||||
}
|
}
|
||||||
|
tabCollectionObserver?.let {
|
||||||
|
requireComponents.core.tabCollectionStorage.getCollections().removeObserver(it)
|
||||||
|
}
|
||||||
|
|
||||||
super.onStop()
|
super.onStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,13 +315,17 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||||
private fun handleCollectionAction(action: CollectionAction) {
|
private fun handleCollectionAction(action: CollectionAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is CollectionAction.Expand -> {
|
is CollectionAction.Expand -> {
|
||||||
storedCollections.find { it.id == action.collection.id }?.apply { expanded = true }
|
getManagedEmitter<SessionControlChange>()
|
||||||
|
.onNext(SessionControlChange.ExpansionChange(action.collection, true))
|
||||||
}
|
}
|
||||||
is CollectionAction.Collapse -> {
|
is CollectionAction.Collapse -> {
|
||||||
storedCollections.find { it.id == action.collection.id }?.apply { expanded = false }
|
getManagedEmitter<SessionControlChange>()
|
||||||
|
.onNext(SessionControlChange.ExpansionChange(action.collection, false))
|
||||||
}
|
}
|
||||||
is CollectionAction.Delete -> {
|
is CollectionAction.Delete -> {
|
||||||
storedCollections.find { it.id == action.collection.id }?.let { storedCollections.remove(it) }
|
launch(Dispatchers.IO) {
|
||||||
|
requireComponents.core.tabCollectionStorage.removeCollection(action.collection)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is CollectionAction.AddTab -> {
|
is CollectionAction.AddTab -> {
|
||||||
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "1575")
|
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "1575")
|
||||||
|
@ -338,17 +340,11 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||||
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "1585")
|
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "1585")
|
||||||
}
|
}
|
||||||
is CollectionAction.RemoveTab -> {
|
is CollectionAction.RemoveTab -> {
|
||||||
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "1578")
|
launch(Dispatchers.IO) {
|
||||||
|
requireComponents.core.tabCollectionStorage.removeTabFromCollection(action.collection, action.tab)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitCollectionChange()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun emitCollectionChange() {
|
|
||||||
storedCollections.map { it.copy() }.let {
|
|
||||||
getManagedEmitter<SessionControlChange>().onNext(SessionControlChange.CollectionsChange(it))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
|
@ -396,6 +392,14 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||||
return getString(resourceId)
|
return getString(resourceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun subscribeToTabCollections(): Observer<List<TabCollection>> {
|
||||||
|
val observer = Observer<List<TabCollection>> {
|
||||||
|
getManagedEmitter<SessionControlChange>().onNext(SessionControlChange.CollectionsChange(it))
|
||||||
|
}
|
||||||
|
requireComponents.core.tabCollectionStorage.getCollections().observe(this, observer)
|
||||||
|
return observer
|
||||||
|
}
|
||||||
|
|
||||||
private fun subscribeToSessions(): SessionManager.Observer {
|
private fun subscribeToSessions(): SessionManager.Observer {
|
||||||
val observer = object : SessionManager.Observer {
|
val observer = object : SessionManager.Observer {
|
||||||
override fun onSessionAdded(session: Session) {
|
override fun onSessionAdded(session: Session) {
|
||||||
|
@ -520,6 +524,5 @@ class HomeFragment : Fragment(), CoroutineScope {
|
||||||
companion object {
|
companion object {
|
||||||
private const val toolbarPaddingDp = 12f
|
private const val toolbarPaddingDp = 12f
|
||||||
private const val KEY_TABS = "tabs"
|
private const val KEY_TABS = "tabs"
|
||||||
private const val KEY_COLLECTIONS = "collections"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ class SessionBottomSheetFragment : BottomSheetDialogFragment(), LayoutContainer
|
||||||
view.current_session_card_title.text = getCardTitle()
|
view.current_session_card_title.text = getCardTitle()
|
||||||
view.current_session_card_tab_list.text = getTabTitles()
|
view.current_session_card_tab_list.text = getTabTitles()
|
||||||
view.archive_session_button.apply {
|
view.archive_session_button.apply {
|
||||||
val drawable = ContextCompat.getDrawable(context!!, R.drawable.ic_archive)
|
val drawable = ContextCompat.getDrawable(context!!, R.drawable.ic_tab_collection)
|
||||||
drawable?.setColorFilter(
|
drawable?.setColorFilter(
|
||||||
ContextCompat.getColor(
|
ContextCompat.getColor(
|
||||||
context!!,
|
context!!,
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingPr
|
||||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingSectionHeaderViewHolder
|
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingSectionHeaderViewHolder
|
||||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingThemePickerViewHolder
|
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingThemePickerViewHolder
|
||||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTrackingProtectionViewHolder
|
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTrackingProtectionViewHolder
|
||||||
|
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
|
|
||||||
sealed class AdapterItem {
|
sealed class AdapterItem {
|
||||||
|
@ -42,7 +43,11 @@ sealed class AdapterItem {
|
||||||
object CollectionHeader : AdapterItem()
|
object CollectionHeader : AdapterItem()
|
||||||
object NoCollectionMessage : AdapterItem()
|
object NoCollectionMessage : AdapterItem()
|
||||||
data class CollectionItem(val collection: TabCollection) : AdapterItem()
|
data class CollectionItem(val collection: TabCollection) : AdapterItem()
|
||||||
data class TabInCollectionItem(val collection: TabCollection, val tab: Tab, val isLastTab: Boolean) : AdapterItem()
|
data class TabInCollectionItem(
|
||||||
|
val collection: TabCollection,
|
||||||
|
val tab: ComponentTab,
|
||||||
|
val isLastTab: Boolean
|
||||||
|
) : AdapterItem()
|
||||||
|
|
||||||
object OnboardingHeader : AdapterItem()
|
object OnboardingHeader : AdapterItem()
|
||||||
data class OnboardingSectionHeader(val labelBuilder: (Context) -> String) : AdapterItem()
|
data class OnboardingSectionHeader(val labelBuilder: (Context) -> String) : AdapterItem()
|
||||||
|
@ -82,9 +87,11 @@ class SessionControlAdapter(
|
||||||
|
|
||||||
private var items: List<AdapterItem> = listOf()
|
private var items: List<AdapterItem> = listOf()
|
||||||
private lateinit var job: Job
|
private lateinit var job: Job
|
||||||
|
private lateinit var expandedCollections: Set<Long>
|
||||||
|
|
||||||
fun reloadData(items: List<AdapterItem>) {
|
fun reloadData(items: List<AdapterItem>, expandedCollections: Set<Long>) {
|
||||||
this.items = items
|
this.items = items
|
||||||
|
this.expandedCollections = expandedCollections
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,9 +145,10 @@ class SessionControlAdapter(
|
||||||
is TabViewHolder -> holder.bindSession(
|
is TabViewHolder -> holder.bindSession(
|
||||||
(items[position] as AdapterItem.TabItem).tab
|
(items[position] as AdapterItem.TabItem).tab
|
||||||
)
|
)
|
||||||
is CollectionViewHolder -> holder.bindSession(
|
is CollectionViewHolder -> {
|
||||||
(items[position] as AdapterItem.CollectionItem).collection
|
val collection = (items[position] as AdapterItem.CollectionItem).collection
|
||||||
)
|
holder.bindSession(collection, expandedCollections.contains(collection.id))
|
||||||
|
}
|
||||||
is TabInCollectionViewHolder -> {
|
is TabInCollectionViewHolder -> {
|
||||||
val item = items[position] as AdapterItem.TabInCollectionItem
|
val item = items[position] as AdapterItem.TabInCollectionItem
|
||||||
holder.bindSession(item.collection, item.tab, item.isLastTab)
|
holder.bindSession(item.collection, item.tab, item.isLastTab)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
package org.mozilla.fenix.home.sessioncontrol
|
package org.mozilla.fenix.home.sessioncontrol
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -12,6 +13,10 @@ import io.reactivex.Observer
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import org.mozilla.fenix.mvi.ViewState
|
import org.mozilla.fenix.mvi.ViewState
|
||||||
import org.mozilla.fenix.mvi.Change
|
import org.mozilla.fenix.mvi.Change
|
||||||
|
import mozilla.components.browser.session.Session
|
||||||
|
import mozilla.components.feature.tab.collections.TabCollection as ACTabCollection
|
||||||
|
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||||
|
import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.mvi.Action
|
import org.mozilla.fenix.mvi.Action
|
||||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||||
import org.mozilla.fenix.mvi.UIComponent
|
import org.mozilla.fenix.mvi.UIComponent
|
||||||
|
@ -49,15 +54,6 @@ data class Tab(
|
||||||
val thumbnail: Bitmap? = null
|
val thumbnail: Bitmap? = null
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
data class TabCollection(
|
|
||||||
val id: Int,
|
|
||||||
val title: String,
|
|
||||||
val tabs: MutableList<Tab>,
|
|
||||||
val iconColor: Int = 0,
|
|
||||||
var expanded: Boolean = false
|
|
||||||
) : Parcelable
|
|
||||||
|
|
||||||
sealed class Mode {
|
sealed class Mode {
|
||||||
object Normal : Mode()
|
object Normal : Mode()
|
||||||
object Private : Mode()
|
object Private : Mode()
|
||||||
|
@ -66,10 +62,13 @@ sealed class Mode {
|
||||||
|
|
||||||
data class SessionControlState(
|
data class SessionControlState(
|
||||||
val tabs: List<Tab>,
|
val tabs: List<Tab>,
|
||||||
|
val expandedCollections: Set<Long>,
|
||||||
val collections: List<TabCollection>,
|
val collections: List<TabCollection>,
|
||||||
val mode: Mode
|
val mode: Mode
|
||||||
) : ViewState
|
) : ViewState
|
||||||
|
|
||||||
|
typealias TabCollection = ACTabCollection
|
||||||
|
|
||||||
sealed class TabAction : Action {
|
sealed class TabAction : Action {
|
||||||
data class SaveTabGroup(val selectedTabSessionId: String?) : TabAction()
|
data class SaveTabGroup(val selectedTabSessionId: String?) : TabAction()
|
||||||
object Add : TabAction()
|
object Add : TabAction()
|
||||||
|
@ -89,7 +88,7 @@ sealed class CollectionAction : Action {
|
||||||
data class Rename(val collection: TabCollection) : CollectionAction()
|
data class Rename(val collection: TabCollection) : CollectionAction()
|
||||||
data class OpenTabs(val collection: TabCollection) : CollectionAction()
|
data class OpenTabs(val collection: TabCollection) : CollectionAction()
|
||||||
data class ShareTabs(val collection: TabCollection) : CollectionAction()
|
data class ShareTabs(val collection: TabCollection) : CollectionAction()
|
||||||
data class RemoveTab(val collection: TabCollection, val tab: Tab) : CollectionAction()
|
data class RemoveTab(val collection: TabCollection, val tab: ComponentTab) : CollectionAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class OnboardingAction : Action {
|
sealed class OnboardingAction : Action {
|
||||||
|
@ -118,17 +117,33 @@ sealed class SessionControlChange : Change {
|
||||||
data class TabsChange(val tabs: List<Tab>) : SessionControlChange()
|
data class TabsChange(val tabs: List<Tab>) : SessionControlChange()
|
||||||
data class ModeChange(val mode: Mode) : SessionControlChange()
|
data class ModeChange(val mode: Mode) : SessionControlChange()
|
||||||
data class CollectionsChange(val collections: List<TabCollection>) : SessionControlChange()
|
data class CollectionsChange(val collections: List<TabCollection>) : SessionControlChange()
|
||||||
|
data class ExpansionChange(val collection: TabCollection, val expand: Boolean) : SessionControlChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
class SessionControlViewModel(
|
class SessionControlViewModel(
|
||||||
initialState: SessionControlState
|
initialState: SessionControlState
|
||||||
) : UIComponentViewModelBase<SessionControlState, SessionControlChange>(initialState, reducer) {
|
) : UIComponentViewModelBase<SessionControlState, SessionControlChange>(initialState, reducer) {
|
||||||
companion object {
|
companion object {
|
||||||
|
fun getSessionFromTab(context: Context, tab: Tab): Session? {
|
||||||
|
return context.components.core.sessionManager.findSessionById(tab.sessionId)
|
||||||
|
}
|
||||||
|
|
||||||
val reducer: (SessionControlState, SessionControlChange) -> SessionControlState = { state, change ->
|
val reducer: (SessionControlState, SessionControlChange) -> SessionControlState = { state, change ->
|
||||||
when (change) {
|
when (change) {
|
||||||
is SessionControlChange.CollectionsChange -> state.copy(collections = change.collections)
|
is SessionControlChange.CollectionsChange -> state.copy(collections = change.collections)
|
||||||
is SessionControlChange.TabsChange -> state.copy(tabs = change.tabs)
|
is SessionControlChange.TabsChange -> state.copy(tabs = change.tabs)
|
||||||
is SessionControlChange.ModeChange -> state.copy(mode = change.mode)
|
is SessionControlChange.ModeChange -> state.copy(mode = change.mode)
|
||||||
|
is SessionControlChange.ExpansionChange -> {
|
||||||
|
val newExpandedCollection = state.expandedCollections.toMutableSet()
|
||||||
|
|
||||||
|
if (change.expand) {
|
||||||
|
newExpandedCollection.add(change.collection.id)
|
||||||
|
} else {
|
||||||
|
newExpandedCollection.remove(change.collection.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
state.copy(expandedCollections = newExpandedCollection)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,11 @@ import org.mozilla.fenix.mvi.UIView
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import org.mozilla.fenix.BuildConfig
|
import org.mozilla.fenix.BuildConfig
|
||||||
|
|
||||||
private fun normalModeAdapterItems(tabs: List<Tab>, collections: List<TabCollection>): List<AdapterItem> {
|
private fun normalModeAdapterItems(
|
||||||
|
tabs: List<Tab>,
|
||||||
|
collections: List<TabCollection>,
|
||||||
|
expandedCollections: Set<Long>
|
||||||
|
): List<AdapterItem> {
|
||||||
val items = mutableListOf<AdapterItem>()
|
val items = mutableListOf<AdapterItem>()
|
||||||
items.add(AdapterItem.TabHeader(false, tabs.isNotEmpty()))
|
items.add(AdapterItem.TabHeader(false, tabs.isNotEmpty()))
|
||||||
|
|
||||||
|
@ -33,9 +37,9 @@ private fun normalModeAdapterItems(tabs: List<Tab>, collections: List<TabCollect
|
||||||
if (collections.isNotEmpty()) {
|
if (collections.isNotEmpty()) {
|
||||||
|
|
||||||
// If the collection is expanded, we want to add all of its tabs beneath it in the adapter
|
// If the collection is expanded, we want to add all of its tabs beneath it in the adapter
|
||||||
collections.reversed().map(AdapterItem::CollectionItem).forEach {
|
collections.map(AdapterItem::CollectionItem).forEach {
|
||||||
items.add(it)
|
items.add(it)
|
||||||
if (it.collection.expanded) {
|
if (it.collection.isExpanded(expandedCollections)) {
|
||||||
items.addAll(collectionTabItems(it.collection))
|
items.addAll(collectionTabItems(it.collection))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,14 +80,18 @@ private fun onboardingAdapterItems(): List<AdapterItem> = listOf(
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun SessionControlState.toAdapterList(): List<AdapterItem> = when (mode) {
|
private fun SessionControlState.toAdapterList(): List<AdapterItem> = when (mode) {
|
||||||
is Mode.Normal -> normalModeAdapterItems(tabs, collections)
|
is Mode.Normal -> normalModeAdapterItems(tabs, collections, expandedCollections)
|
||||||
is Mode.Private -> privateModeAdapterItems(tabs)
|
is Mode.Private -> privateModeAdapterItems(tabs)
|
||||||
is Mode.Onboarding -> onboardingAdapterItems()
|
is Mode.Onboarding -> onboardingAdapterItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectionTabItems(collection: TabCollection) = collection.tabs.mapIndexed { index, tab ->
|
private fun collectionTabItems(collection: TabCollection) = collection.tabs.mapIndexed { index, tab ->
|
||||||
AdapterItem.TabInCollectionItem(collection, tab, index == collection.tabs.lastIndex)
|
AdapterItem.TabInCollectionItem(collection, tab, index == collection.tabs.lastIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun TabCollection.isExpanded(expandedCollections: Set<Long>): Boolean {
|
||||||
|
return expandedCollections.contains(this.id)
|
||||||
|
}
|
||||||
|
|
||||||
class SessionControlUIView(
|
class SessionControlUIView(
|
||||||
container: ViewGroup,
|
container: ViewGroup,
|
||||||
|
@ -101,6 +109,7 @@ class SessionControlUIView(
|
||||||
.findViewById(R.id.home_component)
|
.findViewById(R.id.home_component)
|
||||||
|
|
||||||
private val sessionControlAdapter = SessionControlAdapter(actionEmitter)
|
private val sessionControlAdapter = SessionControlAdapter(actionEmitter)
|
||||||
|
private var expandedCollections = setOf<Long>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
view.apply {
|
view.apply {
|
||||||
|
@ -117,8 +126,8 @@ class SessionControlUIView(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateView() = Consumer<SessionControlState> {
|
override fun updateView() = Consumer<SessionControlState> {
|
||||||
sessionControlAdapter.reloadData(it.toAdapterList())
|
sessionControlAdapter.reloadData(it.toAdapterList(), it.expandedCollections)
|
||||||
|
expandedCollections = it.expandedCollections
|
||||||
// There is a current bug in the combination of MotionLayout~alhpa4 and RecyclerView where it doesn't think
|
// There is a current bug in the combination of MotionLayout~alhpa4 and RecyclerView where it doesn't think
|
||||||
// it has to redraw itself. For some reason calling scrollBy forces this to happen every time
|
// it has to redraw itself. For some reason calling scrollBy forces this to happen every time
|
||||||
// https://stackoverflow.com/a/42549611
|
// https://stackoverflow.com/a/42549611
|
||||||
|
|
|
@ -21,6 +21,7 @@ import mozilla.components.browser.menu.BrowserMenuBuilder
|
||||||
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
|
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
|
||||||
import org.mozilla.fenix.DefaultThemeManager
|
import org.mozilla.fenix.DefaultThemeManager
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.ext.urlToTrimmedHost
|
||||||
import org.mozilla.fenix.home.sessioncontrol.CollectionAction
|
import org.mozilla.fenix.home.sessioncontrol.CollectionAction
|
||||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlAction
|
import org.mozilla.fenix.home.sessioncontrol.SessionControlAction
|
||||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||||
|
@ -40,6 +41,7 @@ class CollectionViewHolder(
|
||||||
get() = Dispatchers.IO + job
|
get() = Dispatchers.IO + job
|
||||||
|
|
||||||
private lateinit var collection: TabCollection
|
private lateinit var collection: TabCollection
|
||||||
|
private var expanded = false
|
||||||
private var state = CollectionState.Collapsed
|
private var state = CollectionState.Collapsed
|
||||||
private var collectionMenu: CollectionItemMenu
|
private var collectionMenu: CollectionItemMenu
|
||||||
|
|
||||||
|
@ -78,8 +80,9 @@ class CollectionViewHolder(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bindSession(collection: TabCollection) {
|
fun bindSession(collection: TabCollection, expanded: Boolean) {
|
||||||
this.collection = collection
|
this.collection = collection
|
||||||
|
this.expanded = expanded
|
||||||
updateCollectionUI()
|
updateCollectionUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +92,7 @@ class CollectionViewHolder(
|
||||||
var hostNameList = listOf<String>()
|
var hostNameList = listOf<String>()
|
||||||
|
|
||||||
collection.tabs.forEach {
|
collection.tabs.forEach {
|
||||||
hostNameList += it.hostname.capitalize()
|
hostNameList += it.url.urlToTrimmedHost().capitalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
var tabsDisplayed = 0
|
var tabsDisplayed = 0
|
||||||
|
@ -106,7 +109,7 @@ class CollectionViewHolder(
|
||||||
|
|
||||||
view.collection_description.text = titleList
|
view.collection_description.text = titleList
|
||||||
|
|
||||||
if (collection.expanded) {
|
if (expanded) {
|
||||||
(view.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin = 0
|
(view.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin = 0
|
||||||
collection_title.setPadding(0, 0, 0, EXPANDED_PADDING)
|
collection_title.setPadding(0, 0, 0, EXPANDED_PADDING)
|
||||||
view.background = ContextCompat.getDrawable(view.context, R.drawable.rounded_top_corners)
|
view.background = ContextCompat.getDrawable(view.context, R.drawable.rounded_top_corners)
|
||||||
|
|
|
@ -23,12 +23,13 @@ import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.ext.components
|
import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.ext.getColorFromAttr
|
import org.mozilla.fenix.ext.getColorFromAttr
|
||||||
import org.mozilla.fenix.ext.increaseTapArea
|
import org.mozilla.fenix.ext.increaseTapArea
|
||||||
|
import org.mozilla.fenix.ext.urlToTrimmedHost
|
||||||
import org.mozilla.fenix.home.sessioncontrol.CollectionAction
|
import org.mozilla.fenix.home.sessioncontrol.CollectionAction
|
||||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlAction
|
import org.mozilla.fenix.home.sessioncontrol.SessionControlAction
|
||||||
import org.mozilla.fenix.home.sessioncontrol.Tab
|
|
||||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||||
import org.mozilla.fenix.home.sessioncontrol.onNext
|
import org.mozilla.fenix.home.sessioncontrol.onNext
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||||
|
|
||||||
class TabInCollectionViewHolder(
|
class TabInCollectionViewHolder(
|
||||||
val view: View,
|
val view: View,
|
||||||
|
@ -42,7 +43,7 @@ class TabInCollectionViewHolder(
|
||||||
|
|
||||||
lateinit var collection: TabCollection
|
lateinit var collection: TabCollection
|
||||||
private set
|
private set
|
||||||
lateinit var tab: Tab
|
lateinit var tab: ComponentTab
|
||||||
private set
|
private set
|
||||||
var isLastTab = false
|
var isLastTab = false
|
||||||
|
|
||||||
|
@ -66,7 +67,7 @@ class TabInCollectionViewHolder(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bindSession(collection: TabCollection, tab: Tab, isLastTab: Boolean) {
|
fun bindSession(collection: TabCollection, tab: ComponentTab, isLastTab: Boolean) {
|
||||||
this.collection = collection
|
this.collection = collection
|
||||||
this.tab = tab
|
this.tab = tab
|
||||||
this.isLastTab = isLastTab
|
this.isLastTab = isLastTab
|
||||||
|
@ -74,7 +75,7 @@ class TabInCollectionViewHolder(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTabUI() {
|
private fun updateTabUI() {
|
||||||
collection_tab_hostname.text = tab.hostname
|
collection_tab_hostname.text = tab.url.urlToTrimmedHost()
|
||||||
collection_tab_title.text = tab.title
|
collection_tab_title.text = tab.title
|
||||||
launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
val bitmap = collection_tab_icon.context.components.utils.icons
|
val bitmap = collection_tab_icon.context.components.utils.icons
|
||||||
|
|
|
@ -47,7 +47,7 @@ import org.mozilla.fenix.ext.allowUndo
|
||||||
import org.mozilla.fenix.ext.components
|
import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.ext.requireComponents
|
import org.mozilla.fenix.ext.requireComponents
|
||||||
import org.mozilla.fenix.ext.share
|
import org.mozilla.fenix.ext.share
|
||||||
import org.mozilla.fenix.ext.urlToHost
|
import org.mozilla.fenix.ext.urlToTrimmedHost
|
||||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||||
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
||||||
import org.mozilla.fenix.mvi.getManagedEmitter
|
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||||
|
@ -238,10 +238,12 @@ class BookmarkFragment : Fragment(), CoroutineScope, BackHandler, AccountObserve
|
||||||
is BookmarkAction.Delete -> {
|
is BookmarkAction.Delete -> {
|
||||||
val components = context?.applicationContext?.components!!
|
val components = context?.applicationContext?.components!!
|
||||||
|
|
||||||
getManagedEmitter<BookmarkChange>().onNext(BookmarkChange.Change(currentRoot - it.item.guid))
|
getManagedEmitter<BookmarkChange>()
|
||||||
|
.onNext(BookmarkChange.Change(currentRoot - it.item.guid))
|
||||||
|
|
||||||
CoroutineScope(Main).allowUndo(
|
CoroutineScope(Main).allowUndo(
|
||||||
view!!, getString(R.string.bookmark_deletion_snackbar_message, it.item.url.urlToHost()),
|
view!!,
|
||||||
|
getString(R.string.bookmark_deletion_snackbar_message, it.item.url.urlToTrimmedHost()),
|
||||||
getString(R.string.bookmark_undo_deletion), { refreshBookmarks(components) }
|
getString(R.string.bookmark_undo_deletion), { refreshBookmarks(components) }
|
||||||
) {
|
) {
|
||||||
components.core.bookmarksStorage.deleteNode(it.item.guid)
|
components.core.bookmarksStorage.deleteNode(it.item.guid)
|
||||||
|
|
|
@ -53,7 +53,8 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat(), Cor
|
||||||
super.onResume()
|
super.onResume()
|
||||||
launch(IO) {
|
launch(IO) {
|
||||||
val context = requireContext()
|
val context = requireContext()
|
||||||
sitePermissions = requireNotNull(context.components.storage.findSitePermissionsBy(sitePermissions.origin))
|
sitePermissions =
|
||||||
|
requireNotNull(context.components.core.permissionStorage.findSitePermissionsBy(sitePermissions.origin))
|
||||||
launch(Main) {
|
launch(Main) {
|
||||||
bindCategoryPhoneFeatures()
|
bindCategoryPhoneFeatures()
|
||||||
}
|
}
|
||||||
|
@ -112,7 +113,7 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat(), Cor
|
||||||
|
|
||||||
private fun clearSitePermissions() {
|
private fun clearSitePermissions() {
|
||||||
launch(IO) {
|
launch(IO) {
|
||||||
requireContext().components.storage.deleteSitePermissions(sitePermissions)
|
requireContext().components.core.permissionStorage.deleteSitePermissions(sitePermissions)
|
||||||
launch(Main) {
|
launch(Main) {
|
||||||
Navigation.findNavController(requireNotNull(view)).popBackStack()
|
Navigation.findNavController(requireNotNull(view)).popBackStack()
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ class SitePermissionsExceptionsFragment : Fragment(), View.OnClickListener, Coro
|
||||||
recyclerView = rootView.findViewById(R.id.exceptions)
|
recyclerView = rootView.findViewById(R.id.exceptions)
|
||||||
recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||||
|
|
||||||
val sitePermissionsPaged = requireContext().components.storage.getSitePermissionsPaged()
|
val sitePermissionsPaged = requireContext().components.core.permissionStorage.getSitePermissionsPaged()
|
||||||
|
|
||||||
val adapter = ExceptionsAdapter(this)
|
val adapter = ExceptionsAdapter(this)
|
||||||
val liveData = LivePagedListBuilder(sitePermissionsPaged, MAX_ITEMS_PER_PAGE).build()
|
val liveData = LivePagedListBuilder(sitePermissionsPaged, MAX_ITEMS_PER_PAGE).build()
|
||||||
|
@ -124,7 +124,7 @@ class SitePermissionsExceptionsFragment : Fragment(), View.OnClickListener, Coro
|
||||||
|
|
||||||
private fun deleteAllSitePermissions() {
|
private fun deleteAllSitePermissions() {
|
||||||
launch(IO) {
|
launch(IO) {
|
||||||
requireContext().components.storage.deleteAllSitePermissions()
|
requireContext().components.core.permissionStorage.deleteAllSitePermissions()
|
||||||
launch(Main) {
|
launch(Main) {
|
||||||
showEmptyListMessage()
|
showEmptyListMessage()
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ class SitePermissionsManageExceptionsPhoneFeatureFragment : Fragment(), Coroutin
|
||||||
PhoneFeature.NOTIFICATION -> sitePermissions.copy(notification = status)
|
PhoneFeature.NOTIFICATION -> sitePermissions.copy(notification = status)
|
||||||
}
|
}
|
||||||
launch(IO) {
|
launch(IO) {
|
||||||
requireContext().components.storage.updateSitePermissions(updatedSitePermissions)
|
requireContext().components.core.permissionStorage.updateSitePermissions(updatedSitePermissions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,10 +60,11 @@ class QuickSettingsComponent(
|
||||||
PhoneFeature.MICROPHONE -> microphone = microphone.toggle()
|
PhoneFeature.MICROPHONE -> microphone = microphone.toggle()
|
||||||
PhoneFeature.NOTIFICATION -> notification = notification.toggle()
|
PhoneFeature.NOTIFICATION -> notification = notification.toggle()
|
||||||
}
|
}
|
||||||
context.components.storage.addSitePermissionException(origin, location, notification, microphone, camera)
|
context.components.core.permissionStorage
|
||||||
|
.addSitePermissionException(origin, location, notification, microphone, camera)
|
||||||
} else {
|
} else {
|
||||||
val updatedSitePermissions = sitePermissions.toggle(featurePhone)
|
val updatedSitePermissions = sitePermissions.toggle(featurePhone)
|
||||||
context.components.storage.updateSitePermissions(updatedSitePermissions)
|
context.components.core.permissionStorage.updateSitePermissions(updatedSitePermissions)
|
||||||
updatedSitePermissions
|
updatedSitePermissions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,7 +245,7 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment(), CoroutineSco
|
||||||
launch {
|
launch {
|
||||||
val host = session.url.toUri()?.host
|
val host = session.url.toUri()?.host
|
||||||
val sitePermissions: SitePermissions? = host?.let {
|
val sitePermissions: SitePermissions? = host?.let {
|
||||||
val storage = requireContext().components.storage
|
val storage = requireContext().components.core.permissionStorage
|
||||||
storage.findSitePermissionsBy(it)
|
storage.findSitePermissionsBy(it)
|
||||||
}
|
}
|
||||||
launch(Dispatchers.Main) {
|
launch(Dispatchers.Main) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,20 @@
|
||||||
|
<?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/. -->
|
||||||
|
|
||||||
|
<vector android:height="24dp" android:viewportHeight="20"
|
||||||
|
android:viewportWidth="20" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="?primaryText" android:fillType="nonZero"
|
||||||
|
android:pathData="M18,6L2,6L2,17C2,17.5523 2.4477,18 3,18L17,18C17.5523,18 18,17.5523 18,17L18,6ZM16.7908,4L15.333,2.3401C15.1432,2.1239 14.8694,2 14.5817,2L5.8284,2C5.5632,2 5.3089,2.1054 5.1213,2.2929L3.4142,4L16.7908,4ZM0,17L0,5.8284C0,5.0328 0.3161,4.2697 0.8787,3.7071L3.7071,0.8787C4.2697,0.3161 5.0328,0 5.8284,0L14.5817,0C15.4448,0 16.2662,0.3718 16.8358,1.0203L19.2541,3.7739C19.7349,4.3213 20,5.025 20,5.7536L20,17C20,18.6569 18.6569,20 17,20L3,20C1.3431,20 0,18.6569 0,17Z"
|
||||||
|
android:strokeColor="#00000000" android:strokeWidth="1"/>
|
||||||
|
<path android:fillColor="?primaryText" android:fillType="nonZero"
|
||||||
|
android:pathData="M14.95,8.5L5.05,8.5C4.7462,8.5 4.5,8.7239 4.5,9C4.5,9.2761 4.7462,9.5 5.05,9.5L14.95,9.5C15.2538,9.5 15.5,9.2761 15.5,9C15.5,8.7239 15.2538,8.5 14.95,8.5Z"
|
||||||
|
android:strokeColor="#00000000" android:strokeWidth="1"/>
|
||||||
|
<path android:fillColor="?primaryText" android:fillType="nonZero"
|
||||||
|
android:pathData="M14.95,14.5L5.05,14.5C4.7462,14.5 4.5,14.7239 4.5,15C4.5,15.2761 4.7462,15.5 5.05,15.5L14.95,15.5C15.2538,15.5 15.5,15.2761 15.5,15C15.5,14.7239 15.2538,14.5 14.95,14.5Z"
|
||||||
|
android:strokeColor="#00000000" android:strokeWidth="1"/>
|
||||||
|
<path android:fillColor="?primaryText" android:fillType="nonZero"
|
||||||
|
android:pathData="M14.95,11.5L5.05,11.5C4.7462,11.5 4.5,11.7239 4.5,12C4.5,12.2761 4.7462,12.5 5.05,12.5L14.95,12.5C15.2538,12.5 15.5,12.2761 15.5,12C15.5,11.7239 15.2538,11.5 14.95,11.5Z"
|
||||||
|
android:strokeColor="#00000000" android:strokeWidth="1"/>
|
||||||
|
</vector>
|
|
@ -27,7 +27,7 @@
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:tint="@null"
|
android:tint="@null"
|
||||||
android:src="@drawable/ic_archive"
|
android:src="@drawable/ic_tab_collection"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"/>
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?foundation"
|
android:background="?foundation"
|
||||||
android:drawableStart="@drawable/ic_archive"
|
android:drawableStart="@drawable/ic_tab_collection"
|
||||||
android:drawablePadding="14dp"
|
android:drawablePadding="14dp"
|
||||||
android:drawableTint="?accent"
|
android:drawableTint="?accent"
|
||||||
android:paddingStart="20dp"
|
android:paddingStart="20dp"
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
android:id="@+id/no_collection_header"
|
android:id="@+id/no_collection_header"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:drawableEnd="@drawable/ic_archive"
|
android:drawableEnd="@drawable/ic_tab_collection"
|
||||||
android:drawableTint="?primaryText"
|
android:drawableTint="?primaryText"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
android:text="@string/no_collections_header"
|
android:text="@string/no_collections_header"
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:drawableTint="?foundation"
|
android:drawableTint="?foundation"
|
||||||
android:drawableStart="@drawable/ic_archive"
|
android:drawableStart="@drawable/ic_tab_collection"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
|
|
@ -100,7 +100,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?foundation"
|
android:background="?foundation"
|
||||||
android:drawableStart="@drawable/ic_archive"
|
android:drawableStart="@drawable/ic_tab_collection"
|
||||||
android:drawablePadding="14dp"
|
android:drawablePadding="14dp"
|
||||||
android:drawableTint="?accent"
|
android:drawableTint="?accent"
|
||||||
android:paddingStart="20dp"
|
android:paddingStart="20dp"
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
android:layout_marginBottom="24dp"
|
android:layout_marginBottom="24dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:tint="@null"
|
android:tint="@null"
|
||||||
android:src="@drawable/ic_archive"
|
android:src="@drawable/ic_tab_collection"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"/>
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
|
||||||
|
|
|
@ -20,5 +20,4 @@ class TestComponents(private val context: Context) : Components(context) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
override val analytics by lazy { Analytics(context) }
|
override val analytics by lazy { Analytics(context) }
|
||||||
override val storage by lazy { Storage(context) }
|
|
||||||
}
|
}
|
|
@ -109,6 +109,7 @@ object Deps {
|
||||||
const val mozilla_feature_session_bundling = "org.mozilla.components:feature-session-bundling:${Versions.mozilla_android_components}"
|
const val mozilla_feature_session_bundling = "org.mozilla.components:feature-session-bundling:${Versions.mozilla_android_components}"
|
||||||
const val mozilla_feature_site_permissions = "org.mozilla.components:feature-sitepermissions:${Versions.mozilla_android_components}"
|
const val mozilla_feature_site_permissions = "org.mozilla.components:feature-sitepermissions:${Versions.mozilla_android_components}"
|
||||||
const val mozilla_feature_readerview = "org.mozilla.components:feature-readerview:${Versions.mozilla_android_components}"
|
const val mozilla_feature_readerview = "org.mozilla.components:feature-readerview:${Versions.mozilla_android_components}"
|
||||||
|
const val mozilla_feature_tab_collections = "org.mozilla.components:feature-tab-collections:${Versions.mozilla_android_components}"
|
||||||
|
|
||||||
const val mozilla_service_firefox_accounts = "org.mozilla.components:service-firefox-accounts:${Versions.mozilla_android_components}"
|
const val mozilla_service_firefox_accounts = "org.mozilla.components:service-firefox-accounts:${Versions.mozilla_android_components}"
|
||||||
const val mozilla_service_fretboard = "org.mozilla.components:service-fretboard:${Versions.mozilla_android_components}"
|
const val mozilla_service_fretboard = "org.mozilla.components:service-fretboard:${Versions.mozilla_android_components}"
|
||||||
|
|
Loading…
Reference in New Issue