2
0
mirror of https://github.com/fork-maintainers/iceraven-browser synced 2024-11-15 18:12:54 +00:00

Revert "For #24455 - Migrate NoCollectionsMessageViewHolder to Compose"

This reverts commit 7b895aba

(cherry picked from commit 6a33f29a91)
This commit is contained in:
Arturo Mejia 2022-06-03 17:04:50 -04:00
parent ace6a83b27
commit e7bac4fcfe
9 changed files with 231 additions and 215 deletions

View File

@ -33,6 +33,7 @@ import androidx.test.uiautomator.Until
import androidx.test.uiautomator.Until.findObject import androidx.test.uiautomator.Until.findObject
import mozilla.components.browser.state.state.searchEngines import mozilla.components.browser.state.state.searchEngines
import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.instanceOf import org.hamcrest.CoreMatchers.instanceOf
import org.hamcrest.CoreMatchers.not import org.hamcrest.CoreMatchers.not
import org.hamcrest.Matchers import org.hamcrest.Matchers
@ -140,11 +141,7 @@ class HomeScreenRobot {
fun verifyRecentBookmarksSectionIsDisplayed() = assertRecentBookmarksSectionIsDisplayed() fun verifyRecentBookmarksSectionIsDisplayed() = assertRecentBookmarksSectionIsDisplayed()
fun verifyRecentBookmarksSectionIsNotDisplayed() = assertRecentBookmarksSectionIsNotDisplayed() fun verifyRecentBookmarksSectionIsNotDisplayed() = assertRecentBookmarksSectionIsNotDisplayed()
fun verifyRecentlyVisitedSearchGroupDisplayed( fun verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed: Boolean, searchTerm: String, groupSize: Int) {
shouldBeDisplayed: Boolean,
searchTerm: String,
groupSize: Int
) {
// checks if the search group exists in the Recently visited section // checks if the search group exists in the Recently visited section
if (shouldBeDisplayed) { if (shouldBeDisplayed) {
recentlyVisitedList.waitForExists(waitingTime) recentlyVisitedList.waitForExists(waitingTime)
@ -165,11 +162,7 @@ class HomeScreenRobot {
} }
} }
fun verifyCurrentSearchGroupIsDisplayed( fun verifyCurrentSearchGroupIsDisplayed(shouldBeDisplayed: Boolean, searchTerm: String, groupSize: Int = 0) {
shouldBeDisplayed: Boolean,
searchTerm: String,
groupSize: Int = 0
) {
// checks search group in the Jump back in section // checks search group in the Jump back in section
if (shouldBeDisplayed) { if (shouldBeDisplayed) {
assertTrue( assertTrue(
@ -415,12 +408,8 @@ class HomeScreenRobot {
// return CollectionRobot.Transition() // return CollectionRobot.Transition()
// } // }
fun openRecentlyVisitedSearchGroupHistoryList( fun openRecentlyVisitedSearchGroupHistoryList(title: String, interact: HistoryRobot.() -> Unit): HistoryRobot.Transition {
title: String, val searchGroup = recentlyVisitedList.getChildByText(UiSelector().text(title), title, true)
interact: HistoryRobot.() -> Unit
): HistoryRobot.Transition {
val searchGroup =
recentlyVisitedList.getChildByText(UiSelector().text(title), title, true)
searchGroup.waitForExists(waitingTimeShort) searchGroup.waitForExists(waitingTimeShort)
searchGroup.click() searchGroup.click()
@ -452,8 +441,7 @@ private fun assertKeyboardVisibility(isExpectedToBeVisible: Boolean) =
.contains("mInputShown=true") .contains("mInputShown=true")
) )
private fun navigationToolbar() = private fun navigationToolbar() = mDevice.findObject(UiSelector().resourceId("$packageName:id/toolbar"))
mDevice.findObject(UiSelector().resourceId("$packageName:id/toolbar"))
private fun assertNavigationToolbar() = assertTrue(navigationToolbar().waitForExists(waitingTime)) private fun assertNavigationToolbar() = assertTrue(navigationToolbar().waitForExists(waitingTime))
@ -462,8 +450,7 @@ private fun assertFocusedNavigationToolbar() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertHomeScreen() { private fun assertHomeScreen() {
mDevice.findObject(UiSelector().resourceId("$packageName:id/homeLayout")) mDevice.findObject(UiSelector().resourceId("$packageName:id/homeLayout")).waitForExists(waitingTime)
.waitForExists(waitingTime)
onView(ViewMatchers.withResourceName("homeLayout")) onView(ViewMatchers.withResourceName("homeLayout"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
@ -487,23 +474,18 @@ private fun assertTabButton() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertCollectionsHeader() = private fun assertCollectionsHeader() =
assertTrue( onView(allOf(withText("Collections")))
mDevice.findObject( .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
UiSelector().textContains(
"Collections"
)
).waitForExists(waitingTime)
)
private fun assertNoCollectionsText() = private fun assertNoCollectionsText() =
assertTrue( onView(
mDevice.findObject( withText(
UiSelector().textContains( containsString(
"Collect the things that matter to you.\n" + "Collect the things that matter to you.\n" +
"Group together similar searches, sites, and tabs for quick access later." "Group together similar searches, sites, and tabs for quick access later."
) )
).waitForExists(waitingTime)
) )
).check(matches(isDisplayed()))
private fun assertHomeComponent() = private fun assertHomeComponent() =
onView(ViewMatchers.withResourceName("sessionControlRecyclerView")) onView(ViewMatchers.withResourceName("sessionControlRecyclerView"))
@ -542,7 +524,6 @@ private fun assertStartSyncHeader() {
onView(allOf(withText(R.string.onboarding_account_sign_in_header_1))) onView(allOf(withText(R.string.onboarding_account_sign_in_header_1)))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertAccountsSignInButton() = private fun assertAccountsSignInButton() =
onView(ViewMatchers.withResourceName("fxa_sign_in_button")) onView(ViewMatchers.withResourceName("fxa_sign_in_button"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
@ -552,7 +533,6 @@ private fun assertChooseThemeHeader() {
onView(withText("Choose your theme")) onView(withText("Choose your theme"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertChooseThemeText() { private fun assertChooseThemeText() {
scrollToElementByText("Choose your theme") scrollToElementByText("Choose your theme")
onView(allOf(withText("Save some battery and your eyesight with dark mode."))) onView(allOf(withText("Save some battery and your eyesight with dark mode.")))
@ -582,7 +562,6 @@ private fun assertDarkThemeDescription() {
onView(allOf(withText("Dark theme"))) onView(allOf(withText("Dark theme")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertAutomaticThemeToggle() { private fun assertAutomaticThemeToggle() {
scrollToElementByText("Choose your theme") scrollToElementByText("Choose your theme")
onView(withId(R.id.theme_automatic_radio_button)) onView(withId(R.id.theme_automatic_radio_button))
@ -707,11 +686,9 @@ private fun assertTopSiteContextMenuItems() {
) )
} }
private fun assertJumpBackInSectionIsDisplayed() = private fun assertJumpBackInSectionIsDisplayed() = assertTrue(jumpBackInSection().waitForExists(waitingTime))
assertTrue(jumpBackInSection().waitForExists(waitingTime))
private fun assertJumpBackInSectionIsNotDisplayed() = private fun assertJumpBackInSectionIsNotDisplayed() = assertFalse(jumpBackInSection().waitForExists(waitingTimeShort))
assertFalse(jumpBackInSection().waitForExists(waitingTimeShort))
private fun assertRecentBookmarksSectionIsDisplayed() = private fun assertRecentBookmarksSectionIsDisplayed() =
assertTrue(recentBookmarksSection().waitForExists(waitingTime)) assertTrue(recentBookmarksSection().waitForExists(waitingTime))
@ -721,8 +698,7 @@ private fun assertRecentBookmarksSectionIsNotDisplayed() =
private fun privateBrowsingButton() = onView(withId(R.id.privateBrowsingButton)) private fun privateBrowsingButton() = onView(withId(R.id.privateBrowsingButton))
private fun saveTabsToCollectionButton() = private fun saveTabsToCollectionButton() = onView(withId(R.id.add_tabs_to_collections_button))
mDevice.findObject(UiSelector().textContains(getStringResource(R.string.tabs_menu_save_to_collection1)))
private fun tabsCounter() = onView(withId(R.id.tab_button)) private fun tabsCounter() = onView(withId(R.id.tab_button))
@ -733,8 +709,7 @@ private fun recentBookmarksSection() =
mDevice.findObject(UiSelector().textContains(getStringResource(R.string.recent_bookmarks_title))) mDevice.findObject(UiSelector().textContains(getStringResource(R.string.recent_bookmarks_title)))
private fun startBrowsingButton(): UiObject { private fun startBrowsingButton(): UiObject {
val startBrowsingButton = val startBrowsingButton = mDevice.findObject(UiSelector().resourceId("$packageName:id/finish_button"))
mDevice.findObject(UiSelector().resourceId("$packageName:id/finish_button"))
homeScreenList() homeScreenList()
.scrollIntoView(startBrowsingButton) .scrollIntoView(startBrowsingButton)
homeScreenList() homeScreenList()

View File

@ -1,117 +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.compose
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.Spacer
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.button.PrimaryButton
import org.mozilla.fenix.compose.ext.dashedBorder
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.theme.Theme
/**
* [CollectionsPlaceholder] for displaying a message detailing the collections feature and
* allowing users to easily start creating their collection.
*
* @param showAddToCollectionButton Whether or not the "Add to Collection" button should be shown.
* @param onAddTabsToCollectionButtonClick Invoked when the user clicks on the "Add Tabs to Collection" button.
* @param onRemovePlaceholderClick Invoked when the user clicks on the close button to remove the Collections
* placeholder.
*/
@Composable
fun CollectionsPlaceholder(
showAddToCollectionButton: Boolean,
onAddTabsToCollectionButtonClick: () -> Unit,
onRemovePlaceholderClick: () -> Unit,
) {
Box(
modifier = Modifier
.semantics(mergeDescendants = true) {}
.dashedBorder(
color = FirefoxTheme.colors.borderPrimary,
cornerRadius = 8.dp,
dashHeight = 2.dp,
dashWidth = 4.dp
)
) {
Column(
Modifier
.padding(16.dp)
.fillMaxWidth()
) {
Row(
modifier = Modifier.fillMaxWidth(),
) {
SectionHeader(
text = stringResource(R.string.collections_header),
modifier = Modifier.weight(1f)
)
IconButton(
onClick = onRemovePlaceholderClick,
modifier = Modifier.size(20.dp),
) {
Icon(
painter = painterResource(R.drawable.ic_close),
contentDescription = stringResource(
R.string.remove_home_collection_placeholder_content_description
),
tint = FirefoxTheme.colors.iconPrimary
)
}
}
Spacer(modifier = Modifier.height(4.dp))
SecondaryText(
text = stringResource(R.string.no_collections_description2),
modifier = Modifier.fillMaxWidth(),
fontSize = 14.sp
)
if (showAddToCollectionButton) {
Spacer(modifier = Modifier.height(12.dp))
PrimaryButton(
text = stringResource(R.string.tabs_menu_save_to_collection1),
icon = painterResource(R.drawable.ic_tab_collection),
onClick = onAddTabsToCollectionButtonClick
)
}
}
}
}
@Composable
@Preview
private fun CollectionsPlaceholderPreview() {
FirefoxTheme(theme = Theme.getTheme(isPrivate = false)) {
Box(Modifier.background(FirefoxTheme.colors.layer1)) {
CollectionsPlaceholder(
showAddToCollectionButton = true,
onAddTabsToCollectionButtonClick = {},
onRemovePlaceholderClick = {}
)
}
}
}

View File

@ -27,6 +27,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
@ -35,6 +36,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.button.MaterialButton
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
@ -956,6 +958,9 @@ class HomeFragment : Fragment() {
} }
binding.tabButton.setCountWithAnimation(tabCount) binding.tabButton.setCountWithAnimation(tabCount)
// The add_tabs_to_collections_button is added at runtime. We need to search for it in the same way.
sessionControlView?.view?.findViewById<MaterialButton>(R.id.add_tabs_to_collections_button)
?.isVisible = tabCount > 0
} }
private fun displayWallpaperIfEnabled() { private fun displayWallpaperIfEnabled() {

View File

@ -176,8 +176,7 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
object PocketStoriesItem : AdapterItem(PocketStoriesViewHolder.LAYOUT_ID) object PocketStoriesItem : AdapterItem(PocketStoriesViewHolder.LAYOUT_ID)
object PocketCategoriesItem : AdapterItem(PocketCategoriesViewHolder.LAYOUT_ID) object PocketCategoriesItem : AdapterItem(PocketCategoriesViewHolder.LAYOUT_ID)
object PocketRecommendationsFooterItem : object PocketRecommendationsFooterItem : AdapterItem(PocketRecommendationsHeaderViewHolder.LAYOUT_ID)
AdapterItem(PocketRecommendationsHeaderViewHolder.LAYOUT_ID)
object BottomSpacer : AdapterItem(BottomSpacerViewHolder.LAYOUT_ID) object BottomSpacer : AdapterItem(BottomSpacerViewHolder.LAYOUT_ID)
@ -278,11 +277,6 @@ class SessionControlAdapter(
composeView = ComposeView(parent.context), composeView = ComposeView(parent.context),
viewLifecycleOwner = viewLifecycleOwner viewLifecycleOwner = viewLifecycleOwner
) )
NoCollectionsMessageViewHolder.LAYOUT_ID -> return NoCollectionsMessageViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner = viewLifecycleOwner,
interactor = interactor
)
CollectionViewHolder.LAYOUT_ID -> return CollectionViewHolder( CollectionViewHolder.LAYOUT_ID -> return CollectionViewHolder(
composeView = ComposeView(parent.context), composeView = ComposeView(parent.context),
viewLifecycleOwner = viewLifecycleOwner, viewLifecycleOwner = viewLifecycleOwner,
@ -303,6 +297,13 @@ class SessionControlAdapter(
viewLifecycleOwner = viewLifecycleOwner, viewLifecycleOwner = viewLifecycleOwner,
interactor = interactor interactor = interactor
) )
NoCollectionsMessageViewHolder.LAYOUT_ID ->
NoCollectionsMessageViewHolder(
view,
viewLifecycleOwner,
components.core.store,
interactor
)
OnboardingHeaderViewHolder.LAYOUT_ID -> OnboardingHeaderViewHolder(view) OnboardingHeaderViewHolder.LAYOUT_ID -> OnboardingHeaderViewHolder(view)
OnboardingSectionHeaderViewHolder.LAYOUT_ID -> OnboardingSectionHeaderViewHolder(view) OnboardingSectionHeaderViewHolder.LAYOUT_ID -> OnboardingSectionHeaderViewHolder(view)
OnboardingManualSignInViewHolder.LAYOUT_ID -> OnboardingManualSignInViewHolder(view) OnboardingManualSignInViewHolder.LAYOUT_ID -> OnboardingManualSignInViewHolder(view)
@ -328,7 +329,6 @@ class SessionControlAdapter(
when (holder) { when (holder) {
is CollectionHeaderViewHolder, is CollectionHeaderViewHolder,
is CustomizeHomeButtonViewHolder, is CustomizeHomeButtonViewHolder,
is NoCollectionsMessageViewHolder,
is RecentlyVisitedViewHolder, is RecentlyVisitedViewHolder,
is RecentVisitsHeaderViewHolder, is RecentVisitsHeaderViewHolder,
is RecentBookmarksViewHolder, is RecentBookmarksViewHolder,

View File

@ -5,63 +5,59 @@
package org.mozilla.fenix.home.sessioncontrol.viewholders package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.View import android.view.View
import androidx.compose.foundation.layout.Column import androidx.core.view.isVisible
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import mozilla.components.browser.state.selector.normalTabs import mozilla.components.browser.state.selector.normalTabs
import mozilla.components.lib.state.ext.observeAsComposableState import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.components import org.mozilla.fenix.databinding.NoCollectionsMessageBinding
import org.mozilla.fenix.compose.CollectionsPlaceholder import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
import org.mozilla.fenix.utils.view.ViewHolder
/** @OptIn(ExperimentalCoroutinesApi::class)
* [RecyclerView.ComposeViewHolder] for displaying a message detailing the collections feature and open class NoCollectionsMessageViewHolder(
* allowing users to easily start creating their first. view: View,
*
* @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
* @param viewLifecycleOwner [LifecycleOwner] to which this Composable will be tied to.
* @param interactor [CollectionInteractor] callback for user interaction.
*/
class NoCollectionsMessageViewHolder(
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner, viewLifecycleOwner: LifecycleOwner,
private val interactor: CollectionInteractor store: BrowserStore,
) : ComposeViewHolder(composeView, viewLifecycleOwner) { interactor: CollectionInteractor
) : ViewHolder(view) {
init { init {
val horizontalPadding = val binding = NoCollectionsMessageBinding.bind(view)
composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0) binding.addTabsToCollectionsButton.apply {
setOnClickListener {
interactor.onAddTabsToCollectionTapped()
}
isVisible = store.state.normalTabs.isNotEmpty()
}
binding.removeCollectionPlaceholder.apply {
increaseTapArea(
view.resources.getDimensionPixelSize(R.dimen.tap_increase_16)
)
setOnClickListener {
interactor.onRemoveCollectionsPlaceholder()
}
}
store.flowScoped(viewLifecycleOwner) { flow ->
flow.map { state -> state.normalTabs.size }
.ifChanged()
.collect { tabs ->
binding.addTabsToCollectionsButton.isVisible = tabs > 0
}
}
} }
companion object { companion object {
val LAYOUT_ID = View.generateViewId() const val LAYOUT_ID = R.layout.no_collections_message
}
@Composable
override fun Content() {
val normalTabsState = components.core.store.observeAsComposableState {
state ->
state.normalTabs
}.value ?: emptyList()
Column {
Spacer(modifier = Modifier.height(40.dp))
CollectionsPlaceholder(
showAddToCollectionButton = normalTabsState.isNotEmpty(),
onAddTabsToCollectionButtonClick = interactor::onAddTabsToCollectionTapped,
onRemovePlaceholderClick = interactor::onRemoveCollectionsPlaceholder
)
Spacer(modifier = Modifier.height(12.dp))
}
} }
} }

View File

@ -0,0 +1,9 @@
<?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/. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:width="2dp" android:dashWidth="4dp" android:color="?borderPrimary" android:dashGap="4dp" />
<corners android:radius="8dp" />
</shape>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/no_collections_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"
android:layout_marginTop="40dp"
android:layout_marginBottom="12dp"
android:background="@drawable/empty_session_control_background"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/no_collections_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/collections_header"
android:textAppearance="@style/HeaderTextStyle"
android:textSize="16sp"
app:fontFamily="@font/metropolis_semibold"
app:layout_constraintEnd_toStartOf="@id/remove_collection_placeholder"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/remove_collection_placeholder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/remove_home_collection_placeholder_content_description"
app:layout_constraintBottom_toBottomOf="@id/no_collections_header"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/no_collections_header"
app:srcCompat="@drawable/ic_close" />
<TextView
android:id="@+id/no_collections_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/no_collections_description2"
android:textAlignment="viewStart"
android:textColor="?attr/textSecondary"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/no_collections_header" />
<com.google.android.material.button.MaterialButton
android:id="@+id/add_tabs_to_collections_button"
style="@style/PositiveButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
android:layout_marginTop="8dp"
android:text="@string/tabs_menu_save_to_collection1"
android:visibility="gone"
app:icon="@drawable/ic_tab_collection"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/no_collections_description" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -185,4 +185,5 @@
<!-- a11y --> <!-- a11y -->
<dimen name="accessibility_min_height">48dp</dimen> <dimen name="accessibility_min_height">48dp</dimen>
<dimen name="tap_increase_16">16dp</dimen>
</resources> </resources>

View File

@ -0,0 +1,78 @@
/* 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.home.sessioncontrol.viewholders
import android.view.LayoutInflater
import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner
import io.mockk.mockk
import io.mockk.verify
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.NoCollectionsMessageBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
@RunWith(FenixRobolectricTestRunner::class)
class NoCollectionsMessageViewHolderTest {
private lateinit var binding: NoCollectionsMessageBinding
private val store: BrowserStore = BrowserStore(
initialState = BrowserState(
listOf(
createTab("https://www.mozilla.org", id = "reader-inactive-tab")
)
)
)
private lateinit var lifecycleOwner: LifecycleOwner
private lateinit var interactor: CollectionInteractor
@Before
fun setup() {
binding = NoCollectionsMessageBinding.inflate(LayoutInflater.from(testContext))
lifecycleOwner = mockk(relaxed = true)
interactor = mockk(relaxed = true)
}
@Test
fun `hide add to collection button when there are no tabs open`() {
val noTabsStore = BrowserStore()
NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, noTabsStore, interactor)
assertFalse(binding.addTabsToCollectionsButton.isVisible)
}
@Test
fun `show add to collection button when there are tabs`() {
NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, store, interactor)
assertTrue(binding.addTabsToCollectionsButton.isVisible)
}
@Test
fun `call interactor on click`() {
NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, store, interactor)
binding.addTabsToCollectionsButton.performClick()
verify { interactor.onAddTabsToCollectionTapped() }
}
@Test
fun `hide view and change setting on remove placeholder click`() {
NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, store, interactor)
binding.removeCollectionPlaceholder.performClick()
verify {
interactor.onRemoveCollectionsPlaceholder()
}
}
}