Skip to content
This repository has been archived by the owner on Jun 17, 2024. It is now read-only.

Commit

Permalink
[components] Closes mozilla-mobile/android-components#2743: Upstream …
Browse files Browse the repository at this point in the history
…tab counter changes from Fenix
  • Loading branch information
Elise Richards authored and mergify[bot] committed Dec 3, 2020
1 parent 70941b7 commit 30ffce0
Show file tree
Hide file tree
Showing 22 changed files with 709 additions and 411 deletions.
2 changes: 2 additions & 0 deletions android-components/components/feature/tabs/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
}

dependencies {
implementation project(':browser-state')
implementation project(':browser-session')
implementation project(':browser-thumbnails')
api project(':feature-session')
implementation project(':concept-engine')
implementation project(':concept-tabstray')
implementation project(':concept-toolbar')
implementation project(':concept-menu')
implementation project(':ui-icons')
implementation project(':ui-tabcounter')
implementation project(':support-ktx')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,67 +4,112 @@

package mozilla.components.feature.tabs.toolbar

import android.view.HapticFeedbackConstants
import android.view.View
import android.view.ViewGroup
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LifecycleOwner
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import mozilla.components.browser.state.selector.getNormalOrPrivateTabs
import mozilla.components.browser.state.selector.selectedTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.toolbar.Toolbar
import mozilla.components.feature.tabs.R
import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.support.ktx.android.content.res.resolveAttribute
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import mozilla.components.ui.tabcounter.TabCounter
import mozilla.components.ui.tabcounter.TabCounterMenu
import java.lang.ref.WeakReference

/**
* A [Toolbar.Action] implementation that shows a [TabCounter].
*/
class TabCounterToolbarButton(
private val sessionManager: SessionManager,
private val showTabs: () -> Unit
@ExperimentalCoroutinesApi
@Suppress("LongParameterList")
open class TabCounterToolbarButton(
private val lifecycleOwner: LifecycleOwner,
private val countBasedOnSelectedTabType: Boolean = true,
private val showTabs: () -> Unit,
private val store: BrowserStore,
private val menu: TabCounterMenu? = null,
private val privateColor: Int? = null
) : Toolbar.Action {
private var reference: WeakReference<TabCounter> = WeakReference<TabCounter>(null)

private var reference = WeakReference<TabCounter>(null)

override fun createView(parent: ViewGroup): View {
sessionManager.register(sessionManagerObserver, view = parent)
store.flowScoped(lifecycleOwner) { flow ->
flow.map { state -> getTabCount(state) }
.ifChanged()
.collect {
tabs -> updateCount(tabs)
}
}

val view = TabCounter(parent.context).apply {
val tabCounter = TabCounter(parent.context).apply {
reference = WeakReference(this)
setCount(sessionManager.sessions.size)
setOnClickListener {
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
showTabs.invoke()
}
contentDescription = parent.context.getString(R.string.mozac_feature_tabs_toolbar_tabs_button)

menu?.let { menu ->
setOnLongClickListener {
menu.menuController.show(anchor = it)
true
}
}

privateColor?.let {
if (isPrivate(store)) {
setColor(it)
}
}

addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View?) {
setCount(getTabCount(store.state))
}

override fun onViewDetachedFromWindow(v: View?) { /* no-op */ }
})
}

// Set selectableItemBackgroundBorderless
val selectableItemBackgroundBorderless =
parent.context.theme.resolveAttribute(android.R.attr.selectableItemBackgroundBorderless)
view.setBackgroundResource(selectableItemBackgroundBorderless)
return view
}

override fun bind(view: View) = Unit
tabCounter.setBackgroundResource(parent.context.theme.resolveAttribute(
android.R.attr.selectableItemBackgroundBorderless
))

private fun updateCount() {
reference.get()?.setCountWithAnimation(sessionManager.sessions.size)
return tabCounter
}

private val sessionManagerObserver = object : SessionManager.Observer {
override fun onSessionAdded(session: Session) {
updateCount()
}
override fun bind(view: View) = Unit

override fun onSessionRemoved(session: Session) {
updateCount()
private fun getTabCount(state: BrowserState): Int {
return if (countBasedOnSelectedTabType) {
state.getNormalOrPrivateTabs(isPrivate(store)).size
} else {
state.tabs.size
}
}

override fun onSessionsRestored() {
updateCount()
}
/**
* Update the tab counter button on the toolbar.
*
* @property count the updated tab count
*/
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun updateCount(count: Int) {
reference.get()?.setCountWithAnimation(count)
}

override fun onAllSessionsRemoved() {
updateCount()
}
/**
* Check if the selected tab is private.
*
* @property store the [BrowserStore] associated with this instance
*/
fun isPrivate(store: BrowserStore): Boolean {
return store.state.selectedTab?.content?.private ?: false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,43 @@

package mozilla.components.feature.tabs.toolbar

import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.session.runWithSession
import androidx.lifecycle.LifecycleOwner
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.state.selector.findCustomTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.toolbar.Toolbar
import mozilla.components.ui.tabcounter.TabCounterMenu

/**
* Feature implementation for connecting a tabs tray implementation with a toolbar implementation.
*
* @param countBasedOnSelectedTabType if true the count is based on the selected tab i.e. if a
* private tab is selected private tabs will be counter, otherwise normal tabs. If false, all
* tabs will be counted.
*/

// TODO Refactor or remove this feature: https://github.com/mozilla-mobile/android-components/issues/9129
@ExperimentalCoroutinesApi
@Suppress("LongParameterList")
class TabsToolbarFeature(
toolbar: Toolbar,
sessionManager: SessionManager,
store: BrowserStore,
sessionId: String? = null,
showTabs: () -> Unit
lifecycleOwner: LifecycleOwner,
showTabs: () -> Unit,
tabCounterMenu: TabCounterMenu? = null,
countBasedOnSelectedTabType: Boolean = true
) {
init {
run {
sessionManager.runWithSession(sessionId) {
it.isCustomTabSession()
}.also { isCustomTab ->
if (isCustomTab) return@run
}
// this feature is not used for Custom Tabs
if (sessionId != null && store.state.findCustomTab(sessionId) != null) return@run

val tabsAction = TabCounterToolbarButton(
sessionManager,
showTabs
lifecycleOwner = lifecycleOwner,
showTabs = showTabs,
store = store,
menu = tabCounterMenu,
countBasedOnSelectedTabType = countBasedOnSelectedTabType
)
toolbar.addBrowserAction(tabsAction)
}
Expand Down
Loading

0 comments on commit 30ffce0

Please sign in to comment.