11package com.addev.listaspam
22
33import android.Manifest
4+ import android.annotation.SuppressLint
45import android.app.NotificationChannel
56import android.app.NotificationManager
67import android.content.BroadcastReceiver
78import android.content.Context
89import android.content.Intent
910import android.content.pm.PackageManager
1011import android.os.Build
12+ import android.telecom.TelecomManager
1113import android.telephony.TelephonyManager
1214import android.widget.Toast
1315import androidx.annotation.RequiresApi
@@ -20,146 +22,32 @@ import java.io.IOException
2022
2123class MyCallReceiver : BroadcastReceiver () {
2224
23- companion object {
24- private const val SPAM_PREFS = " SPAM_PREFS"
25- private const val BLOCK_NUMBERS_KEY = " BLOCK_NUMBERS"
26- private const val SPAM_URL_TEMPLATE = " https://www.listaspam.com/busca.php?Telefono=%s"
27- private const val RESPONDERONO_URL_TEMPLATE = " https://www.responderono.es/numero-de-telefono/%s"
28- private const val NOTIFICATION_CHANNEL_ID = " NOTIFICATION_CHANNEL"
29- private const val NOTIFICATION_ID = 1
30- }
25+ val spamUtils = SpamUtils ()
3126
32- @RequiresApi(Build .VERSION_CODES .CUPCAKE )
27+ @RequiresApi(Build .VERSION_CODES .P )
3328 override fun onReceive (context : Context , intent : Intent ) {
34- val state = intent.getStringExtra(TelephonyManager .EXTRA_STATE )
35- val incomingNumber = intent.getStringExtra(TelephonyManager .EXTRA_INCOMING_NUMBER )
36-
37- if (state == TelephonyManager .EXTRA_STATE_RINGING ) {
38- incomingNumber?.let {
39- checkSpamNumber(context, it)
40- showToast(context, it)
41- }
42- } else if (state == TelephonyManager .EXTRA_STATE_IDLE ) {
43- // Do nothing (remove missed call notification)
44- }
45- }
46-
47- private fun checkSpamNumber (context : Context , number : String ) {
48- val url = SPAM_URL_TEMPLATE .format(number)
49- val request = Request .Builder ().url(url).build()
50-
51- OkHttpClient ().newCall(request).enqueue(object : Callback {
52- override fun onFailure (call : Call , e : IOException ) {
53- // Handle error gracefully
54- showToast(context, " Failed to check number in www.responderono.es" , Toast .LENGTH_LONG )
55- }
56-
57- override fun onResponse (call : Call , response : Response ) {
58- response.body?.string()?.let { body ->
59- val spamData = parseHtmlForSpamReports(body)
60- if (spamData.reports > 1 ) {
61- saveSpamNumber(context, number)
62- sendNotification(context, number)
63- } else {
64- checkResponderono(context, number, spamData)
65- if (spamData.responderONoNegative) {
66- saveSpamNumber(context, number)
67- sendNotification(context, number)
68- } else {
69- removeSpamNumber(context, number)
70- showToast(context, " Incoming call is not spam" , Toast .LENGTH_LONG )
29+ if (intent.action == " android.intent.action.PHONE_STATE" ) {
30+ val state = intent.getStringExtra(TelephonyManager .EXTRA_STATE )
31+ val incomingNumber =
32+ intent.getStringExtra(TelephonyManager .EXTRA_INCOMING_NUMBER ) ? : return
33+
34+ if (state == TelephonyManager .EXTRA_STATE_RINGING ) {
35+ incomingNumber?.let {
36+ spamUtils.checkSpamNumber(context, it) { isSpam ->
37+ if (isSpam) {
38+ endCall(context)
7139 }
7240 }
7341 }
7442 }
75- })
76- }
77-
78- private fun checkResponderono (context : Context , number : String , spamData : SpamData ) {
79- val url = RESPONDERONO_URL_TEMPLATE .format(number)
80- val request = Request .Builder ().url(url).build()
81-
82- OkHttpClient ().newCall(request).enqueue(object : Callback {
83- override fun onFailure (call : Call , e : IOException ) {
84- // Handle error gracefully
85- showToast(context, " Failed to check number in www.responderono.es" , Toast .LENGTH_LONG )
86- }
87-
88- override fun onResponse (call : Call , response : Response ) {
89- response.body?.string()?.let { body ->
90- val isResponderONoNegative = body.contains(" .scoreContainer .score.negative" )
91- spamData.responderONoNegative = isResponderONoNegative
92- }
93- }
94- })
95- }
96-
97- private fun saveSpamNumber (context : Context , number : String ) {
98- val sharedPreferences = context.getSharedPreferences(SPAM_PREFS , Context .MODE_PRIVATE )
99- val blockedNumbers = sharedPreferences.getStringSet(BLOCK_NUMBERS_KEY , mutableSetOf ())?.toMutableSet()
100- blockedNumbers?.add(number)
101- with (sharedPreferences.edit()) {
102- putStringSet(BLOCK_NUMBERS_KEY , blockedNumbers)
103- apply ()
10443 }
10544 }
10645
107- private fun removeSpamNumber (context : Context , number : String ) {
108- val sharedPreferences = context.getSharedPreferences(SPAM_PREFS , Context .MODE_PRIVATE )
109- val blockedNumbers = sharedPreferences.getStringSet(BLOCK_NUMBERS_KEY , mutableSetOf ())?.toMutableSet()
110- blockedNumbers?.remove(number)
111- with (sharedPreferences.edit()) {
112- putStringSet(BLOCK_NUMBERS_KEY , blockedNumbers)
113- apply ()
114- }
115- }
116-
117- private fun showToast (context : Context , message : String , duration : Int = Toast .LENGTH_SHORT ) {
118- Toast .makeText(context, message, duration).show()
119- }
120-
121- private fun sendNotification (context : Context , number : String ) {
122- createNotificationChannel(context)
123- val notification = NotificationCompat .Builder (context, NOTIFICATION_CHANNEL_ID )
124- .setContentTitle(" Spam Number Blocked" )
125- .setContentText(" Blocked spam number: $number " )
126- .setPriority(NotificationCompat .PRIORITY_HIGH )
127- .build()
128-
129- if (ActivityCompat .checkSelfPermission(
130- context,
131- Manifest .permission.POST_NOTIFICATIONS
132- ) != PackageManager .PERMISSION_GRANTED
133- ) {
134- return
135- }
136-
137- NotificationManagerCompat .from(context).notify(NOTIFICATION_ID , notification)
46+ @RequiresApi(Build .VERSION_CODES .P )
47+ @SuppressLint(" MissingPermission" )
48+ private fun endCall (context : Context ) {
49+ val telMgr = context.getSystemService(Context .TELECOM_SERVICE ) as TelecomManager
50+ telMgr.endCall()
13851 }
13952
140- private fun createNotificationChannel (context : Context ) {
141- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
142- val name = " Spam Blocker Channel"
143- val descriptionText = " Notifications for blocked spam numbers"
144- val importance = NotificationManager .IMPORTANCE_HIGH
145- val channel = NotificationChannel (NOTIFICATION_CHANNEL_ID , name, importance).apply {
146- description = descriptionText
147- }
148-
149- val notificationManager: NotificationManager =
150- context.getSystemService(Context .NOTIFICATION_SERVICE ) as NotificationManager
151- notificationManager.createNotificationChannel(channel)
152- }
153- }
154-
155- private fun parseHtmlForSpamReports (html : String ): SpamData {
156- val document = Jsoup .parse(html)
157- val elementReports = document.select(" .n_reports .result" ).first()
158- val elementSearches = document.select(" .n_search .result" ).first()
159-
160- val reports = elementReports?.text()?.toIntOrNull() ? : 0
161- val searches = elementSearches?.text()?.toIntOrNull() ? : 0
162-
163- return SpamData (reports, searches, false )
164- }
16553}
0 commit comments