Closes #8791: Use A-C tab counter and upgrades to A-C 69.0.20201203202830
Upgrades to A-C 69.0.20201203202830 and addresses breaking changes: - Upgrades androidx workmanager to 2.4.0 in line with A-C. - RecordingDevicesNotificationFeature was removed - SearchUseCases accept parent session ID instead of session itself
This commit is contained in:
parent
79d1c08402
commit
77f061c362
|
@ -487,6 +487,7 @@ dependencies {
|
|||
implementation Deps.mozilla_ui_icons
|
||||
implementation Deps.mozilla_lib_publicsuffixlist
|
||||
implementation Deps.mozilla_ui_widgets
|
||||
implementation Deps.mozilla_ui_tabcounter
|
||||
|
||||
implementation Deps.mozilla_lib_crash
|
||||
implementation Deps.mozilla_lib_push_firebase
|
||||
|
|
|
@ -572,7 +572,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|||
useCase.invoke(request.query)
|
||||
requireActivity().startActivity(openInFenixIntent)
|
||||
} else {
|
||||
useCase.invoke(request.query, parentSession = parentSession)
|
||||
useCase.invoke(request.query, parentSessionId = parentSession?.id)
|
||||
}
|
||||
},
|
||||
owner = this,
|
||||
|
|
|
@ -13,8 +13,8 @@ import android.widget.FrameLayout
|
|||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import kotlinx.android.synthetic.main.mozac_ui_tabcounter_layout.view.*
|
||||
import kotlinx.android.synthetic.main.tab_preview.view.*
|
||||
import kotlinx.android.synthetic.main.tabs_tray_tab_counter.view.*
|
||||
import mozilla.components.browser.thumbnails.loader.ThumbnailLoader
|
||||
import mozilla.components.concept.base.images.ImageLoadRequest
|
||||
import org.mozilla.fenix.R
|
||||
|
|
|
@ -23,7 +23,6 @@ import mozilla.components.browser.session.SessionManager
|
|||
import mozilla.components.browser.session.engine.EngineMiddleware
|
||||
import mozilla.components.browser.session.storage.SessionStorage
|
||||
import mozilla.components.browser.session.undo.UndoMiddleware
|
||||
import mozilla.components.browser.state.action.RecentlyClosedAction
|
||||
import mozilla.components.browser.state.action.RestoreCompleteAction
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
|
@ -40,8 +39,8 @@ import mozilla.components.concept.fetch.Client
|
|||
import mozilla.components.feature.customtabs.store.CustomTabsServiceStore
|
||||
import mozilla.components.feature.downloads.DownloadMiddleware
|
||||
import mozilla.components.feature.logins.exceptions.LoginExceptionStorage
|
||||
import mozilla.components.feature.media.RecordingDevicesNotificationFeature
|
||||
import mozilla.components.feature.media.middleware.MediaMiddleware
|
||||
import mozilla.components.feature.media.middleware.RecordingDevicesMiddleware
|
||||
import mozilla.components.feature.pwa.ManifestStorage
|
||||
import mozilla.components.feature.pwa.WebAppShortcutManager
|
||||
import mozilla.components.feature.readerview.ReaderViewMiddleware
|
||||
|
@ -198,11 +197,10 @@ class Core(
|
|||
context,
|
||||
additionalBundledSearchEngineIds = listOf("reddit", "youtube"),
|
||||
migration = SearchMigration(context)
|
||||
)
|
||||
),
|
||||
RecordingDevicesMiddleware(context)
|
||||
) + EngineMiddleware.create(engine, ::findSessionById)
|
||||
).also {
|
||||
it.dispatch(RecentlyClosedAction.InitializeRecentlyClosedState)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun lookupSessionManager(): SessionManager {
|
||||
|
@ -242,10 +240,6 @@ class Core(
|
|||
// Install the "cookies" WebExtension and tracks user interaction with SERPs.
|
||||
searchTelemetry.install(engine, store)
|
||||
|
||||
// Show an ongoing notification when recording devices (camera, microphone) are used by web content
|
||||
RecordingDevicesNotificationFeature(context, sessionManager)
|
||||
.enable()
|
||||
|
||||
// Restore the previous state.
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
withContext(Dispatchers.IO) {
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
package org.mozilla.fenix.components.toolbar
|
||||
|
||||
import mozilla.components.ui.tabcounter.TabCounterMenu
|
||||
|
||||
open class BrowserInteractor(
|
||||
private val browserToolbarController: BrowserToolbarController,
|
||||
private val menuController: BrowserToolbarMenuController
|
||||
|
|
|
@ -9,10 +9,12 @@ import mozilla.components.browser.session.Session
|
|||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.concept.engine.EngineView
|
||||
import mozilla.components.support.ktx.kotlin.isUrl
|
||||
import mozilla.components.ui.tabcounter.TabCounterMenu
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.BrowserAnimator.Companion.getToolbarNavOptions
|
||||
import org.mozilla.fenix.browser.BrowserFragmentDirections
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.browser.readermode.ReaderModeController
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
|
@ -104,6 +106,9 @@ class DefaultBrowserToolbarController(
|
|||
override fun handleTabCounterItemInteraction(item: TabCounterMenu.Item) {
|
||||
when (item) {
|
||||
is TabCounterMenu.Item.CloseTab -> {
|
||||
metrics.track(
|
||||
Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.CLOSE_TAB)
|
||||
)
|
||||
sessionManager.selectedSession?.let {
|
||||
// When closing the last tab we must show the undo snackbar in the home fragment
|
||||
if (sessionManager.sessionsOfType(it.private).count() == 1) {
|
||||
|
@ -120,8 +125,24 @@ class DefaultBrowserToolbarController(
|
|||
}
|
||||
}
|
||||
is TabCounterMenu.Item.NewTab -> {
|
||||
activity.browsingModeManager.mode = item.mode
|
||||
navController.navigate(BrowserFragmentDirections.actionGlobalHome(focusOnAddressBar = true))
|
||||
metrics.track(
|
||||
Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.NEW_TAB)
|
||||
)
|
||||
activity.browsingModeManager.mode = BrowsingMode.Normal
|
||||
navController.navigate(
|
||||
BrowserFragmentDirections.actionGlobalHome(focusOnAddressBar = true)
|
||||
)
|
||||
}
|
||||
is TabCounterMenu.Item.NewPrivateTab -> {
|
||||
metrics.track(
|
||||
Event.TabCounterMenuItemTapped(
|
||||
Event.TabCounterMenuItemTapped.Item.NEW_PRIVATE_TAB
|
||||
)
|
||||
)
|
||||
activity.browsingModeManager.mode = BrowsingMode.Private
|
||||
navController.navigate(
|
||||
BrowserFragmentDirections.actionGlobalHome(focusOnAddressBar = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import mozilla.components.browser.toolbar.BrowserToolbar
|
|||
import mozilla.components.browser.toolbar.behavior.BrowserToolbarBottomBehavior
|
||||
import mozilla.components.browser.toolbar.display.DisplayToolbar
|
||||
import mozilla.components.support.utils.URLStringUtils
|
||||
import mozilla.components.ui.tabcounter.TabCounterMenu
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.customtabs.CustomTabToolbarIntegration
|
||||
import org.mozilla.fenix.customtabs.CustomTabToolbarMenu
|
||||
|
@ -267,10 +268,6 @@ class BrowserToolbarView(
|
|||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TOOLBAR_ELEVATION = 16
|
||||
}
|
||||
|
||||
@Suppress("ComplexCondition")
|
||||
private fun ToolbarMenu.Item.performHapticIfNeeded(view: View) {
|
||||
if (this is ToolbarMenu.Item.Reload && this.bypassCache ||
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/* 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.toolbar
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import mozilla.components.concept.menu.candidate.DividerMenuCandidate
|
||||
import mozilla.components.concept.menu.candidate.MenuCandidate
|
||||
import mozilla.components.ui.tabcounter.TabCounterMenu
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
|
||||
class FenixTabCounterMenu(
|
||||
context: Context,
|
||||
onItemTapped: (Item) -> Unit,
|
||||
iconColor: Int? = null
|
||||
) : TabCounterMenu(context, onItemTapped, iconColor) {
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun menuItems(showOnly: BrowsingMode): List<MenuCandidate> {
|
||||
return when (showOnly) {
|
||||
BrowsingMode.Normal -> listOf(newTabItem)
|
||||
BrowsingMode.Private -> listOf(newPrivateTabItem)
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun menuItems(toolbarPosition: ToolbarPosition): List<MenuCandidate> {
|
||||
val items = listOf(
|
||||
newTabItem,
|
||||
newPrivateTabItem,
|
||||
DividerMenuCandidate(),
|
||||
closeTabItem
|
||||
)
|
||||
|
||||
return when (toolbarPosition) {
|
||||
ToolbarPosition.BOTTOM -> items.reversed()
|
||||
ToolbarPosition.TOP -> items
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the displayed menu items.
|
||||
* @param showOnly Show only the new tab item corresponding to the given [BrowsingMode].
|
||||
*/
|
||||
fun updateMenu(showOnly: BrowsingMode) {
|
||||
val items = menuItems(showOnly)
|
||||
|
||||
menuController.submitList(items)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the displayed menu items.
|
||||
* @param toolbarPosition Return a list that is ordered based on the given [ToolbarPosition].
|
||||
*/
|
||||
fun updateMenu(toolbarPosition: ToolbarPosition) {
|
||||
menuController.submitList(menuItems(toolbarPosition))
|
||||
}
|
||||
}
|
|
@ -1,271 +0,0 @@
|
|||
/* 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.toolbar
|
||||
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ObjectAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.core.view.updatePadding
|
||||
import kotlinx.android.synthetic.main.mozac_ui_tabcounter_layout.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import java.text.NumberFormat
|
||||
|
||||
class TabCounter @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0
|
||||
) : RelativeLayout(context, attrs, defStyle) {
|
||||
|
||||
private val animationSet: AnimatorSet
|
||||
|
||||
init {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
inflater.inflate(R.layout.mozac_ui_tabcounter_layout, this)
|
||||
|
||||
// This is needed because without this counter box will be empty.
|
||||
setCount(INTERNAL_COUNT)
|
||||
|
||||
animationSet = createAnimatorSet()
|
||||
}
|
||||
|
||||
private fun updateContentDescription(count: Int) {
|
||||
counter_root.contentDescription = if (count == 1) {
|
||||
context?.getString(R.string.open_tab_tray_single)
|
||||
} else {
|
||||
String.format(context.getString(R.string.open_tab_tray_plural), count.toString())
|
||||
}
|
||||
}
|
||||
|
||||
fun setCountWithAnimation(count: Int) {
|
||||
setCount(count)
|
||||
|
||||
// No need to animate on these cases.
|
||||
when {
|
||||
INTERNAL_COUNT == 0 -> return // Initial state.
|
||||
INTERNAL_COUNT == count -> return // There isn't any tab added or removed.
|
||||
INTERNAL_COUNT > MAX_VISIBLE_TABS -> return // There are still over MAX_VISIBLE_TABS tabs open.
|
||||
}
|
||||
|
||||
// Cancel previous animations if necessary.
|
||||
if (animationSet.isRunning) {
|
||||
animationSet.cancel()
|
||||
}
|
||||
// Trigger animations.
|
||||
animationSet.start()
|
||||
}
|
||||
|
||||
fun setCount(count: Int) {
|
||||
updateContentDescription(count)
|
||||
adjustTextSize(count)
|
||||
counter_text.text = formatForDisplay(count)
|
||||
INTERNAL_COUNT = count
|
||||
}
|
||||
|
||||
private fun createAnimatorSet(): AnimatorSet {
|
||||
val animatorSet = AnimatorSet()
|
||||
createBoxAnimatorSet(animatorSet)
|
||||
createTextAnimatorSet(animatorSet)
|
||||
return animatorSet
|
||||
}
|
||||
|
||||
private fun createBoxAnimatorSet(animatorSet: AnimatorSet) {
|
||||
// The first animator, fadeout in 33 ms (49~51, 2 frames).
|
||||
val fadeOut = ObjectAnimator.ofFloat(
|
||||
counter_box, "alpha",
|
||||
ANIM_BOX_FADEOUT_FROM, ANIM_BOX_FADEOUT_TO
|
||||
).setDuration(ANIM_BOX_FADEOUT_DURATION)
|
||||
|
||||
// Move up on y-axis, from 0.0 to -5.3 in 50ms, with fadeOut (49~52, 3 frames).
|
||||
val moveUp1 = ObjectAnimator.ofFloat(
|
||||
counter_box, "translationY",
|
||||
ANIM_BOX_MOVEUP1_TO, ANIM_BOX_MOVEUP1_FROM
|
||||
).setDuration(ANIM_BOX_MOVEUP1_DURATION)
|
||||
|
||||
// Move down on y-axis, from -5.3 to -1.0 in 116ms, after moveUp1 (52~59, 7 frames).
|
||||
val moveDown2 = ObjectAnimator.ofFloat(
|
||||
counter_box, "translationY",
|
||||
ANIM_BOX_MOVEDOWN2_FROM, ANIM_BOX_MOVEDOWN2_TO
|
||||
).setDuration(ANIM_BOX_MOVEDOWN2_DURATION)
|
||||
|
||||
// FadeIn in 66ms, with moveDown2 (52~56, 4 frames).
|
||||
val fadeIn = ObjectAnimator.ofFloat(
|
||||
counter_box, "alpha",
|
||||
ANIM_BOX_FADEIN_FROM, ANIM_BOX_FADEIN_TO
|
||||
).setDuration(ANIM_BOX_FADEIN_DURATION)
|
||||
|
||||
// Move down on y-axis, from -1.0 to 2.7 in 116ms, after moveDown2 (59~66, 7 frames).
|
||||
val moveDown3 = ObjectAnimator.ofFloat(
|
||||
counter_box, "translationY",
|
||||
ANIM_BOX_MOVEDOWN3_FROM, ANIM_BOX_MOVEDOWN3_TO
|
||||
).setDuration(ANIM_BOX_MOVEDOWN3_DURATION)
|
||||
|
||||
// Move up on y-axis, from 2.7 to 0 in 133ms, after moveDown3 (66~74, 8 frames).
|
||||
val moveUp4 = ObjectAnimator.ofFloat(
|
||||
counter_box, "translationY",
|
||||
ANIM_BOX_MOVEDOWN4_FROM, ANIM_BOX_MOVEDOWN4_TO
|
||||
).setDuration(ANIM_BOX_MOVEDOWN4_DURATION)
|
||||
|
||||
// Scale up height from 2% to 105% in 100ms, after moveUp1 and delay 16ms (53~59, 6 frames).
|
||||
val scaleUp1 = ObjectAnimator.ofFloat(
|
||||
counter_box, "scaleY",
|
||||
ANIM_BOX_SCALEUP1_FROM, ANIM_BOX_SCALEUP1_TO
|
||||
).setDuration(ANIM_BOX_SCALEUP1_DURATION)
|
||||
scaleUp1.startDelay = ANIM_BOX_SCALEUP1_DELAY // delay 1 frame after moveUp1
|
||||
|
||||
// Scale down height from 105% to 99% in 116ms, after scaleUp1 (59~66, 7 frames).
|
||||
val scaleDown2 = ObjectAnimator.ofFloat(
|
||||
counter_box, "scaleY",
|
||||
ANIM_BOX_SCALEDOWN2_FROM, ANIM_BOX_SCALEDOWN2_TO
|
||||
).setDuration(ANIM_BOX_SCALEDOWN2_DURATION)
|
||||
|
||||
// Scale up height from 99% to 100% in 133ms, after scaleDown2 (66~74, 8 frames).
|
||||
val scaleUp3 = ObjectAnimator.ofFloat(
|
||||
counter_box, "scaleY",
|
||||
ANIM_BOX_SCALEUP3_FROM, ANIM_BOX_SCALEUP3_TO
|
||||
).setDuration(ANIM_BOX_SCALEUP3_DURATION)
|
||||
|
||||
animatorSet.play(fadeOut).with(moveUp1)
|
||||
animatorSet.play(moveUp1).before(moveDown2)
|
||||
animatorSet.play(moveDown2).with(fadeIn)
|
||||
animatorSet.play(moveDown2).before(moveDown3)
|
||||
animatorSet.play(moveDown3).before(moveUp4)
|
||||
|
||||
animatorSet.play(moveUp1).before(scaleUp1)
|
||||
animatorSet.play(scaleUp1).before(scaleDown2)
|
||||
animatorSet.play(scaleDown2).before(scaleUp3)
|
||||
}
|
||||
|
||||
private fun createTextAnimatorSet(animatorSet: AnimatorSet) {
|
||||
val firstAnimator = animatorSet.childAnimations[0]
|
||||
|
||||
// Fadeout in 100ms, with firstAnimator (49~51, 2 frames).
|
||||
val fadeOut = ObjectAnimator.ofFloat(
|
||||
counter_text, "alpha",
|
||||
ANIM_TEXT_FADEOUT_FROM, ANIM_TEXT_FADEOUT_TO
|
||||
).setDuration(ANIM_TEXT_FADEOUT_DURATION)
|
||||
|
||||
// FadeIn in 66 ms, after fadeOut with delay 96ms (57~61, 4 frames).
|
||||
val fadeIn = ObjectAnimator.ofFloat(
|
||||
counter_text, "alpha",
|
||||
ANIM_TEXT_FADEIN_FROM, ANIM_TEXT_FADEIN_TO
|
||||
).setDuration(ANIM_TEXT_FADEIN_DURATION)
|
||||
fadeIn.startDelay = (ANIM_TEXT_FADEIN_DELAY) // delay 6 frames after fadeOut
|
||||
|
||||
// Move down on y-axis, from 0 to 4.4 in 66ms, with fadeIn (57~61, 4 frames).
|
||||
val moveDown = ObjectAnimator.ofFloat(
|
||||
counter_text, "translationY",
|
||||
ANIM_TEXT_MOVEDOWN_FROM, ANIM_TEXT_MOVEDOWN_TO
|
||||
).setDuration(ANIM_TEXT_MOVEDOWN_DURATION)
|
||||
moveDown.startDelay = (ANIM_TEXT_MOVEDOWN_DELAY) // delay 6 frames after fadeOut
|
||||
|
||||
// Move up on y-axis, from 0 to 4.4 in 66ms, after moveDown (61~69, 8 frames).
|
||||
val moveUp = ObjectAnimator.ofFloat(
|
||||
counter_text, "translationY",
|
||||
ANIM_TEXT_MOVEUP_FROM, ANIM_TEXT_MOVEUP_TO
|
||||
).setDuration(ANIM_TEXT_MOVEUP_DURATION)
|
||||
|
||||
animatorSet.play(firstAnimator).with(fadeOut)
|
||||
animatorSet.play(fadeOut).before(fadeIn)
|
||||
animatorSet.play(fadeIn).with(moveDown)
|
||||
animatorSet.play(moveDown).before(moveUp)
|
||||
}
|
||||
|
||||
private fun formatForDisplay(count: Int): String {
|
||||
return if (count > MAX_VISIBLE_TABS) {
|
||||
counter_text.updatePadding(bottom = INFINITE_CHAR_PADDING_BOTTOM)
|
||||
SO_MANY_TABS_OPEN
|
||||
} else NumberFormat.getInstance().format(count.toLong())
|
||||
}
|
||||
|
||||
private fun adjustTextSize(newCount: Int) {
|
||||
val newRatio = if (newCount in TWO_DIGITS_TAB_COUNT_THRESHOLD..MAX_VISIBLE_TABS) {
|
||||
TWO_DIGITS_SIZE_RATIO
|
||||
} else {
|
||||
ONE_DIGIT_SIZE_RATIO
|
||||
}
|
||||
|
||||
val counterBoxWidth =
|
||||
context.resources.getDimensionPixelSize(R.dimen.tab_counter_box_width_height)
|
||||
val textSize = newRatio * counterBoxWidth
|
||||
counter_text.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
||||
counter_text.setTypeface(null, Typeface.BOLD)
|
||||
counter_text.setPadding(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal var INTERNAL_COUNT = 0
|
||||
|
||||
internal const val MAX_VISIBLE_TABS = 99
|
||||
|
||||
internal const val SO_MANY_TABS_OPEN = "∞"
|
||||
|
||||
internal const val INFINITE_CHAR_PADDING_BOTTOM = 6
|
||||
|
||||
internal const val ONE_DIGIT_SIZE_RATIO = 0.5f
|
||||
internal const val TWO_DIGITS_SIZE_RATIO = 0.4f
|
||||
internal const val TWO_DIGITS_TAB_COUNT_THRESHOLD = 10
|
||||
|
||||
// createBoxAnimatorSet
|
||||
private const val ANIM_BOX_FADEOUT_FROM = 1.0f
|
||||
private const val ANIM_BOX_FADEOUT_TO = 0.0f
|
||||
private const val ANIM_BOX_FADEOUT_DURATION = 33L
|
||||
|
||||
private const val ANIM_BOX_MOVEUP1_FROM = 0.0f
|
||||
private const val ANIM_BOX_MOVEUP1_TO = -5.3f
|
||||
private const val ANIM_BOX_MOVEUP1_DURATION = 50L
|
||||
|
||||
private const val ANIM_BOX_MOVEDOWN2_FROM = -5.3f
|
||||
private const val ANIM_BOX_MOVEDOWN2_TO = -1.0f
|
||||
private const val ANIM_BOX_MOVEDOWN2_DURATION = 167L
|
||||
|
||||
private const val ANIM_BOX_FADEIN_FROM = 0.01f
|
||||
private const val ANIM_BOX_FADEIN_TO = 1.0f
|
||||
private const val ANIM_BOX_FADEIN_DURATION = 66L
|
||||
private const val ANIM_BOX_MOVEDOWN3_FROM = -1.0f
|
||||
private const val ANIM_BOX_MOVEDOWN3_TO = 2.7f
|
||||
private const val ANIM_BOX_MOVEDOWN3_DURATION = 116L
|
||||
|
||||
private const val ANIM_BOX_MOVEDOWN4_FROM = 2.7f
|
||||
private const val ANIM_BOX_MOVEDOWN4_TO = 0.0f
|
||||
private const val ANIM_BOX_MOVEDOWN4_DURATION = 133L
|
||||
|
||||
private const val ANIM_BOX_SCALEUP1_FROM = 0.02f
|
||||
private const val ANIM_BOX_SCALEUP1_TO = 1.05f
|
||||
private const val ANIM_BOX_SCALEUP1_DURATION = 100L
|
||||
private const val ANIM_BOX_SCALEUP1_DELAY = 16L
|
||||
|
||||
private const val ANIM_BOX_SCALEDOWN2_FROM = 1.05f
|
||||
private const val ANIM_BOX_SCALEDOWN2_TO = 0.99f
|
||||
private const val ANIM_BOX_SCALEDOWN2_DURATION = 116L
|
||||
|
||||
private const val ANIM_BOX_SCALEUP3_FROM = 0.99f
|
||||
private const val ANIM_BOX_SCALEUP3_TO = 1.00f
|
||||
private const val ANIM_BOX_SCALEUP3_DURATION = 133L
|
||||
|
||||
// createTextAnimatorSet
|
||||
private const val ANIM_TEXT_FADEOUT_FROM = 1.0f
|
||||
private const val ANIM_TEXT_FADEOUT_TO = 0.0f
|
||||
private const val ANIM_TEXT_FADEOUT_DURATION = 33L
|
||||
|
||||
private const val ANIM_TEXT_FADEIN_FROM = 0.01f
|
||||
private const val ANIM_TEXT_FADEIN_TO = 1.0f
|
||||
private const val ANIM_TEXT_FADEIN_DURATION = 66L
|
||||
private const val ANIM_TEXT_FADEIN_DELAY = 16L * 6
|
||||
|
||||
private const val ANIM_TEXT_MOVEDOWN_FROM = 0.0f
|
||||
private const val ANIM_TEXT_MOVEDOWN_TO = 4.4f
|
||||
private const val ANIM_TEXT_MOVEDOWN_DURATION = 66L
|
||||
private const val ANIM_TEXT_MOVEDOWN_DELAY = 16L * 6
|
||||
|
||||
private const val ANIM_TEXT_MOVEUP_FROM = 4.4f
|
||||
private const val ANIM_TEXT_MOVEUP_TO = 0.0f
|
||||
private const val ANIM_TEXT_MOVEUP_DURATION = 66L
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/* 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.toolbar
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import mozilla.components.browser.menu2.BrowserMenuController
|
||||
import mozilla.components.concept.menu.MenuController
|
||||
import mozilla.components.concept.menu.candidate.DividerMenuCandidate
|
||||
import mozilla.components.concept.menu.candidate.DrawableMenuIcon
|
||||
import mozilla.components.concept.menu.candidate.MenuCandidate
|
||||
import mozilla.components.concept.menu.candidate.TextMenuCandidate
|
||||
import mozilla.components.concept.menu.candidate.TextStyle
|
||||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
|
||||
class TabCounterMenu(
|
||||
context: Context,
|
||||
private val metrics: MetricController,
|
||||
private val onItemTapped: (Item) -> Unit
|
||||
) {
|
||||
|
||||
sealed class Item {
|
||||
object CloseTab : Item()
|
||||
data class NewTab(val mode: BrowsingMode) : Item()
|
||||
}
|
||||
|
||||
val menuController: MenuController by lazy { BrowserMenuController() }
|
||||
|
||||
private val newTabItem: TextMenuCandidate
|
||||
private val newPrivateTabItem: TextMenuCandidate
|
||||
private val closeTabItem: TextMenuCandidate
|
||||
|
||||
init {
|
||||
val primaryTextColor = context.getColorFromAttr(R.attr.primaryText)
|
||||
val textStyle = TextStyle(color = primaryTextColor)
|
||||
|
||||
newTabItem = TextMenuCandidate(
|
||||
text = context.getString(R.string.browser_menu_new_tab),
|
||||
start = DrawableMenuIcon(
|
||||
context,
|
||||
R.drawable.ic_new,
|
||||
tint = primaryTextColor
|
||||
),
|
||||
textStyle = textStyle
|
||||
) {
|
||||
metrics.track(Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.NEW_TAB))
|
||||
onItemTapped(Item.NewTab(BrowsingMode.Normal))
|
||||
}
|
||||
|
||||
newPrivateTabItem = TextMenuCandidate(
|
||||
text = context.getString(R.string.home_screen_shortcut_open_new_private_tab_2),
|
||||
start = DrawableMenuIcon(
|
||||
context,
|
||||
R.drawable.ic_private_browsing,
|
||||
tint = primaryTextColor
|
||||
),
|
||||
textStyle = textStyle
|
||||
) {
|
||||
metrics.track(Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.NEW_PRIVATE_TAB))
|
||||
onItemTapped(Item.NewTab(BrowsingMode.Private))
|
||||
}
|
||||
|
||||
closeTabItem = TextMenuCandidate(
|
||||
text = context.getString(R.string.close_tab),
|
||||
start = DrawableMenuIcon(
|
||||
context,
|
||||
R.drawable.ic_close,
|
||||
tint = primaryTextColor
|
||||
),
|
||||
textStyle = textStyle
|
||||
) {
|
||||
metrics.track(Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.CLOSE_TAB))
|
||||
onItemTapped(Item.CloseTab)
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun menuItems(showOnly: BrowsingMode): List<MenuCandidate> {
|
||||
return when (showOnly) {
|
||||
BrowsingMode.Normal -> listOf(newTabItem)
|
||||
BrowsingMode.Private -> listOf(newPrivateTabItem)
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun menuItems(toolbarPosition: ToolbarPosition): List<MenuCandidate> {
|
||||
val items = listOf(
|
||||
newTabItem,
|
||||
newPrivateTabItem,
|
||||
DividerMenuCandidate(),
|
||||
closeTabItem
|
||||
)
|
||||
|
||||
return when (toolbarPosition) {
|
||||
ToolbarPosition.BOTTOM -> items.reversed()
|
||||
ToolbarPosition.TOP -> items
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the displayed menu items.
|
||||
* @param showOnly Show only the new tab item corresponding to the given [BrowsingMode].
|
||||
*/
|
||||
fun updateMenu(showOnly: BrowsingMode) {
|
||||
menuController.submitList(menuItems(showOnly))
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the displayed menu items.
|
||||
* @param toolbarPosition Return a list that is ordered based on the given [ToolbarPosition].
|
||||
*/
|
||||
fun updateMenu(toolbarPosition: ToolbarPosition) {
|
||||
menuController.submitList(menuItems(toolbarPosition))
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/* 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.toolbar
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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.store.BrowserStore
|
||||
import mozilla.components.concept.toolbar.Toolbar
|
||||
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 org.mozilla.fenix.ext.components
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* A [Toolbar.Action] implementation that shows a [TabCounter].
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class TabCounterToolbarButton(
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val onItemTapped: (TabCounterMenu.Item) -> Unit = {},
|
||||
private val showTabs: () -> Unit
|
||||
) : Toolbar.Action {
|
||||
|
||||
private var reference: WeakReference<TabCounter> = WeakReference<TabCounter>(null)
|
||||
|
||||
override fun createView(parent: ViewGroup): View {
|
||||
val store = parent.context.components.core.store
|
||||
val metrics = parent.context.components.analytics.metrics
|
||||
val settings = parent.context.components.settings
|
||||
|
||||
store.flowScoped(lifecycleOwner) { flow ->
|
||||
flow.map { state -> state.getNormalOrPrivateTabs(isPrivate(store)).size }
|
||||
.ifChanged()
|
||||
.collect { tabs -> updateCount(tabs) }
|
||||
}
|
||||
|
||||
val menu = TabCounterMenu(parent.context, metrics, onItemTapped)
|
||||
menu.updateMenu(settings.toolbarPosition)
|
||||
|
||||
val view = TabCounter(parent.context).apply {
|
||||
reference = WeakReference(this)
|
||||
setOnClickListener {
|
||||
showTabs.invoke()
|
||||
}
|
||||
|
||||
setOnLongClickListener {
|
||||
menu.menuController.show(anchor = it)
|
||||
true
|
||||
}
|
||||
|
||||
addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
||||
override fun onViewAttachedToWindow(v: View?) {
|
||||
setCount(store.state.getNormalOrPrivateTabs(isPrivate(store)).size)
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(v: View?) { /* no-op */ }
|
||||
})
|
||||
}
|
||||
|
||||
// Set selectableItemBackgroundBorderless
|
||||
view.setBackgroundResource(parent.context.theme.resolveAttribute(
|
||||
android.R.attr.selectableItemBackgroundBorderless
|
||||
))
|
||||
return view
|
||||
}
|
||||
|
||||
override fun bind(view: View) = Unit
|
||||
|
||||
private fun updateCount(count: Int) {
|
||||
reference.get()?.setCountWithAnimation(count)
|
||||
}
|
||||
|
||||
private fun isPrivate(store: BrowserStore): Boolean {
|
||||
return store.state.selectedTab?.content?.private ?: false
|
||||
}
|
||||
}
|
|
@ -7,13 +7,17 @@ package org.mozilla.fenix.components.toolbar
|
|||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import mozilla.components.browser.domains.autocomplete.DomainAutocompleteProvider
|
||||
import mozilla.components.browser.state.selector.normalTabs
|
||||
import mozilla.components.browser.state.selector.privateTabs
|
||||
import mozilla.components.browser.toolbar.BrowserToolbar
|
||||
import mozilla.components.browser.toolbar.display.DisplayToolbar
|
||||
import mozilla.components.concept.engine.Engine
|
||||
import mozilla.components.concept.storage.HistoryStorage
|
||||
import mozilla.components.feature.tabs.toolbar.TabCounterToolbarButton
|
||||
import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature
|
||||
import mozilla.components.feature.toolbar.ToolbarFeature
|
||||
import mozilla.components.feature.toolbar.ToolbarPresenter
|
||||
|
@ -35,9 +39,10 @@ abstract class ToolbarIntegration(
|
|||
renderStyle: ToolbarFeature.RenderStyle
|
||||
) : LifecycleAwareFeature {
|
||||
|
||||
val store = context.components.core.store
|
||||
private val toolbarPresenter: ToolbarPresenter = ToolbarPresenter(
|
||||
toolbar,
|
||||
context.components.core.store,
|
||||
store,
|
||||
sessionId,
|
||||
ToolbarFeature.UrlRenderConfiguration(
|
||||
PublicSuffixList(context),
|
||||
|
@ -139,16 +144,40 @@ class DefaultToolbarIntegration(
|
|||
)!!
|
||||
)
|
||||
|
||||
val tabsAction = TabCounterToolbarButton(
|
||||
lifecycleOwner,
|
||||
val tabCounterMenu = FenixTabCounterMenu(
|
||||
context = context,
|
||||
onItemTapped = {
|
||||
interactor.onTabCounterMenuItemTapped(it)
|
||||
},
|
||||
iconColor =
|
||||
if (isPrivate) {
|
||||
ContextCompat.getColor(context, R.color.primary_text_private_theme)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
).also {
|
||||
it.updateMenu(context.settings().toolbarPosition)
|
||||
}
|
||||
|
||||
val tabsAction = TabCounterToolbarButton(
|
||||
lifecycleOwner = lifecycleOwner,
|
||||
showTabs = {
|
||||
toolbar.hideKeyboard()
|
||||
interactor.onTabCounterClicked()
|
||||
}
|
||||
},
|
||||
store = store,
|
||||
menu = tabCounterMenu,
|
||||
privateColor = ContextCompat.getColor(context, R.color.primary_text_private_theme)
|
||||
)
|
||||
|
||||
val tabCount = if (isPrivate) {
|
||||
store.state.privateTabs.size
|
||||
} else {
|
||||
store.state.normalTabs.size
|
||||
}
|
||||
|
||||
tabsAction.updateCount(tabCount)
|
||||
|
||||
toolbar.addBrowserAction(tabsAction)
|
||||
|
||||
val engineForSpeculativeConnects = if (!isPrivate) engine else null
|
||||
|
|
|
@ -41,9 +41,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.fragment_home.privateBrowsingButton
|
||||
import kotlinx.android.synthetic.main.fragment_home.search_engine_icon
|
||||
import kotlinx.android.synthetic.main.fragment_home.toolbarLayout
|
||||
import kotlinx.android.synthetic.main.fragment_home.*
|
||||
import kotlinx.android.synthetic.main.fragment_home.view.bottomBarShadow
|
||||
import kotlinx.android.synthetic.main.fragment_home.view.bottom_bar
|
||||
import kotlinx.android.synthetic.main.fragment_home.view.homeAppBar
|
||||
|
@ -81,6 +79,7 @@ import mozilla.components.lib.state.ext.consumeFrom
|
|||
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
||||
import mozilla.components.support.ktx.android.content.res.resolveAttribute
|
||||
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
||||
import mozilla.components.ui.tabcounter.TabCounterMenu
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
|
@ -94,7 +93,7 @@ import org.mozilla.fenix.components.metrics.Event
|
|||
import org.mozilla.fenix.components.tips.FenixTipManager
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
import org.mozilla.fenix.components.tips.providers.MasterPasswordTipProvider
|
||||
import org.mozilla.fenix.components.toolbar.TabCounterMenu
|
||||
import org.mozilla.fenix.components.toolbar.FenixTabCounterMenu
|
||||
import org.mozilla.fenix.components.toolbar.ToolbarPosition
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.hideToolbar
|
||||
|
@ -351,23 +350,7 @@ class HomeFragment : Fragment() {
|
|||
observeSearchEngineChanges()
|
||||
|
||||
createHomeMenu(requireContext(), WeakReference(view.menuButton))
|
||||
val tabCounterMenu = TabCounterMenu(
|
||||
view.context,
|
||||
metrics = view.context.components.analytics.metrics
|
||||
) {
|
||||
if (it is TabCounterMenu.Item.NewTab) {
|
||||
(activity as HomeActivity).browsingModeManager.mode = it.mode
|
||||
}
|
||||
}
|
||||
val inverseBrowsingMode = when ((activity as HomeActivity).browsingModeManager.mode) {
|
||||
BrowsingMode.Normal -> BrowsingMode.Private
|
||||
BrowsingMode.Private -> BrowsingMode.Normal
|
||||
}
|
||||
tabCounterMenu.updateMenu(showOnly = inverseBrowsingMode)
|
||||
view.tab_button.setOnLongClickListener {
|
||||
tabCounterMenu.menuController.show(anchor = it)
|
||||
true
|
||||
}
|
||||
createTabCounterMenu(view)
|
||||
|
||||
view.menuButton.setColorFilter(
|
||||
ContextCompat.getColor(
|
||||
|
@ -463,6 +446,40 @@ class HomeFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun createTabCounterMenu(view: View) {
|
||||
val browsingModeManager = (activity as HomeActivity).browsingModeManager
|
||||
val mode = browsingModeManager.mode
|
||||
|
||||
val onItemTapped: (TabCounterMenu.Item) -> Unit = {
|
||||
if (it is TabCounterMenu.Item.NewTab) {
|
||||
browsingModeManager.mode = BrowsingMode.Normal
|
||||
} else if (it is TabCounterMenu.Item.NewPrivateTab) {
|
||||
browsingModeManager.mode = BrowsingMode.Private
|
||||
}
|
||||
}
|
||||
|
||||
val tabCounterMenu = FenixTabCounterMenu(
|
||||
view.context,
|
||||
onItemTapped,
|
||||
iconColor = if (mode == BrowsingMode.Private) {
|
||||
ContextCompat.getColor(requireContext(), R.color.primary_text_private_theme)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
|
||||
val inverseBrowsingMode = when (mode) {
|
||||
BrowsingMode.Normal -> BrowsingMode.Private
|
||||
BrowsingMode.Private -> BrowsingMode.Normal
|
||||
}
|
||||
|
||||
tabCounterMenu.updateMenu(showOnly = inverseBrowsingMode)
|
||||
view.tab_button.setOnLongClickListener {
|
||||
tabCounterMenu.menuController.show(anchor = it)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeAllTabsAndShowSnackbar(sessionCode: String) {
|
||||
if (sessionCode == ALL_PRIVATE_TABS) {
|
||||
sessionManager.removePrivateSessions()
|
||||
|
@ -960,8 +977,11 @@ class HomeFragment : Fragment() {
|
|||
)
|
||||
}
|
||||
|
||||
// TODO use [FenixTabCounterToolbarButton] instead of [TabCounter]:
|
||||
// https://github.com/mozilla-mobile/fenix/issues/16792
|
||||
private fun updateTabCounter(browserState: BrowserState) {
|
||||
val tabCount = if (browsingModeManager.mode.isPrivate) {
|
||||
view?.tab_button?.setColor(ContextCompat.getColor(requireContext(), R.color.primary_text_private_theme))
|
||||
browserState.privateTabs.size
|
||||
} else {
|
||||
browserState.normalTabs.size
|
||||
|
|
|
@ -68,7 +68,7 @@ class AwesomeBarView(
|
|||
override fun invoke(
|
||||
searchTerms: String,
|
||||
searchEngine: mozilla.components.browser.search.SearchEngine?,
|
||||
parentSession: Session?
|
||||
parentSessionId: String?
|
||||
) {
|
||||
interactor.onSearchTermsTapped(searchTerms)
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ class AwesomeBarView(
|
|||
override fun invoke(
|
||||
searchTerms: String,
|
||||
searchEngine: mozilla.components.browser.search.SearchEngine?,
|
||||
parentSession: Session?
|
||||
parentSessionId: String?
|
||||
) {
|
||||
interactor.onSearchTermsTapped(searchTerms)
|
||||
}
|
||||
|
|
|
@ -42,12 +42,12 @@ import mozilla.components.browser.tabstray.TabViewHolder
|
|||
import mozilla.components.feature.syncedtabs.SyncedTabsFeature
|
||||
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
||||
import mozilla.components.support.ktx.android.util.dpToPx
|
||||
import mozilla.components.ui.tabcounter.TabCounter.Companion.INFINITE_CHAR_PADDING_BOTTOM
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.InfoBanner
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.toolbar.TabCounter.Companion.INFINITE_CHAR_PADDING_BOTTOM
|
||||
import org.mozilla.fenix.components.toolbar.TabCounter.Companion.MAX_VISIBLE_TABS
|
||||
import org.mozilla.fenix.components.toolbar.TabCounter.Companion.SO_MANY_TABS_OPEN
|
||||
import mozilla.components.ui.tabcounter.TabCounter.Companion.MAX_VISIBLE_TABS
|
||||
import mozilla.components.ui.tabcounter.TabCounter.Companion.SO_MANY_TABS_OPEN
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.ext.updateAccessibilityCollectionInfo
|
||||
|
@ -62,7 +62,7 @@ import mozilla.components.browser.storage.sync.Tab as SyncTab
|
|||
/**
|
||||
* View that contains and configures the BrowserAwesomeBar
|
||||
*/
|
||||
@Suppress("LongParameterList", "TooManyFunctions", "LargeClass")
|
||||
@Suppress("LongParameterList", "TooManyFunctions", "LargeClass", "ForbiddenComment")
|
||||
class TabTrayView(
|
||||
private val container: ViewGroup,
|
||||
private val tabsAdapter: FenixTabsAdapter,
|
||||
|
@ -697,7 +697,7 @@ class TabTrayView(
|
|||
view.resources.getDimensionPixelSize(R.dimen.tab_tray_top_offset)
|
||||
}
|
||||
|
||||
behavior.setExpandedOffset(topOffset)
|
||||
behavior.expandedOffset = topOffset
|
||||
}
|
||||
|
||||
fun dismissMenu() {
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
app:barrierDirection="start"
|
||||
app:constraint_referenced_ids="tab_button" />
|
||||
|
||||
<org.mozilla.fenix.components.toolbar.TabCounter
|
||||
<mozilla.components.ui.tabcounter.TabCounter
|
||||
android:id="@+id/tab_button"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
|
|
|
@ -1,37 +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/. -->
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:layout_height="wrap_content"
|
||||
tools:layout_width="wrap_content">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/counter_root"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/counter_box"
|
||||
android:layout_width="@dimen/tab_counter_box_width_height"
|
||||
android:layout_height="@dimen/tab_counter_box_width_height"
|
||||
android:importantForAccessibility="no"
|
||||
app:srcCompat="@drawable/ic_tabs"/>
|
||||
|
||||
<!-- This text size auto adjusts based on num digits in `TabCounter` -->
|
||||
<TextView
|
||||
android:id="@+id/counter_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="16" />
|
||||
</FrameLayout>
|
||||
</merge>
|
|
@ -31,7 +31,7 @@
|
|||
android:layout_weight="1"
|
||||
android:background="@drawable/home_search_background" />
|
||||
|
||||
<org.mozilla.fenix.components.toolbar.TabCounter
|
||||
<mozilla.components.ui.tabcounter.TabCounter
|
||||
android:id="@+id/tab_button"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
|
|
|
@ -100,4 +100,8 @@
|
|||
|
||||
<!-- TextInputLayout default Error Color -->
|
||||
<color name="design_error" tools:override="true">@color/destructive_dark_theme</color>
|
||||
|
||||
<!-- Tab Counter colors -->
|
||||
<color name="mozac_ui_tabcounter_default_tint">@color/primary_text_dark_theme</color>
|
||||
<color name="mozac_ui_tabcounter_default_text">@color/primary_text_dark_theme</color>
|
||||
</resources>
|
||||
|
|
|
@ -413,4 +413,8 @@
|
|||
|
||||
<!-- TextInputLayout default Error Color -->
|
||||
<color name="design_error" tools:override="true">@color/destructive_light_theme</color>
|
||||
|
||||
<!-- Tab Counter colors -->
|
||||
<color name="mozac_ui_tabcounter_default_tint">@color/primary_text_light_theme</color>
|
||||
<color name="mozac_ui_tabcounter_default_text">@color/primary_text_light_theme</color>
|
||||
</resources>
|
||||
|
|
|
@ -152,8 +152,6 @@
|
|||
<string name="browser_menu_find_in_page">Find in page</string>
|
||||
<!-- Browser menu button that creates a private tab -->
|
||||
<string name="browser_menu_private_tab">Private tab</string>
|
||||
<!-- Browser menu button that creates a new tab -->
|
||||
<string name="browser_menu_new_tab">New tab</string>
|
||||
<!-- Browser menu button that saves the current tab to a collection -->
|
||||
<string name="browser_menu_save_to_collection_2">Save to collection</string>
|
||||
<!-- Browser menu button that open a share menu to share the current site -->
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.mockk.MockKAnnotations
|
|||
import io.mockk.impl.annotations.RelaxedMockK
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.ui.tabcounter.TabCounterMenu
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import mozilla.components.concept.engine.EngineView
|
|||
import mozilla.components.feature.search.SearchUseCases
|
||||
import mozilla.components.feature.session.SessionUseCases
|
||||
import mozilla.components.feature.top.sites.TopSitesUseCases
|
||||
import mozilla.components.ui.tabcounter.TabCounterMenu
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -240,7 +241,7 @@ class DefaultBrowserToolbarControllerTest {
|
|||
@Test
|
||||
fun handleToolbarNewTabPress() {
|
||||
val browsingModeManager = SimpleBrowsingModeManager(BrowsingMode.Private)
|
||||
val item = TabCounterMenu.Item.NewTab(BrowsingMode.Normal)
|
||||
val item = TabCounterMenu.Item.NewTab
|
||||
|
||||
every { activity.browsingModeManager } returns browsingModeManager
|
||||
every { navController.navigate(BrowserFragmentDirections.actionGlobalHome(focusOnAddressBar = true)) } just Runs
|
||||
|
@ -254,7 +255,7 @@ class DefaultBrowserToolbarControllerTest {
|
|||
@Test
|
||||
fun handleToolbarNewPrivateTabPress() {
|
||||
val browsingModeManager = SimpleBrowsingModeManager(BrowsingMode.Normal)
|
||||
val item = TabCounterMenu.Item.NewTab(BrowsingMode.Private)
|
||||
val item = TabCounterMenu.Item.NewPrivateTab
|
||||
|
||||
every { activity.browsingModeManager } returns browsingModeManager
|
||||
every { navController.navigate(BrowserFragmentDirections.actionGlobalHome(focusOnAddressBar = true)) } just Runs
|
||||
|
|
|
@ -7,50 +7,31 @@ package org.mozilla.fenix.components.toolbar
|
|||
import android.content.Context
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verifyAll
|
||||
import io.mockk.verify
|
||||
import mozilla.components.concept.menu.candidate.DividerMenuCandidate
|
||||
import mozilla.components.concept.menu.candidate.DrawableMenuIcon
|
||||
import mozilla.components.concept.menu.candidate.TextMenuCandidate
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.components.ui.tabcounter.TabCounterMenu
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class TabCounterMenuTest {
|
||||
|
||||
private lateinit var context: Context
|
||||
private lateinit var metrics: MetricController
|
||||
private lateinit var onItemTapped: (TabCounterMenu.Item) -> Unit
|
||||
private lateinit var menu: TabCounterMenu
|
||||
private lateinit var menu: FenixTabCounterMenu
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
context = ContextThemeWrapper(testContext, R.style.NormalTheme)
|
||||
metrics = mockk(relaxed = true)
|
||||
onItemTapped = mockk(relaxed = true)
|
||||
menu = TabCounterMenu(context, metrics, onItemTapped)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `all items use primary text color styling`() {
|
||||
val items = menu.menuItems(ToolbarPosition.BOTTOM)
|
||||
assertEquals(4, items.size)
|
||||
|
||||
val textItems = items.mapNotNull { it as? TextMenuCandidate }
|
||||
assertEquals(3, textItems.size)
|
||||
|
||||
val primaryTextColor = context.getColor(R.color.primary_text_normal_theme)
|
||||
for (item in textItems) {
|
||||
assertEquals(primaryTextColor, item.textStyle.color)
|
||||
assertEquals(primaryTextColor, (item.start as DrawableMenuIcon).tint)
|
||||
}
|
||||
menu = FenixTabCounterMenu(context, onItemTapped)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -62,10 +43,7 @@ class TabCounterMenuTest {
|
|||
assertEquals("New tab", item.text)
|
||||
item.onClick()
|
||||
|
||||
verifyAll {
|
||||
metrics.track(Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.NEW_TAB))
|
||||
onItemTapped(TabCounterMenu.Item.NewTab(BrowsingMode.Normal))
|
||||
}
|
||||
verify { onItemTapped(TabCounterMenu.Item.NewTab) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -77,10 +55,7 @@ class TabCounterMenuTest {
|
|||
assertEquals("New private tab", item.text)
|
||||
item.onClick()
|
||||
|
||||
verifyAll {
|
||||
metrics.track(Event.TabCounterMenuItemTapped(Event.TabCounterMenuItemTapped.Item.NEW_PRIVATE_TAB))
|
||||
onItemTapped(TabCounterMenu.Item.NewTab(BrowsingMode.Private))
|
||||
}
|
||||
verify { onItemTapped(TabCounterMenu.Item.NewPrivateTab) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
object AndroidComponents {
|
||||
const val VERSION = "68.0.20201201190117"
|
||||
const val VERSION = "69.0.20201203202830"
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ object Versions {
|
|||
const val androidx_core = "1.3.2"
|
||||
const val androidx_paging = "2.1.0"
|
||||
const val androidx_transition = "1.3.0"
|
||||
const val androidx_work = "2.2.0"
|
||||
const val androidx_work = "2.4.0"
|
||||
const val google_material = "1.2.1"
|
||||
|
||||
const val mozilla_android_components = AndroidComponents.VERSION
|
||||
|
@ -139,6 +139,7 @@ object Deps {
|
|||
const val mozilla_ui_colors = "org.mozilla.components:ui-colors:${Versions.mozilla_android_components}"
|
||||
const val mozilla_ui_icons = "org.mozilla.components:ui-icons:${Versions.mozilla_android_components}"
|
||||
const val mozilla_ui_widgets = "org.mozilla.components:ui-widgets:${Versions.mozilla_android_components}"
|
||||
const val mozilla_ui_tabcounter = "org.mozilla.components:ui-tabcounter:${Versions.mozilla_android_components}"
|
||||
|
||||
const val mozilla_lib_crash = "org.mozilla.components:lib-crash:${Versions.mozilla_android_components}"
|
||||
const val mozilla_lib_push_firebase = "org.mozilla.components:lib-push-firebase:${Versions.mozilla_android_components}"
|
||||
|
|
Loading…
Reference in New Issue