Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-selection: Add new FAB to the tab switcher #5467

Open
wants to merge 6 commits into
base: feature/ondrej/tab-multi-selection
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import android.view.MenuItem
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR
import androidx.appcompat.content.res.AppCompatResources
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
Expand All @@ -46,6 +47,7 @@ import com.duckduckgo.app.pixels.AppPixelName
import com.duckduckgo.app.settings.SettingsActivity
import com.duckduckgo.app.settings.db.SettingsDataStore
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.tabs.TabManagerFeatureFlags
import com.duckduckgo.app.tabs.model.TabEntity
import com.duckduckgo.app.tabs.model.TabSwitcherData.LayoutType
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command
Expand All @@ -63,12 +65,14 @@ import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.di.scopes.ActivityScope
import com.duckduckgo.duckchat.api.DuckChat
import com.duckduckgo.duckchat.impl.DuckChatPixelName
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch

Expand Down Expand Up @@ -118,6 +122,9 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
@Inject
lateinit var duckChat: DuckChat

@Inject
lateinit var tabManagerFeatureFlags: TabManagerFeatureFlags

private val viewModel: TabSwitcherViewModel by bindViewModel()

private val tabsAdapter: TabSwitcherAdapter by lazy { TabSwitcherAdapter(this, webViewPreviewPersister, this, faviconManager, dispatchers) }
Expand All @@ -131,6 +138,7 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
private lateinit var tabsRecycler: RecyclerView
private lateinit var tabItemDecorator: TabItemDecorator
private lateinit var toolbar: Toolbar
private lateinit var tabsFab: ExtendedFloatingActionButton

private var layoutTypeMenuItem: MenuItem? = null
private var layoutType: LayoutType? = null
Expand All @@ -141,14 +149,28 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine

firstTimeLoadingTabsList = savedInstanceState?.getBoolean(KEY_FIRST_TIME_LOADING) ?: true

tabsFab = findViewById(R.id.tabsFab)

extractIntentExtras()
configureViewReferences()
setupToolbar(toolbar)
configureRecycler()
configureFab()
configureObservers()
configureOnBackPressedListener()
}

private fun configureFab() {
if (tabManagerFeatureFlags.multiSelection().isEnabled()) {
tabsFab.show()
tabsFab.setOnClickListener {
viewModel.onFabClicked()
}
} else {
tabsFab.hide()
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)

Expand Down Expand Up @@ -185,6 +207,21 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
tabItemDecorator = TabItemDecorator(this, selectedTabId)
tabsRecycler.addItemDecoration(tabItemDecorator)
tabsRecycler.setHasFixedSize(true)

if (tabManagerFeatureFlags.multiSelection().isEnabled()) {
tabsRecycler.addOnScrollListener(
object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0) {
tabsFab.shrink()
} else if (dy < 0) {
tabsFab.extend()
}
}
},
)
}
}

private fun configureObservers() {
Expand Down Expand Up @@ -214,6 +251,12 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
}
}

lifecycleScope.launch {
viewModel.viewState.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED).collectLatest {
updateFabType(it.fabType)
}
}

viewModel.command.observe(this) {
processCommand(it)
}
Expand Down Expand Up @@ -254,6 +297,19 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
tabsRecycler.show()
}

private fun updateFabType(fabType: TabSwitcherViewModel.ViewState.FabType) {
when (fabType) {
TabSwitcherViewModel.ViewState.FabType.NEW_TAB -> {
tabsFab.icon = AppCompatResources.getDrawable(this, com.duckduckgo.mobile.android.R.drawable.ic_add_24)
tabsFab.setText(R.string.tabSwitcherFabNewTab)
}
TabSwitcherViewModel.ViewState.FabType.CLOSE_TABS -> {
tabsFab.icon = AppCompatResources.getDrawable(this, com.duckduckgo.mobile.android.R.drawable.ic_close_24)
tabsFab.setText(R.string.tabSwitcherFabCloseTabs)
}
}
}

private fun scrollToPreviousCenterOffset(centerOffsetPercent: Float) {
tabsRecycler.post {
val newRange = tabsRecycler.computeVerticalScrollRange()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,18 @@ import com.duckduckgo.app.tabs.model.TabEntity
import com.duckduckgo.app.tabs.model.TabRepository
import com.duckduckgo.app.tabs.model.TabSwitcherData.LayoutType.GRID
import com.duckduckgo.app.tabs.model.TabSwitcherData.LayoutType.LIST
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.ViewState.FabType
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.common.utils.SingleLiveEvent
import com.duckduckgo.di.scopes.ActivityScope
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

@ContributesViewModel(ActivityScope::class)
Expand All @@ -65,6 +69,9 @@ class TabSwitcherViewModel @Inject constructor(

val command: SingleLiveEvent<Command> = SingleLiveEvent()

private val _viewState = MutableStateFlow<ViewState>(ViewState())
val viewState = _viewState.asStateFlow()

sealed class Command {
data object Close : Command()
data object CloseAllTabsRequest : Command()
Expand Down Expand Up @@ -188,4 +195,21 @@ class TabSwitcherViewModel @Inject constructor(
tabRepository.setTabLayoutType(newLayoutType)
}
}

fun onFabClicked() {
if (viewState.value.fabType == FabType.NEW_TAB) {
_viewState.update { it.copy(FabType.CLOSE_TABS) }
} else {
_viewState.update { it.copy(FabType.NEW_TAB) }
}
}

data class ViewState(
val fabType: FabType = FabType.NEW_TAB,
) {
enum class FabType {
NEW_TAB,
CLOSE_TABS,
}
}
}
47 changes: 32 additions & 15 deletions app/src/main/res/layout/activity_tab_switcher.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,44 @@
~ limitations under the License.
-->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/daxColorBackground"
android:orientation="vertical"
tools:context="com.duckduckgo.app.tabs.ui.TabSwitcherActivity">

<include layout="@layout/include_default_toolbar" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/tabsRecycler"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="@dimen/keyline_2"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:itemCount="3"
tools:listitem="@layout/item_tab_grid"
tools:showIn="@layout/activity_tab_switcher"
tools:spanCount="2" />
android:orientation="vertical">

<include layout="@layout/include_default_toolbar" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/tabsRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="@dimen/keyline_2"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
tools:itemCount="3"
tools:listitem="@layout/item_tab_grid"
tools:showIn="@layout/activity_tab_switcher"
tools:spanCount="2"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</LinearLayout>

<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/tabsFab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/keyline_4"
android:contentDescription="New Tab"
android:text="New tab"
app:icon="@drawable/ic_add_24" />

</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
6 changes: 6 additions & 0 deletions app/src/main/res/values/donottranslate.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,10 @@
<string name="newTabPageIndonesiaMessageBody">The government may be blocking access to duckduckgo.com on this network provider, which could affect this app\'s functionality. Other providers may not be affected.</string>
<string name="newTabPageIndonesiaMessageCta">Okay</string>

<string name="tabSwitcherFabNewTab">New Tab</string>
<string name="tabSwitcherFabCloseTabs">Close Tabs</string>

<!-- Default Browser Popup Menu Item-->
<string name="defaultBrowserPopupMenuItem">Set As Default Browser</string>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@
<color name="blue50_14">#243969EF</color>
<color name="blue60">#2B55CA</color>
<color name="blue70">#1E42A4</color>
<color name="blue10">#803969EF</color>
<color name="blue10">#803969FF</color>
<color name="blue10_solid">#9CB4FF</color>

<color name="black84">#D6000000</color>
<color name="black60">#99000000</color>
Expand Down Expand Up @@ -322,4 +323,4 @@
2% — 05
1% — 03
0% — 00
-->
-->
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
<dimen name="gridItemTextWidth">72dp</dimen>
<dimen name="gridItemPreviewHeight">170dp</dimen>

<!-- Extended FAB -->
<dimen name="fabHeight">56dp</dimen>

<!-- PopupMenu -->
<dimen name="popupMenuWidth">200dp</dimen>
<dimen name="popupMenuNavigationItemHeight">24dp</dimen>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
<item name="tabStyle">@style/Widget.DuckDuckGo.TabLayout</item>
<item name="radioButtonStyle">@style/Widget.DuckDuckGo.RadioButton</item>
<item name="checkboxStyle">@style/Widget.DuckDuckGo.CheckBox</item>
<item name="extendedFloatingActionButtonStyle">@style/Widget.DuckDuckGo.ExtensibleFloatingActionButton</item>
<item name="sliderStyle">@style/Widget.DuckDuckGo.Slider</item>
<item name="switchStyle">@style/Widget.DuckDuckGo.v3.Switch</item>
<item name="snackbarStyle">@style/Widget.DuckDuckGo.Snackbar</item>
Expand Down
3 changes: 3 additions & 0 deletions common/common-ui/src/main/res/values/widget-colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@
<color name="searchWidgetSearchBarTrailingIconColor">@color/black84</color>
<color name="emptyFavoriteDescriptionTextColor">@color/black60</color>
<color name="emptyFavoriteActionTextColor">@color/blue50</color>
<color name="fabBackgroundTint">@color/blue10_solid</color>
<color name="fabIconTint">@color/blue70</color>
<color name="fabTextColor">@color/blue70</color>
</resources>
14 changes: 14 additions & 0 deletions common/common-ui/src/main/res/values/widgets.xml
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,20 @@
<item name="android:paddingStart">@dimen/keyline_2</item>
</style>

<style name="Widget.DuckDuckGo.ExtensibleFloatingActionButton" parent="Widget.MaterialComponents.ExtendedFloatingActionButton">
<item name="backgroundTint">@color/fabBackgroundTint</item>
<item name="iconTint">@color/fabIconTint</item>
<item name="android:textColor">@color/fabTextColor</item>
<item name="shapeAppearanceOverlay">@style/Widget.DuckDuckGo.ExtensibleFloatingActionButton.Overlay</item>
<item name="fabSize">normal</item>
<item name="android:height">@dimen/fabHeight</item>
</style>

<style name="Widget.DuckDuckGo.ExtensibleFloatingActionButton.Overlay">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">@dimen/keyline_4</item>
</style>

<!-- List Items -->
<style name="Widget.DuckDuckGo.OneLineListItem">
<item name="android:layout_width">match_parent</item>
Expand Down
Loading