Skip to content

Commit d389446

Browse files
authored
Remove SplashScreen Animation (#5842)
Task/Issue URL: https://app.asana.com/0/0/1209841844839950 ### Description Removes the splashscreen animation as we started to receive user complaints pertaining to launch speed. ### Steps to test this PR _Static Launch Screen_ - [x] Open the app on various supported SDK version e.g. 26, 30, 34 - [x] Check that the previous static logo shows on cold launch ### UI changes https://github.com/user-attachments/assets/7abbb7ad-b0ac-4ea8-b290-4792d260053d
1 parent 60905b5 commit d389446

File tree

10 files changed

+7
-12138
lines changed

10 files changed

+7
-12138
lines changed

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +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(),
105-
AppPixelName.SPLASHSCREEN_FAILED_TO_LAUNCH.pixelName to PixelParameter.removeAll(),
106104
AppPixelName.MENU_ACTION_NEW_TAB_PRESSED_FROM_SITE.pixelName to PixelParameter.removeAll(),
107105
AppPixelName.MENU_ACTION_NEW_TAB_PRESSED_FROM_SERP.pixelName to PixelParameter.removeAll(),
108106
AppPixelName.SETTINGS_SYNC_PRESSED.pixelName to PixelParameter.removeAtb(),

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

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package com.duckduckgo.app.launch
1818

19-
import android.content.Intent
20-
import android.content.pm.PackageManager
2119
import android.os.Bundle
2220
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
2321
import androidx.lifecycle.lifecycleScope
@@ -27,9 +25,6 @@ import com.duckduckgo.app.browser.R
2725
import com.duckduckgo.app.onboarding.ui.OnboardingActivity
2826
import com.duckduckgo.common.ui.DuckDuckGoActivity
2927
import com.duckduckgo.di.scopes.ActivityScope
30-
import java.time.Instant
31-
import java.time.temporal.ChronoUnit
32-
import kotlinx.coroutines.delay
3328
import kotlinx.coroutines.launch
3429

3530
@InjectWith(ActivityScope::class)
@@ -40,29 +35,13 @@ class LaunchBridgeActivity : DuckDuckGoActivity() {
4035
override fun onCreate(savedInstanceState: Bundle?) {
4136
val splashScreen = installSplashScreen()
4237
super.onCreate(savedInstanceState)
38+
splashScreen.setKeepOnScreenCondition { true }
4339

4440
setContentView(R.layout.activity_launch)
4541

4642
configureObservers()
4743

48-
viewModel.launchSplashScreenFailToExitJob(getLauncherPackageName())
49-
50-
splashScreen.setOnExitAnimationListener { splashScreenView ->
51-
viewModel.cancelSplashScreenFailToExitJob()
52-
53-
val splashScreenAnimationEndTime =
54-
Instant.ofEpochMilli(splashScreenView.iconAnimationStartMillis + splashScreenView.iconAnimationDurationMillis)
55-
val remainingAnimationTime = Instant.now().until(
56-
splashScreenAnimationEndTime,
57-
ChronoUnit.MILLIS,
58-
)
59-
60-
lifecycleScope.launch {
61-
viewModel.sendWelcomeScreenPixel()
62-
delay(remainingAnimationTime)
63-
viewModel.determineViewToShow()
64-
}
65-
}
44+
lifecycleScope.launch { viewModel.determineViewToShow() }
6645
}
6746

6847
private fun configureObservers() {
@@ -93,12 +72,4 @@ class LaunchBridgeActivity : DuckDuckGoActivity() {
9372
overridePendingTransition(0, 0)
9473
finish()
9574
}
96-
97-
// Temporary to track splashscreen errors
98-
private fun getLauncherPackageName(): String? {
99-
val intent = Intent(Intent.ACTION_MAIN)
100-
intent.addCategory(Intent.CATEGORY_HOME)
101-
val resolveInfo = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
102-
return resolveInfo?.activityInfo?.packageName
103-
}
10475
}

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

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,46 +17,31 @@
1717
package com.duckduckgo.app.launch
1818

1919
import androidx.lifecycle.ViewModel
20-
import androidx.lifecycle.viewModelScope
2120
import com.duckduckgo.anvil.annotations.ContributesViewModel
2221
import com.duckduckgo.app.onboarding.store.UserStageStore
2322
import com.duckduckgo.app.onboarding.store.isNewUser
24-
import com.duckduckgo.app.pixels.AppPixelName.SPLASHSCREEN_FAILED_TO_LAUNCH
25-
import com.duckduckgo.app.pixels.AppPixelName.SPLASHSCREEN_SHOWN
2623
import com.duckduckgo.app.referral.AppInstallationReferrerStateListener
2724
import com.duckduckgo.app.referral.AppInstallationReferrerStateListener.Companion.MAX_REFERRER_WAIT_TIME_MS
28-
import com.duckduckgo.app.statistics.pixels.Pixel
2925
import com.duckduckgo.common.utils.SingleLiveEvent
3026
import com.duckduckgo.di.scopes.ActivityScope
3127
import javax.inject.Inject
32-
import kotlin.time.Duration.Companion.seconds
33-
import kotlinx.coroutines.Job
34-
import kotlinx.coroutines.delay
35-
import kotlinx.coroutines.launch
3628
import kotlinx.coroutines.withTimeoutOrNull
3729
import timber.log.Timber
3830

3931
@ContributesViewModel(ActivityScope::class)
4032
class LaunchViewModel @Inject constructor(
4133
private val userStageStore: UserStageStore,
4234
private val appReferrerStateListener: AppInstallationReferrerStateListener,
43-
private val pixel: Pixel,
4435
) :
4536
ViewModel() {
4637

47-
private var splashScreenFailToExitJob: Job? = null
48-
4938
val command: SingleLiveEvent<Command> = SingleLiveEvent()
5039

5140
sealed class Command {
5241
data object Onboarding : Command()
5342
data class Home(val replaceExistingSearch: Boolean = false) : Command()
5443
}
5544

56-
fun sendWelcomeScreenPixel() {
57-
pixel.fire(SPLASHSCREEN_SHOWN)
58-
}
59-
6045
suspend fun determineViewToShow() {
6146
waitForReferrerData()
6247

@@ -67,19 +52,6 @@ class LaunchViewModel @Inject constructor(
6752
}
6853
}
6954

70-
fun launchSplashScreenFailToExitJob(launcherPackageName: String?) {
71-
splashScreenFailToExitJob = viewModelScope.launch {
72-
delay(1.5.seconds)
73-
sendWelcomeScreenPixel()
74-
determineViewToShow()
75-
sendSplashScreenFailedToLaunchPixel(launcherPackageName)
76-
}
77-
}
78-
79-
fun cancelSplashScreenFailToExitJob() {
80-
splashScreenFailToExitJob?.cancel()
81-
}
82-
8355
private suspend fun waitForReferrerData() {
8456
val startTime = System.currentTimeMillis()
8557

@@ -90,14 +62,4 @@ class LaunchViewModel @Inject constructor(
9062

9163
Timber.d("Waited ${System.currentTimeMillis() - startTime}ms for referrer")
9264
}
93-
94-
// Temporary to track splashscreen errors
95-
private fun sendSplashScreenFailedToLaunchPixel(launcherPackageName: String?) {
96-
val resolvedLauncherPackageName = launcherPackageName ?: "unknown"
97-
val params = mapOf(
98-
"launcherPackageName" to resolvedLauncherPackageName,
99-
"api" to android.os.Build.VERSION.SDK_INT.toString(),
100-
)
101-
pixel.fire(pixel = SPLASHSCREEN_FAILED_TO_LAUNCH, parameters = params)
102-
}
10365
}

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +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-
SPLASHSCREEN_FAILED_TO_LAUNCH("m_splashscreen_failed_to_launch"),
42-
4340
PREONBOARDING_INTRO_SHOWN_UNIQUE("m_preonboarding_intro_shown_unique"),
4441
PREONBOARDING_COMPARISON_CHART_SHOWN_UNIQUE("m_preonboarding_comparison_chart_shown_unique"),
4542
PREONBOARDING_CHOOSE_BROWSER_PRESSED("m_preonboarding_choose_browser_pressed"),

app/src/test/java/com/duckduckgo/app/launch/LaunchViewModelTest.kt

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,15 @@ import com.duckduckgo.app.launch.LaunchViewModel.Command.Home
2222
import com.duckduckgo.app.launch.LaunchViewModel.Command.Onboarding
2323
import com.duckduckgo.app.onboarding.store.AppStage
2424
import com.duckduckgo.app.onboarding.store.UserStageStore
25-
import com.duckduckgo.app.pixels.AppPixelName
2625
import com.duckduckgo.app.referral.StubAppReferrerFoundStateListener
2726
import com.duckduckgo.common.test.CoroutineTestRule
28-
import com.duckduckgo.fakes.FakePixel
29-
import kotlin.time.Duration.Companion.seconds
30-
import kotlinx.coroutines.ExperimentalCoroutinesApi
31-
import kotlinx.coroutines.test.advanceTimeBy
3227
import kotlinx.coroutines.test.runTest
3328
import org.junit.After
34-
import org.junit.Assert.assertTrue
3529
import org.junit.Rule
3630
import org.junit.Test
3731
import org.mockito.kotlin.any
3832
import org.mockito.kotlin.mock
3933
import org.mockito.kotlin.verify
40-
import org.mockito.kotlin.verifyNoInteractions
4134
import org.mockito.kotlin.whenever
4235

4336
class LaunchViewModelTest {
@@ -52,22 +45,18 @@ class LaunchViewModelTest {
5245
private val userStageStore = mock<UserStageStore>()
5346
private val mockCommandObserver: Observer<LaunchViewModel.Command> = mock()
5447

55-
private val fakePixel: FakePixel = FakePixel()
56-
5748
private lateinit var testee: LaunchViewModel
5849

5950
@After
6051
fun after() {
6152
testee.command.removeObserver(mockCommandObserver)
62-
fakePixel.firedPixels.clear()
6353
}
6454

6555
@Test
6656
fun whenOnboardingShouldShowAndReferrerDataReturnsQuicklyThenCommandIsOnboarding() = runTest {
6757
testee = LaunchViewModel(
6858
userStageStore,
6959
StubAppReferrerFoundStateListener("xx"),
70-
fakePixel,
7160
)
7261
whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.NEW)
7362
testee.command.observeForever(mockCommandObserver)
@@ -82,7 +71,6 @@ class LaunchViewModelTest {
8271
testee = LaunchViewModel(
8372
userStageStore,
8473
StubAppReferrerFoundStateListener("xx", mockDelayMs = 1_000),
85-
fakePixel,
8674
)
8775
whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.NEW)
8876
testee.command.observeForever(mockCommandObserver)
@@ -97,7 +85,6 @@ class LaunchViewModelTest {
9785
testee = LaunchViewModel(
9886
userStageStore,
9987
StubAppReferrerFoundStateListener("xx", mockDelayMs = Long.MAX_VALUE),
100-
fakePixel,
10188
)
10289
whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.NEW)
10390
testee.command.observeForever(mockCommandObserver)
@@ -112,7 +99,6 @@ class LaunchViewModelTest {
11299
testee = LaunchViewModel(
113100
userStageStore,
114101
StubAppReferrerFoundStateListener("xx"),
115-
fakePixel,
116102
)
117103
whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.DAX_ONBOARDING)
118104
testee.command.observeForever(mockCommandObserver)
@@ -125,7 +111,6 @@ class LaunchViewModelTest {
125111
testee = LaunchViewModel(
126112
userStageStore,
127113
StubAppReferrerFoundStateListener("xx", mockDelayMs = 1_000),
128-
fakePixel,
129114
)
130115
whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.DAX_ONBOARDING)
131116
testee.command.observeForever(mockCommandObserver)
@@ -138,72 +123,10 @@ class LaunchViewModelTest {
138123
testee = LaunchViewModel(
139124
userStageStore,
140125
StubAppReferrerFoundStateListener("xx", mockDelayMs = Long.MAX_VALUE),
141-
fakePixel,
142126
)
143127
whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.DAX_ONBOARDING)
144128
testee.command.observeForever(mockCommandObserver)
145129
testee.determineViewToShow()
146130
verify(mockCommandObserver).onChanged(any<Home>())
147131
}
148-
149-
@Test
150-
fun whenSendingWelcomeScreenPixelThenSplashScreenShownPixelIsSent() = runTest {
151-
testee = LaunchViewModel(
152-
userStageStore,
153-
StubAppReferrerFoundStateListener("xx", mockDelayMs = Long.MAX_VALUE),
154-
fakePixel,
155-
)
156-
157-
testee.sendWelcomeScreenPixel()
158-
159-
assertTrue(fakePixel.firedPixels.containsKey(AppPixelName.SPLASHSCREEN_SHOWN.pixelName))
160-
}
161-
162-
@OptIn(ExperimentalCoroutinesApi::class)
163-
@Test
164-
fun whenLaunchSplashScreenFailToExitJobCalledThenItExecutesFallbackAfterDelay() = runTest {
165-
whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.ESTABLISHED)
166-
167-
testee = LaunchViewModel(
168-
userStageStore,
169-
StubAppReferrerFoundStateListener("xx", mockDelayMs = Long.MAX_VALUE),
170-
fakePixel,
171-
)
172-
173-
testee.command.observeForever(mockCommandObserver)
174-
testee.launchSplashScreenFailToExitJob("testLauncher")
175-
176-
// Wait for fail to exit timeout and referrer timeout
177-
advanceTimeBy(3.5.seconds)
178-
179-
assertTrue(fakePixel.firedPixels.containsKey(AppPixelName.SPLASHSCREEN_FAILED_TO_LAUNCH.pixelName))
180-
181-
val pixelParams = fakePixel.firedPixels[AppPixelName.SPLASHSCREEN_FAILED_TO_LAUNCH.pixelName]
182-
assertTrue(pixelParams!!.containsValue("testLauncher"))
183-
assertTrue(pixelParams.containsKey("api"))
184-
185-
verify(mockCommandObserver).onChanged(any<Home>())
186-
}
187-
188-
@OptIn(ExperimentalCoroutinesApi::class)
189-
@Test
190-
fun whenCancelSplashScreenFailToExitJobThenStopsFallback() = runTest {
191-
whenever(userStageStore.getUserAppStage()).thenReturn(AppStage.ESTABLISHED)
192-
193-
testee = LaunchViewModel(
194-
userStageStore,
195-
StubAppReferrerFoundStateListener("xx", mockDelayMs = Long.MAX_VALUE),
196-
fakePixel,
197-
)
198-
199-
testee.command.observeForever(mockCommandObserver)
200-
testee.launchSplashScreenFailToExitJob("testLauncher")
201-
testee.cancelSplashScreenFailToExitJob()
202-
203-
// advance time to ensure that the code does not execute after the delay
204-
advanceTimeBy(3.seconds)
205-
206-
assertTrue(fakePixel.firedPixels.isEmpty())
207-
verifyNoInteractions(mockCommandObserver)
208-
}
209132
}

app/src/test/java/com/duckduckgo/fakes/FakePixel.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ import com.duckduckgo.app.statistics.pixels.Pixel.PixelType
2222

2323
internal class FakePixel : Pixel {
2424

25-
val firedPixels = mutableMapOf<String, Map<String, String>>()
25+
val firedPixels = mutableListOf<String>()
2626

2727
override fun fire(
2828
pixel: PixelName,
2929
parameters: Map<String, String>,
3030
encodedParameters: Map<String, String>,
3131
type: PixelType,
3232
) {
33-
firedPixels[pixel.pixelName] = parameters
33+
firedPixels.add(pixel.pixelName)
3434
}
3535

3636
override fun fire(
@@ -39,22 +39,22 @@ internal class FakePixel : Pixel {
3939
encodedParameters: Map<String, String>,
4040
type: PixelType,
4141
) {
42-
firedPixels[pixelName] = parameters
42+
firedPixels.add(pixelName)
4343
}
4444

4545
override fun enqueueFire(
4646
pixel: PixelName,
4747
parameters: Map<String, String>,
4848
encodedParameters: Map<String, String>,
4949
) {
50-
firedPixels[pixel.pixelName] = parameters
50+
firedPixels.add(pixel.pixelName)
5151
}
5252

5353
override fun enqueueFire(
5454
pixelName: String,
5555
parameters: Map<String, String>,
5656
encodedParameters: Map<String, String>,
5757
) {
58-
firedPixels[pixelName] = parameters
58+
firedPixels.add(pixelName)
5959
}
6060
}

0 commit comments

Comments
 (0)