Add call blocking
This commit is contained in:
parent
4bdd91ce20
commit
612d842931
|
@ -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"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package com.android.internal.telephony;
|
||||
|
||||
public interface ITelephony {
|
||||
boolean endCall();
|
||||
void answerRingingCall();
|
||||
void silenceRinger();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) -> {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue