diff --git a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt index 2e8f9e7e9aec..de5cc044b91b 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt @@ -158,6 +158,7 @@ import com.duckduckgo.app.trackerdetection.model.TrackerType import com.duckduckgo.app.trackerdetection.model.TrackingEvent import com.duckduckgo.app.usage.search.SearchCountDao import com.duckduckgo.app.widget.ui.WidgetCapabilities +import com.duckduckgo.appbuildconfig.api.AppBuildConfig import com.duckduckgo.autofill.api.AutofillCapabilityChecker import com.duckduckgo.autofill.api.domain.app.LoginCredentials import com.duckduckgo.autofill.api.email.EmailManager @@ -396,6 +397,9 @@ class BrowserTabViewModelTest { @Mock private lateinit var mockDuckPlayer: DuckPlayer + @Mock + private lateinit var mockAppBuildConfig: AppBuildConfig + private lateinit var remoteMessagingModel: RemoteMessagingModel private val lazyFaviconManager = Lazy { mockFaviconManager } @@ -559,6 +563,7 @@ class BrowserTabViewModelTest { whenever(fireproofDialogsEventHandler.event).thenReturn(fireproofDialogsEventHandlerLiveData) whenever(cameraHardwareChecker.hasCameraHardware()).thenReturn(true) whenever(mockPrivacyProtectionsPopupManager.viewState).thenReturn(flowOf(PrivacyProtectionsPopupViewState.Gone)) + whenever(mockAppBuildConfig.buildType).thenReturn("debug") testee = BrowserTabViewModel( statisticsUpdater = mockStatisticsUpdater, @@ -624,7 +629,7 @@ class BrowserTabViewModelTest { history = mockNavigationHistory, commandActionMapper = commandActionMapper, duckPlayer = mockDuckPlayer, - duckPlayerJSHelper = DuckPlayerJSHelper(mockDuckPlayer), + duckPlayerJSHelper = DuckPlayerJSHelper(mockDuckPlayer, mockAppBuildConfig), ) testee.loadData("abc", null, false) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index 207b52c43fdb..ffc597a86483 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -250,6 +250,7 @@ import com.duckduckgo.downloads.api.DownloadConfirmationDialogListener import com.duckduckgo.downloads.api.DownloadsFileActions import com.duckduckgo.downloads.api.FileDownloader import com.duckduckgo.downloads.api.FileDownloader.PendingFileDownload +import com.duckduckgo.duckplayer.api.DuckPlayerSettingsNoParams import com.duckduckgo.js.messaging.api.JsCallbackData import com.duckduckgo.js.messaging.api.JsMessageCallback import com.duckduckgo.js.messaging.api.JsMessaging @@ -1543,6 +1544,7 @@ class BrowserTabFragment : is Command.HideSSLError -> hideSSLWarning() is Command.LaunchScreen -> launchScreen(it.screen, it.payload) is Command.HideOnboardingDaxDialog -> hideOnboardingDaxDialog(it.onboardingCta) + is Command.OpenDuckPlayerSettings -> globalActivityStarter.start(binding.root.context, DuckPlayerSettingsNoParams) else -> { // NO OP } diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt index e498a253a34d..ba672a3a559c 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt @@ -74,6 +74,8 @@ import com.duckduckgo.app.browser.commands.Command import com.duckduckgo.app.browser.commands.Command.* import com.duckduckgo.app.browser.commands.NavigationCommand import com.duckduckgo.app.browser.customtabs.CustomTabPixelNames +import com.duckduckgo.app.browser.duckplayer.DUCK_PLAYER_FEATURE_NAME +import com.duckduckgo.app.browser.duckplayer.DUCK_PLAYER_PAGE_FEATURE_NAME import com.duckduckgo.app.browser.duckplayer.DuckPlayerJSHelper import com.duckduckgo.app.browser.favicon.FaviconManager import com.duckduckgo.app.browser.favicon.FaviconSource.ImageFavicon @@ -3055,7 +3057,7 @@ class BrowserTabViewModel @Inject constructor( } when (featureName) { - "duckPlayer", "duckPlayerPage" -> { + DUCK_PLAYER_FEATURE_NAME, DUCK_PLAYER_PAGE_FEATURE_NAME -> { viewModelScope.launch { val response = duckPlayerJSHelper.processJsCallbackMessage(featureName, method, id, data) withContext(dispatchers.main()) { diff --git a/app/src/main/java/com/duckduckgo/app/browser/commands/Command.kt b/app/src/main/java/com/duckduckgo/app/browser/commands/Command.kt index 11f78ea1c554..7947653a7155 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/commands/Command.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/commands/Command.kt @@ -232,4 +232,6 @@ sealed class Command { val payload: String, ) : Command() data class HideOnboardingDaxDialog(val onboardingCta: OnboardingDaxDialogCta) : Command() + object OpenDuckPlayerSettings : Command() + object OpenDuckPlayerInfo : Command() } 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 5bf701bcb2f1..204ba1774730 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 @@ -17,16 +17,25 @@ package com.duckduckgo.app.browser.duckplayer import com.duckduckgo.app.browser.commands.Command +import com.duckduckgo.app.browser.commands.Command.OpenDuckPlayerSettings import com.duckduckgo.app.browser.commands.Command.SendResponseToDuckPlayer import com.duckduckgo.app.browser.commands.Command.SendResponseToJs import com.duckduckgo.app.browser.commands.NavigationCommand.Navigate +import com.duckduckgo.appbuildconfig.api.AppBuildConfig import com.duckduckgo.duckplayer.api.DuckPlayer import com.duckduckgo.js.messaging.api.JsCallbackData import javax.inject.Inject import org.json.JSONObject +import timber.log.Timber + +const val DUCK_PLAYER_PAGE_FEATURE_NAME = "duckPlayerPage" +const val DUCK_PLAYER_FEATURE_NAME = "duckPlayer" +private const val OVERLAY_INTERACTED = "overlayInteracted" +private const val PRIVATE_PLAYER_MODE = "privatePlayerMode" class DuckPlayerJSHelper @Inject constructor( private val duckPlayer: DuckPlayer, + private val appBuildConfig: AppBuildConfig, ) { private suspend fun getUserPreferences(featureName: String, method: String, id: String): JsCallbackData { val userValues = duckPlayer.getUserPreferences() @@ -35,8 +44,8 @@ class DuckPlayerJSHelper @Inject constructor( JSONObject( """ { - "overlayInteracted": ${userValues.overlayInteracted}, - "privatePlayerMode": { + $OVERLAY_INTERACTED: ${userValues.overlayInteracted}, + $PRIVATE_PLAYER_MODE: { "${userValues.privatePlayerMode.value}": {} } } @@ -51,9 +60,8 @@ class DuckPlayerJSHelper @Inject constructor( private suspend fun getInitialSetup(featureName: String, method: String, id: String): JsCallbackData { val userValues = duckPlayer.getUserPreferences() - return JsCallbackData( - JSONObject( - """ + val jsonObject = JSONObject( + """ { "settings": { "pip": { @@ -61,14 +69,23 @@ class DuckPlayerJSHelper @Inject constructor( } }, "userValues": { - "overlayInteracted": ${userValues.overlayInteracted}, - "privatePlayerMode": { + $OVERLAY_INTERACTED: ${userValues.overlayInteracted}, + $PRIVATE_PLAYER_MODE: { "${userValues.privatePlayerMode.value}": {} } } } """, - ), + ) + + if (featureName == DUCK_PLAYER_PAGE_FEATURE_NAME) { + jsonObject.put("platform", JSONObject("""{ name: "android" }""")) + jsonObject.put("locale", java.util.Locale.getDefault().language) + jsonObject.put("env", if (appBuildConfig.isDebug) "development" else "production") + } + + return JsCallbackData( + jsonObject, featureName, method, id, @@ -76,8 +93,8 @@ class DuckPlayerJSHelper @Inject constructor( } private fun setUserPreferences(data: JSONObject) { - val overlayInteracted = data.getBoolean("overlayInteracted") - val privatePlayerModeObject = data.getJSONObject("privatePlayerMode") + val overlayInteracted = data.getBoolean(OVERLAY_INTERACTED) + val privatePlayerModeObject = data.getJSONObject(PRIVATE_PLAYER_MODE) duckPlayer.setUserPreferences(overlayInteracted, privatePlayerModeObject.keys().next()) } @@ -102,7 +119,16 @@ class DuckPlayerJSHelper @Inject constructor( "setUserValues" -> if (id != null && data != null) { setUserPreferences(data) - return SendResponseToJs(getUserPreferences(featureName, method, id)) + return when (featureName) { + DUCK_PLAYER_FEATURE_NAME -> { + SendResponseToJs(getUserPreferences(featureName, method, id)) + } + DUCK_PLAYER_PAGE_FEATURE_NAME -> { + SendResponseToDuckPlayer(getUserPreferences(featureName, method, id)) + } else -> { + null + } + } } "sendDuckPlayerPixel" -> if (data != null) { @@ -115,7 +141,23 @@ class DuckPlayerJSHelper @Inject constructor( } } "initialSetup" -> { - return SendResponseToDuckPlayer(getInitialSetup(featureName, method, id ?: "")) + return when (featureName) { + DUCK_PLAYER_FEATURE_NAME -> { + SendResponseToJs(getInitialSetup(featureName, method, id ?: "")) + } + DUCK_PLAYER_PAGE_FEATURE_NAME -> { + SendResponseToDuckPlayer(getInitialSetup(featureName, method, id ?: "")) + } + else -> { + null + } + } + } + "reportPageException", "reportInitException" -> { + Timber.tag(method).d(data.toString()) + } + "openSettings" -> { + return OpenDuckPlayerSettings } else -> { return null diff --git a/content-scope-scripts/content-scope-scripts-impl/src/main/java/com/duckduckgo/contentscopescripts/impl/messaging/ContentScopeScriptsJsMessaging.kt b/content-scope-scripts/content-scope-scripts-impl/src/main/java/com/duckduckgo/contentscopescripts/impl/messaging/ContentScopeScriptsJsMessaging.kt index f4aaa4a2ef05..677bc66c9c8a 100644 --- a/content-scope-scripts/content-scope-scripts-impl/src/main/java/com/duckduckgo/contentscopescripts/impl/messaging/ContentScopeScriptsJsMessaging.kt +++ b/content-scope-scripts/content-scope-scripts-impl/src/main/java/com/duckduckgo/contentscopescripts/impl/messaging/ContentScopeScriptsJsMessaging.kt @@ -128,6 +128,14 @@ class ContentScopeScriptsJsMessaging @Inject constructor( override val allowedDomains: List = emptyList() override val featureName: String = "duckPlayer" - override val methods: List = listOf("getUserValues", "sendDuckPlayerPixel", "setUserValues", "openDuckPlayer") + override val methods: List = listOf( + "getUserValues", + "sendDuckPlayerPixel", + "setUserValues", + "openDuckPlayer", + "initialSetup", + "reportPageException", + "reportInitException", + ) } } diff --git a/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerScriptsJsMessaging.kt b/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerScriptsJsMessaging.kt index 20a89ec56fb4..157d19fd7f2e 100644 --- a/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerScriptsJsMessaging.kt +++ b/duckplayer/duckplayer-impl/src/main/java/com/duckduckgo/duckplayer/impl/DuckPlayerScriptsJsMessaging.kt @@ -116,12 +116,18 @@ class DuckPlayerScriptsJsMessaging @Inject constructor( inner class DuckPlayerPageHandler : JsMessageHandler { override fun process(jsMessage: JsMessage, secret: String, jsMessageCallback: JsMessageCallback?) { - if (jsMessage.id == null) return jsMessageCallback?.process(featureName, jsMessage.method, jsMessage.id ?: "", jsMessage.params) } override val allowedDomains: List = emptyList() override val featureName: String = "duckPlayerPage" - override val methods: List = listOf("initialSetup") + override val methods: List = listOf( + "initialSetup", + "openSettings", + "openInfo", + "setUserValues", + "reportPageException", + "reportInitException", + ) } }