Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add navigatorInterface + messageBridge support #5461

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions content-scope-scripts/content-scope-scripts-impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ plugins {
id 'com.android.library'
id 'kotlin-android'
id 'com.squareup.anvil'
id 'com.google.devtools.ksp'
}

apply from: "$rootProject.projectDir/gradle/android-library.gradle"
Expand Down Expand Up @@ -47,6 +48,11 @@ dependencies {
implementation Google.dagger
implementation AndroidX.core.ktx

// Room
implementation AndroidX.room.runtime
implementation AndroidX.room.ktx
ksp AndroidX.room.compiler

// Testing dependencies
testImplementation "org.mockito.kotlin:mockito-kotlin:_"
testImplementation Testing.junit4
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2025 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.contentscopescripts.impl.features.messagebridge

import com.duckduckgo.contentscopescripts.api.ContentScopeConfigPlugin
import com.duckduckgo.contentscopescripts.impl.features.messagebridge.MessageBridgeFeatureName.MessageBridge
import com.duckduckgo.contentscopescripts.impl.features.messagebridge.store.MessageBridgeRepository
import com.duckduckgo.di.scopes.AppScope
import com.squareup.anvil.annotations.ContributesMultibinding
import javax.inject.Inject

@ContributesMultibinding(AppScope::class)
class MessageBridgeContentScopeConfigPlugin @Inject constructor(
private val messageBridgeRepository: MessageBridgeRepository,
) : ContentScopeConfigPlugin {

override fun config(): String {
val featureName = MessageBridge.value
val config = messageBridgeRepository.messageBridgeEntity.json
return "\"$featureName\":$config"
}

override fun preferences(): String? {
return null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2025 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.contentscopescripts.impl.features.messagebridge

enum class MessageBridgeFeatureName(val value: String) {
MessageBridge("messageBridge"),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2025 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.contentscopescripts.impl.features.messagebridge

/**
* Convenience method to get the [MessageBridgeFeatureName] from its [String] value
*/
fun messageBridgeFeatureValueOf(value: String): MessageBridgeFeatureName? {
return MessageBridgeFeatureName.entries.find { it.value == value }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2025 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.contentscopescripts.impl.features.messagebridge

import com.duckduckgo.contentscopescripts.impl.features.messagebridge.MessageBridgeFeatureName.MessageBridge
import com.duckduckgo.contentscopescripts.impl.features.messagebridge.store.MessageBridgeEntity
import com.duckduckgo.contentscopescripts.impl.features.messagebridge.store.MessageBridgeRepository
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.privacy.config.api.PrivacyFeaturePlugin
import com.squareup.anvil.annotations.ContributesMultibinding
import javax.inject.Inject

@ContributesMultibinding(AppScope::class)
class MessageBridgeFeaturePlugin @Inject constructor(
private val messageBridgeRepository: MessageBridgeRepository,
) : PrivacyFeaturePlugin {

override fun store(featureName: String, jsonString: String): Boolean {
val messageBridgeFeatureName = messageBridgeFeatureValueOf(featureName) ?: return false
if (messageBridgeFeatureName.value == this.featureName) {
val entity = MessageBridgeEntity(json = jsonString)
messageBridgeRepository.updateAll(messageBridgeEntity = entity)
return true
}
return false
}

override val featureName: String = MessageBridge.value
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2025 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.contentscopescripts.impl.features.messagebridge.di

import android.content.Context
import androidx.room.Room
import com.duckduckgo.app.di.AppCoroutineScope
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.contentscopescripts.impl.features.messagebridge.store.ALL_MIGRATIONS
import com.duckduckgo.contentscopescripts.impl.features.messagebridge.store.MessageBridgeDatabase
import com.duckduckgo.contentscopescripts.impl.features.messagebridge.store.MessageBridgeRepository
import com.duckduckgo.contentscopescripts.impl.features.messagebridge.store.RealMessageBridgeRepository
import com.duckduckgo.di.scopes.AppScope
import com.squareup.anvil.annotations.ContributesTo
import dagger.Module
import dagger.Provides
import dagger.SingleInstanceIn
import kotlinx.coroutines.CoroutineScope

@Module
@ContributesTo(AppScope::class)
object MessageBridgeModule {

@SingleInstanceIn(AppScope::class)
@Provides
fun provideMessageBridgeDatabase(context: Context): MessageBridgeDatabase {
return Room.databaseBuilder(context, MessageBridgeDatabase::class.java, "message_bridge.db")
.enableMultiInstanceInvalidation()
.fallbackToDestructiveMigration()
.addMigrations(*ALL_MIGRATIONS)
.build()
}

@SingleInstanceIn(AppScope::class)
@Provides
fun provideMessageBridgeRepository(
database: MessageBridgeDatabase,
@AppCoroutineScope coroutineScope: CoroutineScope,
dispatcherProvider: DispatcherProvider,
): MessageBridgeRepository {
return RealMessageBridgeRepository(database, coroutineScope, dispatcherProvider)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2025 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.contentscopescripts.impl.features.messagebridge.store

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction

@Dao
abstract class MessageBridgeDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insert(messageBridgeEntity: MessageBridgeEntity)

@Transaction
open fun updateAll(
messageBridgeEntity: MessageBridgeEntity,
) {
delete()
insert(messageBridgeEntity)
}

@Query("select * from message_bridge")
abstract fun get(): MessageBridgeEntity?

@Query("delete from message_bridge")
abstract fun delete()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2025 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.contentscopescripts.impl.features.messagebridge.store

import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.migration.Migration

@Database(
exportSchema = true,
version = 1,
entities = [
MessageBridgeEntity::class,
],
)
abstract class MessageBridgeDatabase : RoomDatabase() {
abstract fun messageBridgeDao(): MessageBridgeDao
}

val ALL_MIGRATIONS = emptyArray<Migration>()
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2025 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.contentscopescripts.impl.features.messagebridge.store

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "message_bridge")
data class MessageBridgeEntity(
@PrimaryKey val id: Int = 1,
val json: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2025 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.contentscopescripts.impl.features.messagebridge.store

import com.duckduckgo.common.utils.DispatcherProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

interface MessageBridgeRepository {
fun updateAll(
messageBridgeEntity: MessageBridgeEntity,
)
var messageBridgeEntity: MessageBridgeEntity
}

class RealMessageBridgeRepository(
val database: MessageBridgeDatabase,
val coroutineScope: CoroutineScope,
val dispatcherProvider: DispatcherProvider,
) : MessageBridgeRepository {

private val messageBridgeDao: MessageBridgeDao = database.messageBridgeDao()
override var messageBridgeEntity = MessageBridgeEntity(json = EMPTY_JSON)

init {
coroutineScope.launch(dispatcherProvider.io()) {
loadToMemory()
}
}

override fun updateAll(messageBridgeEntity: MessageBridgeEntity) {
messageBridgeDao.updateAll(messageBridgeEntity)
loadToMemory()
}

private fun loadToMemory() {
messageBridgeEntity =
messageBridgeDao.get() ?: MessageBridgeEntity(json = EMPTY_JSON)
}

companion object {
const val EMPTY_JSON = "{}"
}
}
Loading
Loading