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

Tab Switcher Animation: Add real tracker count #5723

Open
wants to merge 6 commits into
base: feature/mike/tab-switcher-tile-animation/add-animated-tile-placeholder
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 @@ -19,8 +19,8 @@ package com.duckduckgo.app.browser.tabs.adapter
import android.os.Bundle
import androidx.recyclerview.widget.DiffUtil
import com.duckduckgo.app.tabs.ui.TabSwitcherItem
import com.duckduckgo.app.tabs.ui.TabSwitcherItem.TrackerAnimationTile.ANIMATED_TILE_DEFAULT_ALPHA
import com.duckduckgo.app.tabs.ui.TabSwitcherItem.TrackerAnimationTile.ANIMATED_TILE_NO_REPLACE_ALPHA
import com.duckduckgo.app.tabs.ui.TabSwitcherItem.TrackerAnimationTile.Companion.ANIMATED_TILE_DEFAULT_ALPHA
import com.duckduckgo.app.tabs.ui.TabSwitcherItem.TrackerAnimationTile.Companion.ANIMATED_TILE_NO_REPLACE_ALPHA

class TabSwitcherItemDiffCallback(
old: List<TabSwitcherItem>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class TabItemDecorator(
drawSelectedTabDecoration(child, canvas)
}
}
TabSwitcherItem.TrackerAnimationTile -> Unit // No border for animation tile
is TabSwitcherItem.TrackerAnimationTile -> Unit // No border for animation tile
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
)
}
}
TabSwitcherItem.TrackerAnimationTile -> Unit // TODO delete from list
is TabSwitcherItem.TrackerAnimationTile -> Unit // TODO delete from list
}
}
}
Expand Down
19 changes: 8 additions & 11 deletions app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,13 @@ import com.duckduckgo.app.tabs.ui.TabSwitcherAdapter.TabSwitcherViewHolder.Compa
import com.duckduckgo.app.tabs.ui.TabSwitcherAdapter.TabSwitcherViewHolder.Companion.LIST_TAB
import com.duckduckgo.app.tabs.ui.TabSwitcherAdapter.TabSwitcherViewHolder.Companion.LIST_TRACKER_ANIMATION_TILE
import com.duckduckgo.app.tabs.ui.TabSwitcherAdapter.TabSwitcherViewHolder.TabViewHolder
import com.duckduckgo.app.tabs.ui.TabSwitcherItem.TrackerAnimationTile.ANIMATED_TILE_DEFAULT_ALPHA
import com.duckduckgo.app.tabs.ui.TabSwitcherItem.TrackerAnimationTile.ANIMATED_TILE_NO_REPLACE_ALPHA
import com.duckduckgo.app.tabs.ui.TabSwitcherItem.TrackerAnimationTile.Companion.ANIMATED_TILE_DEFAULT_ALPHA
import com.duckduckgo.app.tabs.ui.TabSwitcherItem.TrackerAnimationTile.Companion.ANIMATED_TILE_NO_REPLACE_ALPHA
import com.duckduckgo.common.ui.view.show
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.common.utils.swap
import java.io.File
import kotlin.Int
import kotlin.random.Random
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
Expand Down Expand Up @@ -139,27 +138,25 @@ class TabSwitcherAdapter(
bindListTab(holder, tab)
}
is TabSwitcherViewHolder.GridTrackerAnimationTileViewHolder -> {
val trackerAnimationTile = list[position] as TabSwitcherItem.TrackerAnimationTile

trackerCountAnimator.animateTrackersBlockedCountView(
context = holder.binding.root.context,
stringRes = R.string.trackersBlockedInTheLast7days,
totalTrackerCount = when (Random.Default.nextInt(10)) {
in 0..6 -> Random.Default.nextInt(10, 1000)
else -> Random.Default.nextInt(1000, 10000)
},
totalTrackerCount = trackerAnimationTile.trackerCount,
trackerTextView = holder.binding.text,
)
holder.binding.close.setOnClickListener {
// TODO delete
}
}
is TabSwitcherViewHolder.ListTrackerAnimationTileViewHolder -> {
val trackerAnimationTile = list[position] as TabSwitcherItem.TrackerAnimationTile

trackerCountAnimator.animateTrackersBlockedCountView(
context = holder.binding.root.context,
stringRes = R.string.trackersBlocked,
totalTrackerCount = when (Random.Default.nextInt(10)) {
in 0..6 -> Random.Default.nextInt(10, 1000)
else -> Random.Default.nextInt(1000, 10000)
},
totalTrackerCount = trackerAnimationTile.trackerCount,
trackerTextView = holder.binding.title,
)
holder.binding.close.setOnClickListener {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import com.duckduckgo.app.tabs.model.TabEntity
sealed class TabSwitcherItem(val id: String) {

data class Tab(val tabEntity: TabEntity) : TabSwitcherItem(tabEntity.tabId)
data object TrackerAnimationTile : TabSwitcherItem("TrackerAnimationTile") {
data class TrackerAnimationTile(val trackerCount: Int) : TabSwitcherItem("TrackerAnimationTile") {

const val ANIMATED_TILE_NO_REPLACE_ALPHA = 0.4f
const val ANIMATED_TILE_DEFAULT_ALPHA = 1f
companion object {
const val ANIMATED_TILE_NO_REPLACE_ALPHA = 0.4f
const val ANIMATED_TILE_DEFAULT_ALPHA = 1f
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ package com.duckduckgo.app.tabs.ui
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.map
import androidx.lifecycle.liveData
import androidx.lifecycle.switchMap
import androidx.lifecycle.viewModelScope
import com.duckduckgo.adclick.api.AdClickManager
import com.duckduckgo.anvil.annotations.ContributesViewModel
Expand All @@ -33,12 +34,15 @@ 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.TabSwitcherItem.TrackerAnimationTile
import com.duckduckgo.app.trackerdetection.api.WebTrackersBlockedAppRepository
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.common.utils.SingleLiveEvent
import com.duckduckgo.common.utils.extensions.toBinaryString
import com.duckduckgo.di.scopes.ActivityScope
import com.duckduckgo.duckchat.api.DuckChat
import com.duckduckgo.duckchat.impl.DuckChatPixelName
import java.time.LocalDateTime
import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.first
Expand All @@ -56,18 +60,25 @@ class TabSwitcherViewModel @Inject constructor(
private val swipingTabsFeature: SwipingTabsFeatureProvider,
private val duckChat: DuckChat,
private val tabSwitcherAnimationFeature: TabSwitcherAnimationFeature,
private val webTrackersBlockedAppRepository: WebTrackersBlockedAppRepository,
) : ViewModel() {

val tabSwitcherItems: LiveData<List<TabSwitcherItem>> = tabRepository.liveTabs.map { tabEntities ->
val tabSwitcherItems: LiveData<List<TabSwitcherItem>> = tabRepository.liveTabs.switchMap { tabEntities ->
// TODO use dismissal logic and or test framework to determine whether to show tracker animation tile
if (tabSwitcherAnimationFeature.self().isEnabled()) {
val trackerAnimationTile = TabSwitcherItem.TrackerAnimationTile
val tabItems = tabEntities.map { TabSwitcherItem.Tab(it) }
listOf(trackerAnimationTile) + tabItems
liveData {
val trackerAnimationTile = createTrackerAnimationTile()
val tabItems = tabEntities.map { TabSwitcherItem.Tab(it) }
emit(listOf(trackerAnimationTile) + tabItems)
}
} else {
tabEntities.map { TabSwitcherItem.Tab(it) }
liveData {
val tabItems = tabEntities.map { TabSwitcherItem.Tab(it) }
emit(tabItems)
}
}
}

val activeTab = tabRepository.liveSelectedTab
val deletableTabs: LiveData<List<TabEntity>> = tabRepository.flowDeletableTabs.asLiveData(
context = viewModelScope.coroutineContext,
Expand Down Expand Up @@ -149,7 +160,7 @@ class TabSwitcherViewModel @Inject constructor(
tabSwitcherItems.value?.forEach { tabSwitcherItem ->
when (tabSwitcherItem) {
is TabSwitcherItem.Tab -> onTabDeleted(tabSwitcherItem.tabEntity)
TabSwitcherItem.TrackerAnimationTile -> Unit // TODO delete
is TabSwitcherItem.TrackerAnimationTile -> Unit // TODO delete
}
}
// Make sure all exemptions are removed as all tabs are deleted.
Expand Down Expand Up @@ -215,4 +226,14 @@ class TabSwitcherViewModel @Inject constructor(
duckChat.openDuckChat()
}
}

private suspend fun createTrackerAnimationTile(): TrackerAnimationTile {
val now = LocalDateTime.now()
val trackerCount =
webTrackersBlockedAppRepository.getTrackersCountBetween(
startTime = now.minusDays(7),
endTime = now,
)
return TrackerAnimationTile(trackerCount)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ package com.duckduckgo.app.trackerdetection.api

import com.duckduckgo.app.global.db.AppDatabase
import com.duckduckgo.app.trackerdetection.db.WebTrackerBlocked
import com.duckduckgo.common.utils.formatters.time.DatabaseDateFormatter
import com.duckduckgo.di.scopes.AppScope
import com.squareup.anvil.annotations.ContributesBinding
import java.time.LocalDateTime
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
Expand All @@ -38,4 +40,13 @@ class WebTrackersBlockedAppRepository @Inject constructor(appDatabase: AppDataba
.distinctUntilChanged()
.map { it.filter { tracker -> tracker.timestamp >= startTime() } }
}

// TODO move to public API if experiment kept
suspend fun getTrackersCountBetween(
startTime: LocalDateTime,
endTime: LocalDateTime,
): Int = dao.getTrackersCountBetween(
startTime = DatabaseDateFormatter.timestamp(startTime),
endTime = DatabaseDateFormatter.timestamp(endTime),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,10 @@ interface WebTrackersBlockedDao {
startTime: String,
endTime: String,
): Flow<List<WebTrackerBlocked>>

@Query("SELECT COUNT(*) FROM web_trackers_blocked WHERE timestamp >= :startTime AND timestamp < :endTime")
suspend fun getTrackersCountBetween(
startTime: String,
endTime: String,
): Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import com.duckduckgo.app.tabs.model.TabSwitcherData.LayoutType.LIST
import com.duckduckgo.app.tabs.model.TabSwitcherData.UserState.EXISTING
import com.duckduckgo.app.tabs.model.TabSwitcherData.UserState.NEW
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command
import com.duckduckgo.app.trackerdetection.api.WebTrackersBlockedAppRepository
import com.duckduckgo.common.test.CoroutineTestRule
import com.duckduckgo.common.test.blockingObserve
import com.duckduckgo.duckchat.api.DuckChat
Expand Down Expand Up @@ -98,6 +99,9 @@ class TabSwitcherViewModelTest {
@Mock
private lateinit var duckChatMock: DuckChat

@Mock
private lateinit var mockWebTrackersBlockedAppRepository: WebTrackersBlockedAppRepository

private val swipingTabsFeature = FakeFeatureToggleFactory.create(SwipingTabsFeature::class.java)
private val tabSwitcherAnimationFeature = FakeFeatureToggleFactory.create(TabSwitcherAnimationFeature::class.java)

Expand Down Expand Up @@ -141,6 +145,7 @@ class TabSwitcherViewModelTest {
swipingTabsFeatureProvider,
duckChatMock,
tabSwitcherAnimationFeature,
mockWebTrackersBlockedAppRepository,
)
testee.command.observeForever(mockCommandObserver)
}
Expand Down
Loading