Skip to content

Commit

Permalink
JS messaging support
Browse files Browse the repository at this point in the history
  • Loading branch information
CrisBarreiro committed Aug 8, 2024
1 parent 4330616 commit 1f34224
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,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
Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -558,6 +562,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,
Expand Down Expand Up @@ -621,7 +626,7 @@ class BrowserTabViewModelTest {
history = mockNavigationHistory,
newTabPixels = { mockNewTabPixels },
duckPlayer = mockDuckPlayer,
duckPlayerJSHelper = DuckPlayerJSHelper(mockDuckPlayer),
duckPlayerJSHelper = DuckPlayerJSHelper(mockDuckPlayer, mockAppBuildConfig),
)

testee.loadData("abc", null, false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -3056,7 +3058,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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,6 @@ sealed class Command {
val payload: String,
) : Command()
data class HideOnboardingDaxDialog(val onboardingCta: OnboardingDaxDialogCta) : Command()
object OpenDuckPlayerSettings : Command()
object OpenDuckPlayerInfo : Command()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -35,8 +44,8 @@ class DuckPlayerJSHelper @Inject constructor(
JSONObject(
"""
{
"overlayInteracted": ${userValues.overlayInteracted},
"privatePlayerMode": {
$OVERLAY_INTERACTED: ${userValues.overlayInteracted},
$PRIVATE_PLAYER_MODE: {
"${userValues.privatePlayerMode.value}": {}
}
}
Expand All @@ -51,33 +60,41 @@ 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": {
"state": "enabled"
}
},
"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,
)
}

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())
}

Expand All @@ -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) {
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ class ContentScopeScriptsJsMessaging @Inject constructor(

override val allowedDomains: List<String> = emptyList()
override val featureName: String = "duckPlayer"
override val methods: List<String> = listOf("getUserValues", "sendDuckPlayerPixel", "setUserValues", "openDuckPlayer")
override val methods: List<String> = listOf(
"getUserValues",
"sendDuckPlayerPixel",
"setUserValues",
"openDuckPlayer",
"initialSetup",
"reportPageException",
"reportInitException",
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> = emptyList()
override val featureName: String = "duckPlayerPage"
override val methods: List<String> = listOf("initialSetup")
override val methods: List<String> = listOf(
"initialSetup",
"openSettings",
"openInfo",
"setUserValues",
"reportPageException",
"reportInitException",
)
}
}

0 comments on commit 1f34224

Please sign in to comment.