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

List mode: Translations #4851

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
e04f508
Add new menu button icons
0nko Jul 26, 2024
a6f93c1
Add new string resources
0nko Jul 26, 2024
d0ce474
Add new button to the menu
0nko Jul 26, 2024
55d2950
Limit the drag & drop directions to up/down for list layout
0nko Jul 26, 2024
8f2cc76
Rename layout enum
0nko Jul 26, 2024
ce10baf
Add layout type change handler
0nko Jul 26, 2024
2e444fb
Display the correct button and update layout based on the selection
0nko Jul 26, 2024
413f797
Fix ktlint issues
0nko Jul 26, 2024
e1c5c5a
Replace a single tab item with two types: grid and list
0nko Jul 29, 2024
3ec0bce
Add hew list item ViewHolder
0nko Jul 29, 2024
2d8a01a
Notify the adapter when layout changes
0nko Jul 29, 2024
2754c6d
Save the scroll state when changing layout type
0nko Aug 6, 2024
95e9a14
Move the LayoutType enum to the data class
0nko Jul 31, 2024
be59c18
Add layout persistence logic
0nko Jul 31, 2024
0a8aa77
Do not show the tabs until the layout type is initialized
0nko Jul 31, 2024
9828d88
Fix ktlint issues
0nko Jul 31, 2024
a9ee3a5
Fix the failing tests
0nko Aug 1, 2024
1fa2a39
Fix the disappearing URL
0nko Aug 2, 2024
8278af0
Add new pixels for layout type changes
0nko Aug 1, 2024
3a735b4
Swap the pixels being fired
0nko Aug 1, 2024
6e0caeb
Add unit tests for the new layout type toggle
0nko Aug 1, 2024
aa88bb0
Translate strings to values-da
marcosholgado Aug 2, 2024
f4ea354
Translate strings to values-bg
marcosholgado Aug 2, 2024
e0b4a71
Translate strings to values-de
marcosholgado Aug 2, 2024
d11cee9
Translate strings to values-lv
marcosholgado Aug 2, 2024
a3a4593
Translate strings to values-hu
marcosholgado Aug 2, 2024
bea8e35
Translate strings to values-hr
marcosholgado Aug 2, 2024
ddd93f2
Translate strings to values-lt
marcosholgado Aug 2, 2024
c0a83f4
Translate strings to values-ro
marcosholgado Aug 2, 2024
5fbcfe1
Translate strings to values-cs
marcosholgado Aug 3, 2024
f18f894
Translate strings to values-es
marcosholgado Aug 3, 2024
c8f8b23
Translate strings to values-sk
marcosholgado Aug 4, 2024
5830247
Translate strings to values-tr
marcosholgado Aug 4, 2024
3f71a2e
Translate strings to values-nl
marcosholgado Aug 4, 2024
27a88ef
Translate strings to values-et
marcosholgado Aug 4, 2024
18816ae
Translate strings to values-pt
marcosholgado Aug 4, 2024
65874f8
Translate strings to values-sv
marcosholgado Aug 5, 2024
ea88829
Translate strings to values-sl
marcosholgado Aug 5, 2024
b872e39
Translate strings to values-it
marcosholgado Aug 5, 2024
ed89531
Translate strings to values-pl
marcosholgado Aug 5, 2024
99edb0b
Translate strings to values-fi
marcosholgado Aug 5, 2024
7e0d081
Translate strings to values-ru
marcosholgado Aug 5, 2024
758e90c
Translate strings to values-nb
marcosholgado Aug 5, 2024
bd7d8af
Translate strings to values-el
marcosholgado Aug 6, 2024
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 @@ -38,7 +38,8 @@ class TabEntityDiffCallback(
): Boolean {
return oldItem.tabPreviewFile == newItem.tabPreviewFile &&
oldItem.viewed == newItem.viewed &&
oldItem.title == newItem.title
oldItem.title == newItem.title &&
oldItem.url == newItem.url
}

private fun getChangePayload(
Expand All @@ -51,6 +52,10 @@ class TabEntityDiffCallback(
diffBundle.putString(DIFF_KEY_TITLE, newItem.title)
}

if (oldItem.url != newItem.url) {
diffBundle.putString(DIFF_KEY_URL, newItem.url)
}

if (oldItem.viewed != newItem.viewed) {
diffBundle.putBoolean(DIFF_KEY_VIEWED, newItem.viewed)
}
Expand Down Expand Up @@ -84,6 +89,7 @@ class TabEntityDiffCallback(

companion object {
const val DIFF_KEY_TITLE = "title"
const val DIFF_KEY_URL = "url"
const val DIFF_KEY_PREVIEW = "previewImage"
const val DIFF_KEY_VIEWED = "viewed"
}
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,9 @@ enum class AppPixelName(override val pixelName: String) : Pixel.PixelName {
TAB_MANAGER_REARRANGE_BANNER_MANUAL_CLOSED("m_tab_manager_rearrange_banner_manual_closed"),
TAB_MANAGER_REARRANGE_BANNER_AUTODISMISSED("m_tab_manager_rearrange_banner_autodismissed"),
TAB_MANAGER_REARRANGE_BANNER_DISPLAYED("m_tab_manager_rearrange_banner_displayed"),
TAB_MANAGER_GRID_VIEW_BUTTON_CLICKED("m_tab_manager_grid_view_button_clicked"),
TAB_MANAGER_LIST_VIEW_BUTTON_CLICKED("m_tab_manager_list_view_button_clicked"),
TAB_MANAGER_VIEW_MODE_TOGGLED_DAILY("m_tab_manager_view_mode_toggled_daily"),

ADD_BOOKMARK_CONFIRM_EDITED("m_add_bookmark_confirm_edit"),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.duckduckgo.app.di.AppCoroutineScope
import com.duckduckgo.app.global.model.Site
import com.duckduckgo.app.global.model.SiteFactory
import com.duckduckgo.app.tabs.db.TabsDao
import com.duckduckgo.app.tabs.model.TabSwitcherData.LayoutType
import com.duckduckgo.app.tabs.model.TabSwitcherData.UserState
import com.duckduckgo.app.tabs.store.TabSwitcherDataStore
import com.duckduckgo.common.utils.ConflatedJob
Expand Down Expand Up @@ -193,6 +194,10 @@ class TabDataRepository @Inject constructor(
tabSwitcherDataStore.setAnnouncementDisplayCount(displayCount)
}

override suspend fun setTabLayoutType(layoutType: LayoutType) {
tabSwitcherDataStore.setTabLayoutType(layoutType)
}

override suspend fun addNewTabAfterExistingTab(
url: String?,
tabId: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import com.duckduckgo.app.tabs.model.TabSwitcherData
import com.duckduckgo.app.tabs.model.TabSwitcherData.LayoutType
import com.duckduckgo.app.tabs.model.TabSwitcherData.UserState
import com.duckduckgo.di.scopes.AppScope
import com.squareup.anvil.annotations.ContributesBinding
Expand All @@ -36,6 +37,7 @@ interface TabSwitcherDataStore {
suspend fun setUserState(userState: UserState)
suspend fun setWasAnnouncementDismissed(wasDismissed: Boolean)
suspend fun setAnnouncementDisplayCount(displayCount: Int)
suspend fun setTabLayoutType(layoutType: LayoutType)
}

@ContributesBinding(AppScope::class)
Expand All @@ -46,13 +48,15 @@ class TabSwitcherPrefsDataStore @Inject constructor(
const val KEY_USER_STATE = "KEY_USER_STATE"
const val KEY_WAS_ANNOUNCEMENT_DISMISSED = "KEY_WAS_ANNOUNCEMENT_DISMISSED"
const val KEY_ANNOUNCEMENT_DISPLAY_COUNT = "KEY_ANNOUNCEMENT_DISPLAY_COUNT"
const val KEY_LAYOUT_TYPE = "KEY_LAYOUT_TYPE"
}

override val data: Flow<TabSwitcherData> = store.data.map { preferences ->
TabSwitcherData(
userState = UserState.valueOf(preferences[stringPreferencesKey(KEY_USER_STATE)] ?: UserState.UNKNOWN.name),
wasAnnouncementDismissed = preferences[booleanPreferencesKey(KEY_WAS_ANNOUNCEMENT_DISMISSED)] ?: false,
announcementDisplayCount = preferences[intPreferencesKey(KEY_ANNOUNCEMENT_DISPLAY_COUNT)] ?: 0,
layoutType = LayoutType.valueOf(preferences[stringPreferencesKey(KEY_LAYOUT_TYPE)] ?: LayoutType.GRID.name),
)
}

Expand All @@ -73,4 +77,10 @@ class TabSwitcherPrefsDataStore @Inject constructor(
preferences[intPreferencesKey(KEY_ANNOUNCEMENT_DISPLAY_COUNT)] = displayCount
}
}

override suspend fun setTabLayoutType(layoutType: LayoutType) {
store.edit { preferences ->
preferences[stringPreferencesKey(KEY_LAYOUT_TYPE)] = layoutType.name
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.duckduckgo.common.ui.view.toPx
import com.duckduckgo.mobile.android.R as CommonR

class TabGridItemDecorator(
class TabItemDecorator(
context: Context,
var selectedTabId: String?,
) : RecyclerView.ItemDecoration() {
Expand Down
123 changes: 96 additions & 27 deletions app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.duckduckgo.anvil.annotations.InjectWith
import com.duckduckgo.app.browser.R
Expand All @@ -48,6 +49,7 @@ 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.model.TabEntity
import com.duckduckgo.app.tabs.model.TabSwitcherData.LayoutType
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command.Close
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command.CloseAllTabsRequest
Expand All @@ -63,6 +65,7 @@ import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch

@InjectWith(ActivityScope::class)
Expand Down Expand Up @@ -114,12 +117,16 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine

private var selectedTabId: String? = null

private lateinit var tabTouchHelper: TabTouchHelper
private lateinit var tabsRecycler: RecyclerView
private lateinit var tabGridItemDecorator: TabGridItemDecorator
private lateinit var tabItemDecorator: TabItemDecorator
private lateinit var toolbar: Toolbar
private lateinit var announcement: View
private lateinit var announcementCloseButton: ImageButton

private var layoutTypeMenuItem: MenuItem? = null
private var layoutType: LayoutType? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tab_switcher)
Expand Down Expand Up @@ -151,36 +158,37 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine

private fun configureRecycler() {
val numberColumns = gridViewColumnCalculator.calculateNumberOfColumns(TAB_GRID_COLUMN_WIDTH_DP, TAB_GRID_MAX_COLUMN_COUNT)
val layoutManager = GridLayoutManager(this, numberColumns)
tabsRecycler.layoutManager = layoutManager

// the tabs recycler view is initially hidden until we know what type of layout to show
tabsRecycler.gone()
tabsRecycler.adapter = tabsAdapter

val swipeListener = ItemTouchHelper(
TabTouchHelper(
numberGridColumns = numberColumns,
onTabSwiped = { position -> this.onTabDeleted(position, true) },
onTabMoved = this::onTabMoved,
onTabDraggingStarted = this::onTabDraggingStarted,
onTabDraggingFinished = this::onTabDraggingFinished,
),
tabTouchHelper = TabTouchHelper(
numberGridColumns = numberColumns,
onTabSwiped = { position -> this.onTabDeleted(position, true) },
onTabMoved = this::onTabMoved,
onTabDraggingStarted = this::onTabDraggingStarted,
onTabDraggingFinished = this::onTabDraggingFinished,
)

val swipeListener = ItemTouchHelper(tabTouchHelper)
swipeListener.attachToRecyclerView(tabsRecycler)

tabGridItemDecorator = TabGridItemDecorator(this, selectedTabId)
tabsRecycler.addItemDecoration(tabGridItemDecorator)
tabItemDecorator = TabItemDecorator(this, selectedTabId)
tabsRecycler.addItemDecoration(tabItemDecorator)
}

private fun configureObservers() {
viewModel.tabs.observe(this) { tabs ->
render(tabs)

val noTabSelected = tabs.none { it.tabId == tabGridItemDecorator.selectedTabId }
val noTabSelected = tabs.none { it.tabId == tabItemDecorator.selectedTabId }
if (noTabSelected && tabs.isNotEmpty()) {
updateTabGridItemDecorator(tabs.last())
}
}
viewModel.activeTab.observe(this) { tab ->
if (tab != null && tab.tabId != tabGridItemDecorator.selectedTabId && !tab.deletable) {
if (tab != null && tab.tabId != tabItemDecorator.selectedTabId && !tab.deletable) {
updateTabGridItemDecorator(tab)
}
}
Expand All @@ -191,23 +199,71 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
}

lifecycleScope.launch {
viewModel.isFeatureAnnouncementVisible
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collect { isVisible ->
if (isVisible && [email protected]) {
viewModel.onTabFeatureAnnouncementDisplayed()
announcement.show()
} else {
announcement.gone()
}
}
viewModel.layoutType.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED).filterNotNull().collect {
updateLayoutType(it)
}
}

lifecycleScope.launch {
viewModel.isFeatureAnnouncementVisible.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED).collect {
updateFeatureAnnouncement(it)
}
}

viewModel.command.observe(this) {
processCommand(it)
}
}

private fun updateFeatureAnnouncement(isVisible: Boolean) {
if (isVisible && [email protected]) {
viewModel.onTabFeatureAnnouncementDisplayed()
announcement.show()
} else {
announcement.gone()
}
}

private fun updateLayoutType(layoutType: LayoutType) {
val scrollState = tabsRecycler.layoutManager?.onSaveInstanceState()
this.layoutType = layoutType
when (layoutType) {
LayoutType.GRID -> {
val gridLayoutManager = GridLayoutManager(
this@TabSwitcherActivity,
gridViewColumnCalculator.calculateNumberOfColumns(TAB_GRID_COLUMN_WIDTH_DP, TAB_GRID_MAX_COLUMN_COUNT),
)
tabsRecycler.layoutManager = gridLayoutManager
showListLayoutButton()
}
LayoutType.LIST -> {
tabsRecycler.layoutManager = LinearLayoutManager(this@TabSwitcherActivity)
showGridLayoutButton()
}
}

tabsAdapter.onLayoutTypeChanged(layoutType)
tabTouchHelper.onLayoutTypeChanged(layoutType)
tabsRecycler.layoutManager?.onRestoreInstanceState(scrollState)
tabsRecycler.show()
}

private fun showGridLayoutButton() {
layoutTypeMenuItem?.let { viewModeMenuItem ->
viewModeMenuItem.setIcon(R.drawable.ic_grid_view_24)
viewModeMenuItem.title = getString(R.string.tabSwitcherGridViewMenu)
viewModeMenuItem.setVisible(true)
}
}

private fun showListLayoutButton() {
layoutTypeMenuItem?.let { viewModeMenuItem ->
viewModeMenuItem.setIcon(R.drawable.ic_list_view_24)
viewModeMenuItem.title = getString(R.string.tabSwitcherListViewMenu)
viewModeMenuItem.setVisible(true)
}
}

private fun render(tabs: List<TabEntity>) {
tabsAdapter.updateData(tabs)

Expand All @@ -234,11 +290,20 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine

override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_tab_switcher_activity, menu)
layoutTypeMenuItem = menu.findItem(R.id.layoutType)

when (layoutType) {
LayoutType.GRID -> showListLayoutButton()
LayoutType.LIST -> showGridLayoutButton()
null -> layoutTypeMenuItem?.isVisible = false
}

return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.layoutType -> onLayoutTypeToggled()
R.id.fire -> onFire()
R.id.newTab -> onNewTabRequested(fromOverflowMenu = false)
R.id.newTabOverflow -> onNewTabRequested(fromOverflowMenu = true)
Expand Down Expand Up @@ -282,6 +347,10 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
dialog.show()
}

private fun onLayoutTypeToggled() {
viewModel.onLayoutTypeToggled()
}

override fun onNewTabRequested(fromOverflowMenu: Boolean) {
clearObserversEarlyToStopViewUpdates()
launch { viewModel.onNewTabRequested(fromOverflowMenu) }
Expand All @@ -294,7 +363,7 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
}

private fun updateTabGridItemDecorator(tab: TabEntity) {
tabGridItemDecorator.selectedTabId = tab.tabId
tabItemDecorator.selectedTabId = tab.tabId
tabsRecycler.invalidateItemDecorations()
}

Expand Down Expand Up @@ -328,7 +397,7 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
private fun onTabDraggingFinished() {
tabsAdapter.onDraggingFinished()

tabsRecycler.addItemDecoration(tabGridItemDecorator)
tabsRecycler.addItemDecoration(tabItemDecorator)
}

private fun onDeletableTab(tab: TabEntity) {
Expand Down
Loading
Loading