@@ -21,6 +21,7 @@ import okhttp3.Request
2121import org.jsoup.Jsoup
2222import java.io.IOException
2323import 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 }
0 commit comments