Skip to content

Commit 3ff2482

Browse files
committed
Add OpenNativeSettingsHandler to process SERP openNativeSettings messages
1 parent fe3469b commit 3ff2482

File tree

3 files changed

+146
-0
lines changed

3 files changed

+146
-0
lines changed

settings/settings-impl/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ dependencies {
4848
implementation project(':js-messaging-api')
4949
implementation project(':statistics-api')
5050
implementation project(':content-scope-scripts-api')
51+
implementation project(':duckchat-api')
5152

5253
implementation "com.squareup.logcat:logcat:_"
5354

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2025 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.settings.impl.serpsettings.messaging
18+
19+
import android.content.Context
20+
import android.content.Intent
21+
import com.duckduckgo.app.di.AppCoroutineScope
22+
import com.duckduckgo.common.utils.AppUrl
23+
import com.duckduckgo.common.utils.DispatcherProvider
24+
import com.duckduckgo.contentscopescripts.api.ContentScopeJsMessageHandlersPlugin
25+
import com.duckduckgo.di.scopes.AppScope
26+
import com.duckduckgo.duckchat.api.DuckChatSettingsNoParams
27+
import com.duckduckgo.js.messaging.api.JsMessage
28+
import com.duckduckgo.js.messaging.api.JsMessageCallback
29+
import com.duckduckgo.js.messaging.api.JsMessageHandler
30+
import com.duckduckgo.js.messaging.api.JsMessaging
31+
import com.duckduckgo.navigation.api.GlobalActivityStarter
32+
import com.duckduckgo.settings.api.SettingsPageFeature
33+
import com.squareup.anvil.annotations.ContributesMultibinding
34+
import kotlinx.coroutines.CoroutineScope
35+
import kotlinx.coroutines.launch
36+
import logcat.LogPriority.WARN
37+
import logcat.logcat
38+
import javax.inject.Inject
39+
40+
/**
41+
* Handles the openNativeSettings message from SERP to open native settings screens.
42+
*/
43+
@ContributesMultibinding(AppScope::class)
44+
class OpenNativeSettingsHandler @Inject constructor(
45+
private val dispatcherProvider: DispatcherProvider,
46+
@AppCoroutineScope private val appScope: CoroutineScope,
47+
private val context: Context,
48+
private val globalActivityStarter: GlobalActivityStarter,
49+
private val settingsPageFeature: SettingsPageFeature,
50+
) : ContentScopeJsMessageHandlersPlugin {
51+
52+
override fun getJsMessageHandler(): JsMessageHandler =
53+
object : JsMessageHandler {
54+
override fun process(
55+
jsMessage: JsMessage,
56+
jsMessaging: JsMessaging,
57+
jsMessageCallback: JsMessageCallback?,
58+
) {
59+
appScope.launch(dispatcherProvider.io()) {
60+
if (settingsPageFeature.serpSettingsSync().isEnabled()) {
61+
logcat { "SERP-SETTINGS: OpenNativeSettingsHandler processing message" }
62+
val params = jsMessage.params
63+
64+
when (val screenParam = params.optString("screen", "")) {
65+
AI_FEATURES_SCREEN_NAME -> {
66+
val intent = globalActivityStarter.startIntent(context, DuckChatSettingsNoParams)
67+
intent?.flags = Intent.FLAG_ACTIVITY_NEW_TASK
68+
context.startActivity(intent)
69+
}
70+
else -> {
71+
logcat(WARN) { "No action for given screen param: $screenParam" }
72+
}
73+
}
74+
}
75+
}
76+
}
77+
78+
override val allowedDomains: List<String> = listOf(AppUrl.Url.HOST)
79+
override val featureName: String = "serpSettings"
80+
override val methods: List<String> = listOf("openNativeSettings")
81+
}
82+
83+
companion object {
84+
private const val AI_FEATURES_SCREEN_NAME = "aiFeatures"
85+
}
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2025 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.settings.impl.serpsettings.messaging
18+
19+
import android.content.Context
20+
import com.duckduckgo.common.test.CoroutineTestRule
21+
import com.duckduckgo.navigation.api.GlobalActivityStarter
22+
import com.duckduckgo.settings.api.SettingsPageFeature
23+
import org.junit.Assert.*
24+
import org.junit.Rule
25+
import org.junit.Test
26+
import org.mockito.kotlin.mock
27+
28+
class OpenNativeSettingsHandlerTest {
29+
30+
@get:Rule
31+
val coroutineTestRule: CoroutineTestRule = CoroutineTestRule()
32+
33+
private val handler = OpenNativeSettingsHandler(
34+
dispatcherProvider = coroutineTestRule.testDispatcherProvider,
35+
appScope = coroutineTestRule.testScope,
36+
context = mock<Context>(),
37+
globalActivityStarter = mock<GlobalActivityStarter>(),
38+
settingsPageFeature = mock<SettingsPageFeature>(),
39+
).getJsMessageHandler()
40+
41+
@Test
42+
fun `only allow duckduckgo dot com domains`() {
43+
val domains = handler.allowedDomains
44+
assertEquals(1, domains.size)
45+
assertEquals("duckduckgo.com", domains.first())
46+
}
47+
48+
@Test
49+
fun `feature name is serpSettings`() {
50+
assertEquals("serpSettings", handler.featureName)
51+
}
52+
53+
@Test
54+
fun `only contains openNativeSettings method`() {
55+
val methods = handler.methods
56+
assertEquals(1, methods.size)
57+
assertEquals("openNativeSettings", methods[0])
58+
}
59+
}

0 commit comments

Comments
 (0)