Skip to content

Commit 4cdb239

Browse files
Merge branch 'release/5.227.0'
2 parents 0293446 + 63b5b6c commit 4cdb239

File tree

47 files changed

+386
-12242
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+386
-12242
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright (c) 2019 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.browser.tabs.adapter
18+
19+
import android.os.Bundle
20+
import androidx.recyclerview.widget.DiffUtil
21+
import com.duckduckgo.app.tabs.ui.TabSwitcherItem
22+
23+
class TabSwitcherItemDiffCallback(old: List<TabSwitcherItem>, new: List<TabSwitcherItem>) : DiffUtil.Callback() {
24+
25+
// keep a local copy of the lists to avoid any changes to the lists during the diffing process
26+
private val oldList = old.toList()
27+
private val newList = new.toList()
28+
29+
private fun areItemsTheSame(
30+
oldItem: TabSwitcherItem,
31+
newItem: TabSwitcherItem,
32+
): Boolean {
33+
return oldItem.id == newItem.id
34+
}
35+
36+
private fun areContentsTheSame(
37+
oldItem: TabSwitcherItem,
38+
newItem: TabSwitcherItem,
39+
): Boolean {
40+
return when {
41+
oldItem is TabSwitcherItem.Tab && newItem is TabSwitcherItem.Tab -> {
42+
oldItem.tabEntity.tabPreviewFile == newItem.tabEntity.tabPreviewFile &&
43+
oldItem.tabEntity.viewed == newItem.tabEntity.viewed &&
44+
oldItem.tabEntity.title == newItem.tabEntity.title &&
45+
oldItem.tabEntity.url == newItem.tabEntity.url
46+
}
47+
else -> false
48+
}
49+
}
50+
51+
private fun getChangePayload(
52+
oldItem: TabSwitcherItem,
53+
newItem: TabSwitcherItem,
54+
): Bundle {
55+
val diffBundle = Bundle()
56+
57+
when {
58+
oldItem is TabSwitcherItem.Tab && newItem is TabSwitcherItem.Tab -> {
59+
if (oldItem.tabEntity.title != newItem.tabEntity.title) {
60+
diffBundle.putString(DIFF_KEY_TITLE, newItem.tabEntity.title)
61+
}
62+
63+
if (oldItem.tabEntity.url != newItem.tabEntity.url) {
64+
diffBundle.putString(DIFF_KEY_URL, newItem.tabEntity.url)
65+
}
66+
67+
if (oldItem.tabEntity.viewed != newItem.tabEntity.viewed) {
68+
diffBundle.putBoolean(DIFF_KEY_VIEWED, newItem.tabEntity.viewed)
69+
}
70+
71+
if (oldItem.tabEntity.tabPreviewFile != newItem.tabEntity.tabPreviewFile) {
72+
diffBundle.putString(DIFF_KEY_PREVIEW, newItem.tabEntity.tabPreviewFile)
73+
}
74+
}
75+
}
76+
77+
return diffBundle
78+
}
79+
80+
override fun getOldListSize(): Int {
81+
return oldList.size
82+
}
83+
84+
override fun getNewListSize(): Int {
85+
return newList.size
86+
}
87+
88+
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
89+
return if (oldItemPosition in oldList.indices && newItemPosition in newList.indices) {
90+
areItemsTheSame(oldList[oldItemPosition], newList[newItemPosition])
91+
} else {
92+
false
93+
}
94+
}
95+
96+
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
97+
return if (oldItemPosition in oldList.indices && newItemPosition in newList.indices) {
98+
areContentsTheSame(oldList[oldItemPosition], newList[newItemPosition])
99+
} else {
100+
false
101+
}
102+
}
103+
104+
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any {
105+
return if (oldItemPosition in oldList.indices && newItemPosition in newList.indices) {
106+
getChangePayload(oldList[oldItemPosition], newList[newItemPosition])
107+
} else {
108+
Bundle()
109+
}
110+
}
111+
112+
companion object {
113+
const val DIFF_KEY_TITLE = "title"
114+
const val DIFF_KEY_URL = "url"
115+
const val DIFF_KEY_PREVIEW = "previewImage"
116+
const val DIFF_KEY_VIEWED = "viewed"
117+
}
118+
}

app/src/main/java/com/duckduckgo/app/generalsettings/showonapplaunch/ShowOnAppLaunchViewModel.kt

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import androidx.lifecycle.viewModelScope
2121
import com.duckduckgo.anvil.annotations.ContributesViewModel
2222
import com.duckduckgo.app.generalsettings.showonapplaunch.model.ShowOnAppLaunchOption
2323
import com.duckduckgo.app.generalsettings.showonapplaunch.store.ShowOnAppLaunchOptionDataStore
24-
import com.duckduckgo.app.statistics.pixels.Pixel
2524
import com.duckduckgo.common.utils.DispatcherProvider
2625
import com.duckduckgo.di.scopes.ActivityScope
2726
import javax.inject.Inject
@@ -32,14 +31,12 @@ import kotlinx.coroutines.flow.filterNotNull
3231
import kotlinx.coroutines.flow.flowOn
3332
import kotlinx.coroutines.flow.launchIn
3433
import kotlinx.coroutines.launch
35-
import timber.log.Timber
3634

3735
@ContributesViewModel(ActivityScope::class)
3836
class ShowOnAppLaunchViewModel @Inject constructor(
3937
private val dispatcherProvider: DispatcherProvider,
4038
private val showOnAppLaunchOptionDataStore: ShowOnAppLaunchOptionDataStore,
4139
private val urlConverter: UrlConverter,
42-
private val pixel: Pixel,
4340
) : ViewModel() {
4441

4542
data class ViewState(
@@ -65,23 +62,15 @@ class ShowOnAppLaunchViewModel @Inject constructor(
6562
}
6663

6764
fun onShowOnAppLaunchOptionChanged(option: ShowOnAppLaunchOption) {
68-
Timber.i("User changed show on app launch option to $option")
6965
viewModelScope.launch(dispatcherProvider.io()) {
70-
firePixel(option)
7166
showOnAppLaunchOptionDataStore.setShowOnAppLaunchOption(option)
7267
}
7368
}
7469

7570
fun setSpecificPageUrl(url: String) {
76-
Timber.i("Setting specific page url to $url")
7771
viewModelScope.launch(dispatcherProvider.io()) {
7872
val convertedUrl = urlConverter.convertUrl(url)
7973
showOnAppLaunchOptionDataStore.setSpecificPageUrl(convertedUrl)
8074
}
8175
}
82-
83-
private fun firePixel(option: ShowOnAppLaunchOption) {
84-
val pixelName = ShowOnAppLaunchOption.getPixelName(option)
85-
pixel.fire(pixelName)
86-
}
8776
}

app/src/main/java/com/duckduckgo/app/generalsettings/showonapplaunch/model/ShowOnAppLaunchOption.kt

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616

1717
package com.duckduckgo.app.generalsettings.showonapplaunch.model
1818

19-
import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_GENERAL_APP_LAUNCH_LAST_OPENED_TAB_SELECTED
20-
import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_GENERAL_APP_LAUNCH_NEW_TAB_PAGE_SELECTED
21-
import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_GENERAL_APP_LAUNCH_SPECIFIC_PAGE_SELECTED
22-
2319
sealed class ShowOnAppLaunchOption(val id: Int) {
2420

2521
data object LastOpenedTab : ShowOnAppLaunchOption(1)
@@ -35,12 +31,6 @@ sealed class ShowOnAppLaunchOption(val id: Int) {
3531
else -> throw IllegalArgumentException("Unknown id: $id")
3632
}
3733

38-
fun getPixelName(option: ShowOnAppLaunchOption) = when (option) {
39-
LastOpenedTab -> SETTINGS_GENERAL_APP_LAUNCH_LAST_OPENED_TAB_SELECTED
40-
NewTabPage -> SETTINGS_GENERAL_APP_LAUNCH_NEW_TAB_PAGE_SELECTED
41-
is SpecificPage -> SETTINGS_GENERAL_APP_LAUNCH_SPECIFIC_PAGE_SELECTED
42-
}
43-
4434
fun getDailyPixelValue(option: ShowOnAppLaunchOption) = when (option) {
4535
LastOpenedTab -> "last_opened_tab"
4636
NewTabPage -> "new_tab_page"

app/src/main/java/com/duckduckgo/app/global/api/PixelParamRemovalInterceptor.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ object PixelInterceptorPixelsRequiringDataCleaning : PixelParamRemovalPlugin {
101101
AppPixelName.SET_AS_DEFAULT_PROMPT_CLICK.pixelName to PixelParameter.removeAll(),
102102
AppPixelName.SET_AS_DEFAULT_PROMPT_DISMISSED.pixelName to PixelParameter.removeAll(),
103103
AppPixelName.SET_AS_DEFAULT_IN_MENU_CLICK.pixelName to PixelParameter.removeAll(),
104-
AppPixelName.SPLASHSCREEN_SHOWN.pixelName to PixelParameter.removeAll(),
105104
AppPixelName.MENU_ACTION_NEW_TAB_PRESSED_FROM_SITE.pixelName to PixelParameter.removeAll(),
106105
AppPixelName.MENU_ACTION_NEW_TAB_PRESSED_FROM_SERP.pixelName to PixelParameter.removeAll(),
107106
)

app/src/main/java/com/duckduckgo/app/launch/LaunchBridgeActivity.kt

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ import com.duckduckgo.app.browser.R
2525
import com.duckduckgo.app.onboarding.ui.OnboardingActivity
2626
import com.duckduckgo.common.ui.DuckDuckGoActivity
2727
import com.duckduckgo.di.scopes.ActivityScope
28-
import java.time.Instant
29-
import java.time.temporal.ChronoUnit
30-
import kotlinx.coroutines.delay
3128
import kotlinx.coroutines.launch
3229

3330
@InjectWith(ActivityScope::class)
@@ -38,25 +35,13 @@ class LaunchBridgeActivity : DuckDuckGoActivity() {
3835
override fun onCreate(savedInstanceState: Bundle?) {
3936
val splashScreen = installSplashScreen()
4037
super.onCreate(savedInstanceState)
38+
splashScreen.setKeepOnScreenCondition { true }
4139

4240
setContentView(R.layout.activity_launch)
4341

4442
configureObservers()
4543

46-
splashScreen.setOnExitAnimationListener { splashScreenView ->
47-
val splashScreenAnimationEndTime =
48-
Instant.ofEpochMilli(splashScreenView.iconAnimationStartMillis + splashScreenView.iconAnimationDurationMillis)
49-
val remainingAnimationTime = Instant.now().until(
50-
splashScreenAnimationEndTime,
51-
ChronoUnit.MILLIS,
52-
)
53-
54-
lifecycleScope.launch {
55-
viewModel.sendWelcomeScreenPixel()
56-
delay(remainingAnimationTime)
57-
viewModel.determineViewToShow()
58-
}
59-
}
44+
lifecycleScope.launch { viewModel.determineViewToShow() }
6045
}
6146

6247
private fun configureObservers() {

app/src/main/java/com/duckduckgo/app/launch/LaunchViewModel.kt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@ import androidx.lifecycle.ViewModel
2020
import com.duckduckgo.anvil.annotations.ContributesViewModel
2121
import com.duckduckgo.app.onboarding.store.UserStageStore
2222
import com.duckduckgo.app.onboarding.store.isNewUser
23-
import com.duckduckgo.app.pixels.AppPixelName.SPLASHSCREEN_SHOWN
2423
import com.duckduckgo.app.referral.AppInstallationReferrerStateListener
2524
import com.duckduckgo.app.referral.AppInstallationReferrerStateListener.Companion.MAX_REFERRER_WAIT_TIME_MS
26-
import com.duckduckgo.app.statistics.pixels.Pixel
2725
import com.duckduckgo.common.utils.SingleLiveEvent
2826
import com.duckduckgo.di.scopes.ActivityScope
2927
import javax.inject.Inject
@@ -34,7 +32,6 @@ import timber.log.Timber
3432
class LaunchViewModel @Inject constructor(
3533
private val userStageStore: UserStageStore,
3634
private val appReferrerStateListener: AppInstallationReferrerStateListener,
37-
private val pixel: Pixel,
3835
) :
3936
ViewModel() {
4037

@@ -45,10 +42,6 @@ class LaunchViewModel @Inject constructor(
4542
data class Home(val replaceExistingSearch: Boolean = false) : Command()
4643
}
4744

48-
fun sendWelcomeScreenPixel() {
49-
pixel.fire(SPLASHSCREEN_SHOWN)
50-
}
51-
5245
suspend fun determineViewToShow() {
5346
waitForReferrerData()
5447

app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ enum class AppPixelName(override val pixelName: String) : Pixel.PixelName {
3737
BROKEN_SITE_ALLOWLIST_REMOVE("m_broken_site_allowlist_remove"),
3838
PROTECTION_TOGGLE_BROKEN_SITE_REPORT("m_protection-toggled-off-breakage-report"),
3939

40-
SPLASHSCREEN_SHOWN("m_splashscreen_shown"),
41-
4240
PREONBOARDING_INTRO_SHOWN_UNIQUE("m_preonboarding_intro_shown_unique"),
4341
PREONBOARDING_COMPARISON_CHART_SHOWN_UNIQUE("m_preonboarding_comparison_chart_shown_unique"),
4442
PREONBOARDING_CHOOSE_BROWSER_PRESSED("m_preonboarding_choose_browser_pressed"),
@@ -148,9 +146,6 @@ enum class AppPixelName(override val pixelName: String) : Pixel.PixelName {
148146
SETTINGS_COOKIE_POPUP_PROTECTION_PRESSED("ms_cookie_popup_protection_setting_pressed"),
149147
SETTINGS_FIRE_BUTTON_PRESSED("ms_fire_button_setting_pressed"),
150148
SETTINGS_GENERAL_APP_LAUNCH_PRESSED("m_settings_general_app_launch_pressed"),
151-
SETTINGS_GENERAL_APP_LAUNCH_LAST_OPENED_TAB_SELECTED("m_settings_general_app_launch_last_opened_tab_selected"),
152-
SETTINGS_GENERAL_APP_LAUNCH_NEW_TAB_PAGE_SELECTED("m_settings_general_app_launch_new_tab_page_selected"),
153-
SETTINGS_GENERAL_APP_LAUNCH_SPECIFIC_PAGE_SELECTED("m_settings_general_app_launch_specific_page_selected"),
154149

155150
SURVEY_CTA_SHOWN(pixelName = "mus_cs"),
156151
SURVEY_CTA_DISMISSED(pixelName = "mus_cd"),

app/src/main/java/com/duckduckgo/app/tabs/ui/TabItemDecorator.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import com.duckduckgo.mobile.android.R as CommonR
2929

3030
class TabItemDecorator(
3131
context: Context,
32-
var selectedTabId: String?,
32+
var tabSwitcherItemId: String?,
3333
) : RecyclerView.ItemDecoration() {
3434

3535
private val borderStroke: Paint = Paint().apply {
@@ -53,8 +53,8 @@ class TabItemDecorator(
5353
val child = recyclerView.getChildAt(i)
5454

5555
val positionInAdapter = recyclerView.getChildAdapterPosition(child)
56-
adapter.getTab(positionInAdapter)?.let { tab ->
57-
if (tab.tabId == selectedTabId) {
56+
adapter.getTabSwitcherItem(positionInAdapter)?.let { tabSwitcherItem ->
57+
if (tabSwitcherItem.id == tabSwitcherItemId) {
5858
drawSelectedTabDecoration(child, canvas)
5959
}
6060
}

app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -188,17 +188,18 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
188188
}
189189

190190
private fun configureObservers() {
191-
viewModel.tabs.observe(this) { tabs ->
192-
render(tabs)
191+
viewModel.tabSwitcherItems.observe(this) { tabSwitcherItems ->
193192

194-
val noTabSelected = tabs.none { it.tabId == tabItemDecorator.selectedTabId }
195-
if (noTabSelected && tabs.isNotEmpty()) {
196-
updateTabGridItemDecorator(tabs.last())
193+
render(tabSwitcherItems)
194+
195+
val noTabSelected = tabSwitcherItems.none { it.id == tabItemDecorator.tabSwitcherItemId }
196+
if (noTabSelected && tabSwitcherItems.isNotEmpty()) {
197+
updateTabGridItemDecorator(tabSwitcherItems.last().id)
197198
}
198199
}
199200
viewModel.activeTab.observe(this) { tab ->
200-
if (tab != null && tab.tabId != tabItemDecorator.selectedTabId && !tab.deletable) {
201-
updateTabGridItemDecorator(tab)
201+
if (tab != null && tab.tabId != tabItemDecorator.tabSwitcherItemId && !tab.deletable) {
202+
updateTabGridItemDecorator(tab.tabId)
202203
}
203204
}
204205
viewModel.deletableTabs.observe(this) {
@@ -286,7 +287,7 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
286287
}
287288
}
288289

289-
private fun render(tabs: List<TabEntity>) {
290+
private fun render(tabs: List<TabSwitcherItem>) {
290291
tabsAdapter.updateData(tabs)
291292
}
292293

@@ -349,7 +350,7 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
349350

350351
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
351352
val closeAllTabsMenuItem = menu?.findItem(R.id.closeAllTabs)
352-
closeAllTabsMenuItem?.isVisible = viewModel.tabs.value?.isNotEmpty() == true
353+
closeAllTabsMenuItem?.isVisible = viewModel.tabSwitcherItems.value?.isNotEmpty() == true
353354
val duckChatMenuItem = menu?.findItem(R.id.duckChat)
354355
duckChatMenuItem?.isVisible = duckChat.showInBrowserMenu()
355356

@@ -390,23 +391,27 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
390391

391392
override fun onTabSelected(tab: TabEntity) {
392393
selectedTabId = tab.tabId
393-
updateTabGridItemDecorator(tab)
394+
updateTabGridItemDecorator(tab.tabId)
394395
launch { viewModel.onTabSelected(tab) }
395396
}
396397

397-
private fun updateTabGridItemDecorator(tab: TabEntity) {
398-
tabItemDecorator.selectedTabId = tab.tabId
398+
private fun updateTabGridItemDecorator(tabSwitcherItemId: String) {
399+
tabItemDecorator.tabSwitcherItemId = tabSwitcherItemId
399400
tabsRecycler.invalidateItemDecorations()
400401
}
401402

402403
override fun onTabDeleted(position: Int, deletedBySwipe: Boolean) {
403-
tabsAdapter.getTab(position)?.let { tab ->
404-
launch { viewModel.onMarkTabAsDeletable(tab, deletedBySwipe) }
404+
tabsAdapter.getTabSwitcherItem(position)?.let { tab ->
405+
when (tab) {
406+
is TabSwitcherItem.Tab -> {
407+
launch { viewModel.onTabDeleted(tab.tabEntity) }
408+
}
409+
}
405410
}
406411
}
407412

408413
override fun onTabMoved(from: Int, to: Int) {
409-
val tabCount = viewModel.tabs.value?.size ?: 0
414+
val tabCount = viewModel.tabSwitcherItems.value?.size ?: 0
410415
val canSwap = from in 0..< tabCount && to in 0..< tabCount
411416
if (canSwap) {
412417
tabsAdapter.onTabMoved(from, to)
@@ -489,7 +494,7 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
489494
}
490495

491496
private fun clearObserversEarlyToStopViewUpdates() {
492-
viewModel.tabs.removeObservers(this)
497+
viewModel.tabSwitcherItems.removeObservers(this)
493498
viewModel.deletableTabs.removeObservers(this)
494499
}
495500

0 commit comments

Comments
 (0)