mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat/decoupling (#4685)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
40244f8337
commit
2c49db8041
254 changed files with 5132 additions and 2666 deletions
|
|
@ -16,11 +16,14 @@
|
|||
*/
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.meshtastic.core.data.repository.NodeRepository
|
||||
import org.meshtastic.core.model.ConnectionState
|
||||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.model.RadioController
|
||||
import org.meshtastic.core.model.service.ServiceAction
|
||||
import org.meshtastic.core.repository.NodeRepository
|
||||
import org.meshtastic.proto.ClientNotification
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
|
@ -30,7 +33,8 @@ import javax.inject.Singleton
|
|||
class AndroidRadioControllerImpl
|
||||
@Inject
|
||||
constructor(
|
||||
private val serviceRepository: ServiceRepository,
|
||||
@ApplicationContext private val context: Context,
|
||||
private val serviceRepository: AndroidServiceRepository,
|
||||
private val nodeRepository: NodeRepository,
|
||||
) : RadioController {
|
||||
|
||||
|
|
@ -65,6 +69,14 @@ constructor(
|
|||
serviceRepository.onServiceAction(ServiceAction.SendContact(contact))
|
||||
}
|
||||
|
||||
override suspend fun setLocalConfig(config: org.meshtastic.proto.Config) {
|
||||
serviceRepository.meshService?.setConfig(config.encode())
|
||||
}
|
||||
|
||||
override suspend fun setLocalChannel(channel: org.meshtastic.proto.Channel) {
|
||||
serviceRepository.meshService?.setChannel(channel.encode())
|
||||
}
|
||||
|
||||
override suspend fun setOwner(destNum: Int, user: org.meshtastic.proto.User, packetId: Int) {
|
||||
serviceRepository.meshService?.setRemoteOwner(packetId, destNum, user.encode())
|
||||
}
|
||||
|
|
@ -125,6 +137,14 @@ constructor(
|
|||
serviceRepository.meshService?.requestReboot(packetId, destNum)
|
||||
}
|
||||
|
||||
override suspend fun rebootToDfu(nodeNum: Int) {
|
||||
serviceRepository.meshService?.rebootToDfu(nodeNum)
|
||||
}
|
||||
|
||||
override suspend fun requestRebootOta(requestId: Int, destNum: Int, mode: Int, hash: ByteArray?) {
|
||||
serviceRepository.meshService?.requestRebootOta(requestId, destNum, mode, hash)
|
||||
}
|
||||
|
||||
override suspend fun shutdown(destNum: Int, packetId: Int) {
|
||||
serviceRepository.meshService?.requestShutdown(packetId, destNum)
|
||||
}
|
||||
|
|
@ -141,6 +161,26 @@ constructor(
|
|||
serviceRepository.meshService?.removeByNodenum(packetId, nodeNum)
|
||||
}
|
||||
|
||||
override suspend fun requestPosition(destNum: Int, currentPosition: org.meshtastic.core.model.Position) {
|
||||
serviceRepository.meshService?.requestPosition(destNum, currentPosition)
|
||||
}
|
||||
|
||||
override suspend fun requestUserInfo(destNum: Int) {
|
||||
serviceRepository.meshService?.requestUserInfo(destNum)
|
||||
}
|
||||
|
||||
override suspend fun requestTraceroute(requestId: Int, destNum: Int) {
|
||||
serviceRepository.meshService?.requestTraceroute(requestId, destNum)
|
||||
}
|
||||
|
||||
override suspend fun requestTelemetry(requestId: Int, destNum: Int, typeValue: Int) {
|
||||
serviceRepository.meshService?.requestTelemetry(requestId, destNum, typeValue)
|
||||
}
|
||||
|
||||
override suspend fun requestNeighborInfo(requestId: Int, destNum: Int) {
|
||||
serviceRepository.meshService?.requestNeighborInfo(requestId, destNum)
|
||||
}
|
||||
|
||||
override suspend fun beginEditSettings(destNum: Int) {
|
||||
serviceRepository.meshService?.beginEditSettings(destNum)
|
||||
}
|
||||
|
|
@ -158,4 +198,14 @@ constructor(
|
|||
override fun stopProvideLocation() {
|
||||
serviceRepository.meshService?.stopProvideLocation()
|
||||
}
|
||||
|
||||
override fun setDeviceAddress(address: String) {
|
||||
serviceRepository.meshService?.setDeviceAddress(address)
|
||||
// Ensure service is running/restarted to handle the new address
|
||||
val intent =
|
||||
android.content.Intent().apply {
|
||||
setClassName("com.geeksville.mesh", "com.geeksville.mesh.service.MeshService")
|
||||
}
|
||||
context.startForegroundService(intent)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,33 +19,25 @@ package org.meshtastic.core.service
|
|||
import co.touchlab.kermit.Logger
|
||||
import co.touchlab.kermit.Severity
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import org.meshtastic.core.model.ConnectionState
|
||||
import org.meshtastic.core.model.service.ServiceAction
|
||||
import org.meshtastic.core.model.service.TracerouteResponse
|
||||
import org.meshtastic.core.repository.ServiceRepository
|
||||
import org.meshtastic.proto.ClientNotification
|
||||
import org.meshtastic.proto.MeshPacket
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
data class TracerouteResponse(
|
||||
val message: String,
|
||||
val destinationNodeNum: Int,
|
||||
val requestId: Int,
|
||||
val forwardRoute: List<Int> = emptyList(),
|
||||
val returnRoute: List<Int> = emptyList(),
|
||||
val logUuid: String? = null,
|
||||
) {
|
||||
val hasOverlay: Boolean
|
||||
get() = forwardRoute.isNotEmpty() || returnRoute.isNotEmpty()
|
||||
}
|
||||
|
||||
/** Repository class for managing the [IMeshService] instance and connection state */
|
||||
@Suppress("TooManyFunctions")
|
||||
@Singleton
|
||||
open class ServiceRepository @Inject constructor() {
|
||||
open class AndroidServiceRepository @Inject constructor() : ServiceRepository {
|
||||
var meshService: IMeshService? = null
|
||||
private set
|
||||
|
||||
|
|
@ -55,86 +47,86 @@ open class ServiceRepository @Inject constructor() {
|
|||
|
||||
// Connection state to our radio device
|
||||
private val _connectionState: MutableStateFlow<ConnectionState> = MutableStateFlow(ConnectionState.Disconnected)
|
||||
open val connectionState: StateFlow<ConnectionState>
|
||||
override val connectionState: StateFlow<ConnectionState>
|
||||
get() = _connectionState
|
||||
|
||||
fun setConnectionState(connectionState: ConnectionState) {
|
||||
override fun setConnectionState(connectionState: ConnectionState) {
|
||||
_connectionState.value = connectionState
|
||||
}
|
||||
|
||||
private val _clientNotification = MutableStateFlow<ClientNotification?>(null)
|
||||
val clientNotification: StateFlow<ClientNotification?>
|
||||
override val clientNotification: StateFlow<ClientNotification?>
|
||||
get() = _clientNotification
|
||||
|
||||
fun setClientNotification(notification: ClientNotification?) {
|
||||
override fun setClientNotification(notification: ClientNotification?) {
|
||||
notification?.message?.let { Logger.w { it } }
|
||||
|
||||
_clientNotification.value = notification
|
||||
}
|
||||
|
||||
fun clearClientNotification() {
|
||||
override fun clearClientNotification() {
|
||||
_clientNotification.value = null
|
||||
}
|
||||
|
||||
private val _errorMessage = MutableStateFlow<String?>(null)
|
||||
val errorMessage: StateFlow<String?>
|
||||
override val errorMessage: StateFlow<String?>
|
||||
get() = _errorMessage
|
||||
|
||||
fun setErrorMessage(text: String, severity: Severity = Severity.Error) {
|
||||
override fun setErrorMessage(text: String, severity: Severity) {
|
||||
Logger.log(severity, "ServiceRepository", null, text)
|
||||
_errorMessage.value = text
|
||||
}
|
||||
|
||||
fun clearErrorMessage() {
|
||||
override fun clearErrorMessage() {
|
||||
_errorMessage.value = null
|
||||
}
|
||||
|
||||
private val _connectionProgress = MutableStateFlow<String?>(null)
|
||||
val connectionProgress: StateFlow<String?>
|
||||
override val connectionProgress: StateFlow<String?>
|
||||
get() = _connectionProgress
|
||||
|
||||
fun setConnectionProgress(text: String) {
|
||||
override fun setConnectionProgress(text: String) {
|
||||
if (connectionState.value != ConnectionState.Connected) {
|
||||
_connectionProgress.value = text
|
||||
}
|
||||
}
|
||||
|
||||
private val _meshPacketFlow = MutableSharedFlow<MeshPacket>(extraBufferCapacity = 64)
|
||||
val meshPacketFlow: SharedFlow<MeshPacket>
|
||||
override val meshPacketFlow: SharedFlow<MeshPacket>
|
||||
get() = _meshPacketFlow
|
||||
|
||||
suspend fun emitMeshPacket(packet: MeshPacket) {
|
||||
override suspend fun emitMeshPacket(packet: MeshPacket) {
|
||||
_meshPacketFlow.emit(packet)
|
||||
}
|
||||
|
||||
private val _tracerouteResponse = MutableStateFlow<TracerouteResponse?>(null)
|
||||
val tracerouteResponse: StateFlow<TracerouteResponse?>
|
||||
override val tracerouteResponse: StateFlow<TracerouteResponse?>
|
||||
get() = _tracerouteResponse
|
||||
|
||||
fun setTracerouteResponse(value: TracerouteResponse?) {
|
||||
override fun setTracerouteResponse(value: TracerouteResponse?) {
|
||||
_tracerouteResponse.value = value
|
||||
}
|
||||
|
||||
fun clearTracerouteResponse() {
|
||||
override fun clearTracerouteResponse() {
|
||||
setTracerouteResponse(null)
|
||||
}
|
||||
|
||||
private val _neighborInfoResponse = MutableStateFlow<String?>(null)
|
||||
val neighborInfoResponse: StateFlow<String?>
|
||||
override val neighborInfoResponse: StateFlow<String?>
|
||||
get() = _neighborInfoResponse
|
||||
|
||||
fun setNeighborInfoResponse(value: String?) {
|
||||
override fun setNeighborInfoResponse(value: String?) {
|
||||
_neighborInfoResponse.value = value
|
||||
}
|
||||
|
||||
fun clearNeighborInfoResponse() {
|
||||
override fun clearNeighborInfoResponse() {
|
||||
setNeighborInfoResponse(null)
|
||||
}
|
||||
|
||||
private val _serviceAction = Channel<ServiceAction>()
|
||||
val serviceAction = _serviceAction.receiveAsFlow()
|
||||
override val serviceAction: Flow<ServiceAction> = _serviceAction.receiveAsFlow()
|
||||
|
||||
suspend fun onServiceAction(action: ServiceAction) {
|
||||
override suspend fun onServiceAction(action: ServiceAction) {
|
||||
_serviceAction.send(action)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import android.app.Notification
|
||||
import org.meshtastic.core.database.entity.NodeEntity
|
||||
import org.meshtastic.proto.ClientNotification
|
||||
import org.meshtastic.proto.Telemetry
|
||||
|
||||
const val SERVICE_NOTIFY_ID = 101
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
interface MeshServiceNotifications {
|
||||
fun clearNotifications()
|
||||
|
||||
fun initChannels()
|
||||
|
||||
fun updateServiceStateNotification(summaryString: String?, telemetry: Telemetry?): Notification
|
||||
|
||||
suspend fun updateMessageNotification(
|
||||
contactKey: String,
|
||||
name: String,
|
||||
message: String,
|
||||
isBroadcast: Boolean,
|
||||
channelName: String?,
|
||||
isSilent: Boolean = false,
|
||||
)
|
||||
|
||||
suspend fun updateWaypointNotification(
|
||||
contactKey: String,
|
||||
name: String,
|
||||
message: String,
|
||||
waypointId: Int,
|
||||
isSilent: Boolean = false,
|
||||
)
|
||||
|
||||
suspend fun updateReactionNotification(
|
||||
contactKey: String,
|
||||
name: String,
|
||||
emoji: String,
|
||||
isBroadcast: Boolean,
|
||||
channelName: String?,
|
||||
isSilent: Boolean = false,
|
||||
)
|
||||
|
||||
fun showAlertNotification(contactKey: String, name: String, alert: String)
|
||||
|
||||
fun showNewNodeSeenNotification(node: NodeEntity)
|
||||
|
||||
fun showOrUpdateLowBatteryNotification(node: NodeEntity, isRemote: Boolean)
|
||||
|
||||
fun showClientNotification(clientNotification: ClientNotification)
|
||||
|
||||
fun cancelMessageNotification(contactKey: String)
|
||||
|
||||
fun cancelLowBatteryNotification(node: NodeEntity)
|
||||
|
||||
fun clearClientNotification(notification: ClientNotification)
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.service
|
||||
|
||||
import org.meshtastic.core.database.model.Node
|
||||
import org.meshtastic.proto.SharedContact
|
||||
|
||||
sealed class ServiceAction {
|
||||
data class GetDeviceMetadata(val destNum: Int) : ServiceAction()
|
||||
|
||||
data class Favorite(val node: Node) : ServiceAction()
|
||||
|
||||
data class Ignore(val node: Node) : ServiceAction()
|
||||
|
||||
data class Mute(val node: Node) : ServiceAction()
|
||||
|
||||
data class Reaction(val emoji: String, val replyId: Int, val contactKey: String) : ServiceAction()
|
||||
|
||||
data class ImportContact(val contact: SharedContact) : ServiceAction()
|
||||
|
||||
data class SendContact(val contact: SharedContact) : ServiceAction()
|
||||
}
|
||||
|
|
@ -21,11 +21,18 @@ import dagger.Module
|
|||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import org.meshtastic.core.model.RadioController
|
||||
import org.meshtastic.core.repository.ServiceRepository
|
||||
import org.meshtastic.core.service.AndroidRadioControllerImpl
|
||||
import org.meshtastic.core.service.AndroidServiceRepository
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class ServiceModule {
|
||||
|
||||
@Binds abstract fun bindRadioController(impl: AndroidRadioControllerImpl): RadioController
|
||||
@Binds @Singleton
|
||||
abstract fun bindRadioController(impl: AndroidRadioControllerImpl): RadioController
|
||||
|
||||
@Binds @Singleton
|
||||
abstract fun bindServiceRepository(impl: AndroidServiceRepository): ServiceRepository
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.service.filter
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import org.meshtastic.core.prefs.filter.FilterPrefs
|
||||
import java.util.regex.PatternSyntaxException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Service for filtering messages based on user-configured filter words. Supports both plain text word matching and
|
||||
* regex patterns.
|
||||
*/
|
||||
@Singleton
|
||||
class MessageFilterService @Inject constructor(private val filterPrefs: FilterPrefs) {
|
||||
private var compiledPatterns: List<Regex> = emptyList()
|
||||
|
||||
init {
|
||||
rebuildPatterns()
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a message should be filtered based on the configured filter words.
|
||||
*
|
||||
* @param message The message text to check.
|
||||
* @param isFilteringDisabled Whether filtering is disabled for this contact.
|
||||
* @return true if the message should be filtered, false otherwise.
|
||||
*/
|
||||
fun shouldFilter(message: String, isFilteringDisabled: Boolean = false): Boolean {
|
||||
if (!filterPrefs.filterEnabled || compiledPatterns.isEmpty() || isFilteringDisabled) {
|
||||
return false
|
||||
}
|
||||
val textToCheck = message.take(MAX_CHECK_LENGTH)
|
||||
return compiledPatterns.any { it.containsMatchIn(textToCheck) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds the compiled regex patterns from the current filter words. Should be called whenever the filter words
|
||||
* are updated.
|
||||
*/
|
||||
fun rebuildPatterns() {
|
||||
compiledPatterns =
|
||||
filterPrefs.filterWords.mapNotNull { word ->
|
||||
try {
|
||||
if (word.startsWith(REGEX_PREFIX)) {
|
||||
Regex(word.removePrefix(REGEX_PREFIX), RegexOption.IGNORE_CASE)
|
||||
} else {
|
||||
Regex("\\b${Regex.escape(word)}\\b", RegexOption.IGNORE_CASE)
|
||||
}
|
||||
} catch (e: PatternSyntaxException) {
|
||||
Logger.w { "Invalid filter pattern: $word - ${e.message}" }
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MAX_CHECK_LENGTH = 10_000
|
||||
private const val REGEX_PREFIX = "regex:"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025-2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.core.service.filter
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.meshtastic.core.prefs.filter.FilterPrefs
|
||||
|
||||
class MessageFilterServiceTest {
|
||||
private lateinit var filterPrefs: FilterPrefs
|
||||
private lateinit var filterService: MessageFilterService
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
filterPrefs = mockk {
|
||||
every { filterEnabled } returns true
|
||||
every { filterWords } returns setOf("spam", "bad")
|
||||
}
|
||||
filterService = MessageFilterService(filterPrefs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldFilter returns false when filter is disabled`() {
|
||||
every { filterPrefs.filterEnabled } returns false
|
||||
assertFalse(filterService.shouldFilter("spam message"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldFilter returns false when filter words is empty`() {
|
||||
every { filterPrefs.filterWords } returns emptySet()
|
||||
filterService.rebuildPatterns()
|
||||
assertFalse(filterService.shouldFilter("any message"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldFilter returns true for exact word match`() {
|
||||
filterService.rebuildPatterns()
|
||||
assertTrue(filterService.shouldFilter("this is spam"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldFilter is case insensitive`() {
|
||||
filterService.rebuildPatterns()
|
||||
assertTrue(filterService.shouldFilter("This is SPAM"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldFilter matches whole words only`() {
|
||||
filterService.rebuildPatterns()
|
||||
assertFalse(filterService.shouldFilter("antispam software"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldFilter supports regex patterns`() {
|
||||
every { filterPrefs.filterWords } returns setOf("regex:test\\d+")
|
||||
filterService.rebuildPatterns()
|
||||
assertTrue(filterService.shouldFilter("this is test123"))
|
||||
assertFalse(filterService.shouldFilter("this is test"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldFilter handles invalid regex gracefully`() {
|
||||
every { filterPrefs.filterWords } returns setOf("regex:[invalid")
|
||||
filterService.rebuildPatterns()
|
||||
assertFalse(filterService.shouldFilter("any message"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldFilter returns false when contact has filtering disabled`() {
|
||||
filterService.rebuildPatterns()
|
||||
assertFalse(filterService.shouldFilter("spam message", isFilteringDisabled = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldFilter filters when contact has filtering enabled`() {
|
||||
filterService.rebuildPatterns()
|
||||
assertTrue(filterService.shouldFilter("spam message", isFilteringDisabled = false))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue