diff --git a/components/browser/tabstray/build.gradle b/components/browser/tabstray/build.gradle index 4540990cfb9..ad30551b59d 100644 --- a/components/browser/tabstray/build.gradle +++ b/components/browser/tabstray/build.gradle @@ -28,6 +28,7 @@ android { dependencies { api project(':concept-tabstray') + implementation project(':ui-icons') implementation project(':ui-colors') implementation project(':support-base') diff --git a/components/concept/tabstray/build.gradle b/components/concept/tabstray/build.gradle index cb214f5438a..af1fac27588 100644 --- a/components/concept/tabstray/build.gradle +++ b/components/concept/tabstray/build.gradle @@ -22,8 +22,9 @@ android { } dependencies { - implementation project(':support-base') + api project(':concept-engine') + implementation project(':support-base') implementation Dependencies.kotlin_stdlib } diff --git a/components/concept/tabstray/src/main/java/mozilla/components/concept/tabstray/Tab.kt b/components/concept/tabstray/src/main/java/mozilla/components/concept/tabstray/Tab.kt index 79a07dca9d1..64a5bf2d4a9 100644 --- a/components/concept/tabstray/src/main/java/mozilla/components/concept/tabstray/Tab.kt +++ b/components/concept/tabstray/src/main/java/mozilla/components/concept/tabstray/Tab.kt @@ -5,6 +5,7 @@ package mozilla.components.concept.tabstray import android.graphics.Bitmap +import mozilla.components.concept.engine.media.Media /** * Data class representing a tab to be displayed in a [TabsTray]. @@ -14,11 +15,13 @@ import android.graphics.Bitmap * @property title Current title of the tab (or an empty [String]]). * @property icon Current icon of the tab (or null) * @property thumbnail Current thumbnail of the tab (or null) + * @property mediaState Current media state for the tab (or null) */ data class Tab( val id: String, val url: String, val title: String = "", val icon: Bitmap? = null, - val thumbnail: Bitmap? = null + val thumbnail: Bitmap? = null, + val mediaState: Media.State? = null ) diff --git a/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/ext/BrowserState.kt b/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/ext/BrowserState.kt index d5dd4a6577d..5441b8c219c 100644 --- a/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/ext/BrowserState.kt +++ b/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/ext/BrowserState.kt @@ -5,14 +5,22 @@ package mozilla.components.feature.tabs.ext import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.MediaState import mozilla.components.browser.state.state.TabSessionState import mozilla.components.concept.tabstray.Tabs +private fun BrowserState.mediaStateForTab(tab: TabSessionState): MediaState.State = + if (media.aggregate.activeTabId == tab.id) { + media.aggregate.state + } else { + MediaState.State.NONE + } + internal fun BrowserState.toTabs( tabsFilter: (TabSessionState) -> Boolean = { true } ) = Tabs( list = tabs .filter(tabsFilter) - .map { it.toTab() }, + .map { it.toTab(mediaStateForTab(it)) }, selectedIndex = tabs.indexOfFirst { it.id == selectedTabId } ) diff --git a/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/ext/TabSessionState.kt b/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/ext/TabSessionState.kt index 4ad7c4c2219..ad1eb3f2b1c 100644 --- a/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/ext/TabSessionState.kt +++ b/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/ext/TabSessionState.kt @@ -4,13 +4,20 @@ package mozilla.components.feature.tabs.ext +import mozilla.components.browser.state.state.MediaState import mozilla.components.browser.state.state.TabSessionState +import mozilla.components.concept.engine.media.Media import mozilla.components.concept.tabstray.Tab -internal fun TabSessionState.toTab() = Tab( +internal fun TabSessionState.toTab(mediaState: MediaState.State) = Tab( id, content.url, content.title, content.icon, - content.thumbnail + content.thumbnail, + when (mediaState) { + MediaState.State.PLAYING -> Media.State.PLAYING + MediaState.State.PAUSED -> Media.State.PAUSED + else -> null + } ) diff --git a/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/tabstray/TabsTrayPresenter.kt b/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/tabstray/TabsTrayPresenter.kt index 48f5993d4a4..f10411918dc 100644 --- a/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/tabstray/TabsTrayPresenter.kt +++ b/components/feature/tabs/src/main/java/mozilla/components/feature/tabs/tabstray/TabsTrayPresenter.kt @@ -42,10 +42,9 @@ class TabsTrayPresenter( } private suspend fun collect(flow: Flow) { - flow.map { state -> state.toTabs(tabsFilter) } + flow.map { it.toTabs(tabsFilter) } .ifChanged() .collect { tabs -> - // Do not invoke the callback on start if this is the initial state. if (tabs.list.isEmpty() && this.tabs != null) { closeTabsTray.invoke() diff --git a/components/feature/tabs/src/test/java/mozilla/components/feature/tabs/ext/TabSessionStateTest.kt b/components/feature/tabs/src/test/java/mozilla/components/feature/tabs/ext/TabSessionStateTest.kt new file mode 100644 index 00000000000..4fe059b37f5 --- /dev/null +++ b/components/feature/tabs/src/test/java/mozilla/components/feature/tabs/ext/TabSessionStateTest.kt @@ -0,0 +1,40 @@ +/* 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 mozilla.components.feature.tabs.ext + +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.MediaState +import mozilla.components.browser.state.state.createTab +import mozilla.components.concept.engine.media.Media +import org.junit.Test +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull + +class TabSessionStateTest { + @Test + fun `mediaState gets set correctly`() { + var browserState = BrowserState( + tabs = listOf( + createTab("https://www.mozilla.org", id = "a"), + createTab("https://getpocket.com", id = "b"), + createTab("https://developer.mozilla.org", id = "c"), + createTab("https://www.firefox.com", id = "d"), + createTab("https://www.google.com", id = "e") + ), + selectedTabId = "a", + media = MediaState(MediaState.Aggregate(activeTabId = "a", state = MediaState.State.PLAYING)) + ) + + var tabs = browserState.toTabs() + assertEquals(Media.State.PLAYING, tabs.list[0].mediaState) + + browserState = browserState.copy( + media = MediaState(MediaState.Aggregate(activeTabId = "b", state = MediaState.State.PAUSED)) + ) + tabs = browserState.toTabs() + assertNull(tabs.list[0].mediaState) + assertEquals(Media.State.PAUSED, tabs.list[1].mediaState) + } +} diff --git a/components/feature/tabs/src/test/java/mozilla/components/feature/tabs/tabstray/TabsTrayPresenterTest.kt b/components/feature/tabs/src/test/java/mozilla/components/feature/tabs/tabstray/TabsTrayPresenterTest.kt index d06ea3cb8c2..3af3e74436b 100644 --- a/components/feature/tabs/src/test/java/mozilla/components/feature/tabs/tabstray/TabsTrayPresenterTest.kt +++ b/components/feature/tabs/src/test/java/mozilla/components/feature/tabs/tabstray/TabsTrayPresenterTest.kt @@ -12,13 +12,16 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestCoroutineDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain +import mozilla.components.browser.state.action.MediaAction import mozilla.components.browser.state.action.TabListAction import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.MediaState import mozilla.components.browser.state.state.createTab import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.tabstray.Tabs import mozilla.components.concept.tabstray.TabsTray import mozilla.components.feature.tabs.ext.toTabs +import mozilla.components.support.test.any import mozilla.components.support.test.ext.joinBlocking import mozilla.components.support.test.mock import org.junit.After @@ -216,6 +219,38 @@ class TabsTrayPresenterTest { verify(tabsTray).onTabsChanged(3, 1) } + @Test + fun `tabs tray will get updated if mediaState changes`() { + val store = BrowserStore( + BrowserState( + tabs = listOf( + createTab("https://www.mozilla.org", id = "a"), + createTab("https://getpocket.com", id = "b"), + createTab("https://developer.mozilla.org", id = "c"), + createTab("https://www.firefox.com", id = "d"), + createTab("https://www.google.com", id = "e") + ), + selectedTabId = "a" + ) + ) + + val tabsTray: MockedTabsTray = spy(MockedTabsTray()) + val presenter = TabsTrayPresenter(tabsTray, store, { true }, mock()) + + presenter.start() + testDispatcher.advanceUntilIdle() + + store.dispatch( + MediaAction.UpdateMediaAggregateAction( + store.state.media.aggregate.copy(activeTabId = "a", state = MediaState.State.PLAYING) + ) + ).joinBlocking() + + testDispatcher.advanceUntilIdle() + + verify(tabsTray, times(2)).updateTabs(any()) + } + @Test fun `presenter will close tabs tray when all sessions get removed`() { val store = BrowserStore( diff --git a/docs/changelog.md b/docs/changelog.md index fb7dec38f54..8c968e10fed 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -12,6 +12,12 @@ permalink: /changelog/ * [Gecko](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Gecko.kt) * [Configuration](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Config.kt) +* **feature-tabs** + * Fixed issue [#6907](https://github.com/mozilla-mobile/android-components/issues/6907). Uses MediaState when mapping BrowserState to tabs. + +* **concept-tabstray** + * For issue [#6907](https://github.com/mozilla-mobile/android-components/issues/6907). Adds `Media.State` to `Tab` + * **feature-session** * ⚠️ **This is a breaking change**: Added optional `crashReporting` param to [PictureInPictureFeature] so we can record caught exceptions.