Skip to content

Commit d7e3fa4

Browse files
committed
integrate TrueCaller API
1 parent 0d8164f commit d7e3fa4

File tree

11 files changed

+638
-18
lines changed

11 files changed

+638
-18
lines changed

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ android {
1212
minSdk = 29
1313
//noinspection OldTargetApi
1414
targetSdk = 34
15-
versionCode = 15
16-
versionName = "2.2.4"
15+
versionCode = 16
16+
versionName = "2.3.0"
1717

1818
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
1919
}

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import com.addev.listaspam.util.setListaSpamApiLang
3636
import com.addev.listaspam.util.setTellowsApiCountry
3737
import java.util.Locale
3838
import androidx.core.net.toUri
39+
import com.addev.listaspam.util.getTruecallerApiCountry
40+
import com.addev.listaspam.util.setTruecallerApiCountry
3941

4042
class MainActivity : AppCompatActivity(), CallLogAdapter.OnItemChangedListener {
4143

@@ -65,7 +67,8 @@ class MainActivity : AppCompatActivity(), CallLogAdapter.OnItemChangedListener {
6567
recyclerView?.layoutManager = LinearLayoutManager(this)
6668

6769
setLanguage()
68-
setCountry()
70+
setTellowsCountry()
71+
setTruecallerCountry()
6972
checkUpdates()
7073
}
7174

@@ -124,7 +127,7 @@ class MainActivity : AppCompatActivity(), CallLogAdapter.OnItemChangedListener {
124127
setListaSpamApiLang(this, finalLang.uppercase())
125128
}
126129

127-
private fun setCountry() {
130+
private fun setTellowsCountry() {
128131
if (getTellowsApiCountry(this) != null) return
129132

130133
val systemCountry = Locale.getDefault().country.lowercase()
@@ -135,6 +138,17 @@ class MainActivity : AppCompatActivity(), CallLogAdapter.OnItemChangedListener {
135138
setTellowsApiCountry(this, finalCountry)
136139
}
137140

141+
private fun setTruecallerCountry() {
142+
if (getTruecallerApiCountry(this) != null) return
143+
144+
val systemCountry = Locale.getDefault().country.uppercase()
145+
val supportedCountries =
146+
resources.getStringArray(R.array.truecaller_region_code).toSet()
147+
148+
val finalCountry = if (supportedCountries.contains(systemCountry)) systemCountry else "US"
149+
setTruecallerApiCountry(this, finalCountry)
150+
}
151+
138152

139153
private fun showNumberInputDialog() {
140154
val builder = AlertDialog.Builder(this)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.addev.listaspam.R
1010
import okhttp3.OkHttpClient
1111
import okhttp3.Request
1212
import org.json.JSONObject
13+
import androidx.core.net.toUri
1314

1415
/**
1516
* Class responsible for checking if a newer version of the app is available on a GitHub repository.
@@ -81,7 +82,7 @@ class UpdateChecker(
8182

8283
companion object {
8384
fun redirectToGitHubReleasePage(context: Context, url: String) {
84-
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
85+
val intent = Intent(Intent.ACTION_VIEW, url.toUri())
8586
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
8687
context.startActivity(intent)
8788
}

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

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ object ApiUtils {
1717
private const val TELLOWS_API_URL = "www.tellows.de"
1818
private const val TELLOWS_API_KEY = "koE5hjkOwbHnmcADqZuqqq2"
1919

20+
private const val TRUECALLER_API_URL_EU = "search5-eu.truecaller.com"
21+
private const val TRUECALLER_API_URL_NONEU = "search5-noneu.truecaller.com"
22+
private const val TRUECALLER_API_KEY = "a1i1V--ua298eldF0hb0rL520GjDz7bzVAdt63J2nzZBnWlEKNCJUeln_7kWj4Ir"
23+
24+
private val EU_COUNTRIES = setOf(
25+
"AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR",
26+
"DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL",
27+
"PL", "PT", "RO", "SK", "SI", "ES", "SE"
28+
)
29+
2030
private val client = OkHttpClient()
2131

2232
/**
@@ -111,6 +121,55 @@ object ApiUtils {
111121
}
112122
}
113123

124+
fun checkTruecallerSpamApi(number: String, countryCode: String): Boolean {
125+
val host = if (EU_COUNTRIES.contains(countryCode.uppercase())) {
126+
TRUECALLER_API_URL_EU
127+
} else {
128+
TRUECALLER_API_URL_NONEU
129+
}
130+
131+
val url = HttpUrl.Builder()
132+
.scheme("https")
133+
.host(host)
134+
.addPathSegments("v2/search")
135+
.addQueryParameter("q", number)
136+
.addQueryParameter("countryCode", countryCode)
137+
.addQueryParameter("type", "4")
138+
.addQueryParameter("locAddr", "")
139+
.addQueryParameter("placement", "SEARCHRESULTS,HISTORY,DETAILS")
140+
.addQueryParameter("adId", "")
141+
.addQueryParameter("encoding", "json")
142+
.build()
143+
144+
val request = Request.Builder()
145+
.url(url)
146+
.get()
147+
.header("Connection", "Keep-Alive")
148+
.header("User-Agent", "Truecaller/9.00.3 (Android;10)")
149+
.header("Authorization", "Bearer $TRUECALLER_API_KEY")
150+
.header("Host", "search5-eu.truecaller.com")
151+
.build()
152+
153+
return try {
154+
val response = client.newCall(request).execute()
155+
if (!response.isSuccessful) return false
156+
157+
val bodyString = response.body?.string() ?: return false
158+
val json = JSONObject(bodyString)
159+
val dataArray = json.optJSONArray("data") ?: return false
160+
if (dataArray.length() == 0) return false
161+
162+
val firstEntry = dataArray.getJSONObject(0)
163+
val spamInfo = firstEntry.optJSONObject("spamInfo")
164+
val spamType = spamInfo?.optString("spamType")
165+
val spamScore = spamInfo?.optInt("spamScore", 0) ?: 0 // Reports quantity
166+
167+
!spamType.isNullOrBlank() && spamScore > 1
168+
} catch (e: Exception) {
169+
false
170+
}
171+
}
172+
114173
fun checkTellowsSpamApi(number: String, country: String): Boolean {
115174
val url = HttpUrl.Builder()
116175
.scheme("https")
@@ -159,8 +218,7 @@ object ApiUtils {
159218
*
160219
* @param phone The phone number to report (without country code prefix, if already localized).
161220
* @param comment A description of the issue or behavior associated with the number.
162-
* @param complainTypeId Type of complaint, e.g. 5 = "estafa" (scam).
163-
* @param userScore The danger score (1 = safe, 9 = dangerous).
221+
* @param isSpam
164222
* @param lang Language and country code, e.g. "es".
165223
* @return `true` if the report was accepted; `false` otherwise.
166224
*/
@@ -169,9 +227,9 @@ object ApiUtils {
169227
comment: String,
170228
isSpam: Boolean,
171229
lang: String = "es",
172-
complainTypeId: Int = 5
173230
): Boolean {
174231
val userScore = if (isSpam) 9 else 1
232+
val complainTypeId = if (isSpam) 5 else 2 // 5 is aggressive advertising and 2 is reliable number
175233

176234
val url = HttpUrl.Builder()
177235
.scheme("https")

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,21 @@ fun setListaSpamApiLang(context: Context, languageCode: String) =
4141
fun shouldFilterWithTellowsApi(context: Context): Boolean =
4242
getBooleanPref(context, "pref_filter_tellows_api", true)
4343

44+
fun shouldFilterWithTruecallerApi(context: Context): Boolean =
45+
getBooleanPref(context, "pref_truecaller_api", true)
46+
4447
fun getTellowsApiCountry(context: Context): String? =
45-
getStringPref(context, "pref_tellows_country")?.lowercase()
48+
getStringPref(context, "pref_tellows_country")?.uppercase()
4649

4750
fun setTellowsApiCountry(context: Context, countryCode: String) =
4851
setStringPref(context, "pref_tellows_country", countryCode.lowercase())
4952

53+
fun getTruecallerApiCountry(context: Context): String? =
54+
getStringPref(context, "pref_truecaller_country")?.uppercase()
55+
56+
fun setTruecallerApiCountry(context: Context, countryCode: String) =
57+
setStringPref(context, "pref_truecaller_country", countryCode.uppercase())
58+
5059
fun shouldFilterWithListaSpamScraper(context: Context): Boolean =
5160
getBooleanPref(context, "pref_filter_lista_spam_scraper", false)
5261

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,13 +248,22 @@ class SpamUtils {
248248
}
249249
}
250250

251+
// Tellows
251252
val tellowsApi = shouldFilterWithTellowsApi(context)
252253
if (tellowsApi) {
253254
spamCheckers.add { number ->
254255
ApiUtils.checkTellowsSpamApi(number, getTellowsApiCountry(context) ?: "us")
255256
}
256257
}
257258

259+
// Truecaller
260+
val truecallerApi = shouldFilterWithTruecallerApi(context)
261+
if (truecallerApi) {
262+
spamCheckers.add { number ->
263+
ApiUtils.checkTruecallerSpamApi(number, getTruecallerApiCountry(context) ?: "US")
264+
}
265+
}
266+
258267
if (shouldFilterWithListaSpamScraper(context) && !listaSpamApi) spamCheckers.add(::checkListaSpam)
259268

260269
if (shouldFilterWithResponderONo(context)) spamCheckers.add(::checkResponderono)

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@
7878
android:checked="true"
7979
android:text="Tellows" />
8080

81+
<!-- <CheckBox-->
82+
<!-- android:id="@+id/checkboxTruecaller"-->
83+
<!-- android:layout_width="wrap_content"-->
84+
<!-- android:layout_height="wrap_content"-->
85+
<!-- android:checked="true"-->
86+
<!-- android:text="Truecaller" />-->
87+
8188
<View
8289
android:layout_width="match_parent"
8390
android:layout_height="20dp" />

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
<!-- Preferences Sections -->
3737
<string name="pref_category_general_blocking">General</string>
3838
<string name="pref_category_unknown_phone_api">API Unknownphone</string>
39-
<string name="pref_category_tellows_api">API Tellows</string>
39+
<string name="pref_category_tellows_api">API Tellows 🌐</string>
40+
<string name="pref_category_truecaller_api">API Truecaller 🌐</string>
4041
<string name="pref_category_scrapers_es">Scrapers 🇪🇸</string>
4142
<string name="pref_category_spam_filters">Filtros de números de spam</string>
4243
<string name="pref_category_other_options">Otras opciones de bloqueo</string>
@@ -48,27 +49,34 @@
4849

4950
<!-- API Unknownphone -->
5051
<string name="pref_filter_lista_spam_title">API Unknownphone</string>
51-
<string name="pref_filter_lista_spam_summary">[🌐] (Recomendado) Bloquea llamadas de números spam incluidos en la API unknownphone.com</string>
52+
<string name="pref_filter_lista_spam_summary">(Recomendado) Bloquea llamadas de números spam incluidos en la API unknownphone.com</string>
5253

5354
<string name="pref_filter_lista_spam_api_language_title">Idioma API unknownphone.com</string>
5455
<string name="pref_filter_lista_spam_api_language_summary">Seleccione el idioma para realizar las búsquedas en la API de unknownphone.com (afecta a los resultados)</string>
5556

5657
<!-- API Tellows -->
5758
<string name="pref_filter_tellows_title">API Tellows</string>
58-
<string name="pref_filter_tellows_summary">[🌐] (Recomendado) Bloquea llamadas de números spam listados en la API de Tellows</string>
59+
<string name="pref_filter_tellows_summary">(Recomendado) Bloquea llamadas de números spam listados en la API de Tellows</string>
5960

6061
<string name="pref_filter_tellows_api_country_title">País API Tellows</string>
6162
<string name="pref_filter_tellows_api_country_summary">Seleccione el país para las búsquedas en la API de Tellows (afecta a los resultados)</string>
6263

64+
<!-- API Truecaller -->
65+
<string name="pref_filter_truecaller_title">API Truecaller</string>
66+
<string name="pref_filter_truecaller_summary">(Recomendado) Bloquea llamadas de números spam identificados por la API de Truecaller</string>
67+
68+
<string name="pref_filter_truecaller_api_country_title">País API Truecaller</string>
69+
<string name="pref_filter_truecaller_api_country_summary">Seleccione el país para las búsquedas en la API de Truecaller (afecta a los resultados)</string>
70+
6371
<!-- Spam Number Filters -->
64-
<string name="pref_filter_lista_spam_scraper_title">Scraper listaspam.com</string>
65-
<string name="pref_filter_lista_spam_scraper_summary">[🇪🇸] (Obsoleto) Bloquea llamadas de números spam incluidos en la web listaspam.es</string>
72+
<string name="pref_filter_lista_spam_scraper_title">Scraper listaspam.com 🇪🇸</string>
73+
<string name="pref_filter_lista_spam_scraper_summary">(Obsoleto) Bloquea llamadas de números spam incluidos en la web listaspam.es</string>
6674

6775
<string name="pref_filter_responder_o_no_title">Scraper responderono.es</string>
68-
<string name="pref_filter_responder_o_no_summary">[🇪🇸] Bloquea llamadas de números spam incluidos en la web responderono.es</string>
76+
<string name="pref_filter_responder_o_no_summary">Bloquea llamadas de números spam incluidos en la web responderono.es</string>
6977

7078
<string name="pref_filter_cleverdialer_title">Scraper cleverdialer.es</string>
71-
<string name="pref_filter_cleverdialer_summary">[🇪🇸] Bloquea llamadas de números spam incluidos en la web cleverdialer.es</string>
79+
<string name="pref_filter_cleverdialer_summary">Bloquea llamadas de números spam incluidos en la web cleverdialer.es</string>
7280

7381
<!-- Other Blocking Options -->
7482
<string name="pref_block_non_contacts_title">Bloquear llamadas de desconocidos</string>
@@ -80,7 +88,7 @@
8088
<string name="pref_block_international_numbers_title">Bloquear llamadas internacionales</string>
8189
<string name="pref_block_international_numbers_summary">Bloquear llamadas de otros países</string>
8290

83-
<string name="pref_block_stir_shaken_risk_title">Bloquear llamadas STIR/SHAKEN</string>
91+
<string name="pref_block_stir_shaken_risk_title">Verificar con STIR/SHAKEN</string>
8492
<string name="pref_block_stir_shaken_risk_summary">Bloquear llamadas marcadas como potencialmente fraudulentas por el sistema STIR/SHAKEN (solo Android 11+, disponible en algunos países y operadoras)</string>
8593

8694
<string name="pref_mute_instead_of_block_title">Silenciar en lugar de bloquear</string>

0 commit comments

Comments
 (0)