Skip to content

Commit bd11e79

Browse files
committed
Add 2 more features: block international calls and block hidden number calls
1 parent 55ae87a commit bd11e79

File tree

9 files changed

+168
-6
lines changed

9 files changed

+168
-6
lines changed

app/src/main/java/com/addev/listaspam/MainActivity.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,6 @@ class MainActivity : AppCompatActivity(), CallLogAdapter.OnItemChangedListener {
117117
positions.forEach { position ->
118118
callLogAdapter?.notifyItemChanged(position)
119119
}
120-
} else {
121-
callLogAdapter?.notifyDataSetChanged()
122120
}
123121
}
124122

@@ -192,6 +190,7 @@ class MainActivity : AppCompatActivity(), CallLogAdapter.OnItemChangedListener {
192190

193191
permissionDeniedDialog = AlertDialog.Builder(this)
194192
.setCancelable(false)
193+
.setIcon(android.R.drawable.ic_dialog_alert)
195194
.setTitle(R.string.permissions_required_title)
196195
.setMessage(message)
197196
.setPositiveButton(R.string.go_to_settings) { _, _ ->

app/src/main/java/com/addev/listaspam/adapter/CallLogAdapter.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import android.content.Context
77
import android.content.Intent
88
import android.content.pm.PackageManager
99
import android.net.Uri
10+
import android.provider.CallLog
1011
import android.provider.ContactsContract
1112
import android.view.Gravity
1213
import android.view.LayoutInflater
@@ -63,6 +64,7 @@ class CallLogAdapter(
6364
private val numberTextView: TextView = itemView.findViewById(R.id.numberTextView)
6465
private val dateTextView: TextView = itemView.findViewById(R.id.dateTextView)
6566
private val durationTextView: TextView = itemView.findViewById(R.id.durationTextView)
67+
private val actionTextView: TextView = itemView.findViewById(R.id.actionTextView)
6668
private val overflowMenuButton = itemView.findViewById<ImageButton>(R.id.overflowMenuButton)
6769

6870
fun bind(callLog: CallLogEntry, isBlocked: Boolean, isWhitelisted: Boolean = false) {
@@ -79,7 +81,21 @@ class CallLogAdapter(
7981
dateTextView.text = formatter.format(callLog.date)
8082
durationTextView.text = context.getString(R.string.duration_label, callLog.duration)
8183

84+
val action = when (callLog.type) {
85+
CallLog.Calls.INCOMING_TYPE -> context.getString(R.string.call_incoming)
86+
CallLog.Calls.MISSED_TYPE -> context.getString(R.string.call_missed)
87+
CallLog.Calls.REJECTED_TYPE -> context.getString(R.string.call_rejected)
88+
CallLog.Calls.BLOCKED_TYPE -> context.getString(R.string.call_blocked)
89+
else -> context.getString(R.string.call_unknown)
90+
}
91+
92+
actionTextView.text = action
93+
8294
when {
95+
callLog.type == CallLog.Calls.BLOCKED_TYPE -> {
96+
numberTextView.setTextColor(ContextCompat.getColor(context, android.R.color.holo_red_light))
97+
actionTextView.setTextColor(ContextCompat.getColor(context, android.R.color.holo_red_light))
98+
}
8399
isBlocked -> numberTextView.setTextColor(ContextCompat.getColor(context, android.R.color.holo_red_light))
84100
isWhitelisted -> numberTextView.setTextColor(ContextCompat.getColor(context, android.R.color.holo_blue_dark))
85101
else -> numberTextView.setTextColor(ContextCompat.getColor(context, android.R.color.darker_gray))

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ fun isBlockingEnabled(context: Context): Boolean {
1212
return sharedPreferences.getBoolean("pref_enable_blocking", true)
1313
}
1414

15+
fun shouldBlockHiddenNumbers(context: Context): Boolean {
16+
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
17+
return sharedPreferences.getBoolean("pref_block_hidden_numbers", true)
18+
}
19+
20+
fun shouldBlockInternationalNumbers(context: Context): Boolean {
21+
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
22+
return sharedPreferences.getBoolean("pref_block_international_numbers", true)
23+
}
24+
1525
fun shouldFilterWithListaSpam(context: Context): Boolean {
1626
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
1727
return sharedPreferences.getBoolean("pref_filter_lista_spam", true)

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

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import android.provider.ContactsContract
1010
import android.widget.Toast
1111
import androidx.core.content.ContextCompat
1212
import com.addev.listaspam.R
13+
import com.google.i18n.phonenumbers.PhoneNumberUtil
14+
import com.google.i18n.phonenumbers.Phonenumber
1315
import kotlinx.coroutines.CoroutineScope
1416
import kotlinx.coroutines.Dispatchers
1517
import kotlinx.coroutines.launch
@@ -18,6 +20,7 @@ import okhttp3.OkHttpClient
1820
import okhttp3.Request
1921
import org.jsoup.Jsoup
2022
import java.io.IOException
23+
import java.util.Locale
2124

2225
/**
2326
* Utility class for handling spam number checks and notifications.
@@ -27,8 +30,10 @@ class SpamUtils {
2730
companion object {
2831
// URLs
2932
const val LISTA_SPAM_URL_TEMPLATE = "https://www.listaspam.com/busca.php?Telefono=%s"
33+
const val LISTA_SPAM_CSS_SELECTOR = ".data_top .phone_rating.result-3, .data_top .phone_rating.result-2, .data_top .phone_rating.result-1, .alert-icon-big"
3034
private const val RESPONDERONO_URL_TEMPLATE =
3135
"https://www.responderono.es/numero-de-telefono/%s"
36+
private const val RESPONDERONO_CSS_SELECTOR = ".scoreContainer .score.negative"
3237
private const val CLEVER_DIALER_URL_TEMPLATE = "https://www.cleverdialer.es/numero/%s"
3338
}
3439

@@ -44,14 +49,28 @@ class SpamUtils {
4449
fun checkSpamNumber(context: Context, number: String, callback: (isSpam: Boolean) -> Unit) {
4550
CoroutineScope(Dispatchers.IO).launch {
4651
if (!isBlockingEnabled(context)) {
52+
showToast(context, context.getString(R.string.blocking_disabled), Toast.LENGTH_LONG)
53+
return@launch
54+
}
55+
56+
if (number.isNullOrBlank() && shouldBlockHiddenNumbers(context)) {
57+
showToast(context, context.getString(R.string.block_hidden_number), Toast.LENGTH_LONG)
58+
handleSpamNumber(context, number, false, callback)
4759
return@launch
4860
}
4961

5062
if (isNumberWhitelisted(context, number)) {
5163
return@launch
5264
}
5365

66+
if (isInternationalCall(number) && shouldBlockInternationalNumbers(context)) {
67+
showToast(context, context.getString(R.string.block_international_call), Toast.LENGTH_LONG)
68+
handleSpamNumber(context, number, false, callback)
69+
return@launch
70+
}
71+
5472
if (isNumberBlocked(context, number)) {
73+
showToast(context, context.getString(R.string.block_already_blocked_number), Toast.LENGTH_LONG)
5574
handleSpamNumber(context, number, callback)
5675
return@launch
5776
}
@@ -60,7 +79,9 @@ class SpamUtils {
6079
if (isNumberInAgenda(context, number)) {
6180
return@launch
6281
} else if (shouldBlockNonContacts(context)) {
63-
handleSpamNumber(context, number, callback)
82+
showToast(context, context.getString(R.string.block_non_contact), Toast.LENGTH_LONG)
83+
handleSpamNumber(context, number, false, callback)
84+
return@launch
6485
}
6586
}
6687

@@ -81,13 +102,39 @@ class SpamUtils {
81102
}
82103

83104
if (isSpam) {
105+
showToast(context, context.getString(R.string.block_non_contact), Toast.LENGTH_LONG)
84106
handleSpamNumber(context, number, callback)
85107
} else {
86108
handleNonSpamNumber(context, number, callback)
87109
}
88110
}
89111
}
90112

113+
114+
fun isInternationalCall(phoneNumber: String): Boolean {
115+
// Get an instance of PhoneNumberUtil
116+
val phoneNumberUtil = PhoneNumberUtil.getInstance()
117+
118+
try {
119+
// Parse the phone number
120+
val parsedNumber: Phonenumber.PhoneNumber = phoneNumberUtil.parse(phoneNumber, null)
121+
122+
// Get the country code of the parsed number
123+
val phoneCountryCode = parsedNumber.countryCode
124+
125+
// Get the device's country code based on locale
126+
val deviceCountryCode = PhoneNumberUtil.getInstance()
127+
.getCountryCodeForRegion(Locale.getDefault().country)
128+
129+
// Check if the country codes are different
130+
return phoneCountryCode != deviceCountryCode
131+
132+
} catch (e: Exception) {
133+
e.printStackTrace()
134+
return false
135+
}
136+
}
137+
91138
/**
92139
* Normalizes a phone number by removing all non-digit characters.
93140
*
@@ -140,7 +187,7 @@ class SpamUtils {
140187
val url = LISTA_SPAM_URL_TEMPLATE.format(number)
141188
return checkUrlForSpam(
142189
url,
143-
".data_top .phone_rating.result-3, .data_top .phone_rating.result-2, .data_top .phone_rating.result-1, .alert-icon-big"
190+
LISTA_SPAM_CSS_SELECTOR
144191
)
145192
}
146193

@@ -152,7 +199,7 @@ class SpamUtils {
152199
*/
153200
private suspend fun checkResponderono(number: String): Boolean {
154201
val url = RESPONDERONO_URL_TEMPLATE.format(number)
155-
return checkUrlForSpam(url, ".scoreContainer .score.negative")
202+
return checkUrlForSpam(url, RESPONDERONO_CSS_SELECTOR)
156203
}
157204

158205
/**
@@ -191,7 +238,24 @@ class SpamUtils {
191238
number: String,
192239
callback: (isSpam: Boolean) -> Unit
193240
) {
194-
saveSpamNumber(context, number)
241+
handleSpamNumber(context, number, true, callback)
242+
}
243+
244+
/**
245+
* Handles the scenario when a phone number is identified as spam.
246+
* @param context Context for accessing resources.
247+
* @param number Phone number identified as spam.
248+
* @param callback Callback function to handle the result.
249+
*/
250+
private fun handleSpamNumber(
251+
context: Context,
252+
number: String,
253+
saveNumber: Boolean,
254+
callback: (isSpam: Boolean) -> Unit
255+
) {
256+
if (saveNumber) {
257+
saveSpamNumber(context, number)
258+
}
195259
sendNotification(context, number)
196260
callback(true)
197261
}

app/src/main/res/layout/item_call_log.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@
3131
android:layout_alignParentStart="true"
3232
android:layout_alignStart="@id/numberTextView" />
3333

34+
<TextView
35+
android:id="@+id/actionTextView"
36+
android:layout_width="wrap_content"
37+
android:layout_height="wrap_content"
38+
android:textSize="14sp"
39+
android:layout_below="@id/durationTextView"
40+
android:layout_alignParentStart="true"
41+
android:layout_alignStart="@id/numberTextView" />
42+
3443
<ImageButton
3544
android:id="@+id/overflowMenuButton"
3645
android:layout_width="wrap_content"

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@
2222
<string name="notification_text_spam_blocked">Número de spam bloqueado: %1$s</string>
2323
<string name="number_copied_to_clipboard">Número copiado al portapapeles</string>
2424

25+
<!-- Call types -->
26+
<string name="call_incoming">Llamada entrante</string>
27+
<string name="call_missed">Llamada perdida</string>
28+
<string name="call_rejected">Llamada rechazada</string>
29+
<string name="call_blocked">Llamada bloqueada</string>
30+
<string name="call_unknown">Desconocido</string>
31+
2532
<!-- Preferences -->
2633
<string name="action_settings">Ajustes</string>
2734

@@ -37,6 +44,12 @@
3744
<string name="pref_block_non_contacts_title">Bloquear números desconocidos</string>
3845
<string name="pref_block_non_contacts_summary">Bloquea todas las llamadas de números que no están en tu lista de contactos</string>
3946

47+
<string name="pref_block_hidden_numbers_title">Bloquear números ocultos</string>
48+
<string name="pref_block_hidden_numbers_summary">Bloquea todas las llamadas de números ocultos</string>
49+
50+
<string name="pref_block_international_numbers_title">Bloquear números internacionales</string>
51+
<string name="pref_block_international_numbers_summary">Bloquea todas las llamadas de un país diferente al tuyo</string>
52+
4053
<string name="pref_show_notification_title">Mostrar notificación al bloquear llamadas</string>
4154
<string name="pref_show_notification_summary">Recibe una notificación cuando una llamada es bloqueada</string>
4255

@@ -47,4 +60,12 @@
4760
<string name="add_to_whitelist">Añadir a lista blanca</string>
4861
<string name="unblock">Desbloquear</string>
4962
<string name="remove_from_whitelist">Eliminar de la lista blanca</string>
63+
64+
<!-- SpamUtils blocking reasons -->
65+
<string name="blocking_disabled">El bloqueo de llamadas está desactivado en la configuración</string>
66+
<string name="block_spam_number">Bloqueando llamada porque está reportada como spam</string>
67+
<string name="block_hidden_number">Bloqueando llamada porque es de un número oculto</string>
68+
<string name="block_international_call">Bloqueando llamada porque es una llamada internacional</string>
69+
<string name="block_already_blocked_number">Bloqueando llamada porque es de un número ya bloqueado</string>
70+
<string name="block_non_contact">Bloqueando llamada porque no es de un contacto en tu agenda</string>
5071
</resources>

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@
2222
<string name="notification_text_spam_blocked">Blocked spam number: %1$s</string>
2323
<string name="number_copied_to_clipboard">Phone number copied to clipboard</string>
2424

25+
<!-- Call types -->
26+
<string name="call_incoming">Incoming call</string>
27+
<string name="call_missed">Missed call</string>
28+
<string name="call_rejected">Rejected call</string>
29+
<string name="call_blocked">Blocked call</string>
30+
<string name="call_unknown">Unknown</string>
31+
2532
<!-- Preferences -->
2633
<string name="action_settings">Settings</string>
2734

@@ -37,14 +44,29 @@
3744
<string name="pref_block_non_contacts_title">Block Unknown Numbers</string>
3845
<string name="pref_block_non_contacts_summary">Blocks all calls from numbers not in your contact list</string>
3946

47+
<string name="pref_block_hidden_numbers_title">Block Hidden Numbers</string>
48+
<string name="pref_block_hidden_numbers_summary">Block all calls from hidden numbers</string>
49+
50+
<string name="pref_block_international_numbers_title">Block International Numbers</string>
51+
<string name="pref_block_international_numbers_summary">Block all calls from a country different from yours</string>
52+
4053
<string name="pref_show_notification_title">Show Notification on call blocking</string>
4154
<string name="pref_show_notification_summary">Receive a notification when a call is blocked</string>
4255

56+
4357
<!-- item_actions -->
4458
<string name="report_in_listaspam_com">Report in ListaSpam</string>
4559
<string name="search_in_google">Search in Google</string>
4660
<string name="block">Block</string>
4761
<string name="add_to_whitelist">Add to whitelist</string>
4862
<string name="unblock">Unblock</string>
4963
<string name="remove_from_whitelist">Remove from whitelist</string>
64+
65+
<!-- SpamUtils blocking reasons -->
66+
<string name="blocking_disabled">Call blocking is disabled in settings</string>
67+
<string name="block_hidden_number">Blocking call because it\'s from a hidden number</string>
68+
<string name="block_spam_number">Blocking call because it\'s reported as spam</string>
69+
<string name="block_international_call">Blocking call because it\'s an international call</string>
70+
<string name="block_already_blocked_number">Blocking call because it\'s from an already blocked number</string>
71+
<string name="block_non_contact">Blocking call because it\'s not from a contact in your agenda</string>
5072
</resources>

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@
2222
android:summary="@string/pref_filter_responder_o_no_summary"
2323
android:defaultValue="true" />
2424

25+
<CheckBoxPreference
26+
android:key="pref_block_hidden_numbers"
27+
android:title="@string/pref_block_hidden_numbers_title"
28+
android:summary="@string/pref_block_hidden_numbers_summary"
29+
android:defaultValue="true" />
30+
31+
<CheckBoxPreference
32+
android:key="pref_block_international_numbers"
33+
android:title="@string/pref_block_international_numbers_title"
34+
android:summary="@string/pref_block_international_numbers_summary"
35+
android:defaultValue="false" />
36+
2537
<!-- Bloquear todos los números que no sean contactos -->
2638
<CheckBoxPreference
2739
android:key="pref_block_non_contacts"

gradle/libs.versions.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
11
[versions]
22
agp = "8.5.1"
3+
jsoup = "1.14.3"
34
kotlin = "1.9.0"
45
coreKtx = "1.13.1"
56
junit = "4.13.2"
67
junitVersion = "1.2.1"
78
espressoCore = "3.6.1"
89
appcompat = "1.7.0"
10+
kotlinxCoroutinesCore = "1.7.3"
11+
libphonenumber = "8.12.35"
912
material = "1.12.0"
1013
activity = "1.9.1"
1114
constraintlayout = "2.1.4"
15+
okhttp = "4.9.3"
1216
preferenceKtx = "1.2.1"
1317

1418
[libraries]
1519
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
20+
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
1621
junit = { group = "junit", name = "junit", version.ref = "junit" }
1722
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
1823
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
1924
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
25+
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesCore" }
26+
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
27+
libphonenumber = { module = "com.googlecode.libphonenumber:libphonenumber", version.ref = "libphonenumber" }
2028
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
2129
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
2230
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
2331
androidx-preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preferenceKtx" }
32+
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
2433

2534
[plugins]
2635
android-application = { id = "com.android.application", version.ref = "agp" }

0 commit comments

Comments
 (0)