Add support for blocking keywords for incoming messages
This adds support for filtering incoming messages based on message body by checking if it contains any of the blocked keywords (case insensitive). Regex and patterns are not supported at the moment. NOTE: This does not currently support MMS, only SMS. This closes #33
This commit is contained in:
parent
9942fb788a
commit
674a467694
61 changed files with 752 additions and 21 deletions
|
|
@ -0,0 +1,89 @@
|
|||
package com.simplemobiletools.smsmessenger.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.commons.extensions.beVisibleIf
|
||||
import com.simplemobiletools.commons.extensions.getProperPrimaryColor
|
||||
import com.simplemobiletools.commons.extensions.underlineText
|
||||
import com.simplemobiletools.commons.extensions.updateTextColors
|
||||
import com.simplemobiletools.commons.helpers.APP_ICON_IDS
|
||||
import com.simplemobiletools.commons.helpers.APP_LAUNCHER_NAME
|
||||
import com.simplemobiletools.commons.helpers.NavigationIcon
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.interfaces.RefreshRecyclerViewListener
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.dialogs.AddBlockedKeywordDialog
|
||||
import com.simplemobiletools.smsmessenger.dialogs.ManageBlockedKeywordsAdapter
|
||||
import com.simplemobiletools.smsmessenger.extensions.config
|
||||
import com.simplemobiletools.smsmessenger.extensions.toArrayList
|
||||
import kotlinx.android.synthetic.main.activity_manage_blocked_keywords.*
|
||||
|
||||
class ManageBlockedKeywordsActivity : BaseSimpleActivity(), RefreshRecyclerViewListener {
|
||||
override fun getAppIconIDs() = intent.getIntegerArrayListExtra(APP_ICON_IDS) ?: ArrayList()
|
||||
|
||||
override fun getAppLauncherName() = intent.getStringExtra(APP_LAUNCHER_NAME) ?: ""
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
isMaterialActivity = true
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_manage_blocked_keywords)
|
||||
updateBlockedKeywords()
|
||||
setupOptionsMenu()
|
||||
|
||||
updateMaterialActivityViews(block_keywords_coordinator, manage_blocked_keywords_list, useTransparentNavigation = true, useTopSearchMenu = false)
|
||||
setupMaterialScrollListener(manage_blocked_keywords_list, block_keywords_toolbar)
|
||||
updateTextColors(manage_blocked_keywords_wrapper)
|
||||
|
||||
manage_blocked_keywords_placeholder_2.apply {
|
||||
underlineText()
|
||||
setTextColor(getProperPrimaryColor())
|
||||
setOnClickListener {
|
||||
addOrEditBlockedKeyword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
setupToolbar(block_keywords_toolbar, NavigationIcon.Arrow)
|
||||
}
|
||||
|
||||
private fun setupOptionsMenu() {
|
||||
block_keywords_toolbar.setOnMenuItemClickListener { menuItem ->
|
||||
when (menuItem.itemId) {
|
||||
R.id.add_blocked_keyword -> {
|
||||
addOrEditBlockedKeyword()
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun refreshItems() {
|
||||
updateBlockedKeywords()
|
||||
}
|
||||
|
||||
private fun updateBlockedKeywords() {
|
||||
ensureBackgroundThread {
|
||||
val blockedKeywords = config.blockedKeywords
|
||||
runOnUiThread {
|
||||
ManageBlockedKeywordsAdapter(this, blockedKeywords.toArrayList(), this, manage_blocked_keywords_list) {
|
||||
addOrEditBlockedKeyword(it as String)
|
||||
}.apply {
|
||||
manage_blocked_keywords_list.adapter = this
|
||||
}
|
||||
|
||||
manage_blocked_keywords_placeholder.beVisibleIf(blockedKeywords.isEmpty())
|
||||
manage_blocked_keywords_placeholder_2.beVisibleIf(blockedKeywords.isEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addOrEditBlockedKeyword(keyword: String? = null) {
|
||||
AddBlockedKeywordDialog(this, keyword) {
|
||||
updateBlockedKeywords()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,7 @@ class SettingsActivity : SimpleActivity() {
|
|||
setupUseEnglish()
|
||||
setupLanguage()
|
||||
setupManageBlockedNumbers()
|
||||
setupManageBlockedKeywords()
|
||||
setupChangeDateTimeFormat()
|
||||
setupFontSize()
|
||||
setupShowCharacterCounter()
|
||||
|
|
@ -126,6 +127,20 @@ class SettingsActivity : SimpleActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupManageBlockedKeywords() {
|
||||
settings_manage_blocked_keywords.text = addLockedLabelIfNeeded(R.string.manage_blocked_keywords)
|
||||
|
||||
settings_manage_blocked_keywords_holder.setOnClickListener {
|
||||
if (isOrWasThankYouInstalled()) {
|
||||
Intent(this, ManageBlockedKeywordsActivity::class.java).apply {
|
||||
startActivity(this)
|
||||
}
|
||||
} else {
|
||||
FeatureLockedDialog(this) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupChangeDateTimeFormat() {
|
||||
settings_change_date_time_format_holder.setOnClickListener {
|
||||
ChangeDateTimeFormatDialog(this) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
package com.simplemobiletools.smsmessenger.dialogs
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.commons.extensions.getAlertDialogBuilder
|
||||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||
import com.simplemobiletools.commons.extensions.showKeyboard
|
||||
import com.simplemobiletools.commons.extensions.value
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.extensions.config
|
||||
import kotlinx.android.synthetic.main.dialog_add_blocked_keyword.view.add_blocked_keyword_edittext
|
||||
|
||||
class AddBlockedKeywordDialog(val activity: BaseSimpleActivity, private val originalKeyword: String? = null, val callback: () -> Unit) {
|
||||
init {
|
||||
val view = activity.layoutInflater.inflate(R.layout.dialog_add_blocked_keyword, null).apply {
|
||||
if (originalKeyword != null) {
|
||||
add_blocked_keyword_edittext.setText(originalKeyword)
|
||||
}
|
||||
}
|
||||
|
||||
activity.getAlertDialogBuilder()
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.apply {
|
||||
activity.setupDialogStuff(view, this) { alertDialog ->
|
||||
alertDialog.showKeyboard(view.add_blocked_keyword_edittext)
|
||||
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
val newBlockedKeyword = view.add_blocked_keyword_edittext.value
|
||||
if (originalKeyword != null && newBlockedKeyword != originalKeyword) {
|
||||
activity.config.removeBlockedKeyword(originalKeyword)
|
||||
}
|
||||
|
||||
if (newBlockedKeyword.isNotEmpty()) {
|
||||
activity.config.addBlockedKeyword(newBlockedKeyword)
|
||||
}
|
||||
|
||||
callback()
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package com.simplemobiletools.smsmessenger.dialogs
|
||||
|
||||
import android.view.*
|
||||
import android.widget.PopupMenu
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
|
||||
import com.simplemobiletools.commons.extensions.copyToClipboard
|
||||
import com.simplemobiletools.commons.extensions.getPopupMenuTheme
|
||||
import com.simplemobiletools.commons.extensions.getProperTextColor
|
||||
import com.simplemobiletools.commons.extensions.setupViewBackground
|
||||
import com.simplemobiletools.commons.interfaces.RefreshRecyclerViewListener
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
import com.simplemobiletools.smsmessenger.R
|
||||
import com.simplemobiletools.smsmessenger.extensions.config
|
||||
import kotlinx.android.synthetic.main.item_manage_blocked_keyword.view.manage_blocked_keyword_holder
|
||||
import kotlinx.android.synthetic.main.item_manage_blocked_keyword.view.manage_blocked_keyword_title
|
||||
import kotlinx.android.synthetic.main.item_manage_blocked_keyword.view.overflow_menu_anchor
|
||||
import kotlinx.android.synthetic.main.item_manage_blocked_keyword.view.overflow_menu_icon
|
||||
|
||||
class ManageBlockedKeywordsAdapter(
|
||||
activity: BaseSimpleActivity, var blockedKeywords: ArrayList<String>, val listener: RefreshRecyclerViewListener?,
|
||||
recyclerView: MyRecyclerView, itemClick: (Any) -> Unit
|
||||
) : MyRecyclerViewAdapter(activity, recyclerView, itemClick) {
|
||||
init {
|
||||
setupDragListener(true)
|
||||
}
|
||||
|
||||
override fun getActionMenuId() = R.menu.cab_blocked_keywords
|
||||
|
||||
override fun prepareActionMode(menu: Menu) {
|
||||
menu.apply {
|
||||
findItem(R.id.cab_copy_keyword).isVisible = isOneItemSelected()
|
||||
}
|
||||
}
|
||||
|
||||
override fun actionItemPressed(id: Int) {
|
||||
if (selectedKeys.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
when (id) {
|
||||
R.id.cab_copy_keyword -> copyKeywordToClipboard()
|
||||
R.id.cab_delete -> deleteSelection()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelectableItemCount() = blockedKeywords.size
|
||||
|
||||
override fun getIsItemSelectable(position: Int) = true
|
||||
|
||||
override fun getItemSelectionKey(position: Int) = blockedKeywords.getOrNull(position)?.hashCode()
|
||||
|
||||
override fun getItemKeyPosition(key: Int) = blockedKeywords.indexOfFirst { it.hashCode() == key }
|
||||
|
||||
override fun onActionModeCreated() {}
|
||||
|
||||
override fun onActionModeDestroyed() {}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_manage_blocked_keyword, parent)
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val blockedKeyword = blockedKeywords[position]
|
||||
holder.bindView(blockedKeyword, true, true) { itemView, _ ->
|
||||
setupView(itemView, blockedKeyword)
|
||||
}
|
||||
bindViewHolder(holder)
|
||||
}
|
||||
|
||||
override fun getItemCount() = blockedKeywords.size
|
||||
|
||||
private fun getSelectedItems() = blockedKeywords.filter { selectedKeys.contains(it.hashCode()) }
|
||||
|
||||
private fun setupView(view: View, blockedKeyword: String) {
|
||||
view.apply {
|
||||
setupViewBackground(activity)
|
||||
manage_blocked_keyword_holder?.isSelected = selectedKeys.contains(blockedKeyword.hashCode())
|
||||
manage_blocked_keyword_title.apply {
|
||||
text = blockedKeyword
|
||||
setTextColor(textColor)
|
||||
}
|
||||
|
||||
overflow_menu_icon.drawable.apply {
|
||||
mutate()
|
||||
setTint(activity.getProperTextColor())
|
||||
}
|
||||
|
||||
overflow_menu_icon.setOnClickListener {
|
||||
showPopupMenu(overflow_menu_anchor, blockedKeyword)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showPopupMenu(view: View, blockedKeyword: String) {
|
||||
finishActMode()
|
||||
val theme = activity.getPopupMenuTheme()
|
||||
val contextTheme = ContextThemeWrapper(activity, theme)
|
||||
|
||||
PopupMenu(contextTheme, view, Gravity.END).apply {
|
||||
inflate(getActionMenuId())
|
||||
setOnMenuItemClickListener { item ->
|
||||
val blockedKeywordId = blockedKeyword.hashCode()
|
||||
when (item.itemId) {
|
||||
R.id.cab_copy_keyword -> {
|
||||
executeItemMenuOperation(blockedKeywordId) {
|
||||
copyKeywordToClipboard()
|
||||
}
|
||||
}
|
||||
|
||||
R.id.cab_delete -> {
|
||||
executeItemMenuOperation(blockedKeywordId) {
|
||||
deleteSelection()
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun executeItemMenuOperation(blockedKeywordId: Int, callback: () -> Unit) {
|
||||
selectedKeys.add(blockedKeywordId)
|
||||
callback()
|
||||
selectedKeys.remove(blockedKeywordId)
|
||||
}
|
||||
|
||||
private fun copyKeywordToClipboard() {
|
||||
val selectedKeyword = getSelectedItems().firstOrNull() ?: return
|
||||
activity.copyToClipboard(selectedKeyword)
|
||||
finishActMode()
|
||||
}
|
||||
|
||||
private fun deleteSelection() {
|
||||
val deleteBlockedKeywords = HashSet<String>(selectedKeys.size)
|
||||
val positions = getSelectedItemPositions()
|
||||
|
||||
getSelectedItems().forEach {
|
||||
deleteBlockedKeywords.add(it)
|
||||
activity.config.removeBlockedKeyword(it)
|
||||
}
|
||||
|
||||
blockedKeywords.removeAll(deleteBlockedKeywords)
|
||||
removeSelectedItems(positions)
|
||||
if (blockedKeywords.isEmpty()) {
|
||||
listener?.refreshItems()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -68,6 +68,18 @@ class Config(context: Context) : BaseConfig(context) {
|
|||
pinnedConversations = pinnedConversations.minus(conversations.map { it.threadId.toString() })
|
||||
}
|
||||
|
||||
var blockedKeywords: Set<String>
|
||||
get() = prefs.getStringSet(BLOCKED_KEYWORDS, HashSet<String>())!!
|
||||
set(blockedKeywords) = prefs.edit().putStringSet(BLOCKED_KEYWORDS, blockedKeywords).apply()
|
||||
|
||||
fun addBlockedKeyword(keyword: String) {
|
||||
blockedKeywords = blockedKeywords.plus(keyword)
|
||||
}
|
||||
|
||||
fun removeBlockedKeyword(keyword: String) {
|
||||
blockedKeywords = blockedKeywords.minus(keyword)
|
||||
}
|
||||
|
||||
var exportSms: Boolean
|
||||
get() = prefs.getBoolean(EXPORT_SMS, true)
|
||||
set(exportSms) = prefs.edit().putBoolean(EXPORT_SMS, exportSms).apply()
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ const val SEND_LONG_MESSAGE_MMS = "send_long_message_mms"
|
|||
const val SEND_GROUP_MESSAGE_MMS = "send_group_message_mms"
|
||||
const val MMS_FILE_SIZE_LIMIT = "mms_file_size_limit"
|
||||
const val PINNED_CONVERSATIONS = "pinned_conversations"
|
||||
const val BLOCKED_KEYWORDS = "blocked_keywords"
|
||||
const val EXPORT_SMS = "export_sms"
|
||||
const val EXPORT_MMS = "export_mms"
|
||||
const val EXPORT_MIME_TYPE = "application/json"
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ class SmsReceiver : BroadcastReceiver() {
|
|||
private fun handleMessage(
|
||||
context: Context, address: String, subject: String, body: String, date: Long, read: Int, threadId: Long, type: Int, subscriptionId: Int, status: Int
|
||||
) {
|
||||
if (isMessageFilteredOut(context, address, subject, body)) {
|
||||
return
|
||||
}
|
||||
|
||||
val photoUri = SimpleContactsHelper(context).getPhotoUriFromPhoneNumber(address)
|
||||
val bitmap = context.getNotificationBitmap(photoUri)
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
|
|
@ -106,4 +110,14 @@ class SmsReceiver : BroadcastReceiver() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isMessageFilteredOut(context: Context, address: String, subject: String, body: String): Boolean {
|
||||
for (blockedKeyword in context.config.blockedKeywords) {
|
||||
if (body.contains(blockedKeyword, ignoreCase = true)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue