Add experimental support for blocking hidden numbers

This commit is contained in:
xynngh 2020-07-28 17:39:49 +04:00
parent c1b7c4e9cd
commit 6909c344b5
22 changed files with 338 additions and 72 deletions

View File

@ -153,7 +153,9 @@ public class CallLogItemRecyclerViewAdapter
}
label.setText(ellipsize(
item.numberInfo.name != null ? item.numberInfo.name : item.number, 15));
item.numberInfo.noNumber ? label.getContext().getString(R.string.no_number) :
item.numberInfo.name != null ? item.numberInfo.name : item.number,
15));
IconAndColor iconAndColor = IconAndColor.forNumberRating(
item.numberInfo.rating, item.numberInfo.contactItem != null);

View File

@ -5,6 +5,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
@ -21,7 +22,6 @@ import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
import dummydomain.yetanothercallblocker.data.NumberInfo;
import dummydomain.yetanothercallblocker.event.CallEndedEvent;
import dummydomain.yetanothercallblocker.event.CallOngoingEvent;
import dummydomain.yetanothercallblocker.event.CallStartedEvent;
import static dummydomain.yetanothercallblocker.EventUtils.postEvent;
import static java.util.Objects.requireNonNull;
@ -38,28 +38,38 @@ public class CallReceiver extends BroadcastReceiver {
String telephonyExtraState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
LOG.info("Received intent: action={}, extraState={}, incomingNumber={}",
intent.getAction(), telephonyExtraState, incomingNumber);
boolean hasNumberExtra = intent.hasExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
LOG.info("Received intent: extraState={}, incomingNumber={}, hasNumberExtra={}",
telephonyExtraState, incomingNumber, hasNumberExtra);
extraLogging(intent); // TODO: make optional or remove
if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(telephonyExtraState)) {
isOnCall = true;
postEvent(new CallOngoingEvent());
} else if (TelephonyManager.EXTRA_STATE_RINGING.equals(telephonyExtraState)) {
if (incomingNumber == null) return;
postEvent(new CallStartedEvent());
if (incomingNumber == null) {
if (hasNumberExtra) {
incomingNumber = "";
} else {
if (!PermissionHelper.hasNumberInfoPermissions(context)) {
LOG.warn("No info permissions");
return;
}
return; // TODO: check
}
}
Settings settings = App.getSettings();
boolean blockCalls = settings.getBlockCalls();
boolean blockingEnabled = settings.getCallBlockingEnabled();
boolean showNotifications = settings.getIncomingCallNotifications();
if (blockCalls || showNotifications) {
if (blockingEnabled || showNotifications) {
NumberInfo numberInfo = DatabaseSingleton.getNumberInfo(incomingNumber);
boolean blocked = false;
if (blockCalls && !isOnCall && numberInfo.rating == NumberInfo.Rating.NEGATIVE
&& numberInfo.contactItem == null) {
if (blockingEnabled && !isOnCall && DatabaseSingleton.shouldBlock(numberInfo)) {
blocked = rejectCall(context);
if (blocked) {
@ -79,6 +89,26 @@ public class CallReceiver extends BroadcastReceiver {
}
}
private void extraLogging(Intent intent) {
Bundle extras = intent.getExtras();
if (extras != null) {
LOG.trace("extraLogging() extras:");
for (String k : extras.keySet()) {
LOG.trace("extraLogging() key={}, value={}", k, extras.get(k));
}
Object subscription = extras.get("subscription"); // PhoneConstants.SUBSCRIPTION_KEY
if (subscription != null) {
LOG.trace("extraLogging() subscription.class={}", subscription.getClass());
if (subscription instanceof Number) {
long subId = ((Number) subscription).longValue();
LOG.trace("extraLogging() subId={}, check={}",
subId, subId < Integer.MAX_VALUE);
}
}
}
}
@SuppressLint("MissingPermission")
private boolean rejectCall(@NonNull Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {

View File

@ -2,9 +2,14 @@ package dummydomain.yetanothercallblocker;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.telecom.Call;
import android.telecom.CallScreeningService;
import android.telecom.Connection;
import android.telecom.GatewayInfo;
import android.telecom.PhoneAccount;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@ -38,23 +43,61 @@ public class CallScreeningServiceImpl extends CallScreeningService {
}
}
if (!ignore && !App.getSettings().getBlockCalls()) {
if (!ignore && !App.getSettings().getCallBlockingEnabled()) {
ignore = true;
}
extraLogging(callDetails); // TODO: make optional or remove
String number = null;
if (!ignore) {
Uri handle = callDetails.getHandle();
if (PhoneAccount.SCHEME_TEL.equals(handle.getScheme())) {
String number = handle.getSchemeSpecificPart();
LOG.debug("onScreenCall() number={}", number);
LOG.trace("onScreenCall() handle: {}", handle);
if (handle != null && PhoneAccount.SCHEME_TEL.equals(handle.getScheme())) {
number = handle.getSchemeSpecificPart();
LOG.debug("onScreenCall() number from handle: {}", number);
}
if (number == null) {
Bundle intentExtras = callDetails.getIntentExtras();
if (intentExtras != null) {
Object o = intentExtras.get(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
LOG.trace("onScreenCall() EXTRA_INCOMING_CALL_ADDRESS={}", o);
if (o instanceof Uri) {
Uri uri = (Uri) o;
if (PhoneAccount.SCHEME_TEL.equals(uri.getScheme())) {
number = uri.getSchemeSpecificPart();
}
}
if (number == null && intentExtras.containsKey(
"com.google.android.apps.hangouts.telephony.hangout_info_bundle")) {
// NB: SIA doesn't block (based on number) hangouts if there's no number in intentExtras
number = "YACB_hangouts_stub";
}
}
if (number == null && callDetails.getExtras() != null) {
// NB: this part is broken in SIA
number = callDetails.getExtras().getString(Connection.EXTRA_CHILD_ADDRESS);
LOG.trace("onScreenCall() EXTRA_CHILD_ADDRESS={}", number);
}
}
if (TextUtils.isEmpty(number)
&& !PermissionHelper.hasNumberInfoPermissions(this)) {
ignore = true;
LOG.warn("onScreenCall() no info permissions");
}
}
if (!ignore) {
numberInfo = DatabaseSingleton.getNumberInfo(number);
if (numberInfo.rating == NumberInfo.Rating.NEGATIVE
&& numberInfo.contactItem == null) {
shouldBlock = true;
}
}
shouldBlock = DatabaseSingleton.shouldBlock(numberInfo);
}
} finally {
LOG.debug("onScreenCall() blocking call: {}", shouldBlock);
@ -86,4 +129,38 @@ public class CallScreeningServiceImpl extends CallScreeningService {
}
}
private void extraLogging(Call.Details callDetails) {
LOG.trace("extraLogging() handle={}", callDetails.getHandle());
if (callDetails.getStatusHints() != null) {
LOG.trace("extraLogging() statusHints.label={}",
callDetails.getStatusHints().getLabel());
}
GatewayInfo gatewayInfo = callDetails.getGatewayInfo();
if (gatewayInfo != null) {
LOG.trace("extraLogging() gatewayInfo provider={}," +
"gatewayAddress={}, originalAddress={}",
gatewayInfo.getGatewayProviderPackageName(),
gatewayInfo.getGatewayAddress(),
gatewayInfo.getOriginalAddress());
}
Bundle intentExtras = callDetails.getIntentExtras();
if (intentExtras != null) {
LOG.trace("extraLogging() intentExtras:");
for (String k : intentExtras.keySet()) {
LOG.trace("extraLogging() key={}, value={}", k, intentExtras.get(k));
}
}
Bundle extras = callDetails.getExtras();
if (intentExtras != null) {
LOG.trace("extraLogging() intentExtras:");
for (String k : extras.keySet()) {
LOG.trace("extraLogging() key={}, value={}", k, extras.get(k));
}
}
}
}

View File

@ -61,4 +61,8 @@ public class GenericSettings {
return pref.contains(key);
}
public void unset(String key) {
pref.edit().remove(key).apply();
}
}

View File

@ -24,10 +24,12 @@ public class InfoDialogHelper {
public static void showDialog(Context context, NumberInfo numberInfo,
DialogInterface.OnDismissListener onDismissListener) {
AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(numberInfo.number);
.setTitle(!numberInfo.noNumber
? numberInfo.number : context.getString(R.string.no_number));
@SuppressLint("InflateParams")
View view = LayoutInflater.from(context).inflate(R.layout.info_dialog, null);
builder.setView(view);
TextView categoryView = view.findViewById(R.id.category);
@ -66,6 +68,13 @@ public class InfoDialogHelper {
ReviewsSummaryHelper.populateSummary(view.findViewById(R.id.reviews_summary),
numberInfo.communityDatabaseItem);
if (onDismissListener != null) builder.setOnDismissListener(onDismissListener);
if (numberInfo.noNumber) {
builder.show();
return;
}
Runnable reviewsAction = () -> {
context.startActivity(clearTop(
ReviewsActivity.getNumberIntent(context, numberInfo.number)));
@ -77,12 +86,9 @@ public class InfoDialogHelper {
IntentHelper.startActivity(context, new Intent(Intent.ACTION_VIEW, uri));
};
builder.setView(view)
.setPositiveButton(R.string.add_web_review, null)
builder.setPositiveButton(R.string.add_web_review, null)
.setNeutralButton(R.string.online_reviews, null);
if (onDismissListener != null) builder.setOnDismissListener(onDismissListener);
AlertDialog dialog = builder.create();
// avoid dismissing the original dialog on button press

View File

@ -65,7 +65,7 @@ public class MainActivity extends AppCompatActivity {
settings.getIncomingCallNotifications());
menu.findItem(R.id.menu_block_calls).setChecked(
settings.getBlockCalls());
settings.getBlockNegativeSiaNumbers());
menu.findItem(R.id.menu_auto_updates).setChecked(
updateScheduler.isAutoUpdateScheduled());
@ -82,7 +82,7 @@ public class MainActivity extends AppCompatActivity {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionHelper.handlePermissionsResult(this, requestCode, permissions, grantResults,
settings.getIncomingCallNotifications(), settings.getBlockCalls(),
settings.getIncomingCallNotifications(), settings.getCallBlockingEnabled(),
settings.getUseContacts());
loadCallLog();
@ -134,7 +134,7 @@ public class MainActivity extends AppCompatActivity {
private void checkPermissions() {
PermissionHelper.checkPermissions(this,
settings.getIncomingCallNotifications(), settings.getBlockCalls(),
settings.getIncomingCallNotifications(), settings.getCallBlockingEnabled(),
settings.getUseContacts());
}
@ -185,7 +185,7 @@ public class MainActivity extends AppCompatActivity {
}
public void onBlockCallsChanged(MenuItem item) {
settings.setBlockCalls(!item.isChecked());
settings.setBlockNegativeSiaNumbers(!item.isChecked());
checkPermissions();
}

View File

@ -86,7 +86,8 @@ public class NotificationHelper {
public static void showBlockedCallNotification(Context context, NumberInfo numberInfo) {
Notification notification = createBlockedCallNotification(context, numberInfo);
String tag = numberInfo.number != null ? NOTIFICATION_TAG_BLOCKED_CALL + numberInfo.number : null; // TODO: handle repeating
String tag = NOTIFICATION_TAG_BLOCKED_CALL
+ (!numberInfo.noNumber ? numberInfo.number : System.nanoTime()); // TODO: handle repeating
notify(context, tag, NOTIFICATION_ID_BLOCKED_CALL, notification);
}
@ -210,7 +211,7 @@ public class NotificationHelper {
NumberInfo numberInfo) {
builder.setContentIntent(createInfoIntent(context, numberInfo));
if (numberInfo.contactItem == null) {
if (!numberInfo.noNumber && numberInfo.contactItem == null) {
builder.addAction(0, context.getString(R.string.online_reviews),
createReviewsIntent(context, numberInfo));
}

View File

@ -128,6 +128,13 @@ public class PermissionHelper {
}
}
public static boolean hasNumberInfoPermissions(Context context) {
for (String permission : INFO_PERMISSIONS) {
if (!hasPermission(context, permission)) return false;
}
return true;
}
public static boolean hasCallLogPermission(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return hasPermission(context, Manifest.permission.READ_CALL_LOG);

View File

@ -6,12 +6,16 @@ import android.text.TextUtils;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dummydomain.yetanothercallblocker.data.CountryHelper;
public class Settings extends GenericSettings {
public static final String PREF_INCOMING_CALL_NOTIFICATIONS = "incomingCallNotifications";
public static final String PREF_BLOCK_CALLS = "blockCalls";
public static final String PREF_BLOCK_NEGATIVE_SIA_NUMBERS = "blockNegativeSiaNumbers";
public static final String PREF_BLOCK_HIDDEN_NUMBERS = "blockHiddenNumbers";
public static final String PREF_USE_CONTACTS = "useContacts";
public static final String PREF_UI_MODE = "uiMode";
public static final String PREF_NUMBER_OF_RECENT_CALLS = "numberOfRecentCalls";
@ -26,7 +30,9 @@ public class Settings extends GenericSettings {
static final String SYS_PREFERENCES_VERSION = "__preferencesVersion";
private static final int PREFERENCES_VERSION = 1;
private static final Logger LOG = LoggerFactory.getLogger(Settings.class);
private static final int PREFERENCES_VERSION = 2;
private volatile String cachedAutoDetectedCountryCode;
@ -43,7 +49,13 @@ public class Settings extends GenericSettings {
if (preferencesVersion == PREFERENCES_VERSION) return;
LOG.info("init() preferencesVersion={}", preferencesVersion);
String prefBlockCalls = "blockCalls";
if (preferencesVersion < 1) {
LOG.debug("init() upgrading to 1");
PreferenceManager.setDefaultValues(context, R.xml.root_preferences, false);
Settings oldSettings = new Settings(context, "yacb_preferences");
@ -51,8 +63,8 @@ public class Settings extends GenericSettings {
if (oldSettings.isSet(PREF_INCOMING_CALL_NOTIFICATIONS)) {
setIncomingCallNotifications(oldSettings.getIncomingCallNotifications());
}
if (oldSettings.isSet(PREF_BLOCK_CALLS)) {
setBlockCalls(oldSettings.getBlockCalls());
if (oldSettings.isSet(prefBlockCalls)) {
setBoolean(prefBlockCalls, oldSettings.getBoolean(prefBlockCalls));
}
if (oldSettings.isSet(PREF_USE_CONTACTS)) {
setUseContacts(oldSettings.getUseContacts());
@ -60,8 +72,17 @@ public class Settings extends GenericSettings {
setLastUpdateTime(oldSettings.getLastUpdateTime());
setLastUpdateCheckTime(oldSettings.getLastUpdateCheckTime());
}
if (preferencesVersion < 2) {
LOG.debug("init() upgrading to 2");
if (isSet(prefBlockCalls)) {
setBlockNegativeSiaNumbers(getBoolean(prefBlockCalls));
unset(prefBlockCalls);
}
}
setInt(SYS_PREFERENCES_VERSION, PREFERENCES_VERSION);
LOG.debug("init() finished upgrade");
}
public boolean getIncomingCallNotifications() {
@ -72,12 +93,24 @@ public class Settings extends GenericSettings {
setBoolean(PREF_INCOMING_CALL_NOTIFICATIONS, show);
}
public boolean getBlockCalls() {
return getBoolean(PREF_BLOCK_CALLS);
public boolean getCallBlockingEnabled() {
return getBlockNegativeSiaNumbers() || getBlockHiddenNumbers();
}
public void setBlockCalls(boolean block) {
setBoolean(PREF_BLOCK_CALLS, block);
public boolean getBlockNegativeSiaNumbers() {
return getBoolean(PREF_BLOCK_NEGATIVE_SIA_NUMBERS);
}
public void setBlockNegativeSiaNumbers(boolean block) {
setBoolean(PREF_BLOCK_NEGATIVE_SIA_NUMBERS, block);
}
public boolean getBlockHiddenNumbers() {
return getBoolean(PREF_BLOCK_HIDDEN_NUMBERS);
}
public void setBlockHiddenNumbers(boolean block) {
setBoolean(PREF_BLOCK_HIDDEN_NUMBERS, block);
}
public boolean getUseContacts() {

View File

@ -76,7 +76,7 @@ public class SettingsActivity extends AppCompatActivity
Settings settings = App.getSettings();
PermissionHelper.handlePermissionsResult(this, requestCode, permissions, grantResults,
settings.getIncomingCallNotifications(), settings.getBlockCalls(),
settings.getIncomingCallNotifications(), settings.getCallBlockingEnabled(),
settings.getUseContacts());
}
@ -143,14 +143,16 @@ public class SettingsActivity extends AppCompatActivity
return true;
});
SwitchPreferenceCompat blockCallsPref =
requireNonNull(findPreference(Settings.PREF_BLOCK_CALLS));
blockCallsPref.setOnPreferenceChangeListener((preference, newValue) -> {
Preference.OnPreferenceChangeListener callBlockingListener = (preference, newValue) -> {
if (Boolean.TRUE.equals(newValue)) {
PermissionHelper.checkPermissions((AppCompatActivity) getActivity(), false, true, false);
}
return true;
});
};
requireNonNull((SwitchPreferenceCompat) findPreference(Settings.PREF_BLOCK_NEGATIVE_SIA_NUMBERS))
.setOnPreferenceChangeListener(callBlockingListener);
requireNonNull((SwitchPreferenceCompat) findPreference(Settings.PREF_BLOCK_HIDDEN_NUMBERS))
.setOnPreferenceChangeListener(callBlockingListener);
SwitchPreferenceCompat callScreeningPref =
requireNonNull(findPreference(PREF_USE_CALL_SCREENING_SERVICE));

View File

@ -0,0 +1,29 @@
package dummydomain.yetanothercallblocker.data;
import dummydomain.yetanothercallblocker.Settings;
public class BlockingDecisionMaker implements DatabaseSingleton.BlockingDecisionMaker {
private final Settings settings;
public BlockingDecisionMaker(Settings settings) {
this.settings = settings;
}
@Override
public boolean shouldBlock(NumberInfo numberInfo) {
if (numberInfo.contactItem != null) return false;
if (numberInfo.isHiddenNumber && settings.getBlockHiddenNumbers()) {
return true;
}
if (numberInfo.rating == NumberInfo.Rating.NEGATIVE
&& settings.getBlockNegativeSiaNumbers()) {
return true;
}
return false;
}
}

View File

@ -104,6 +104,8 @@ public class Config {
DatabaseSingleton.setDbManager(new DbManager(storage, SIA_PATH_PREFIX,
new DbDownloader(okHttpClientFactory)));
DatabaseSingleton.setHiddenNumberDetector(NumberUtils::isHiddenNumber);
CommunityDatabase communityDatabase = new CommunityDatabase(
storage, AbstractDatabase.Source.ANY, SIA_PATH_PREFIX,
SIA_SECONDARY_PATH_PREFIX, siaSettings, webService);
@ -129,6 +131,8 @@ public class Config {
}
return null;
});
DatabaseSingleton.setBlockingDecisionMaker(new BlockingDecisionMaker(settings));
}
}

View File

@ -24,6 +24,8 @@ public class DatabaseSingleton {
private static SiaMetadata siaMetadata;
private static HiddenNumberDetector hiddenNumberDetector;
private static CommunityDatabase communityDatabase;
private static FeaturedDatabase featuredDatabase;
@ -32,6 +34,8 @@ public class DatabaseSingleton {
private static ContactsProvider contactsProvider;
private static BlockingDecisionMaker blockingDecisionMaker;
static void setWebService(WebService webService) {
DatabaseSingleton.webService = webService;
}
@ -44,6 +48,10 @@ public class DatabaseSingleton {
DatabaseSingleton.siaMetadata = siaMetadata;
}
static void setHiddenNumberDetector(HiddenNumberDetector hiddenNumberDetector) {
DatabaseSingleton.hiddenNumberDetector = hiddenNumberDetector;
}
static void setCommunityDatabase(CommunityDatabase communityDatabase) {
DatabaseSingleton.communityDatabase = communityDatabase;
}
@ -60,6 +68,10 @@ public class DatabaseSingleton {
DatabaseSingleton.contactsProvider = contactsProvider;
}
static void setBlockingDecisionMaker(BlockingDecisionMaker blockingDecisionMaker) {
DatabaseSingleton.blockingDecisionMaker = blockingDecisionMaker;
}
public static WebService getWebService() {
return webService;
}
@ -88,24 +100,37 @@ public class DatabaseSingleton {
LOG.debug("getNumberInfo({}) started", number);
// TODO: check number format
CommunityDatabaseItem communityItem = DatabaseSingleton.getCommunityDatabase()
.getDbItemByNumber(number);
LOG.trace("getNumberInfo() communityItem={}", communityItem);
FeaturedDatabaseItem featuredItem = DatabaseSingleton.getFeaturedDatabase()
.getDbItemByNumber(number);
LOG.trace("getNumberInfo() featuredItem={}", featuredItem);
ContactItem contactItem = DatabaseSingleton.contactsProvider.get(number);
LOG.trace("getNumberInfo() contactItem={}", contactItem);
NumberInfo numberInfo = new NumberInfo();
numberInfo.number = number;
numberInfo.communityDatabaseItem = communityItem;
numberInfo.featuredDatabaseItem = featuredItem;
numberInfo.contactItem = contactItem;
if (hiddenNumberDetector != null) {
numberInfo.isHiddenNumber = hiddenNumberDetector.isHiddenNumber(number);
}
LOG.trace("getNumberInfo() isHiddenNumber={}", numberInfo.isHiddenNumber);
if (numberInfo.isHiddenNumber || TextUtils.isEmpty(numberInfo.number)) {
numberInfo.noNumber = true;
}
LOG.trace("getNumberInfo() noNumber={}", numberInfo.noNumber);
if (numberInfo.noNumber) {
LOG.debug("getNumberInfo() finished");
return numberInfo;
}
numberInfo.communityDatabaseItem = DatabaseSingleton.getCommunityDatabase()
.getDbItemByNumber(number);
LOG.trace("getNumberInfo() communityItem={}", numberInfo.communityDatabaseItem);
numberInfo.featuredDatabaseItem = DatabaseSingleton.getFeaturedDatabase()
.getDbItemByNumber(number);
LOG.trace("getNumberInfo() featuredItem={}", numberInfo.featuredDatabaseItem);
numberInfo.contactItem = DatabaseSingleton.contactsProvider.get(number);
LOG.trace("getNumberInfo() contactItem={}", numberInfo.contactItem);
ContactItem contactItem = numberInfo.contactItem;
FeaturedDatabaseItem featuredItem = numberInfo.featuredDatabaseItem;
if (contactItem != null && !TextUtils.isEmpty(contactItem.displayName)) {
numberInfo.name = contactItem.displayName;
} else if (featuredItem != null && !TextUtils.isEmpty(featuredItem.getName())) {
@ -113,6 +138,7 @@ public class DatabaseSingleton {
}
LOG.trace("getNumberInfo() name={}", numberInfo.name);
CommunityDatabaseItem communityItem = numberInfo.communityDatabaseItem;
if (communityItem != null && communityItem.hasRatings()) {
if (communityItem.getNegativeRatingsCount() > communityItem.getPositiveRatingsCount()
+ communityItem.getNeutralRatingsCount()) {
@ -127,7 +153,23 @@ public class DatabaseSingleton {
}
LOG.trace("getNumberInfo() rating={}", numberInfo.rating);
LOG.debug("getNumberInfo() finished");
return numberInfo;
}
public static boolean shouldBlock(NumberInfo numberInfo) {
if (blockingDecisionMaker != null) {
return blockingDecisionMaker.shouldBlock(numberInfo);
}
return false;
}
public interface HiddenNumberDetector {
boolean isHiddenNumber(String number);
}
public interface BlockingDecisionMaker {
boolean shouldBlock(NumberInfo numberInfo);
}
}

View File

@ -13,6 +13,7 @@ public class NumberInfo {
public String number;
// info from various sources
public boolean isHiddenNumber;
public CommunityDatabaseItem communityDatabaseItem;
public FeaturedDatabaseItem featuredDatabaseItem;
public ContactItem contactItem;
@ -20,7 +21,8 @@ public class NumberInfo {
// computed rating
public Rating rating = Rating.UNKNOWN;
// name (for convenience)
// precomputed for convenience
public boolean noNumber;
public String name;
}

View File

@ -0,0 +1,21 @@
package dummydomain.yetanothercallblocker.data;
import android.text.TextUtils;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
public class NumberUtils {
private static final Set<String> HIDDEN_NUMBERS = new HashSet<>(Arrays.asList(
"-1", "-2", "UNAVAILABLE", "ABSENT NUMBER", "NNN", "PRIVATE NUMBER"
));
public static boolean isHiddenNumber(String number) {
if (TextUtils.isEmpty(number) || TextUtils.getTrimmedLength(number) == 0) return true;
return HIDDEN_NUMBERS.contains(number.toUpperCase(Locale.ENGLISH));
}
}

View File

@ -1,3 +0,0 @@
package dummydomain.yetanothercallblocker.event;
public class CallStartedEvent extends CallEvent {}

View File

@ -11,7 +11,7 @@
android:id="@+id/menu_block_calls"
android:checkable="true"
android:onClick="onBlockCallsChanged"
android:title="@string/block_calls" />
android:title="@string/block_negative_sia_short" />
<item
android:id="@+id/menu_auto_updates"

View File

@ -84,8 +84,6 @@
<string name="incoming_call_notifications">Notificaciones de llamadas entrantes</string>
<string name="incoming_call_notifications_summary">Muestra una notificación con el resumen del número de teléfono (clasificación, recuento de revisiones, categoría) durante las llamadas entrantes</string>
<string name="block_calls">Bloqueo de llamadas no deseadas</string>
<string name="block_calls_summary">Automáticamente bloquea las llamadas con calificación negativa</string>
<string name="auto_updates">Base de datos de actualización automática</string>
<string name="auto_updates_summary">Automáticamente recibe actualizaciones diarias de la BD (estas son actualizaciones incrementales/delta, por lo que consumen muy poco tráfico)</string>
<string name="use_contacts">Usar contactos</string>

View File

@ -74,7 +74,6 @@
<string name="load_reviews_confirmation_message">Door online-recensies te laden, lekt het nummer uit naar een externe dienst. Weet je zeker dat je dit wilt doen met een nummer uit je contactpersonenlijst?</string>
<string name="incoming_call_notifications">Inkomende oproep-meldingen</string>
<string name="block_calls">Ongewenste oproepen blokkeren</string>
<string name="auto_updates">Databank automatisch bijwerken</string>
<string name="use_contacts">Contactpersonen uitlezen</string>

View File

@ -20,8 +20,11 @@
<string name="sia_category_service">Сервис/услуги</string>
<string name="sia_category_robocall">Робот/автомат</string>
<string name="sia_category_nonprofit">Некоммерческая орг</string>
<string name="block_calls">Блокир-ть нежелат. вызовы</string>
<string name="block_calls_summary">Автоматически сбрасывать звонки c номеров с отрицательным рейтингом</string>
<string name="block_negative_sia_short">Блокир. по отзывам</string>
<string name="block_negative_sia">Блокировать по отзывам</string>
<string name="block_negative_sia_numbers_summary">Блокировать звонки c номеров с отрицательным рейтингом</string>
<string name="block_hidden_number">Блокировать скрытые номера</string>
<string name="block_hidden_number_summary">Блокировать звонки со скрытых номеров. Экспериментальная функция. Вероятно, работает лучше в "продвинутом режиме блокирования". Пожалуйста, сообщите о своём опыте использования в репозиторий на gitlab</string>
<string name="use_call_screening_service">Продвинутый режим блокирования вызовов</string>
<string name="use_call_screening_service_summary">Позволяет блокировать вызовы до того, как телефон зазвонит. Это требует назначить приложение \"приложением для звонков\" (на Android 79) или \"приложением для АОН и защиты от спама\" (на Android 10+)</string>
<string name="use_call_screening_service_disable_message">Выберите другое приложение для звонков или защиты от спама в меню Настройки - Приложения - Приложения по умолчанию</string>
@ -112,4 +115,5 @@
<string name="save_logcat_to_file_summary">Сохранить содержимое logcat в файл в общем хранилище (/sdcard/Android/data/dummydomain.yetanothercallblocker/files/). Отчёты могут содержать конфиденциальные данные (номера телефонов, имена контактов). Другие приложения с разрешением на доступ к хранилищу могут иметь доступ к этим данным в общем хранилище</string>
<string name="save_logcat_to_file_done">Выполнено</string>
<string name="save_logcat_to_file_error">Ошибка</string>
<string name="no_number"><![CDATA[<нет номера>]]></string>
</resources>

View File

@ -89,8 +89,11 @@
<string name="incoming_call_notifications">Incoming call notifications</string>
<string name="incoming_call_notifications_summary">Displays a notification with phone number summary (rating, reviews count, category) during incoming calls</string>
<string name="block_calls">Block unwanted calls</string>
<string name="block_calls_summary">Automatically blocks calls with negative rating</string>
<string name="block_negative_sia_short">Block by rating</string>
<string name="block_negative_sia">Block based on rating</string>
<string name="block_negative_sia_numbers_summary">Block calls from numbers with negative rating (based on a community database)</string>
<string name="block_hidden_number">Block hidden numbers</string>
<string name="block_hidden_number_summary">Block calls from hidden numbers. Experimental. Probably works better in \"advanced call blocking mode\". Please report your experience in the repo issues on gitlab</string>
<string name="use_call_screening_service">Advanced call blocking mode</string>
<string name="use_call_screening_service_summary">Allows to block calls before the phone starts to ring. Requires the app to be set as the \"Phone app\" (Android 79) or as the \"Caller ID app\" (Android 10+)</string>
<string name="use_call_screening_service_disable_message">Select different \"Phone app\" or \"Caller ID app\" in Settings - Apps - Default apps</string>
@ -124,6 +127,7 @@
<string name="save_logcat_to_file_summary">Save logcat output to a file in public storage (/sdcard/Android/data/dummydomain.yetanothercallblocker/files/). The reports may contain sensitive data (phone numbers, contact names). Other apps with storage permission may have access to this data in public storage</string>
<string name="save_logcat_to_file_done">Done</string>
<string name="save_logcat_to_file_error">Error</string>
<string name="no_number"><![CDATA[<no number>]]></string>
<string name="open_debug_activity">Open debug screen</string>
<string name="debug_activity_label">Debug</string>

View File

@ -31,9 +31,13 @@
<PreferenceCategory app:title="@string/settings_category_call_blocking">
<SwitchPreferenceCompat
app:key="blockCalls"
app:summary="@string/block_calls_summary"
app:title="@string/block_calls" />
app:key="blockNegativeSiaNumbers"
app:summary="@string/block_negative_sia_numbers_summary"
app:title="@string/block_negative_sia" />
<SwitchPreferenceCompat
app:key="blockHiddenNumbers"
app:summary="@string/block_hidden_number_summary"
app:title="@string/block_hidden_number" />
<SwitchPreferenceCompat
app:key="useCallScreeningService"
app:persistent="false"