Skip to content

Commit d80b639

Browse files
committed
SERP Settings Sync: Add subscription event data handling in SettingsWebViewActivity and ViewModel
Same as BrowserTabViewModel and Activity
1 parent e83684f commit d80b639

File tree

3 files changed

+157
-2
lines changed

3 files changed

+157
-2
lines changed

settings/settings-impl/src/main/java/com/duckduckgo/settings/impl/SettingsWebViewActivity.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import com.duckduckgo.settings.impl.databinding.ActivitySettingsWebviewBinding
3939
import kotlinx.coroutines.flow.launchIn
4040
import kotlinx.coroutines.flow.onEach
4141
import kotlinx.coroutines.launch
42+
import logcat.logcat
4243
import org.json.JSONObject
4344
import javax.inject.Inject
4445
import javax.inject.Named
@@ -84,6 +85,13 @@ class SettingsWebViewActivity : DuckDuckGoActivity() {
8485

8586
viewModel.onStart(url)
8687
}
88+
89+
observeSubscriptionEventDataChannel()
90+
}
91+
92+
override fun onResume() {
93+
super.onResume()
94+
viewModel.onResume()
8795
}
8896

8997
private fun setupBackPressedDispatcher() {
@@ -115,6 +123,13 @@ class SettingsWebViewActivity : DuckDuckGoActivity() {
115123
}
116124
}
117125

126+
private fun observeSubscriptionEventDataChannel() {
127+
viewModel.subscriptionEventDataFlow.onEach { subscriptionEventData ->
128+
logcat { "SERP-Settings: Sending subscription event data to content scope scripts: $subscriptionEventData" }
129+
contentScopeScripts.sendSubscriptionEvent(subscriptionEventData)
130+
}.launchIn(lifecycleScope)
131+
}
132+
118133
private fun exit() {
119134
binding.settingsWebView.stopLoading()
120135
binding.root.removeView(binding.settingsWebView)

settings/settings-impl/src/main/java/com/duckduckgo/settings/impl/SettingsWebViewViewModel.kt

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,32 @@ package com.duckduckgo.settings.impl
1919
import androidx.lifecycle.ViewModel
2020
import androidx.lifecycle.viewModelScope
2121
import com.duckduckgo.anvil.annotations.ContributesViewModel
22+
import com.duckduckgo.common.utils.plugins.PluginPoint
23+
import com.duckduckgo.contentscopescripts.api.ContentScopeScriptsSubscriptionEventPlugin
2224
import com.duckduckgo.di.scopes.ActivityScope
25+
import com.duckduckgo.js.messaging.api.SubscriptionEventData
26+
import com.duckduckgo.settings.api.SettingsPageFeature
2327
import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST
2428
import kotlinx.coroutines.channels.Channel
29+
import kotlinx.coroutines.flow.Flow
2530
import kotlinx.coroutines.flow.receiveAsFlow
2631
import kotlinx.coroutines.launch
2732
import logcat.LogPriority
2833
import logcat.logcat
2934
import javax.inject.Inject
3035

3136
@ContributesViewModel(ActivityScope::class)
32-
class SettingsWebViewViewModel @Inject constructor() : ViewModel() {
37+
class SettingsWebViewViewModel @Inject constructor(
38+
private val contentScopeScriptsSubscriptionEventPluginPoint: PluginPoint<ContentScopeScriptsSubscriptionEventPlugin>,
39+
private val settingsPageFeature: SettingsPageFeature,
40+
) : ViewModel() {
41+
3342
private val commandChannel = Channel<Command>(capacity = 1, onBufferOverflow = DROP_OLDEST)
3443
val commands = commandChannel.receiveAsFlow()
3544

45+
private val _subscriptionEventDataChannel = Channel<SubscriptionEventData>(capacity = Channel.BUFFERED)
46+
val subscriptionEventDataFlow: Flow<SubscriptionEventData> = _subscriptionEventDataChannel.receiveAsFlow()
47+
3648
sealed class Command {
3749
data class LoadUrl(
3850
val url: String,
@@ -50,6 +62,20 @@ class SettingsWebViewViewModel @Inject constructor() : ViewModel() {
5062
}
5163
}
5264

65+
fun onResume() {
66+
processContentScopeScriptsSubscriptionEventPlugin()
67+
}
68+
69+
private fun processContentScopeScriptsSubscriptionEventPlugin() {
70+
if (settingsPageFeature.serpSettingsSync().isEnabled()) {
71+
viewModelScope.launch {
72+
contentScopeScriptsSubscriptionEventPluginPoint.getPlugins().forEach { plugin ->
73+
_subscriptionEventDataChannel.send(plugin.getSubscriptionEventData())
74+
}
75+
}
76+
}
77+
}
78+
5379
private fun sendCommand(command: Command) {
5480
viewModelScope.launch {
5581
commandChannel.send(command)

settings/settings-impl/src/test/java/com/duckduckgo/settings/impl/SettingsWebViewViewModelTest.kt

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,28 @@
1616

1717
package com.duckduckgo.settings.impl
1818

19+
import android.annotation.SuppressLint
1920
import androidx.test.ext.junit.runners.AndroidJUnit4
2021
import app.cash.turbine.test
2122
import com.duckduckgo.common.test.CoroutineTestRule
23+
import com.duckduckgo.common.utils.plugins.PluginPoint
24+
import com.duckduckgo.contentscopescripts.api.ContentScopeScriptsSubscriptionEventPlugin
25+
import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory
26+
import com.duckduckgo.feature.toggles.api.Toggle.State
27+
import com.duckduckgo.js.messaging.api.SubscriptionEventData
28+
import com.duckduckgo.settings.api.SettingsPageFeature
2229
import com.duckduckgo.settings.impl.SettingsWebViewViewModel.Command
2330
import kotlinx.coroutines.ExperimentalCoroutinesApi
2431
import kotlinx.coroutines.test.runTest
32+
import org.json.JSONObject
2533
import org.junit.Assert.assertEquals
2634
import org.junit.Assert.assertTrue
2735
import org.junit.Before
2836
import org.junit.Rule
2937
import org.junit.Test
3038
import org.junit.runner.RunWith
3139

40+
@SuppressLint("DenyListedApi")
3241
@OptIn(ExperimentalCoroutinesApi::class)
3342
@RunWith(AndroidJUnit4::class)
3443
class SettingsWebViewViewModelTest {
@@ -37,10 +46,17 @@ class SettingsWebViewViewModelTest {
3746
val coroutineTestRule: CoroutineTestRule = CoroutineTestRule()
3847

3948
private lateinit var viewModel: SettingsWebViewViewModel
49+
private lateinit var fakeContentScopeScriptsSubscriptionEventPluginPoint: FakeContentScopeScriptsSubscriptionEventPluginPoint
50+
private var fakeSettingsPageFeature = FakeFeatureToggleFactory.create(SettingsPageFeature::class.java)
4051

4152
@Before
4253
fun setup() {
43-
viewModel = SettingsWebViewViewModel()
54+
fakeContentScopeScriptsSubscriptionEventPluginPoint = FakeContentScopeScriptsSubscriptionEventPluginPoint()
55+
56+
viewModel = SettingsWebViewViewModel(
57+
contentScopeScriptsSubscriptionEventPluginPoint = fakeContentScopeScriptsSubscriptionEventPluginPoint,
58+
settingsPageFeature = fakeSettingsPageFeature,
59+
)
4460
}
4561

4662
@Test
@@ -64,4 +80,102 @@ class SettingsWebViewViewModelTest {
6480
assertTrue(command is Command.Exit)
6581
}
6682
}
83+
84+
@Test
85+
fun whenOnViewResumedWithNoPluginsThenNoSubscriptionEventsSent() = runTest {
86+
fakeSettingsPageFeature.serpSettingsSync().setRawStoredState(State(enable = true))
87+
88+
viewModel.onResume()
89+
90+
viewModel.subscriptionEventDataFlow.test {
91+
expectNoEvents()
92+
cancelAndIgnoreRemainingEvents()
93+
}
94+
}
95+
96+
@Test
97+
fun whenOnViewResumedWithPluginsThenSubscriptionEventsSent() = runTest {
98+
fakeSettingsPageFeature.serpSettingsSync().setRawStoredState(State(enable = true))
99+
val events = mutableListOf<SubscriptionEventData>().apply {
100+
add(
101+
SubscriptionEventData(
102+
featureName = "event1",
103+
subscriptionName = "subscription1",
104+
params = JSONObject().put("param1", "value1"),
105+
),
106+
)
107+
add(
108+
SubscriptionEventData(
109+
featureName = "event2",
110+
subscriptionName = "subscription2",
111+
params = JSONObject().put("param2", "value2"),
112+
),
113+
)
114+
}
115+
116+
fakeContentScopeScriptsSubscriptionEventPluginPoint.addPlugins(
117+
events.map { FakeContentScopeScriptsSubscriptionEventPlugin(it) },
118+
)
119+
120+
viewModel.onResume()
121+
122+
viewModel.subscriptionEventDataFlow.test {
123+
for (expectedEvent in events) {
124+
val emittedEvent = awaitItem()
125+
assertEquals(expectedEvent.featureName, emittedEvent.featureName)
126+
assertEquals(expectedEvent.subscriptionName, emittedEvent.subscriptionName)
127+
assertEquals(expectedEvent.params.toString(), emittedEvent.params.toString())
128+
}
129+
cancelAndIgnoreRemainingEvents()
130+
}
131+
}
132+
133+
@Test
134+
fun whenOnViewResumedWithPluginsAndSerpSettingsFeatureFlagOffThenNoEventsSent() = runTest {
135+
fakeSettingsPageFeature.serpSettingsSync().setRawStoredState(State(enable = false))
136+
val events = mutableListOf<SubscriptionEventData>().apply {
137+
add(
138+
SubscriptionEventData(
139+
featureName = "event1",
140+
subscriptionName = "subscription1",
141+
params = JSONObject().put("param1", "value1"),
142+
),
143+
)
144+
add(
145+
SubscriptionEventData(
146+
featureName = "event2",
147+
subscriptionName = "subscription2",
148+
params = JSONObject().put("param2", "value2"),
149+
),
150+
)
151+
}
152+
153+
fakeContentScopeScriptsSubscriptionEventPluginPoint.addPlugins(
154+
events.map { FakeContentScopeScriptsSubscriptionEventPlugin(it) },
155+
)
156+
157+
viewModel.onResume()
158+
159+
viewModel.subscriptionEventDataFlow.test {
160+
expectNoEvents()
161+
cancelAndIgnoreRemainingEvents()
162+
}
163+
}
164+
}
165+
166+
class FakeContentScopeScriptsSubscriptionEventPlugin(
167+
private val eventData: SubscriptionEventData,
168+
) : ContentScopeScriptsSubscriptionEventPlugin {
169+
override fun getSubscriptionEventData(): SubscriptionEventData = eventData
170+
}
171+
172+
class FakeContentScopeScriptsSubscriptionEventPluginPoint() : PluginPoint<ContentScopeScriptsSubscriptionEventPlugin> {
173+
174+
private val plugins: MutableList<ContentScopeScriptsSubscriptionEventPlugin> = mutableListOf()
175+
176+
fun addPlugins(plugins: List<ContentScopeScriptsSubscriptionEventPlugin>) {
177+
this.plugins.addAll(plugins)
178+
}
179+
180+
override fun getPlugins(): Collection<ContentScopeScriptsSubscriptionEventPlugin> = plugins
67181
}

0 commit comments

Comments
 (0)