forked from upstream-mirrors/quik
Merge pull request #496 from peanut250/feat/message-content-filters
Block messages based on content
This commit is contained in:
@@ -242,6 +242,17 @@ class QkRealmMigration @Inject constructor(
|
||||
version++
|
||||
}
|
||||
|
||||
if (version == 11L) {
|
||||
realm.schema.create("MessageContentFilter")
|
||||
.addField("id", Long::class.java, FieldAttribute.PRIMARY_KEY, FieldAttribute.REQUIRED)
|
||||
.addField("value", String::class.java, FieldAttribute.REQUIRED)
|
||||
.addField("caseSensitive", Boolean::class.java, FieldAttribute.REQUIRED)
|
||||
.addField("isRegex", Boolean::class.java, FieldAttribute.REQUIRED)
|
||||
.addField("includeContacts", Boolean::class.java, FieldAttribute.REQUIRED)
|
||||
|
||||
version++
|
||||
}
|
||||
|
||||
check(version >= newVersion) { "Migration missing from v$oldVersion to v$newVersion" }
|
||||
}
|
||||
|
||||
|
||||
@@ -171,4 +171,14 @@ class ContactRepositoryImpl @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun isContact(address: String): Boolean {
|
||||
val uri : Uri
|
||||
if (address.contains('@')) {
|
||||
uri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, Uri.encode(address))
|
||||
} else {
|
||||
uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(address))
|
||||
}
|
||||
return context.contentResolver.query(uri, arrayOf(BaseColumns._ID), null, null, null)?.count!! > 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS 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.
|
||||
*
|
||||
* QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package dev.octoshrimpy.quik.repository
|
||||
|
||||
import dev.octoshrimpy.quik.model.MessageContentFilter
|
||||
import dev.octoshrimpy.quik.model.MessageContentFilterData
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmResults
|
||||
import javax.inject.Inject
|
||||
|
||||
class MessageContentFilterRepositoryImpl @Inject constructor() : MessageContentFilterRepository {
|
||||
override fun createFilter(data: MessageContentFilterData) {
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
realm.refresh()
|
||||
val maxId = realm.where(MessageContentFilter::class.java)
|
||||
.max("id")?.toLong() ?: -1
|
||||
|
||||
realm.executeTransaction {
|
||||
realm.insert(MessageContentFilter(maxId + 1, data.value, data.caseSensitive, data.isRegex, data.includeContacts))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessageContentFilters(): RealmResults<MessageContentFilter> {
|
||||
return Realm.getDefaultInstance()
|
||||
.where(MessageContentFilter::class.java)
|
||||
.findAllAsync()
|
||||
}
|
||||
|
||||
override fun getMessageContentFilter(id: Long): MessageContentFilter? {
|
||||
return Realm.getDefaultInstance()
|
||||
.where(MessageContentFilter::class.java)
|
||||
.equalTo("id", id)
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
override fun isBlocked(messageBody: String, address: String, contactsRepo: ContactRepository): Boolean {
|
||||
val isContact = contactsRepo.isContact(address)
|
||||
|
||||
return Realm.getDefaultInstance().use { realm ->
|
||||
realm.where(MessageContentFilter::class.java)
|
||||
.findAll()
|
||||
.any { filter ->
|
||||
if (isContact && !filter.includeContacts) {
|
||||
false
|
||||
} else if (filter.isRegex) {
|
||||
Regex(filter.value).matches(messageBody)
|
||||
} else if (filter.caseSensitive) {
|
||||
val regexp = ".*\\b" + Regex.escape(filter.value) + "\\b.*"
|
||||
Regex(regexp).matches(messageBody)
|
||||
} else {
|
||||
val regexp = ".*\\b" + Regex.escape(filter.value.lowercase()) + "\\b.*"
|
||||
Regex(regexp).matches(messageBody.lowercase())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeFilter(id: Long) {
|
||||
Realm.getDefaultInstance().use { realm ->
|
||||
realm.executeTransaction {
|
||||
realm.where(MessageContentFilter::class.java)
|
||||
.equalTo("id", id)
|
||||
.findAll()
|
||||
.deleteAllFromRealm()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,7 +23,9 @@ import dev.octoshrimpy.quik.blocking.BlockingClient
|
||||
import dev.octoshrimpy.quik.extensions.mapNotNull
|
||||
import dev.octoshrimpy.quik.manager.ActiveConversationManager
|
||||
import dev.octoshrimpy.quik.manager.NotificationManager
|
||||
import dev.octoshrimpy.quik.repository.ContactRepository
|
||||
import dev.octoshrimpy.quik.repository.ConversationRepository
|
||||
import dev.octoshrimpy.quik.repository.MessageContentFilterRepository
|
||||
import dev.octoshrimpy.quik.repository.MessageRepository
|
||||
import dev.octoshrimpy.quik.repository.SyncRepository
|
||||
import dev.octoshrimpy.quik.util.Preferences
|
||||
@@ -39,7 +41,9 @@ class ReceiveMms @Inject constructor(
|
||||
private val syncManager: SyncRepository,
|
||||
private val messageRepo: MessageRepository,
|
||||
private val notificationManager: NotificationManager,
|
||||
private val updateBadge: UpdateBadge
|
||||
private val updateBadge: UpdateBadge,
|
||||
private val filterRepo: MessageContentFilterRepository,
|
||||
private val contactsRepo: ContactRepository
|
||||
) : Interactor<Uri>() {
|
||||
|
||||
override fun buildObservable(params: Uri): Flowable<*> {
|
||||
@@ -75,6 +79,12 @@ class ReceiveMms @Inject constructor(
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
if (filterRepo.isBlocked(message.getText(), message.address, contactsRepo)) {
|
||||
Timber.v("message dropped based on content filters")
|
||||
messageRepo.deleteMessages(listOf(message.id))
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
message
|
||||
}
|
||||
.doOnNext { message ->
|
||||
|
||||
@@ -22,7 +22,9 @@ import dev.octoshrimpy.quik.blocking.BlockingClient
|
||||
import dev.octoshrimpy.quik.extensions.mapNotNull
|
||||
import dev.octoshrimpy.quik.manager.NotificationManager
|
||||
import dev.octoshrimpy.quik.manager.ShortcutManager
|
||||
import dev.octoshrimpy.quik.repository.ContactRepository
|
||||
import dev.octoshrimpy.quik.repository.ConversationRepository
|
||||
import dev.octoshrimpy.quik.repository.MessageContentFilterRepository
|
||||
import dev.octoshrimpy.quik.repository.MessageRepository
|
||||
import dev.octoshrimpy.quik.util.Preferences
|
||||
import io.reactivex.Flowable
|
||||
@@ -36,7 +38,9 @@ class ReceiveSms @Inject constructor(
|
||||
private val messageRepo: MessageRepository,
|
||||
private val notificationManager: NotificationManager,
|
||||
private val updateBadge: UpdateBadge,
|
||||
private val shortcutManager: ShortcutManager
|
||||
private val shortcutManager: ShortcutManager,
|
||||
private val filterRepo: MessageContentFilterRepository,
|
||||
private val contactsRepo: ContactRepository
|
||||
) : Interactor<Long>() {
|
||||
|
||||
override fun buildObservable(params: Long): Flowable<*> {
|
||||
@@ -69,6 +73,12 @@ class ReceiveSms @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
if (filterRepo.isBlocked(it.getText(), it.address, contactsRepo)) {
|
||||
Timber.v("message dropped based on content filters")
|
||||
messageRepo.deleteMessages(listOf(it.id))
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
// update and fetch conversation
|
||||
conversationRepo.updateConversations(it.threadId)
|
||||
conversationRepo.getOrCreateConversation(it.threadId)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package dev.octoshrimpy.quik.model
|
||||
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
open class MessageContentFilter(
|
||||
@PrimaryKey var id: Long = 0,
|
||||
|
||||
var value: String = "",
|
||||
var caseSensitive: Boolean = false,
|
||||
var isRegex: Boolean = false,
|
||||
var includeContacts: Boolean = false
|
||||
) : RealmObject()
|
||||
|
||||
data class MessageContentFilterData(
|
||||
var value: String = "",
|
||||
var caseSensitive: Boolean = false,
|
||||
var isRegex: Boolean = false,
|
||||
var includeContacts: Boolean = false
|
||||
)
|
||||
@@ -41,4 +41,6 @@ interface ContactRepository {
|
||||
|
||||
fun setDefaultPhoneNumber(lookupKey: String, phoneNumberId: Long)
|
||||
|
||||
fun isContact(address: String): Boolean
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS 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.
|
||||
*
|
||||
* QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package dev.octoshrimpy.quik.repository
|
||||
|
||||
import dev.octoshrimpy.quik.model.MessageContentFilter
|
||||
import dev.octoshrimpy.quik.model.MessageContentFilterData
|
||||
import io.realm.RealmResults
|
||||
|
||||
interface MessageContentFilterRepository {
|
||||
|
||||
fun createFilter(data: MessageContentFilterData)
|
||||
|
||||
fun getMessageContentFilters(): RealmResults<MessageContentFilter>
|
||||
|
||||
fun getMessageContentFilter(id: Long): MessageContentFilter?
|
||||
|
||||
fun isBlocked(messageBody: String, address: String, contactsRepo: ContactRepository): Boolean
|
||||
|
||||
fun removeFilter(id: Long)
|
||||
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import dev.octoshrimpy.quik.common.util.extensions.animateLayoutChanges
|
||||
import dev.octoshrimpy.quik.feature.blocking.manager.BlockingManagerController
|
||||
import dev.octoshrimpy.quik.feature.blocking.messages.BlockedMessagesController
|
||||
import dev.octoshrimpy.quik.feature.blocking.numbers.BlockedNumbersController
|
||||
import dev.octoshrimpy.quik.feature.blocking.filters.MessageContentFiltersController
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
import kotlinx.android.synthetic.main.blocking_controller.*
|
||||
import kotlinx.android.synthetic.main.settings_switch_widget.view.*
|
||||
@@ -38,6 +39,7 @@ class BlockingController : QkController<BlockingView, BlockingState, BlockingPre
|
||||
|
||||
override val blockingManagerIntent by lazy { blockingManager.clicks() }
|
||||
override val blockedNumbersIntent by lazy { blockedNumbers.clicks() }
|
||||
override val messageContentFiltersIntent by lazy { messageContentFilters.clicks() }
|
||||
override val blockedMessagesIntent by lazy { blockedMessages.clicks() }
|
||||
override val dropClickedIntent by lazy { drop.clicks() }
|
||||
|
||||
@@ -74,6 +76,12 @@ class BlockingController : QkController<BlockingView, BlockingState, BlockingPre
|
||||
.popChangeHandler(QkChangeHandler()))
|
||||
}
|
||||
|
||||
override fun openMessageContentFilters() {
|
||||
router.pushController(RouterTransaction.with(MessageContentFiltersController())
|
||||
.pushChangeHandler(QkChangeHandler())
|
||||
.popChangeHandler(QkChangeHandler()))
|
||||
}
|
||||
|
||||
override fun openBlockedMessages() {
|
||||
router.pushController(RouterTransaction.with(BlockedMessagesController())
|
||||
.pushChangeHandler(QkChangeHandler())
|
||||
|
||||
@@ -69,6 +69,10 @@ class BlockingPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
view.messageContentFiltersIntent
|
||||
.autoDisposable(view.scope())
|
||||
.subscribe { view.openMessageContentFilters() }
|
||||
|
||||
view.blockedMessagesIntent
|
||||
.autoDisposable(view.scope())
|
||||
.subscribe { view.openBlockedMessages() }
|
||||
|
||||
@@ -25,10 +25,12 @@ interface BlockingView : QkViewContract<BlockingState> {
|
||||
|
||||
val blockingManagerIntent: Observable<*>
|
||||
val blockedNumbersIntent: Observable<*>
|
||||
val messageContentFiltersIntent: Observable<*>
|
||||
val blockedMessagesIntent: Observable<*>
|
||||
val dropClickedIntent: Observable<*>
|
||||
|
||||
fun openBlockingManager()
|
||||
fun openBlockedNumbers()
|
||||
fun openMessageContentFilters()
|
||||
fun openBlockedMessages()
|
||||
}
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS 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.
|
||||
*
|
||||
* QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package dev.octoshrimpy.quik.feature.blocking.filters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkRealmAdapter
|
||||
import dev.octoshrimpy.quik.common.base.QkViewHolder
|
||||
import dev.octoshrimpy.quik.model.MessageContentFilter
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.message_content_filter_list_item.*
|
||||
import kotlinx.android.synthetic.main.message_content_filter_list_item.view.*
|
||||
|
||||
class MessageContentFiltersAdapter : QkRealmAdapter<MessageContentFilter>() {
|
||||
|
||||
val removeMessageContentFilter: Subject<Long> = PublishSubject.create()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.message_content_filter_list_item, parent, false)
|
||||
return QkViewHolder(view).apply {
|
||||
containerView.removeFilter.setOnClickListener {
|
||||
val filter = getItem(adapterPosition) ?: return@setOnClickListener
|
||||
removeMessageContentFilter.onNext(filter.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: QkViewHolder, position: Int) {
|
||||
val item = getItem(position)!!
|
||||
holder.caseIcon.visibility = if (item.caseSensitive) View.VISIBLE else View.GONE
|
||||
holder.regexIcon.visibility = if (item.isRegex) View.VISIBLE else View.GONE
|
||||
holder.contactsIcon.visibility = if (item.includeContacts) View.VISIBLE else View.GONE
|
||||
holder.filter.text = item.value
|
||||
}
|
||||
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS 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.
|
||||
*
|
||||
* QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package dev.octoshrimpy.quik.feature.blocking.filters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.jakewharton.rxbinding2.view.clicks
|
||||
import com.uber.autodispose.android.lifecycle.scope
|
||||
import com.uber.autodispose.autoDisposable
|
||||
import dev.octoshrimpy.quik.R
|
||||
import dev.octoshrimpy.quik.common.base.QkController
|
||||
import dev.octoshrimpy.quik.common.util.Colors
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setBackgroundTint
|
||||
import dev.octoshrimpy.quik.common.util.extensions.setTint
|
||||
import dev.octoshrimpy.quik.common.widget.PreferenceView
|
||||
import dev.octoshrimpy.quik.injection.appComponent
|
||||
import dev.octoshrimpy.quik.model.MessageContentFilterData
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import io.reactivex.subjects.Subject
|
||||
import kotlinx.android.synthetic.main.message_content_filters_add_dialog.view.*
|
||||
import kotlinx.android.synthetic.main.message_content_filters_controller.*
|
||||
import kotlinx.android.synthetic.main.settings_switch_widget.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class MessageContentFiltersController : QkController<MessageContentFiltersView, MessageContentFiltersState,
|
||||
MessageContentFiltersPresenter>(), MessageContentFiltersView {
|
||||
|
||||
@Inject override lateinit var presenter: MessageContentFiltersPresenter
|
||||
@Inject lateinit var colors: Colors
|
||||
|
||||
private val adapter = MessageContentFiltersAdapter()
|
||||
private val saveFilterSubject: Subject<MessageContentFilterData> = PublishSubject.create()
|
||||
|
||||
init {
|
||||
appComponent.inject(this)
|
||||
retainViewMode = RetainViewMode.RETAIN_DETACH
|
||||
layoutRes = R.layout.message_content_filters_controller
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
super.onAttach(view)
|
||||
presenter.bindIntents(this)
|
||||
setTitle(R.string.message_content_filters_title)
|
||||
showBackButton(true)
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
super.onViewCreated()
|
||||
add.setBackgroundTint(colors.theme().theme)
|
||||
add.setTint(colors.theme().textPrimary)
|
||||
adapter.emptyView = empty
|
||||
filters.adapter = adapter
|
||||
}
|
||||
|
||||
override fun render(state: MessageContentFiltersState) {
|
||||
adapter.updateData(state.filters)
|
||||
}
|
||||
|
||||
override fun removeFilter(): Observable<Long> = adapter.removeMessageContentFilter
|
||||
override fun addFilter(): Observable<*> = add.clicks()
|
||||
override fun saveFilter(): Observable<MessageContentFilterData> = saveFilterSubject
|
||||
|
||||
override fun showAddDialog() {
|
||||
val layout = LayoutInflater.from(activity).inflate(R.layout.message_content_filters_add_dialog, null)
|
||||
|
||||
(0 until layout.add_dialog.childCount)
|
||||
.map { index -> layout.add_dialog.getChildAt(index) }
|
||||
.mapNotNull { view -> view as? PreferenceView }
|
||||
.map { preference -> preference.clicks().map { preference } }
|
||||
.let { Observable.merge(it) }
|
||||
.autoDisposable(scope())
|
||||
.subscribe {
|
||||
it.checkbox.isChecked = !it.checkbox.isChecked
|
||||
layout.caseSensitivity.isEnabled = !layout.regexp.checkbox.isChecked
|
||||
}
|
||||
|
||||
val dialog = AlertDialog.Builder(activity!!)
|
||||
.setView(layout)
|
||||
.setPositiveButton(R.string.message_content_filters_dialog_create) { _, _ ->
|
||||
var text = layout.input.text.toString();
|
||||
if (!text.isBlank()) {
|
||||
if (!layout.regexp.checkbox.isChecked) text = text.trim()
|
||||
saveFilterSubject.onNext(
|
||||
MessageContentFilterData(
|
||||
text,
|
||||
layout.caseSensitivity.checkbox.isChecked && !layout.regexp.checkbox.isChecked,
|
||||
layout.regexp.checkbox.isChecked,
|
||||
layout.contacts.checkbox.isChecked
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.button_cancel) { _, _ -> }
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS 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.
|
||||
*
|
||||
* QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package dev.octoshrimpy.quik.feature.blocking.filters
|
||||
|
||||
import com.uber.autodispose.android.lifecycle.scope
|
||||
import com.uber.autodispose.autoDisposable
|
||||
import dev.octoshrimpy.quik.common.base.QkPresenter
|
||||
import dev.octoshrimpy.quik.repository.MessageContentFilterRepository
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import javax.inject.Inject
|
||||
|
||||
class MessageContentFiltersPresenter @Inject constructor(
|
||||
private val filterRepo: MessageContentFilterRepository,
|
||||
) : QkPresenter<MessageContentFiltersView, MessageContentFiltersState>(
|
||||
MessageContentFiltersState(filters = filterRepo.getMessageContentFilters())
|
||||
) {
|
||||
|
||||
override fun bindIntents(view: MessageContentFiltersView) {
|
||||
super.bindIntents(view)
|
||||
|
||||
view.removeFilter()
|
||||
.observeOn(Schedulers.io())
|
||||
.doOnNext(filterRepo::removeFilter)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.autoDisposable(view.scope())
|
||||
.subscribe()
|
||||
|
||||
view.addFilter()
|
||||
.autoDisposable(view.scope())
|
||||
.subscribe { view.showAddDialog() }
|
||||
|
||||
view.saveFilter()
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.autoDisposable(view.scope())
|
||||
.subscribe { filterData -> filterRepo.createFilter(filterData) }
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS 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.
|
||||
*
|
||||
* QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package dev.octoshrimpy.quik.feature.blocking.filters
|
||||
|
||||
import dev.octoshrimpy.quik.model.MessageContentFilter
|
||||
import io.realm.RealmResults
|
||||
|
||||
data class MessageContentFiltersState(
|
||||
val filters: RealmResults<MessageContentFilter>? = null
|
||||
)
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
*
|
||||
* This file is part of QKSMS.
|
||||
*
|
||||
* QKSMS 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.
|
||||
*
|
||||
* QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package dev.octoshrimpy.quik.feature.blocking.filters
|
||||
|
||||
import dev.octoshrimpy.quik.common.base.QkViewContract
|
||||
import dev.octoshrimpy.quik.model.MessageContentFilterData
|
||||
import io.reactivex.Observable
|
||||
|
||||
interface MessageContentFiltersView : QkViewContract<MessageContentFiltersState> {
|
||||
|
||||
fun removeFilter(): Observable<Long>
|
||||
fun addFilter(): Observable<*>
|
||||
fun saveFilter(): Observable<MessageContentFilterData>
|
||||
|
||||
fun showAddDialog()
|
||||
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import dev.octoshrimpy.quik.common.widget.QkTextView
|
||||
import dev.octoshrimpy.quik.common.widget.RadioPreferenceView
|
||||
import dev.octoshrimpy.quik.feature.backup.BackupController
|
||||
import dev.octoshrimpy.quik.feature.blocking.BlockingController
|
||||
import dev.octoshrimpy.quik.feature.blocking.filters.MessageContentFiltersController
|
||||
import dev.octoshrimpy.quik.feature.blocking.manager.BlockingManagerController
|
||||
import dev.octoshrimpy.quik.feature.blocking.messages.BlockedMessagesController
|
||||
import dev.octoshrimpy.quik.feature.blocking.numbers.BlockedNumbersController
|
||||
@@ -65,6 +66,7 @@ interface AppComponent {
|
||||
fun inject(controller: BackupController)
|
||||
fun inject(controller: BlockedMessagesController)
|
||||
fun inject(controller: BlockedNumbersController)
|
||||
fun inject(controller: MessageContentFiltersController)
|
||||
fun inject(controller: BlockingController)
|
||||
fun inject(controller: BlockingManagerController)
|
||||
fun inject(controller: SettingsController)
|
||||
|
||||
@@ -81,6 +81,8 @@ import dev.octoshrimpy.quik.repository.ContactRepository
|
||||
import dev.octoshrimpy.quik.repository.ContactRepositoryImpl
|
||||
import dev.octoshrimpy.quik.repository.ConversationRepository
|
||||
import dev.octoshrimpy.quik.repository.ConversationRepositoryImpl
|
||||
import dev.octoshrimpy.quik.repository.MessageContentFilterRepository
|
||||
import dev.octoshrimpy.quik.repository.MessageContentFilterRepositoryImpl
|
||||
import dev.octoshrimpy.quik.repository.MessageRepository
|
||||
import dev.octoshrimpy.quik.repository.MessageRepositoryImpl
|
||||
import dev.octoshrimpy.quik.repository.ScheduledMessageRepository
|
||||
@@ -199,6 +201,9 @@ class AppModule(private var application: Application) {
|
||||
@Provides
|
||||
fun provideBlockingRepository(repository: BlockingRepositoryImpl): BlockingRepository = repository
|
||||
|
||||
@Provides
|
||||
fun provideMessageContentFilterRepository(repository: MessageContentFilterRepositoryImpl): MessageContentFilterRepository = repository
|
||||
|
||||
@Provides
|
||||
fun provideContactRepository(repository: ContactRepositoryImpl): ContactRepository = repository
|
||||
|
||||
|
||||
@@ -46,6 +46,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/blocked_numbers_title" />
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.PreferenceView
|
||||
android:id="@+id/messageContentFilters"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/message_content_filters_title" />
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.PreferenceView
|
||||
android:id="@+id/drop"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
~
|
||||
~ This file is part of QKSMS.
|
||||
~
|
||||
~ QKSMS 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.
|
||||
~
|
||||
~ QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.QkTextView
|
||||
android:id="@+id/filter"
|
||||
style="@style/TextPrimary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="24dp"
|
||||
tools:text="value"
|
||||
android:textDirection="ltr" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/caseIcon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="12dp"
|
||||
android:src="@drawable/ic_format_size_black_24dp"
|
||||
android:tint="?android:attr/textColorSecondary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/regexIcon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="12dp"
|
||||
android:src="@drawable/ic_settings_black_24dp"
|
||||
android:tint="?android:attr/textColorSecondary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/contactsIcon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:padding="12dp"
|
||||
android:src="@drawable/ic_person_black_24dp"
|
||||
android:tint="?android:attr/textColorSecondary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/removeFilter"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:padding="12dp"
|
||||
android:src="@drawable/ic_close_black_24dp"
|
||||
android:tint="?android:attr/textColorSecondary" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
~
|
||||
~ This file is part of QKSMS.
|
||||
~
|
||||
~ QKSMS 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.
|
||||
~
|
||||
~ QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/add_dialog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.QkTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:text="@string/message_content_filters_dialog_title"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
app:textSize="dialog" />
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.QkEditText
|
||||
android:id="@+id/input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:hint="@string/message_content_filters_dialog_hint"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:textColorHint="?android:attr/textColorTertiary"
|
||||
app:textSize="primary" />
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.PreferenceView
|
||||
android:id="@+id/caseSensitivity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/message_content_filters_dialog_case_switch"
|
||||
app:icon="@drawable/ic_format_size_black_24dp"
|
||||
app:widget="@layout/settings_switch_widget" />
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.PreferenceView
|
||||
android:id="@+id/regexp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/message_content_filters_dialog_regex_switch"
|
||||
app:icon="@drawable/ic_settings_black_24dp"
|
||||
app:widget="@layout/settings_switch_widget" />
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.PreferenceView
|
||||
android:id="@+id/contacts"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/message_content_filters_dialog_contacts_switch"
|
||||
app:icon="@drawable/ic_person_black_24dp"
|
||||
app:widget="@layout/settings_switch_widget" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
||||
~
|
||||
~ This file is part of QKSMS.
|
||||
~
|
||||
~ QKSMS 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.
|
||||
~
|
||||
~ QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/windowBackground"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/filters"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
<dev.octoshrimpy.quik.common.widget.QkTextView
|
||||
android:id="@+id/empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/message_content_filters_empty"
|
||||
android:textColor="?android:attr/textColorTertiary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/add"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:background="@drawable/circle_ripple"
|
||||
android:contentDescription="@string/message_content_filters_add_cd"
|
||||
android:elevation="8dp"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_add_black_24dp"
|
||||
android:tint="@color/white"
|
||||
tools:backgroundTint="@color/tools_theme" />
|
||||
|
||||
</FrameLayout>
|
||||
@@ -362,6 +362,16 @@
|
||||
<string name="blocked_numbers_dialog_hint">Phone number</string>
|
||||
<string name="blocked_numbers_dialog_block">Block</string>
|
||||
|
||||
<string name="message_content_filters_title">Message content filters</string>
|
||||
<string name="message_content_filters_empty">Your message content filters will appear here</string>
|
||||
<string name="message_content_filters_add_cd">Create a new filter</string>
|
||||
<string name="message_content_filters_dialog_title">Drop messages matching</string>
|
||||
<string name="message_content_filters_dialog_hint">Word, phrase or regular expression</string>
|
||||
<string name="message_content_filters_dialog_case_switch">Case sensitive</string>
|
||||
<string name="message_content_filters_dialog_regex_switch">Regular expression</string>
|
||||
<string name="message_content_filters_dialog_contacts_switch">Include contacts</string>
|
||||
<string name="message_content_filters_dialog_create">Create</string>
|
||||
|
||||
<string name="blocked_messages_title">Blocked messages</string>
|
||||
<string name="blocked_messages_empty">Your blocked messages will appear here</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user