From 89679f7051da015c19db35085d7172905c23da39 Mon Sep 17 00:00:00 2001 From: Cris Barreiro Date: Mon, 21 Oct 2024 19:06:07 +0200 Subject: [PATCH] Cache datastore values using StateFlows to improve performance and address ANRs --- .../app/browser/SpecialUrlDetector.kt | 5 +- .../browser/duckplayer/DuckPlayerJSHelper.kt | 2 +- .../duckduckgo/duckplayer/api/DuckPlayer.kt | 12 +- .../duckplayer/impl/DuckPlayerDataStore.kt | 249 +++++++++--------- .../impl/DuckPlayerFeatureRepository.kt | 60 ++--- .../impl/DuckPlayerSettingsViewModel.kt | 3 +- .../duckplayer/impl/RealDuckPlayer.kt | 44 ++-- 7 files changed, 183 insertions(+), 192 deletions(-) diff --git a/app/src/main/java/com/duckduckgo/app/browser/SpecialUrlDetector.kt b/app/src/main/java/com/duckduckgo/app/browser/SpecialUrlDetector.kt index 56596289ec71..3eb00f793a94 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/SpecialUrlDetector.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/SpecialUrlDetector.kt @@ -34,7 +34,6 @@ import com.duckduckgo.privacy.config.api.TrackingParameters import com.duckduckgo.subscriptions.api.Subscriptions import java.net.URISyntaxException import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking import timber.log.Timber @@ -92,9 +91,7 @@ class SpecialUrlDetectorImpl( val uri = uriString.toUri() - val willNavigateToDuckPlayerDeferred = scope.async(dispatcherProvider.io()) { duckPlayer.willNavigateToDuckPlayer(uri) } - - val willNavigateToDuckPlayer = runBlocking(dispatcherProvider.io()) { willNavigateToDuckPlayerDeferred.await() } + val willNavigateToDuckPlayer = runBlocking(dispatcherProvider.io()) { duckPlayer.willNavigateToDuckPlayer(uri) } if (willNavigateToDuckPlayer) { return UrlType.ShouldLaunchDuckPlayerLink(url = uri) diff --git a/app/src/main/java/com/duckduckgo/app/browser/duckplayer/DuckPlayerJSHelper.kt b/app/src/main/java/com/duckduckgo/app/browser/duckplayer/DuckPlayerJSHelper.kt index 0200f3977ba7..397f248d9cef 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/duckplayer/DuckPlayerJSHelper.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/duckplayer/DuckPlayerJSHelper.kt @@ -98,7 +98,7 @@ class DuckPlayerJSHelper @Inject constructor( } } - private suspend fun getInitialSetup(featureName: String, method: String, id: String): JsCallbackData { + private fun getInitialSetup(featureName: String, method: String, id: String): JsCallbackData { val userValues = duckPlayer.getUserPreferences() val privatePlayerMode = if (duckPlayer.getDuckPlayerState() == ENABLED) userValues.privatePlayerMode else PrivatePlayerMode.Disabled diff --git a/duckplayer/duckplayer-api/src/main/java/com/duckduckgo/duckplayer/api/DuckPlayer.kt b/duckplayer/duckplayer-api/src/main/java/com/duckduckgo/duckplayer/api/DuckPlayer.kt index 26d64586cbd0..ec42974b27c6 100644 --- a/duckplayer/duckplayer-api/src/main/java/com/duckduckgo/duckplayer/api/DuckPlayer.kt +++ b/duckplayer/duckplayer-api/src/main/java/com/duckduckgo/duckplayer/api/DuckPlayer.kt @@ -50,7 +50,7 @@ interface DuckPlayer { * * @return The current state of the DuckPlayer as a DuckPlayerState enum. */ - suspend fun getDuckPlayerState(): DuckPlayerState + fun getDuckPlayerState(): DuckPlayerState /** * Sends a pixel with the given name and data. @@ -65,7 +65,7 @@ interface DuckPlayer { * * @return The user values. */ - suspend fun getUserPreferences(): UserPreferences + fun getUserPreferences(): UserPreferences /** * Checks if the DuckPlayer overlay should be hidden after navigating back from Duck Player @@ -105,7 +105,7 @@ interface DuckPlayer { * @param uri The YouTube no-cookie URI. * @return The DuckPlayer URI. */ - suspend fun createDuckPlayerUriFromYoutubeNoCookie(uri: Uri): String? + fun createDuckPlayerUriFromYoutubeNoCookie(uri: Uri): String? /** * Checks if a string is a DuckPlayer URI. @@ -120,7 +120,7 @@ interface DuckPlayer { * @param uri The DuckPlayer URI. * @return The YouTube URI. */ - suspend fun createYoutubeWatchUrlFromDuckPlayer(uri: Uri): String? + fun createYoutubeWatchUrlFromDuckPlayer(uri: Uri): String? /** * Checks if a URI is a simulated YouTube no-cookie URI. @@ -136,7 +136,7 @@ interface DuckPlayer { * @param uri The URI to check. * @return True if the URI is a YouTube no-cookie URI, false otherwise. */ - suspend fun isYoutubeWatchUrl(uri: Uri): Boolean + fun isYoutubeWatchUrl(uri: Uri): Boolean /** * Checks if a URI is a YouTube URL. @@ -184,7 +184,7 @@ interface DuckPlayer { destinationUrl: Uri, ): Boolean - suspend fun shouldOpenDuckPlayerInNewTab(): OpenDuckPlayerInNewTab + fun shouldOpenDuckPlayerInNewTab(): OpenDuckPlayerInNewTab fun observeShouldOpenInNewTab(): Flow diff --git a/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerDataStore.kt b/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerDataStore.kt index 2e57336f5e00..69f450fca0ac 100644 --- a/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerDataStore.kt +++ b/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerDataStore.kt @@ -22,6 +22,7 @@ import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringSetPreferencesKey +import com.duckduckgo.app.di.AppCoroutineScope import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.duckplayer.impl.SharedPreferencesDuckPlayerDataStore.Keys.DUCK_PLAYER_DISABLED_HELP_PAGE import com.duckduckgo.duckplayer.impl.SharedPreferencesDuckPlayerDataStore.Keys.DUCK_PLAYER_OPEN_IN_NEW_TAB @@ -32,71 +33,77 @@ import com.duckduckgo.duckplayer.impl.SharedPreferencesDuckPlayerDataStore.Keys. import com.duckduckgo.duckplayer.impl.SharedPreferencesDuckPlayerDataStore.Keys.OVERLAY_INTERACTED import com.duckduckgo.duckplayer.impl.SharedPreferencesDuckPlayerDataStore.Keys.PRIVATE_PLAYER_MODE import com.squareup.anvil.annotations.ContributesBinding +import dagger.SingleInstanceIn import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn interface DuckPlayerDataStore { - suspend fun getDuckPlayerRemoteConfigJson(): String + fun getDuckPlayerRemoteConfigJson(): String suspend fun setDuckPlayerRemoteConfigJson(value: String) - suspend fun getOverlayInteracted(): Boolean + fun getOverlayInteracted(): Boolean fun observeOverlayInteracted(): Flow suspend fun setOverlayInteracted(value: Boolean) - suspend fun getPrivatePlayerMode(): String + fun getPrivatePlayerMode(): String fun observePrivatePlayerMode(): Flow suspend fun setPrivatePlayerMode(value: String) - suspend fun getDuckPlayerDisabledHelpPageLink(): String? + fun getDuckPlayerDisabledHelpPageLink(): String? suspend fun storeDuckPlayerDisabledHelpPageLink(duckPlayerDisabledHelpPageLink: String?) - suspend fun getYouTubeWatchPath(): String + fun getYouTubeWatchPath(): String suspend fun storeYouTubeWatchPath(youtubePath: String) - suspend fun getYoutubeEmbedUrl(): String + fun getYoutubeEmbedUrl(): String suspend fun storeYoutubeEmbedUrl(embedUrl: String) - suspend fun getYouTubeUrl(): String + fun getYouTubeUrl(): String suspend fun storeYouTubeUrl(youtubeUrl: String) - suspend fun getYouTubeVideoIDQueryParam(): String + fun getYouTubeVideoIDQueryParam(): String suspend fun storeYouTubeVideoIDQueryParam(youtubeVideoIDQueryParams: String) - suspend fun getYouTubeReferrerQueryParams(): List + fun getYouTubeReferrerQueryParams(): List suspend fun storeYouTubeReferrerQueryParams(youtubeReferrerQueryParams: List) - suspend fun getYouTubeReferrerHeaders(): List + fun getYouTubeReferrerHeaders(): List suspend fun storeYouTubeReferrerHeaders(youtubeReferrerHeaders: List) suspend fun setUserOnboarded() - suspend fun getUserOnboarded(): Boolean + fun getUserOnboarded(): Boolean suspend fun setOpenInNewTab(enabled: Boolean) fun observeOpenInNewTab(): Flow - suspend fun getOpenInNewTab(): Boolean + fun getOpenInNewTab(): Boolean } @ContributesBinding(AppScope::class) +@SingleInstanceIn(AppScope::class) class SharedPreferencesDuckPlayerDataStore @Inject constructor( @DuckPlayer private val store: DataStore, + @AppCoroutineScope private val appCoroutineScope: CoroutineScope, ) : DuckPlayerDataStore { private object Keys { @@ -114,100 +121,100 @@ class SharedPreferencesDuckPlayerDataStore @Inject constructor( val DUCK_PLAYER_OPEN_IN_NEW_TAB = booleanPreferencesKey(name = "DUCK_PLAYER_OPEN_IN_NEW_TAB") } - private val overlayInteracted: Flow - get() = store.data - .map { prefs -> - prefs[OVERLAY_INTERACTED] ?: false - } - .distinctUntilChanged() - - private val duckPlayerRC: Flow - get() = store.data - .map { prefs -> - prefs[DUCK_PLAYER_RC] ?: "{}" - } - .distinctUntilChanged() - - private val privatePlayerMode: Flow - get() = store.data - .map { prefs -> - prefs[PRIVATE_PLAYER_MODE] ?: "ALWAYS_ASK" - } - .distinctUntilChanged() - - private val duckPlayerDisabledHelpPageLink: Flow - get() = store.data - .map { prefs -> - prefs[DUCK_PLAYER_DISABLED_HELP_PAGE] ?: "" - } - .distinctUntilChanged() - - private val youtubePath: Flow - get() = store.data - .map { prefs -> - prefs[DUCK_PLAYER_YOUTUBE_PATH] ?: "" - } - .distinctUntilChanged() - - private val youtubeReferrerHeaders: Flow> - get() = store.data - .map { prefs -> - prefs[DUCK_PLAYER_YOUTUBE_REFERRER_HEADERS]?.toList() ?: listOf() - } - .distinctUntilChanged() - - private val youtubeReferrerQueryParams: Flow> - get() = store.data - .map { prefs -> - prefs[Keys.DUCK_PLAYER_YOUTUBE_REFERRER_QUERY_PARAMS]?.toList() ?: listOf() - } - .distinctUntilChanged() - - private val youtubeUrl: Flow - get() = store.data - .map { prefs -> - prefs[Keys.DUCK_PLAYER_YOUTUBE_URL] ?: "" - } - .distinctUntilChanged() - - private val youtubeVideoIDQueryParams: Flow - get() = store.data - .map { prefs -> - prefs[Keys.DUCK_PLAYER_YOUTUBE_VIDEO_ID_QUERY_PARAMS] ?: "" - } - .distinctUntilChanged() - - private val youtubeEmbedUrl: Flow - get() = store.data - .map { prefs -> - prefs[Keys.DUCK_PLAYER_YOUTUBE_EMBED_URL] ?: "" - } - .distinctUntilChanged() - - private val duckPlayerUserOnboarded: Flow - get() = store.data - .map { prefs -> - prefs[Keys.DUCK_PLAYER_USER_ONBOARDED] ?: false - } - .distinctUntilChanged() - - private val duckPlayerOpenInNewTab: Flow - get() = store.data - .map { prefs -> - prefs[Keys.DUCK_PLAYER_OPEN_IN_NEW_TAB] ?: true - } - .distinctUntilChanged() - - override suspend fun getDuckPlayerRemoteConfigJson(): String { - return duckPlayerRC.first() + private val overlayInteracted: StateFlow = store.data + .map { prefs -> + prefs[OVERLAY_INTERACTED] ?: false + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, false) + + private val duckPlayerRC: StateFlow = store.data + .map { prefs -> + prefs[DUCK_PLAYER_RC] ?: "{}" + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, "{}") + + private val privatePlayerMode: StateFlow = store.data + .map { prefs -> + prefs[PRIVATE_PLAYER_MODE] ?: "ALWAYS_ASK" + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, "ALWAYS_ASK") + + private val duckPlayerDisabledHelpPageLink: StateFlow = store.data + .map { prefs -> + prefs[DUCK_PLAYER_DISABLED_HELP_PAGE] ?: "" + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, "") + + private val youtubePath: StateFlow = store.data + .map { prefs -> + prefs[DUCK_PLAYER_YOUTUBE_PATH] ?: "" + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, "") + + private val youtubeReferrerHeaders: StateFlow> = store.data + .map { prefs -> + prefs[DUCK_PLAYER_YOUTUBE_REFERRER_HEADERS]?.toList() ?: listOf() + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, listOf()) + + private val youtubeReferrerQueryParams: StateFlow> = store.data + .map { prefs -> + prefs[Keys.DUCK_PLAYER_YOUTUBE_REFERRER_QUERY_PARAMS]?.toList() ?: listOf() + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, listOf()) + + private val youtubeUrl: StateFlow = store.data + .map { prefs -> + prefs[Keys.DUCK_PLAYER_YOUTUBE_URL] ?: "" + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, "") + + private val youtubeVideoIDQueryParams: StateFlow = store.data + .map { prefs -> + prefs[Keys.DUCK_PLAYER_YOUTUBE_VIDEO_ID_QUERY_PARAMS] ?: "" + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, "") + + private val youtubeEmbedUrl: StateFlow = store.data + .map { prefs -> + prefs[Keys.DUCK_PLAYER_YOUTUBE_EMBED_URL] ?: "" + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, "") + + private val duckPlayerUserOnboarded: StateFlow = store.data + .map { prefs -> + prefs[Keys.DUCK_PLAYER_USER_ONBOARDED] ?: false + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, false) + + private val duckPlayerOpenInNewTab: StateFlow = store.data + .map { prefs -> + prefs[Keys.DUCK_PLAYER_OPEN_IN_NEW_TAB] ?: true + } + .distinctUntilChanged() + .stateIn(appCoroutineScope, SharingStarted.Eagerly, true) + + override fun getDuckPlayerRemoteConfigJson(): String { + return duckPlayerRC.value } override suspend fun setDuckPlayerRemoteConfigJson(value: String) { store.edit { prefs -> prefs[DUCK_PLAYER_RC] = value } } - override suspend fun getOverlayInteracted(): Boolean { - return overlayInteracted.first() + override fun getOverlayInteracted(): Boolean { + return overlayInteracted.value } override fun observeOverlayInteracted(): Flow { @@ -218,8 +225,8 @@ class SharedPreferencesDuckPlayerDataStore @Inject constructor( store.edit { prefs -> prefs[OVERLAY_INTERACTED] = value } } - override suspend fun getPrivatePlayerMode(): String { - return privatePlayerMode.first() + override fun getPrivatePlayerMode(): String { + return privatePlayerMode.value } override fun observePrivatePlayerMode(): Flow { @@ -234,8 +241,8 @@ class SharedPreferencesDuckPlayerDataStore @Inject constructor( store.edit { prefs -> prefs[DUCK_PLAYER_DISABLED_HELP_PAGE] = duckPlayerDisabledHelpPageLink ?: "" } } - override suspend fun getDuckPlayerDisabledHelpPageLink(): String? { - return duckPlayerDisabledHelpPageLink.first().let { it.ifBlank { null } } + override fun getDuckPlayerDisabledHelpPageLink(): String? { + return duckPlayerDisabledHelpPageLink.value.let { it.ifBlank { null } } } override suspend fun storeYouTubeWatchPath(youtubePath: String) { @@ -246,52 +253,52 @@ class SharedPreferencesDuckPlayerDataStore @Inject constructor( store.edit { prefs -> prefs[DUCK_PLAYER_YOUTUBE_REFERRER_HEADERS] = youtubeReferrerHeaders.toSet() } } - override suspend fun getYouTubeReferrerHeaders(): List { - return youtubeReferrerHeaders.first() + override fun getYouTubeReferrerHeaders(): List { + return youtubeReferrerHeaders.value } override suspend fun storeYouTubeReferrerQueryParams(youtubeReferrerQueryParams: List) { store.edit { prefs -> prefs[Keys.DUCK_PLAYER_YOUTUBE_REFERRER_QUERY_PARAMS] = youtubeReferrerQueryParams.toSet() } } - override suspend fun getYouTubeReferrerQueryParams(): List { - return youtubeReferrerQueryParams.first() + override fun getYouTubeReferrerQueryParams(): List { + return youtubeReferrerQueryParams.value } override suspend fun storeYouTubeUrl(youtubeUrl: String) { store.edit { prefs -> prefs[Keys.DUCK_PLAYER_YOUTUBE_URL] = youtubeUrl } } - override suspend fun getYouTubeUrl(): String { - return youtubeUrl.first() + override fun getYouTubeUrl(): String { + return youtubeUrl.value } override suspend fun storeYouTubeVideoIDQueryParam(youtubeVideoIDQueryParams: String) { store.edit { prefs -> prefs[Keys.DUCK_PLAYER_YOUTUBE_VIDEO_ID_QUERY_PARAMS] = youtubeVideoIDQueryParams } } - override suspend fun getYouTubeVideoIDQueryParam(): String { - return youtubeVideoIDQueryParams.first() + override fun getYouTubeVideoIDQueryParam(): String { + return youtubeVideoIDQueryParams.value } override suspend fun storeYoutubeEmbedUrl(embedUrl: String) { store.edit { prefs -> prefs[Keys.DUCK_PLAYER_YOUTUBE_EMBED_URL] = embedUrl } } - override suspend fun getYoutubeEmbedUrl(): String { - return youtubeEmbedUrl.first() + override fun getYoutubeEmbedUrl(): String { + return youtubeEmbedUrl.value } - override suspend fun getYouTubeWatchPath(): String { - return youtubePath.first() + override fun getYouTubeWatchPath(): String { + return youtubePath.value } override suspend fun setUserOnboarded() { store.edit { prefs -> prefs[DUCK_PLAYER_USER_ONBOARDED] = true } } - override suspend fun getUserOnboarded(): Boolean { - return duckPlayerUserOnboarded.first() + override fun getUserOnboarded(): Boolean { + return duckPlayerUserOnboarded.value } override suspend fun setOpenInNewTab(enabled: Boolean) { @@ -302,7 +309,7 @@ class SharedPreferencesDuckPlayerDataStore @Inject constructor( return duckPlayerOpenInNewTab } - override suspend fun getOpenInNewTab(): Boolean { - return duckPlayerOpenInNewTab.first() + override fun getOpenInNewTab(): Boolean { + return duckPlayerOpenInNewTab.value } } diff --git a/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerFeatureRepository.kt b/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerFeatureRepository.kt index d0093a6d5f0e..87780b32b9ad 100644 --- a/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerFeatureRepository.kt +++ b/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerFeatureRepository.kt @@ -17,7 +17,6 @@ package com.duckduckgo.duckplayer.impl import com.duckduckgo.app.di.AppCoroutineScope -import com.duckduckgo.app.di.IsMainProcess import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.duckplayer.api.DuckPlayer.UserPreferences @@ -38,7 +37,7 @@ interface DuckPlayerFeatureRepository { fun setDuckPlayerRemoteConfigJson(jsonString: String) - suspend fun getUserPreferences(): UserPreferences + fun getUserPreferences(): UserPreferences fun observeUserPreferences(): Flow @@ -46,7 +45,7 @@ interface DuckPlayerFeatureRepository { suspend fun storeDuckPlayerDisabledHelpPageLink(duckPlayerDisabledHelpPageLink: String?) - suspend fun getDuckPlayerDisabledHelpPageLink(): String? + fun getDuckPlayerDisabledHelpPageLink(): String? suspend fun storeYouTubePath(youtubePath: String) @@ -60,17 +59,17 @@ interface DuckPlayerFeatureRepository { suspend fun storeYouTubeVideoIDQueryParam(youtubeVideoIDQueryParam: String) - suspend fun getVideoIDQueryParam(): String - suspend fun getYouTubeReferrerQueryParams(): List - suspend fun getYouTubeReferrerHeaders(): List - suspend fun getYouTubeWatchPath(): String - suspend fun getYouTubeUrl(): String - suspend fun getYouTubeEmbedUrl(): String - suspend fun isOnboarded(): Boolean + fun getVideoIDQueryParam(): String + fun getYouTubeReferrerQueryParams(): List + fun getYouTubeReferrerHeaders(): List + fun getYouTubeWatchPath(): String + fun getYouTubeUrl(): String + fun getYouTubeEmbedUrl(): String + fun isOnboarded(): Boolean suspend fun setUserOnboarded() fun setOpenInNewTab(enabled: Boolean) fun observeOpenInNewTab(): Flow - suspend fun shouldOpenInNewTab(): Boolean + fun shouldOpenInNewTab(): Boolean } @SingleInstanceIn(AppScope::class) @@ -79,32 +78,15 @@ class RealDuckPlayerFeatureRepository @Inject constructor( private val duckPlayerDataStore: DuckPlayerDataStore, @AppCoroutineScope private val appCoroutineScope: CoroutineScope, private val dispatcherProvider: DispatcherProvider, - @IsMainProcess isMainProcess: Boolean, ) : DuckPlayerFeatureRepository { - private var duckPlayerRC = "{}" - - init { - if (isMainProcess) { - loadToMemory() - } - } - - private fun loadToMemory() { - appCoroutineScope.launch(dispatcherProvider.io()) { - duckPlayerRC = - duckPlayerDataStore.getDuckPlayerRemoteConfigJson() - } - } - override fun getDuckPlayerRemoteConfigJson(): String { - return duckPlayerRC + return duckPlayerDataStore.getDuckPlayerRemoteConfigJson() } override fun setDuckPlayerRemoteConfigJson(jsonString: String) { appCoroutineScope.launch(dispatcherProvider.io()) { duckPlayerDataStore.setDuckPlayerRemoteConfigJson(jsonString) - loadToMemory() } } @@ -131,7 +113,7 @@ class RealDuckPlayerFeatureRepository @Inject constructor( } } - override suspend fun getUserPreferences(): UserPreferences { + override fun getUserPreferences(): UserPreferences { return UserPreferences( overlayInteracted = duckPlayerDataStore.getOverlayInteracted(), privatePlayerMode = when (duckPlayerDataStore.getPrivatePlayerMode()) { @@ -146,7 +128,7 @@ class RealDuckPlayerFeatureRepository @Inject constructor( duckPlayerDataStore.storeDuckPlayerDisabledHelpPageLink(duckPlayerDisabledHelpPageLink) } - override suspend fun getDuckPlayerDisabledHelpPageLink(): String? { + override fun getDuckPlayerDisabledHelpPageLink(): String? { return duckPlayerDataStore.getDuckPlayerDisabledHelpPageLink() } @@ -174,31 +156,31 @@ class RealDuckPlayerFeatureRepository @Inject constructor( duckPlayerDataStore.storeYoutubeEmbedUrl(embedUrl) } - override suspend fun getVideoIDQueryParam(): String { + override fun getVideoIDQueryParam(): String { return duckPlayerDataStore.getYouTubeVideoIDQueryParam() } - override suspend fun getYouTubeReferrerQueryParams(): List { + override fun getYouTubeReferrerQueryParams(): List { return duckPlayerDataStore.getYouTubeReferrerQueryParams() } - override suspend fun getYouTubeReferrerHeaders(): List { + override fun getYouTubeReferrerHeaders(): List { return duckPlayerDataStore.getYouTubeReferrerHeaders() } - override suspend fun getYouTubeWatchPath(): String { + override fun getYouTubeWatchPath(): String { return duckPlayerDataStore.getYouTubeWatchPath() } - override suspend fun getYouTubeUrl(): String { + override fun getYouTubeUrl(): String { return duckPlayerDataStore.getYouTubeUrl() } - override suspend fun getYouTubeEmbedUrl(): String { + override fun getYouTubeEmbedUrl(): String { return duckPlayerDataStore.getYoutubeEmbedUrl() } - override suspend fun isOnboarded(): Boolean { + override fun isOnboarded(): Boolean { return duckPlayerDataStore.getUserOnboarded() } @@ -216,7 +198,7 @@ class RealDuckPlayerFeatureRepository @Inject constructor( return duckPlayerDataStore.observeOpenInNewTab() } - override suspend fun shouldOpenInNewTab(): Boolean { + override fun shouldOpenInNewTab(): Boolean { return duckPlayerDataStore.getOpenInNewTab() } } diff --git a/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerSettingsViewModel.kt b/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerSettingsViewModel.kt index daa0d197ebfc..8cb694834dbb 100644 --- a/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerSettingsViewModel.kt +++ b/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerSettingsViewModel.kt @@ -42,7 +42,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking @ContributesViewModel(ActivityScope::class) class DuckPlayerSettingsViewModel @Inject constructor( @@ -65,7 +64,7 @@ class DuckPlayerSettingsViewModel @Inject constructor( }.stateIn( viewModelScope, started = SharingStarted.WhileSubscribed(), - initialValue = runBlocking { Enabled(duckPlayer.getUserPreferences().privatePlayerMode, duckPlayer.shouldOpenDuckPlayerInNewTab()) }, + initialValue = Enabled(duckPlayer.getUserPreferences().privatePlayerMode, duckPlayer.shouldOpenDuckPlayerInNewTab()), ) sealed class Command { diff --git a/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/RealDuckPlayer.kt b/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/RealDuckPlayer.kt index 9f892ee93a65..8f3ee33d7e9a 100644 --- a/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/RealDuckPlayer.kt +++ b/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/RealDuckPlayer.kt @@ -18,6 +18,8 @@ package com.duckduckgo.duckplayer.impl import android.content.res.Configuration import android.net.Uri +import android.os.SystemClock +import android.util.Log import android.webkit.MimeTypeMap import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse @@ -112,16 +114,20 @@ class RealDuckPlayer @Inject constructor( private lateinit var duckPlayerDisabledHelpLink: String - override suspend fun getDuckPlayerState(): DuckPlayerState { + override fun getDuckPlayerState(): DuckPlayerState { if (!::duckPlayerDisabledHelpLink.isInitialized) { duckPlayerDisabledHelpLink = duckPlayerFeatureRepository.getDuckPlayerDisabledHelpPageLink() ?: "" } return if (isFeatureEnabled) { ENABLED - } else if (duckPlayerDisabledHelpLink.isNotBlank()) { - DISABLED_WIH_HELP_LINK } else { - DISABLED + if (duckPlayerDisabledHelpLink.isNotBlank()) { + DISABLED_WIH_HELP_LINK + } else { + DISABLED + }.also { + Log.v("Cris", "getDuckPlayerState: $it") + } } } @@ -137,10 +143,8 @@ class RealDuckPlayer @Inject constructor( duckPlayerFeatureRepository.setUserPreferences(UserPreferences(overlayInteracted, playerMode)) } - override suspend fun getUserPreferences(): UserPreferences { - return duckPlayerFeatureRepository.getUserPreferences().let { - UserPreferences(it.overlayInteracted, it.privatePlayerMode) - } + override fun getUserPreferences(): UserPreferences { + return duckPlayerFeatureRepository.getUserPreferences() } override fun shouldHideDuckPlayerOverlay(): Boolean { @@ -151,7 +155,7 @@ class RealDuckPlayer @Inject constructor( shouldHideOverlay = false } - private suspend fun shouldNavigateToDuckPlayer(): Boolean { + private fun shouldNavigateToDuckPlayer(): Boolean { if (!isFeatureEnabled) return false val result = getUserPreferences().privatePlayerMode == Enabled && !shouldForceYTNavigation return result @@ -196,7 +200,7 @@ class RealDuckPlayer @Inject constructor( } } - private suspend fun createYoutubeNoCookieFromDuckPlayer(uri: Uri): String? { + private fun createYoutubeNoCookieFromDuckPlayer(uri: Uri): String? { if (!isFeatureEnabled) return null val embedUrl = duckPlayerFeatureRepository.getYouTubeEmbedUrl() uri.pathSegments?.firstOrNull()?.let { videoID -> @@ -205,7 +209,7 @@ class RealDuckPlayer @Inject constructor( return null } - override suspend fun createYoutubeWatchUrlFromDuckPlayer(uri: Uri): String? { + override fun createYoutubeWatchUrlFromDuckPlayer(uri: Uri): String? { val videoIdQueryParam = duckPlayerFeatureRepository.getVideoIDQueryParam() val youTubeWatchPath = duckPlayerFeatureRepository.getYouTubeWatchPath() val youTubeHost = duckPlayerFeatureRepository.getYouTubeUrl() @@ -217,7 +221,7 @@ class RealDuckPlayer @Inject constructor( return null } - private suspend fun youTubeRequestedFromDuckPlayer() { + private fun youTubeRequestedFromDuckPlayer() { shouldForceYTNavigation = true if (getUserPreferences().privatePlayerMode == AlwaysAsk) { shouldHideOverlay = true @@ -263,9 +267,9 @@ class RealDuckPlayer @Inject constructor( return url.path?.takeIf { it.isNotBlank() }?.removePrefix("/")?.let { "$DUCK_PLAYER_ASSETS_PATH$it" } } - override suspend fun isYoutubeWatchUrl(uri: Uri): Boolean { + override fun isYoutubeWatchUrl(uri: Uri): Boolean { val youTubeWatchPath = duckPlayerFeatureRepository.getYouTubeWatchPath() - return isYouTubeUrl(uri) && uri.pathSegments.firstOrNull() == youTubeWatchPath + return (isYouTubeUrl(uri) && uri.pathSegments.firstOrNull() == youTubeWatchPath) } override fun isYouTubeUrl(uri: Uri): Boolean { @@ -273,14 +277,14 @@ class RealDuckPlayer @Inject constructor( return host == YOUTUBE_HOST || host == YOUTUBE_MOBILE_HOST } - override suspend fun createDuckPlayerUriFromYoutubeNoCookie(uri: Uri): String? { + override fun createDuckPlayerUriFromYoutubeNoCookie(uri: Uri): String? { if (!isFeatureEnabled) return null return uri.getQueryParameter(DUCK_PLAYER_VIDEO_ID_QUERY_PARAM)?.let { "$DUCK_PLAYER_URL_BASE$it" } } - private suspend fun createDuckPlayerUriFromYoutube(uri: Uri): String { + private fun createDuckPlayerUriFromYoutube(uri: Uri): String { val videoIdQueryParam = duckPlayerFeatureRepository.getVideoIDQueryParam() val origin = uri.getQueryParameter(ORIGIN_QUERY_PARAM)?.let { it } ?: ORIGIN_QUERY_PARAM_AUTO return "$DUCK_PLAYER_URL_BASE${uri.getQueryParameter(videoIdQueryParam)}?$ORIGIN_QUERY_PARAM=$origin" @@ -303,7 +307,8 @@ class RealDuckPlayer @Inject constructor( } return null } - private suspend fun processSimulatedYouTubeNoCookieUri( + + private fun processSimulatedYouTubeNoCookieUri( url: Uri, webView: WebView, ): WebResourceResponse { @@ -371,6 +376,7 @@ class RealDuckPlayer @Inject constructor( } private suspend fun doesYoutubeUrlComeFromDuckPlayer(url: Uri, request: WebResourceRequest? = null): Boolean { + val start = SystemClock.elapsedRealtime() val videoIdQueryParam = duckPlayerFeatureRepository.getVideoIDQueryParam() val requestedVideoId = url.getQueryParameter(videoIdQueryParam) val isSimulated: suspend (String?) -> Boolean = { uri -> @@ -390,7 +396,7 @@ class RealDuckPlayer @Inject constructor( .firstOrNull { url.getQueryParameter(it) != null } ?.let { url.getQueryParameter(it) } - return isSimulated(previousUrl) && isMatchingVideoId(previousUrl) + return (isSimulated(previousUrl) && isMatchingVideoId(previousUrl)) } private suspend fun processDuckPlayerUri( @@ -449,7 +455,7 @@ class RealDuckPlayer @Inject constructor( ) } - override suspend fun shouldOpenDuckPlayerInNewTab(): OpenDuckPlayerInNewTab { + override fun shouldOpenDuckPlayerInNewTab(): OpenDuckPlayerInNewTab { if (!duckPlayerFeature.openInNewTab().isEnabled()) return Unavailable return if (duckPlayerFeatureRepository.shouldOpenInNewTab()) On else Off }