Skip to content

Commit 0036c92

Browse files
committed
Create SpamUtils
1 parent d4b36ee commit 0036c92

File tree

1 file changed

+166
-0
lines changed

1 file changed

+166
-0
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package com.addev.listaspam
2+
3+
import android.Manifest
4+
import android.app.Activity
5+
import android.app.NotificationChannel
6+
import android.app.NotificationManager
7+
import android.content.Context
8+
import android.content.pm.PackageManager
9+
import android.os.Build
10+
import android.os.Handler
11+
import android.os.Looper
12+
import android.widget.Toast
13+
import androidx.core.app.ActivityCompat
14+
import androidx.core.app.NotificationCompat
15+
import androidx.core.app.NotificationManagerCompat
16+
import okhttp3.Call
17+
import okhttp3.Callback
18+
import okhttp3.OkHttpClient
19+
import okhttp3.Request
20+
import okhttp3.Response
21+
import org.jsoup.Jsoup
22+
import java.io.IOException
23+
24+
class SpamUtils {
25+
26+
companion object {
27+
private const val SPAM_PREFS = "SPAM_PREFS"
28+
private const val BLOCK_NUMBERS_KEY = "BLOCK_NUMBERS"
29+
private const val SPAM_URL_TEMPLATE = "https://www.listaspam.com/busca.php?Telefono=%s"
30+
private const val RESPONDERONO_URL_TEMPLATE = "https://www.responderono.es/numero-de-telefono/%s"
31+
private const val NOTIFICATION_CHANNEL_ID = "NOTIFICATION_CHANNEL"
32+
private const val NOTIFICATION_ID = 1
33+
}
34+
35+
fun checkSpamNumber(context: Context, number: String, callback: (isSpam: Boolean) -> Unit) {
36+
val url = SPAM_URL_TEMPLATE.format(number)
37+
val request = Request.Builder().url(url).build()
38+
39+
OkHttpClient().newCall(request).enqueue(object : Callback {
40+
override fun onFailure(call: Call, e: IOException) {
41+
// Handle error gracefully
42+
Handler(Looper.getMainLooper()).post {
43+
showToast(context, "Failed to check number in www.listaspam.com", Toast.LENGTH_LONG)
44+
callback(false)
45+
}
46+
}
47+
48+
override fun onResponse(call: Call, response: Response) {
49+
response.body?.string()?.let { body ->
50+
val spamData = parseHtmlForSpamReports(body)
51+
if (spamData.reports > 1) {
52+
saveSpamNumber(context, number)
53+
sendNotification(context, number)
54+
callback(true)
55+
} else {
56+
checkResponderono(context, number) { isResponderONoNegative ->
57+
if (isResponderONoNegative) {
58+
saveSpamNumber(context, number)
59+
sendNotification(context, number)
60+
callback(true)
61+
} else {
62+
Handler(Looper.getMainLooper()).post {
63+
showToast(context, "Incoming call is not spam", Toast.LENGTH_LONG)
64+
}
65+
removeSpamNumber(context, number)
66+
callback(false)
67+
}
68+
}
69+
}
70+
}
71+
}
72+
})
73+
}
74+
75+
fun checkResponderono(context: Context, number: String, callback: (isNegative: Boolean) -> Unit) {
76+
val url = RESPONDERONO_URL_TEMPLATE.format(number)
77+
val request = Request.Builder().url(url).build()
78+
79+
OkHttpClient().newCall(request).enqueue(object : Callback {
80+
override fun onFailure(call: Call, e: IOException) {
81+
// Handle error gracefully
82+
Handler(Looper.getMainLooper()).post {
83+
showToast(context, "Failed to check number in www.responderono.es", Toast.LENGTH_LONG)
84+
callback(false)
85+
}
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+
callback(isResponderONoNegative)
92+
}
93+
}
94+
})
95+
}
96+
97+
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()
104+
}
105+
}
106+
107+
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+
fun showToast(context: Context, message: String, duration: Int = Toast.LENGTH_SHORT) {
118+
Toast.makeText(context, message, duration).show()
119+
}
120+
121+
fun sendNotification(context: Context, number: String) {
122+
createNotificationChannel(context)
123+
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
124+
.setSmallIcon(R.mipmap.ic_launcher)
125+
.setContentTitle("Spam Number Blocked")
126+
.setContentText("Blocked spam number: $number")
127+
.setPriority(NotificationCompat.PRIORITY_HIGH)
128+
.build()
129+
130+
if (ActivityCompat.checkSelfPermission(
131+
context,
132+
Manifest.permission.POST_NOTIFICATIONS
133+
) != PackageManager.PERMISSION_GRANTED
134+
) {
135+
return
136+
}
137+
138+
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, notification)
139+
}
140+
141+
private fun createNotificationChannel(context: Context) {
142+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
143+
val name = "Spam Blocker Channel"
144+
val descriptionText = "Notifications for blocked spam numbers"
145+
val importance = NotificationManager.IMPORTANCE_HIGH
146+
val channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance).apply {
147+
description = descriptionText
148+
}
149+
150+
val notificationManager: NotificationManager =
151+
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
152+
notificationManager.createNotificationChannel(channel)
153+
}
154+
}
155+
156+
fun parseHtmlForSpamReports(html: String): SpamData {
157+
val document = Jsoup.parse(html)
158+
val elementReports = document.select(".n_reports .result").first()
159+
val elementSearches = document.select(".n_search .result").first()
160+
161+
val reports = elementReports?.text()?.toIntOrNull() ?: 0
162+
val searches = elementSearches?.text()?.toIntOrNull() ?: 0
163+
164+
return SpamData(reports, searches, false)
165+
}
166+
}

0 commit comments

Comments
 (0)