Skip to content

Commit b856dbe

Browse files
committed
Add filter with cleverdialer.es database
1 parent 259b65e commit b856dbe

File tree

6 files changed

+67
-42
lines changed

6 files changed

+67
-42
lines changed

app/src/main/java/com/addev/listaspam/service/MyCallReceiver.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import com.addev.listaspam.util.SpamUtils
1717
* Note: This class will not work on Android Q (API level 29) or higher due to changes in privacy permissions.
1818
* For Android Q or higher, use MyCallScreeningService instead.
1919
*/
20-
@RequiresApi(Build.VERSION_CODES.P)
2120
class MyCallReceiver : BroadcastReceiver() {
2221

2322
companion object {

app/src/main/java/com/addev/listaspam/util/SharedPreferencesUtils.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ fun shouldFilterWithResponderONo(context: Context): Boolean {
3232
return sharedPreferences.getBoolean("pref_filter_responder_o_no", true)
3333
}
3434

35+
fun shouldFilterWithCleverdialer(context: Context): Boolean {
36+
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
37+
return sharedPreferences.getBoolean("pref_filter_cleverdialer", false)
38+
}
39+
3540
fun shouldBlockNonContacts(context: Context): Boolean {
3641
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
3742
return sharedPreferences.getBoolean("pref_block_non_contacts", false)

app/src/main/java/com/addev/listaspam/util/SpamUtils.kt

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import okhttp3.Request
2121
import org.jsoup.Jsoup
2222
import java.io.IOException
2323
import java.util.Locale
24+
import java.util.logging.Logger
2425

2526
/**
2627
* Utility class for handling spam number checks and notifications.
@@ -35,6 +36,7 @@ class SpamUtils {
3536
"https://www.responderono.es/numero-de-telefono/%s"
3637
private const val RESPONDERONO_CSS_SELECTOR = ".scoreContainer .score.negative"
3738
private const val CLEVER_DIALER_URL_TEMPLATE = "https://www.cleverdialer.es/numero/%s"
39+
private const val CLEVER_DIALER_CSS_SELECTOR = ".front-stars:not(.star-rating .stars-4, .star-rating .stars-5):not(.page_speed_767712278), .circle-spam"
3840

3941
private const val USER_AGENT =
4042
"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.103 Mobile Safari/537.36"
@@ -56,49 +58,24 @@ class SpamUtils {
5658
return@launch
5759
}
5860

59-
if (number.isNullOrBlank() && shouldBlockHiddenNumbers(context)) {
61+
if (number.isBlank() && shouldBlockHiddenNumbers(context)) {
6062
handleSpamNumber(context, number, false, context.getString(R.string.block_hidden_number), callback)
6163
return@launch
6264
}
6365

64-
if (isNumberWhitelisted(context, number)) {
66+
if (isNumberWhitelisted(context, number) ||
67+
isNumberBlocked(context, number) ||
68+
isContactOrShouldBlockNonContacts(context, number)) {
6569
return@launch
6670
}
6771

68-
if (isNumberBlocked(context, number)) {
69-
handleSpamNumber(context, number, context.getString(R.string.block_already_blocked_number), callback)
70-
return@launch
71-
}
72-
73-
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
74-
if (isNumberInAgenda(context, number)) {
75-
return@launch
76-
} else if (shouldBlockNonContacts(context)) {
77-
handleSpamNumber(context, number, false, context.getString(R.string.block_non_contact), callback)
78-
return@launch
79-
}
80-
}
81-
8272
if (isInternationalCall(number) && shouldBlockInternationalNumbers(context)) {
8373
handleSpamNumber(context, number, false, context.getString(R.string.block_international_call), callback)
8474
return@launch
8575
}
8676

87-
// List to hold the functions that should be used
88-
val spamCheckers = mutableListOf<suspend (String) -> Boolean>()
89-
90-
// Add functions based on preferences
91-
if (shouldFilterWithListaSpam(context)) {
92-
spamCheckers.add(::checkListaSpam)
93-
}
94-
95-
if (shouldFilterWithResponderONo(context)) {
96-
spamCheckers.add(::checkResponderono)
97-
}
98-
99-
val isSpam = spamCheckers.any { checker ->
100-
runCatching { checker(number) }.getOrDefault(false)
101-
}
77+
val spamCheckers = buildSpamCheckers(context)
78+
val isSpam = spamCheckers.any { checker -> runCatching { checker(number) }.getOrDefault(false) }
10279

10380
if (isSpam) {
10481
handleSpamNumber(context, number, context.getString(R.string.block_spam_number), callback)
@@ -108,6 +85,24 @@ class SpamUtils {
10885
}
10986
}
11087

88+
private fun isContactOrShouldBlockNonContacts(context: Context, number: String): Boolean {
89+
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
90+
if (isNumberInAgenda(context, number)) return true
91+
if (shouldBlockNonContacts(context)) {
92+
handleSpamNumber(context, number, false, context.getString(R.string.block_non_contact), {})
93+
return true
94+
}
95+
}
96+
return false
97+
}
98+
99+
private fun buildSpamCheckers(context: Context): List<suspend (String) -> Boolean> {
100+
val spamCheckers = mutableListOf<suspend (String) -> Boolean>()
101+
if (shouldFilterWithListaSpam(context)) spamCheckers.add(::checkListaSpam)
102+
if (shouldFilterWithResponderONo(context)) spamCheckers.add(::checkResponderono)
103+
if (shouldFilterWithCleverdialer(context)) spamCheckers.add(::checkCleverdialer)
104+
return spamCheckers
105+
}
111106

112107
private fun isInternationalCall(phoneNumber: String): Boolean {
113108
// Get an instance of PhoneNumberUtil
@@ -155,11 +150,13 @@ class SpamUtils {
155150
val contentResolver = context.contentResolver
156151
val uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
157152
val projection = arrayOf(ContactsContract.CommonDataKinds.Phone.NUMBER)
153+
val selection = "${ContactsContract.CommonDataKinds.Phone.NUMBER} LIKE ? OR ${ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER} LIKE ?"
158154
val normalizedNumber = normalizePhoneNumber(number)
155+
val selectionArgs = arrayOf("%$normalizedNumber%", "%$normalizedNumber%")
159156

160157
var cursor: Cursor? = null
161158
return try {
162-
cursor = contentResolver.query(uri, projection, null, null, null)
159+
cursor = contentResolver.query(uri, projection, selection, selectionArgs, null)
163160
cursor?.use {
164161
val numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
165162
while (cursor.moveToNext()) {
@@ -200,6 +197,17 @@ class SpamUtils {
200197
return checkUrlForSpam(url, RESPONDERONO_CSS_SELECTOR)
201198
}
202199

200+
/**
201+
* Checks if a number is marked as spam on Cleverdialer.
202+
*
203+
* @param number The phone number to check.
204+
* @return True if the number is marked as spam, false otherwise.
205+
*/
206+
private suspend fun checkCleverdialer(number: String): Boolean {
207+
val url = CLEVER_DIALER_URL_TEMPLATE.format(number)
208+
return checkUrlForSpam(url, CLEVER_DIALER_CSS_SELECTOR)
209+
}
210+
203211
/**
204212
* Checks a URL for spam indicators using a CSS selector.
205213
*
@@ -210,18 +218,18 @@ class SpamUtils {
210218
private suspend fun checkUrlForSpam(url: String, cssSelector: String): Boolean {
211219
val request = Request.Builder()
212220
.header("User-Agent", USER_AGENT)
213-
.url(url).build()
221+
.url(url)
222+
.build()
223+
214224
return withContext(Dispatchers.IO) {
215225
try {
216-
val response = client.newCall(request).execute()
217-
val body = response.body?.string()
218-
body?.let {
219-
val doc = Jsoup.parse(it)
220-
val found = doc.select(cssSelector).first() != null
221-
found
222-
} ?: false
226+
client.newCall(request).execute().use { response ->
227+
val body = response.body?.string() ?: return@withContext false
228+
val doc = Jsoup.parse(body)
229+
doc.select(cssSelector).isNotEmpty()
230+
}
223231
} catch (e: IOException) {
224-
e.printStackTrace()
232+
Logger.getLogger("checkUrlForSpam").warning("Error checking URL: $url with error ${e.message}")
225233
false
226234
}
227235
}

app/src/main/res/values-es/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
<string name="pref_filter_responder_o_no_title">Filtrar con responderono.es</string>
4242
<string name="pref_filter_responder_o_no_summary">Bloquea llamadas de números de spam incluidos en la base de datos de responderono.es</string>
4343

44+
<string name="pref_filter_cleverdialer_title">Filtrar con cleverdialer.es</string>
45+
<string name="pref_filter_cleverdialer_summary">Bloquea llamadas de números de spam incluidos en la base de datos de cleverdialer.es</string>
46+
4447
<string name="pref_block_non_contacts_title">Bloquear números desconocidos</string>
4548
<string name="pref_block_non_contacts_summary">Bloquea todas las llamadas de números que no están en tu lista de contactos</string>
4649

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
<string name="pref_filter_responder_o_no_title">Filter with responderono.es</string>
4141
<string name="pref_filter_responder_o_no_summary">Blocks calls from spam numbers included in the responderono.es database</string>
4242

43+
<string name="pref_filter_cleverdialer_title">Filter with cleverdialer.es</string>
44+
<string name="pref_filter_cleverdialer_summary">Blocks calls from spam numbers included in the cleverdialer.es database</string>
45+
4346
<string name="pref_block_non_contacts_title">Block Unknown Numbers</string>
4447
<string name="pref_block_non_contacts_summary">Blocks all calls from numbers not in your contact list</string>
4548

app/src/main/res/xml/preferences.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
android:summary="@string/pref_filter_responder_o_no_summary"
2424
android:defaultValue="true" />
2525

26+
<CheckBoxPreference
27+
app:iconSpaceReserved="false"
28+
android:key="pref_filter_cleverdialer"
29+
android:title="@string/pref_filter_cleverdialer_title"
30+
android:summary="@string/pref_filter_cleverdialer_summary"
31+
android:defaultValue="true" />
32+
2633
<CheckBoxPreference
2734
app:iconSpaceReserved="false"
2835
android:key="pref_block_non_contacts"

0 commit comments

Comments
 (0)