-
Notifications
You must be signed in to change notification settings - Fork 935
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add pixels for multiple refreshes (#5094)
Task/Issue URL: https://app.asana.com/0/72649045549333/1208470923077006/f ### Description This adds two pixels: - One for when the user refreshes twice within 12 seconds. - And another when the user refreshes three times within 20 seconds. It also refactors all the refresh pixel sending logic into `RefreshPixelSender`, removing it from `BrowserTabFragment`. ### Steps to test this PR Filter by `m_reload` _Two refreshes in twelve seconds_ - [x] Refresh once - [x] Verify no pixel sent - [x] Refresh within 12 seconds - [x] Verify `m_reload_twice_within_12_seconds` is sent _Three refreshes in twenty seconds_ - [x] Refresh once - [x] Refresh again immediately (`m_reload_twice_within_12_seconds` will be sent) - [x] Wait 12 seconds - [x] Verify only `m_reload_three_times_within_20_seconds` is sent _Three refreshes in twenty seconds and two refreshes in twelve seconds_ - [x] Refresh once - [x] Refresh again immediately (`m_reload_twice_within_12_seconds` will be sent) - [x] Refresh again immediately - [x] Verify `m_reload_twice_within_12_seconds` is sent - [x] Verify `m_reload_three_times_within_20_seconds` is sent _Two refreshes in twelve seconds after killing the app_ - [x] Refresh once - [x] Verify no pixel sent - [x] Kill the app - [x] Refresh within 12 seconds - [x] Verify `m_reload_twice_within_12_seconds` is sent
- Loading branch information
Showing
10 changed files
with
575 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
app/src/main/java/com/duckduckgo/app/browser/refreshpixels/RefreshDao.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright (c) 2024 DuckDuckGo | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.duckduckgo.app.browser.refreshpixels | ||
|
||
import androidx.room.Dao | ||
import androidx.room.Insert | ||
import androidx.room.Query | ||
import androidx.room.Transaction | ||
|
||
@Dao | ||
abstract class RefreshDao { | ||
|
||
@Transaction | ||
open fun updateRecentRefreshes(minTime: Long, currentTime: Long): List<RefreshEntity> { | ||
insert(RefreshEntity(timestamp = currentTime)) | ||
deleteInvalidRefreshes(minTime, currentTime) | ||
return all() | ||
} | ||
|
||
@Insert | ||
abstract fun insert(entity: RefreshEntity) | ||
|
||
@Query("DELETE FROM refreshes WHERE timestamp NOT BETWEEN :minTime AND :currentTime") | ||
abstract fun deleteInvalidRefreshes(minTime: Long, currentTime: Long) | ||
|
||
@Query("SELECT * FROM refreshes") | ||
abstract fun all(): List<RefreshEntity> | ||
} |
26 changes: 26 additions & 0 deletions
26
app/src/main/java/com/duckduckgo/app/browser/refreshpixels/RefreshEntity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright (c) 2024 DuckDuckGo | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.duckduckgo.app.browser.refreshpixels | ||
|
||
import androidx.room.Entity | ||
import androidx.room.PrimaryKey | ||
|
||
@Entity(tableName = "refreshes") | ||
class RefreshEntity( | ||
@PrimaryKey(autoGenerate = true) val id: Long = 0, | ||
val timestamp: Long, | ||
) |
122 changes: 122 additions & 0 deletions
122
app/src/main/java/com/duckduckgo/app/browser/refreshpixels/RefreshPixelSender.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/* | ||
* Copyright (c) 2024 DuckDuckGo | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.duckduckgo.app.browser.refreshpixels | ||
|
||
import com.duckduckgo.app.browser.customtabs.CustomTabPixelNames | ||
import com.duckduckgo.app.di.AppCoroutineScope | ||
import com.duckduckgo.app.pixels.AppPixelName | ||
import com.duckduckgo.app.pixels.AppPixelName.RELOAD_THREE_TIMES_WITHIN_20_SECONDS | ||
import com.duckduckgo.app.pixels.AppPixelName.RELOAD_TWICE_WITHIN_12_SECONDS | ||
import com.duckduckgo.app.statistics.pixels.Pixel | ||
import com.duckduckgo.app.statistics.pixels.Pixel.PixelParameter.LOADING_BAR_EXPERIMENT | ||
import com.duckduckgo.app.statistics.pixels.Pixel.PixelType.Daily | ||
import com.duckduckgo.common.utils.CurrentTimeProvider | ||
import com.duckduckgo.common.utils.DispatcherProvider | ||
import com.duckduckgo.common.utils.extensions.toBinaryString | ||
import com.duckduckgo.di.scopes.AppScope | ||
import com.duckduckgo.experiments.api.loadingbarexperiment.LoadingBarExperimentManager | ||
import com.squareup.anvil.annotations.ContributesBinding | ||
import dagger.SingleInstanceIn | ||
import javax.inject.Inject | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.launch | ||
|
||
interface RefreshPixelSender { | ||
fun sendMenuRefreshPixels() | ||
fun sendCustomTabRefreshPixel() | ||
fun sendPullToRefreshPixels() | ||
} | ||
|
||
@ContributesBinding(AppScope::class) | ||
@SingleInstanceIn(AppScope::class) | ||
class DuckDuckGoRefreshPixelSender @Inject constructor( | ||
private val pixel: Pixel, | ||
private val dao: RefreshDao, | ||
private val loadingBarExperimentManager: LoadingBarExperimentManager, | ||
private val currentTimeProvider: CurrentTimeProvider, | ||
@AppCoroutineScope private val appCoroutineScope: CoroutineScope, | ||
private val dispatcherProvider: DispatcherProvider, | ||
) : RefreshPixelSender { | ||
|
||
override fun sendMenuRefreshPixels() { | ||
sendTimeBasedPixels() | ||
|
||
// Loading Bar Experiment | ||
if (loadingBarExperimentManager.isExperimentEnabled()) { | ||
pixel.fire( | ||
AppPixelName.MENU_ACTION_REFRESH_PRESSED, | ||
mapOf(LOADING_BAR_EXPERIMENT to loadingBarExperimentManager.variant.toBinaryString()), | ||
) | ||
pixel.fire( | ||
AppPixelName.REFRESH_ACTION_DAILY_PIXEL, | ||
mapOf(LOADING_BAR_EXPERIMENT to loadingBarExperimentManager.variant.toBinaryString()), | ||
type = Daily(), | ||
) | ||
} else { | ||
pixel.fire(AppPixelName.MENU_ACTION_REFRESH_PRESSED) | ||
pixel.fire(AppPixelName.REFRESH_ACTION_DAILY_PIXEL, type = Daily()) | ||
} | ||
} | ||
|
||
override fun sendPullToRefreshPixels() { | ||
sendTimeBasedPixels() | ||
|
||
// Loading Bar Experiment | ||
if (loadingBarExperimentManager.isExperimentEnabled()) { | ||
pixel.fire( | ||
AppPixelName.BROWSER_PULL_TO_REFRESH, | ||
mapOf(LOADING_BAR_EXPERIMENT to loadingBarExperimentManager.variant.toBinaryString()), | ||
) | ||
pixel.fire( | ||
AppPixelName.REFRESH_ACTION_DAILY_PIXEL, | ||
mapOf(LOADING_BAR_EXPERIMENT to loadingBarExperimentManager.variant.toBinaryString()), | ||
type = Daily(), | ||
) | ||
} else { | ||
pixel.fire(AppPixelName.BROWSER_PULL_TO_REFRESH) | ||
pixel.fire(AppPixelName.REFRESH_ACTION_DAILY_PIXEL, type = Daily()) | ||
} | ||
} | ||
|
||
override fun sendCustomTabRefreshPixel() { | ||
sendTimeBasedPixels() | ||
|
||
pixel.fire(CustomTabPixelNames.CUSTOM_TABS_MENU_REFRESH) | ||
} | ||
|
||
private fun sendTimeBasedPixels() { | ||
appCoroutineScope.launch(dispatcherProvider.io()) { | ||
val now = currentTimeProvider.currentTimeMillis() | ||
val twelveSecondsAgo = now - TWELVE_SECONDS | ||
val twentySecondsAgo = now - TWENTY_SECONDS | ||
|
||
val refreshes = dao.updateRecentRefreshes(twentySecondsAgo, now) | ||
|
||
if (refreshes.count { it.timestamp >= twelveSecondsAgo } >= 2) { | ||
pixel.fire(RELOAD_TWICE_WITHIN_12_SECONDS) | ||
} | ||
if (refreshes.size >= 3) { | ||
pixel.fire(RELOAD_THREE_TIMES_WITHIN_20_SECONDS) | ||
} | ||
} | ||
} | ||
|
||
companion object { | ||
const val TWENTY_SECONDS = 20000L | ||
const val TWELVE_SECONDS = 12000L | ||
} | ||
} |
Oops, something went wrong.