Add call blocking

This commit is contained in:
xynngh 2019-06-09 22:13:27 +04:00
parent 4bdd91ce20
commit 612d842931
9 changed files with 171 additions and 30 deletions

View File

@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<application
android:name=".App"

View File

@ -0,0 +1,7 @@
package com.android.internal.telephony;
public interface ITelephony {
boolean endCall();
void answerRingingCall();
void silenceRinger();
}

View File

@ -5,11 +5,16 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.telephony.TelephonyManager;
import com.android.internal.telephony.ITelephony;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import dummydomain.yetanothercallblocker.sia.DatabaseSingleton;
import dummydomain.yetanothercallblocker.sia.model.NumberInfo;
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabaseItem;
@ -19,6 +24,8 @@ public class CallReceiver extends BroadcastReceiver {
private static final Logger LOG = LoggerFactory.getLogger(CallReceiver.class);
private boolean isOnCall; // TODO: check: is this object not destroyed in-between calls?
public static boolean isEnabled(Context context) {
return context.getPackageManager()
.getComponentEnabledSetting(new ComponentName(context, CallReceiver.class))
@ -44,11 +51,26 @@ public class CallReceiver extends BroadcastReceiver {
LOG.info("Received intent: action={}, extraState={}, incomingNumber={}",
intent.getAction(), telephonyExtraState, incomingNumber);
if (TelephonyManager.EXTRA_STATE_RINGING.equals(telephonyExtraState)) {
if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(telephonyExtraState)) {
isOnCall = true;
} else if (TelephonyManager.EXTRA_STATE_RINGING.equals(telephonyExtraState)) {
if (incomingNumber == null) return;
NotificationHelper.showIncomingCallNotification(context, getNumberInfo(incomingNumber));
NumberInfo numberInfo = getNumberInfo(incomingNumber);
boolean blocked = false;
if (!isOnCall && numberInfo.rating == NumberInfo.Rating.NEGATIVE
&& new Settings(context).getBlockCalls()) {
blocked = rejectCall(context);
if (blocked) {
NotificationHelper.showBlockedCallNotification(context, numberInfo);
}
}
if (!blocked) NotificationHelper.showIncomingCallNotification(context, numberInfo);
} else if(TelephonyManager.EXTRA_STATE_IDLE.equals(telephonyExtraState)) {
isOnCall = false;
NotificationHelper.hideIncomingCallNotification(context, incomingNumber);
}
}
@ -88,4 +110,21 @@ public class CallReceiver extends BroadcastReceiver {
return numberInfo;
}
private boolean rejectCall(@NonNull Context context) {
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
try {
Method m = tm.getClass().getDeclaredMethod("getITelephony");
m.setAccessible(true);
ITelephony telephony = (ITelephony)m.invoke(tm);
telephony.endCall();
return true;
} catch (Exception e) {
LOG.warn("Error while rejecting call", e);
}
return false;
}
}

View File

@ -20,6 +20,11 @@ public class MainActivity extends AppCompatActivity {
notificationsSwitch.setOnCheckedChangeListener((buttonView, isChecked)
-> CallReceiver.setEnabled(MainActivity.this, isChecked));
SwitchCompat blockCallsSwitch = findViewById(R.id.blockCallsSwitch);
blockCallsSwitch.setChecked(new Settings(this).getBlockCalls());
blockCallsSwitch.setOnCheckedChangeListener((buttonView, isChecked)
-> new Settings(this).setBlockCalls(isChecked));
SwitchCompat autoUpdateSwitch = findViewById(R.id.autoUpdateEnabledSwitch);
autoUpdateSwitch.setChecked(Updater.isAutoUpdateScheduled());
autoUpdateSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {

View File

@ -22,28 +22,41 @@ import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabaseIte
public class NotificationHelper {
private static final String NOTIFICATION_TAG = "incomingCallNotification";
private static final int NOTIFICATION_ID = 1;
private static final String NOTIFICATION_TAG_INCOMING_CALL = "incomingCallNotification";
private static final String NOTIFICATION_TAG_BLOCKED_CALL = "blockedCallNotification";
private static final int NOTIFICATION_ID_INCOMING_CALL = 1;
private static final int NOTIFICATION_ID_BLOCKED_CALL = 2;
private static final String CHANNEL_GROUP_ID_INCOMING_CALLS = "incoming_calls";
private static final String CHANNEL_GROUP_ID_BLOCKED_CALLS = "blocked_calls";
private static final String CHANNEL_ID_POSITIVE_KNOWN = "positive_known_calls";
private static final String CHANNEL_ID_POSITIVE = "positive_calls";
private static final String CHANNEL_ID_NEUTRAL = "neutral_calls";
private static final String CHANNEL_ID_UNKNOWN = "unknown_calls";
private static final String CHANNEL_ID_NEGATIVE = "negative_calls";
private static final String CHANNEL_ID_BLOCKED_INFO = "blocked_info";
public static void showIncomingCallNotification(Context context, NumberInfo numberInfo) {
createNotificationChannels(context);
Notification notification = createIncomingCallNotification(context, numberInfo);
String tag = numberInfo.number != null ? NOTIFICATION_TAG + numberInfo.number : null;
NotificationManagerCompat.from(context).notify(tag, NOTIFICATION_ID, notification);
String tag = numberInfo.number != null ? NOTIFICATION_TAG_INCOMING_CALL + numberInfo.number : null;
NotificationManagerCompat.from(context).notify(tag, NOTIFICATION_ID_INCOMING_CALL, notification);
}
public static void hideIncomingCallNotification(Context context, String number) {
String tag = number != null ? NOTIFICATION_TAG + number : null;
NotificationManagerCompat.from(context).cancel(tag, NOTIFICATION_ID);
String tag = number != null ? NOTIFICATION_TAG_INCOMING_CALL + number : null;
NotificationManagerCompat.from(context).cancel(tag, NOTIFICATION_ID_INCOMING_CALL);
}
public static void showBlockedCallNotification(Context context, NumberInfo numberInfo) {
createNotificationChannels(context);
Notification notification = createBlockedCallNotification(context, numberInfo);
String tag = numberInfo.number != null ? NOTIFICATION_TAG_BLOCKED_CALL + numberInfo.number : null; // TODO: handle repeating
NotificationManagerCompat.from(context).notify(tag, NOTIFICATION_ID_BLOCKED_CALL, notification);
}
private static Notification createIncomingCallNotification(Context context, NumberInfo numberInfo) {
@ -89,6 +102,43 @@ public class NotificationHelper {
if (category != null && category != NumberCategory.NONE) {
title += " - " + NumberCategory.getString(context, category);
}
}
text += getDescription(context, numberInfo);
return new NotificationCompat.Builder(context, channelId)
.setSmallIcon(icon)
.setColor(color)
.setContentTitle(title)
.setContentText(text)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setShowWhen(false)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) // TODO: check
.setContentIntent(createReviewsIntent(context, numberInfo))
.build();
}
private static Notification createBlockedCallNotification(Context context, NumberInfo numberInfo) {
String title = context.getString(R.string.notification_blocked_call);
String text = getDescription(context, numberInfo);
return new NotificationCompat.Builder(context, CHANNEL_ID_BLOCKED_INFO)
.setSmallIcon(R.drawable.ic_thumb_down_black_24dp)
.setColor(0xffffff60)
.setContentTitle(title)
.setContentText(text)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setContentIntent(createReviewsIntent(context, numberInfo))
.build();
}
private static String getDescription(Context context, NumberInfo numberInfo) {
String text = "";
if (numberInfo.communityDatabaseItem != null) {
CommunityDatabaseItem communityItem = numberInfo.communityDatabaseItem;
if (numberInfo.name != null) {
text += numberInfo.name;
@ -102,22 +152,12 @@ public class NotificationHelper {
}
}
return text;
}
private static PendingIntent createReviewsIntent(Context context, NumberInfo numberInfo) {
Intent intent = ReviewsActivity.getNumberIntent(context, numberInfo.number);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(context, channelId)
.setSmallIcon(icon)
.setColor(color)
.setContentTitle(title)
.setContentText(text)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setShowWhen(false)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) // TODO: check
.setContentIntent(pendingIntent)
.build();
return notification;
return PendingIntent.getActivity(context, 0, intent, 0);
}
private static void createNotificationChannels(Context context) {
@ -129,9 +169,15 @@ public class NotificationHelper {
return;
}
NotificationChannelGroup channelGroup = new NotificationChannelGroup(CHANNEL_GROUP_ID_INCOMING_CALLS,
NotificationChannelGroup channelGroupIncoming = new NotificationChannelGroup(
CHANNEL_GROUP_ID_INCOMING_CALLS,
context.getString(R.string.notification_channel_group_name_incoming_calls));
notificationManager.createNotificationChannelGroup(channelGroup);
notificationManager.createNotificationChannelGroup(channelGroupIncoming);
NotificationChannelGroup channelGroupBlocked = new NotificationChannelGroup(
CHANNEL_GROUP_ID_BLOCKED_CALLS,
context.getString(R.string.notification_channel_group_name_blocked_calls));
notificationManager.createNotificationChannelGroup(channelGroupBlocked);
List<NotificationChannel> channels = new ArrayList<>();
@ -141,35 +187,42 @@ public class NotificationHelper {
CHANNEL_ID_POSITIVE_KNOWN, context.getString(R.string.notification_channel_name_positive_known),
NotificationManager.IMPORTANCE_MIN
);
channel.setGroup(channelGroup.getId());
channel.setGroup(channelGroupIncoming.getId());
channels.add(channel);
channel = new NotificationChannel(
CHANNEL_ID_POSITIVE, context.getString(R.string.notification_channel_name_positive),
NotificationManager.IMPORTANCE_LOW
);
channel.setGroup(channelGroup.getId());
channel.setGroup(channelGroupIncoming.getId());
channels.add(channel);
channel = new NotificationChannel(
CHANNEL_ID_NEUTRAL, context.getString(R.string.notification_channel_name_neutral),
NotificationManager.IMPORTANCE_MIN
);
channel.setGroup(channelGroup.getId());
channel.setGroup(channelGroupIncoming.getId());
channels.add(channel);
channel = new NotificationChannel(
CHANNEL_ID_UNKNOWN, context.getString(R.string.notification_channel_name_unknown),
NotificationManager.IMPORTANCE_MIN
);
channel.setGroup(channelGroup.getId());
channel.setGroup(channelGroupIncoming.getId());
channels.add(channel);
channel = new NotificationChannel(
CHANNEL_ID_NEGATIVE, context.getString(R.string.notification_channel_name_negative),
NotificationManager.IMPORTANCE_LOW
);
channel.setGroup(channelGroup.getId());
channel.setGroup(channelGroupIncoming.getId());
channels.add(channel);
channel = new NotificationChannel(
CHANNEL_ID_BLOCKED_INFO, context.getString(R.string.notification_channel_name_blocked_info),
NotificationManager.IMPORTANCE_LOW
);
channel.setGroup(channelGroupBlocked.getId());
channels.add(channel);
notificationManager.createNotificationChannels(channels);

View File

@ -17,6 +17,7 @@ public class PermissionHelper {
public static void checkPermissions(AppCompatActivity activity) {
List<String> requiredPermissions = new ArrayList<>();
requiredPermissions.add(Manifest.permission.READ_PHONE_STATE);
requiredPermissions.add(Manifest.permission.CALL_PHONE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
requiredPermissions.add(Manifest.permission.READ_CALL_LOG);
}

View File

@ -0,0 +1,24 @@
package dummydomain.yetanothercallblocker;
import android.content.Context;
import android.content.SharedPreferences;
public class Settings {
private static final String PREF_BLOCK_CALLS = "blockCalls";
private final SharedPreferences pref;
public Settings(Context context) {
pref = context.getSharedPreferences("yacb_preferences", Context.MODE_PRIVATE);
}
public boolean getBlockCalls() {
return pref.getBoolean(PREF_BLOCK_CALLS, false);
}
public void setBlockCalls(boolean block) {
pref.edit().putBoolean(PREF_BLOCK_CALLS, block).apply();
}
}

View File

@ -13,6 +13,12 @@
android:layout_height="wrap_content"
android:text="@string/incoming_call_notifications_enabled" />
<android.support.v7.widget.SwitchCompat
android:id="@+id/blockCallsSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/block_calls" />
<android.support.v7.widget.SwitchCompat
android:id="@+id/autoUpdateEnabledSwitch"
android:layout_width="wrap_content"

View File

@ -2,18 +2,22 @@
<string name="app_name">Yet Another Call Blocker</string>
<string name="notification_channel_group_name_incoming_calls">Incoming calls</string>
<string name="notification_channel_group_name_blocked_calls">Blocked calls</string>
<string name="notification_channel_name_positive_known">Known positive calls</string>
<string name="notification_channel_name_positive">Positive calls</string>
<string name="notification_channel_name_neutral">Neutral calls</string>
<string name="notification_channel_name_unknown">Unknown calls</string>
<string name="notification_channel_name_negative">Negative calls</string>
<string name="notification_channel_name_blocked_info">Blocked info</string>
<string name="notification_incoming_call_positive">Positive call</string>
<string name="notification_incoming_call_neutral">Neutral call</string>
<string name="notification_incoming_call_unknown">Unknown call</string>
<string name="notification_incoming_call_negative">Negative call</string>
<string name="notification_blocked_call">Blocked call</string>
<string name="notification_incoming_call_text_description">Negative: %d, positive: %d, neutral: %d</string>
<string name="sia_category_none">None</string>
@ -42,6 +46,7 @@
<string name="title_activity_reviews">Reviews</string>
<string name="incoming_call_notifications_enabled">Incoming call notifications enabled</string>
<string name="block_calls">Block unwanted calls</string>
<string name="auto_update_enabled">Auto-update enabled</string>
<string name="open_debug_activity">Open debug screen</string>