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 commit7b895aba
(cherry picked from commit6a33f29a91
)
This commit is contained in:
parent
ace6a83b27
commit
e7bac4fcfe
@ -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()
|
||||||
|
@ -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 = {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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() {
|
||||||
|
@ -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,
|
||||||
|
@ -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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
69
app/src/main/res/layout/no_collections_message.xml
Normal file
69
app/src/main/res/layout/no_collections_message.xml
Normal 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>
|
@ -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>
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user