Contacts and refactoring
This commit is contained in:
parent
0fa0e13da2
commit
28b7a9ca55
|
@ -8,6 +8,7 @@
|
|||
<uses-permission android:name="android.permission.READ_CALL_LOG" />
|
||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
|
||||
<!-- may be needed for call blocking on Android 9, also requires to be installed as a system app -->
|
||||
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"
|
||||
|
|
|
@ -4,6 +4,9 @@ import android.app.Application;
|
|||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import dummydomain.yetanothercallblocker.data.ContactsHelper;
|
||||
import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
|
||||
|
||||
public class App extends Application {
|
||||
|
||||
private static App instance;
|
||||
|
@ -27,6 +30,9 @@ public class App extends Application {
|
|||
EventHandler.create(this);
|
||||
|
||||
NotificationHelper.createNotificationChannels(this);
|
||||
|
||||
DatabaseSingleton.setContactsProvider(
|
||||
ContactsHelper.getContactsProvider(this, settings));
|
||||
}
|
||||
|
||||
public static App getInstance() {
|
||||
|
|
|
@ -13,6 +13,8 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import dummydomain.yetanothercallblocker.data.CallLogItem;
|
||||
|
||||
public class CallLogItemRecyclerViewAdapter
|
||||
extends RecyclerView.Adapter<CallLogItemRecyclerViewAdapter.ViewHolder> {
|
||||
|
||||
|
@ -57,7 +59,7 @@ public class CallLogItemRecyclerViewAdapter
|
|||
final View view;
|
||||
|
||||
final AppCompatImageView callTypeIcon;
|
||||
final TextView number;
|
||||
final TextView label;
|
||||
final AppCompatImageView numberInfoIcon;
|
||||
final TextView time;
|
||||
|
||||
|
@ -67,7 +69,7 @@ public class CallLogItemRecyclerViewAdapter
|
|||
this.view = view;
|
||||
|
||||
callTypeIcon = view.findViewById(R.id.callTypeIcon);
|
||||
number = view.findViewById(R.id.item_number);
|
||||
label = view.findViewById(R.id.item_label);
|
||||
numberInfoIcon = view.findViewById(R.id.numberInfoIcon);
|
||||
time = view.findViewById(R.id.time);
|
||||
|
||||
|
@ -100,9 +102,11 @@ public class CallLogItemRecyclerViewAdapter
|
|||
callTypeIcon.setImageDrawable(null);
|
||||
}
|
||||
|
||||
number.setText(item.number);
|
||||
label.setText(item.numberInfo.name != null ? item.numberInfo.name : item.number);
|
||||
|
||||
IconAndColor iconAndColor = IconAndColor.forNumberRating(
|
||||
item.numberInfo.rating, item.numberInfo.contactItem != null);
|
||||
|
||||
IconAndColor iconAndColor = IconAndColor.forNumberRating(item.numberInfo.rating);
|
||||
if (!iconAndColor.noInfo) {
|
||||
iconAndColor.setOnImageView(numberInfoIcon);
|
||||
} else {
|
||||
|
@ -114,7 +118,7 @@ public class CallLogItemRecyclerViewAdapter
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " '" + number.getText() + "'";
|
||||
return super.toString() + " '" + label.getText() + "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,11 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
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 dummydomain.yetanothercallblocker.sia.DatabaseSingleton;
|
||||
import dummydomain.yetanothercallblocker.sia.model.NumberInfo;
|
||||
|
||||
import static dummydomain.yetanothercallblocker.EventUtils.postEvent;
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ class CustomListViewAdapter extends RecyclerView.Adapter<CustomListViewAdapter.C
|
|||
tvDescription.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
IconAndColor.forNumberRating(item.getRating()).setOnImageView(ivRating);
|
||||
IconAndColor.forReviewRating(item.getRating()).setOnImageView(ivRating);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ import org.greenrobot.eventbus.ThreadMode;
|
|||
|
||||
import java.util.Date;
|
||||
|
||||
import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
|
||||
import dummydomain.yetanothercallblocker.event.SecondaryDbUpdateFinished;
|
||||
import dummydomain.yetanothercallblocker.sia.DatabaseSingleton;
|
||||
import dummydomain.yetanothercallblocker.sia.model.NumberCategory;
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabase;
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabaseItem;
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
package dummydomain.yetanothercallblocker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.core.widget.ImageViewCompat;
|
||||
|
||||
import dummydomain.yetanothercallblocker.sia.model.NumberInfo;
|
||||
import dummydomain.yetanothercallblocker.sia.model.NumberRating;
|
||||
import dummydomain.yetanothercallblocker.data.NumberInfo;
|
||||
import dummydomain.yetanothercallblocker.sia.model.CommunityReview;
|
||||
|
||||
class IconAndColor {
|
||||
|
||||
|
@ -28,19 +32,22 @@ class IconAndColor {
|
|||
this.noInfo = noInfo;
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
int getColorInt(@NonNull Context context) {
|
||||
return ResourcesCompat.getColor(context.getResources(), colorResId, context.getTheme());
|
||||
}
|
||||
|
||||
void setOnImageView(AppCompatImageView imageView) {
|
||||
imageView.setImageResource(iconResId);
|
||||
ImageViewCompat.setImageTintList(imageView, ColorStateList.valueOf(
|
||||
imageView.getContext().getResources().getColor(colorResId)));
|
||||
getColorInt(imageView.getContext())));
|
||||
}
|
||||
|
||||
static IconAndColor of(@DrawableRes int icon, @ColorRes int color) {
|
||||
return new IconAndColor(icon, color);
|
||||
}
|
||||
|
||||
// TODO: fix duplication
|
||||
|
||||
static IconAndColor forNumberRating(NumberRating rating) {
|
||||
static IconAndColor forReviewRating(CommunityReview.Rating rating) {
|
||||
switch (rating) {
|
||||
case NEUTRAL:
|
||||
return of(R.drawable.ic_thumbs_up_down_24dp, R.color.rateNeutral);
|
||||
|
@ -52,15 +59,39 @@ class IconAndColor {
|
|||
return new IconAndColor(R.drawable.ic_thumbs_up_down_24dp, R.color.notFound, true);
|
||||
}
|
||||
|
||||
static IconAndColor forNumberRating(NumberInfo.Rating rating) {
|
||||
static IconAndColor forNumberRating(NumberInfo.Rating rating, boolean contact) {
|
||||
boolean noInfo = false;
|
||||
@DrawableRes int icon;
|
||||
@ColorInt int color;
|
||||
|
||||
switch (rating) {
|
||||
case NEUTRAL:
|
||||
return of(R.drawable.ic_thumbs_up_down_24dp, R.color.rateNeutral);
|
||||
icon = R.drawable.ic_thumbs_up_down_24dp;
|
||||
color = R.color.rateNeutral;
|
||||
break;
|
||||
|
||||
case POSITIVE:
|
||||
return of(R.drawable.ic_thumb_up_24dp, R.color.ratePositive);
|
||||
icon = R.drawable.ic_thumb_up_24dp;
|
||||
color = R.color.ratePositive;
|
||||
break;
|
||||
|
||||
case NEGATIVE:
|
||||
return of(R.drawable.ic_thumb_down_24dp, R.color.rateNegative);
|
||||
icon = R.drawable.ic_thumb_down_24dp;
|
||||
color = R.color.rateNegative;
|
||||
break;
|
||||
|
||||
default:
|
||||
noInfo = true;
|
||||
icon = R.drawable.ic_thumbs_up_down_24dp;
|
||||
color = R.color.notFound;
|
||||
break;
|
||||
}
|
||||
return new IconAndColor(R.drawable.ic_thumbs_up_down_24dp, R.color.notFound, true);
|
||||
|
||||
if (contact) {
|
||||
noInfo = false;
|
||||
icon = R.drawable.ic_person_24dp;
|
||||
}
|
||||
|
||||
return new IconAndColor(icon, color, noInfo);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package dummydomain.yetanothercallblocker;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
@ -23,11 +23,13 @@ import org.greenrobot.eventbus.ThreadMode;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import dummydomain.yetanothercallblocker.data.CallLogHelper;
|
||||
import dummydomain.yetanothercallblocker.data.CallLogItem;
|
||||
import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
|
||||
import dummydomain.yetanothercallblocker.data.NumberInfo;
|
||||
import dummydomain.yetanothercallblocker.event.CallEndedEvent;
|
||||
import dummydomain.yetanothercallblocker.event.MainDbDownloadFinishedEvent;
|
||||
import dummydomain.yetanothercallblocker.event.MainDbDownloadingEvent;
|
||||
import dummydomain.yetanothercallblocker.sia.DatabaseSingleton;
|
||||
import dummydomain.yetanothercallblocker.sia.model.NumberInfo;
|
||||
import dummydomain.yetanothercallblocker.work.TaskService;
|
||||
import dummydomain.yetanothercallblocker.work.UpdateScheduler;
|
||||
|
||||
|
@ -70,6 +72,9 @@ public class MainActivity extends AppCompatActivity {
|
|||
menu.findItem(R.id.menu_auto_updates).setChecked(
|
||||
updateScheduler.isAutoUpdateScheduled());
|
||||
|
||||
menu.findItem(R.id.menu_use_contacts).setChecked(
|
||||
settings.getUseContacts());
|
||||
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
|
@ -79,7 +84,8 @@ public class MainActivity extends AppCompatActivity {
|
|||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
PermissionHelper.onRequestPermissionsResult(this, requestCode, permissions, grantResults,
|
||||
settings.getIncomingCallNotifications(), settings.getBlockCalls());
|
||||
settings.getIncomingCallNotifications(), settings.getBlockCalls(),
|
||||
settings.getUseContacts());
|
||||
|
||||
loadCallLog();
|
||||
}
|
||||
|
@ -124,7 +130,8 @@ public class MainActivity extends AppCompatActivity {
|
|||
|
||||
private void checkPermissions() {
|
||||
PermissionHelper.checkPermissions(this,
|
||||
settings.getIncomingCallNotifications(), settings.getBlockCalls());
|
||||
settings.getIncomingCallNotifications(), settings.getBlockCalls(),
|
||||
settings.getUseContacts());
|
||||
}
|
||||
|
||||
private void startCheckMainDbTask() {
|
||||
|
@ -183,6 +190,12 @@ public class MainActivity extends AppCompatActivity {
|
|||
else updateScheduler.cancelAutoUpdateWorker();
|
||||
}
|
||||
|
||||
public void onUseContactsChanged(MenuItem item) {
|
||||
settings.setUseContacts(!item.isChecked());
|
||||
checkPermissions();
|
||||
loadCallLog();
|
||||
}
|
||||
|
||||
public void onOpenDebugActivity(MenuItem item) {
|
||||
startActivity(new Intent(this, DebugActivity.class));
|
||||
}
|
||||
|
@ -194,11 +207,31 @@ public class MainActivity extends AppCompatActivity {
|
|||
@SuppressLint("InflateParams")
|
||||
View view = getLayoutInflater().inflate(R.layout.info_dialog, null);
|
||||
|
||||
TextView featuredNameView = view.findViewById(R.id.featuredName);
|
||||
if (item.numberInfo.name != null) {
|
||||
featuredNameView.setText(item.numberInfo.name);
|
||||
String name = "";
|
||||
|
||||
String contactName = item.numberInfo.contactItem != null
|
||||
? item.numberInfo.contactItem.displayName : null;
|
||||
|
||||
if (!TextUtils.isEmpty(contactName)) {
|
||||
name += contactName;
|
||||
}
|
||||
|
||||
String featuredName = item.numberInfo.featuredDatabaseItem != null
|
||||
? item.numberInfo.featuredDatabaseItem.getName() : null;
|
||||
|
||||
if (!TextUtils.isEmpty(featuredName)) {
|
||||
if (name.isEmpty()) {
|
||||
name = featuredName;
|
||||
} else {
|
||||
name += "\n(" + featuredName + ")";
|
||||
}
|
||||
}
|
||||
|
||||
TextView nameView = view.findViewById(R.id.name);
|
||||
if (!TextUtils.isEmpty(name)) {
|
||||
nameView.setText(name);
|
||||
} else {
|
||||
featuredNameView.setVisibility(View.GONE);
|
||||
nameView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
ReviewsSummaryHelper.populateSummary(view.findViewById(R.id.reviews_summary),
|
||||
|
@ -215,7 +248,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
private void loadCallLog() {
|
||||
if (!PermissionHelper.havePermission(this, Manifest.permission.READ_CALL_LOG)) {
|
||||
if (!PermissionHelper.hasCallLogPermission(this)) {
|
||||
setCallLogVisibility(false);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -8,17 +8,16 @@ import android.app.PendingIntent;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import dummydomain.yetanothercallblocker.data.NumberInfo;
|
||||
import dummydomain.yetanothercallblocker.sia.model.NumberCategory;
|
||||
import dummydomain.yetanothercallblocker.sia.model.NumberInfo;
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabaseItem;
|
||||
|
||||
public class NotificationHelper {
|
||||
|
@ -34,7 +33,7 @@ public class NotificationHelper {
|
|||
private static final String CHANNEL_GROUP_ID_BLOCKED_CALLS = "blocked_calls";
|
||||
private static final String CHANNEL_GROUP_ID_TASKS = "tasks";
|
||||
|
||||
private static final String CHANNEL_ID_POSITIVE_KNOWN = "positive_known_calls";
|
||||
private static final String CHANNEL_ID_KNOWN = "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";
|
||||
|
@ -83,41 +82,39 @@ public class NotificationHelper {
|
|||
}
|
||||
|
||||
private static Notification createIncomingCallNotification(Context context, NumberInfo numberInfo) {
|
||||
boolean unknown = false;
|
||||
String channelId;
|
||||
String title;
|
||||
String text = "";
|
||||
@DrawableRes int icon;
|
||||
@ColorInt int color;
|
||||
switch (numberInfo.rating) {
|
||||
case POSITIVE:
|
||||
channelId = numberInfo.known ? CHANNEL_ID_POSITIVE_KNOWN : CHANNEL_ID_POSITIVE;
|
||||
channelId = CHANNEL_ID_POSITIVE;
|
||||
title = context.getString(R.string.notification_incoming_call_positive);
|
||||
icon = R.drawable.ic_thumb_up_24dp;
|
||||
color = 0xff00ff00;
|
||||
break;
|
||||
|
||||
case NEUTRAL:
|
||||
channelId = CHANNEL_ID_NEUTRAL;
|
||||
title = context.getString(R.string.notification_incoming_call_neutral);
|
||||
icon = R.drawable.ic_thumbs_up_down_24dp;
|
||||
color = 0xffffff60;
|
||||
break;
|
||||
|
||||
case NEGATIVE:
|
||||
channelId = CHANNEL_ID_NEGATIVE;
|
||||
title = context.getString(R.string.notification_incoming_call_negative);
|
||||
icon = R.drawable.ic_thumb_down_24dp;
|
||||
color = 0xffff0000;
|
||||
break;
|
||||
|
||||
default:
|
||||
unknown = true;
|
||||
channelId = CHANNEL_ID_UNKNOWN;
|
||||
title = context.getString(R.string.notification_incoming_call_unknown);
|
||||
icon = R.drawable.ic_thumbs_up_down_24dp;
|
||||
color = 0xffffff60;
|
||||
break;
|
||||
}
|
||||
|
||||
// up for debate
|
||||
if (numberInfo.contactItem != null && unknown) {
|
||||
channelId = CHANNEL_ID_KNOWN;
|
||||
title = context.getString(R.string.notification_incoming_call_contact);
|
||||
}
|
||||
|
||||
if (numberInfo.communityDatabaseItem != null) {
|
||||
CommunityDatabaseItem communityItem = numberInfo.communityDatabaseItem;
|
||||
|
||||
|
@ -129,9 +126,12 @@ public class NotificationHelper {
|
|||
|
||||
text += getDescription(context, numberInfo);
|
||||
|
||||
IconAndColor iconAndColor = IconAndColor.forNumberRating(
|
||||
numberInfo.rating, numberInfo.contactItem != null);
|
||||
|
||||
return new NotificationCompat.Builder(context, channelId)
|
||||
.setSmallIcon(icon)
|
||||
.setColor(color)
|
||||
.setSmallIcon(iconAndColor.iconResId)
|
||||
.setColor(iconAndColor.getColorInt(context))
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
|
@ -163,7 +163,7 @@ public class NotificationHelper {
|
|||
if (numberInfo.communityDatabaseItem != null) {
|
||||
CommunityDatabaseItem communityItem = numberInfo.communityDatabaseItem;
|
||||
|
||||
if (numberInfo.name != null) {
|
||||
if (!TextUtils.isEmpty(numberInfo.name)) {
|
||||
text += numberInfo.name;
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ public class NotificationHelper {
|
|||
NotificationChannel channel;
|
||||
|
||||
channel = new NotificationChannel(
|
||||
CHANNEL_ID_POSITIVE_KNOWN, context.getString(R.string.notification_channel_name_positive_known),
|
||||
CHANNEL_ID_KNOWN, context.getString(R.string.notification_channel_name_known),
|
||||
NotificationManager.IMPORTANCE_MIN
|
||||
);
|
||||
channel.setGroup(channelGroupIncoming.getId());
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.Manifest;
|
|||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -15,8 +16,6 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -29,6 +28,7 @@ public class PermissionHelper {
|
|||
|
||||
private static final Set<String> INFO_PERMISSIONS = new HashSet<>();
|
||||
private static final Set<String> BLOCKING_PERMISSIONS = new HashSet<>();
|
||||
private static final Set<String> CONTACTS_PERMISSIONS = new HashSet<>();
|
||||
|
||||
static {
|
||||
INFO_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
|
||||
|
@ -42,11 +42,17 @@ public class PermissionHelper {
|
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
BLOCKING_PERMISSIONS.add(Manifest.permission.ANSWER_PHONE_CALLS);
|
||||
}
|
||||
|
||||
CONTACTS_PERMISSIONS.add(Manifest.permission.READ_CONTACTS);
|
||||
}
|
||||
|
||||
public static void checkPermissions(AppCompatActivity activity, boolean info, boolean block) {
|
||||
Collection<String> requiredPermissions = block ? BLOCKING_PERMISSIONS
|
||||
: info ? INFO_PERMISSIONS : Collections.emptyList();
|
||||
public static void checkPermissions(AppCompatActivity activity, boolean info,
|
||||
boolean block, boolean contacts) {
|
||||
Set<String> requiredPermissions = new HashSet<>();
|
||||
|
||||
if (info) requiredPermissions.addAll(INFO_PERMISSIONS);
|
||||
if (block) requiredPermissions.addAll(BLOCKING_PERMISSIONS);
|
||||
if (contacts) requiredPermissions.addAll(CONTACTS_PERMISSIONS);
|
||||
|
||||
List<String> missingPermissions = new ArrayList<>(requiredPermissions.size());
|
||||
|
||||
|
@ -66,53 +72,69 @@ public class PermissionHelper {
|
|||
public static void onRequestPermissionsResult(@NonNull Context context, int requestCode,
|
||||
@NonNull String[] permissions,
|
||||
@NonNull int[] grantResults,
|
||||
boolean infoExpected, boolean blockingExpected) {
|
||||
boolean infoExpected, boolean blockingExpected,
|
||||
boolean contactsExpected) {
|
||||
if (requestCode != PERMISSION_REQUEST_CODE) return;
|
||||
|
||||
boolean infoDenied = false;
|
||||
boolean blockingDenied = false;
|
||||
boolean contactsDenied = false;
|
||||
|
||||
if (permissions.length == 0) {
|
||||
infoDenied = true;
|
||||
blockingDenied = true;
|
||||
contactsDenied = true;
|
||||
} else {
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
String permission = permissions[i];
|
||||
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
|
||||
if (INFO_PERMISSIONS.contains(permission)) {
|
||||
infoDenied = true;
|
||||
}
|
||||
if (BLOCKING_PERMISSIONS.contains(permission)) {
|
||||
blockingDenied = true;
|
||||
break;
|
||||
} else if (BLOCKING_PERMISSIONS.contains(permission)) {
|
||||
blockingDenied = true;
|
||||
}
|
||||
if (CONTACTS_PERMISSIONS.contains(permission)) {
|
||||
contactsDenied = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG.debug("onRequestPermissionsResult() infoDenied={}, blockingDenied={}",
|
||||
infoDenied, blockingDenied);
|
||||
LOG.debug("onRequestPermissionsResult() infoDenied={}, blockingDenied={}, contactsDenied={}",
|
||||
infoDenied, blockingDenied, contactsDenied);
|
||||
|
||||
if (!blockingExpected) blockingDenied = false;
|
||||
if (!infoExpected) infoDenied = false;
|
||||
if (!blockingExpected) blockingDenied = false;
|
||||
if (!contactsExpected) contactsDenied = false;
|
||||
|
||||
String message;
|
||||
if (infoDenied && blockingDenied) {
|
||||
message = context.getString(R.string.denied_permissions_message_blocking_and_info);
|
||||
} else if (infoDenied) {
|
||||
message = context.getString(R.string.denied_permissions_message_info);
|
||||
} else if (blockingDenied) {
|
||||
message = context.getString(R.string.denied_permissions_message_blocking);
|
||||
} else {
|
||||
message = null;
|
||||
}
|
||||
List<String> features = new ArrayList<>(3);
|
||||
|
||||
if (infoDenied) features.add(context.getString(R.string.feature_info));
|
||||
if (blockingDenied) features.add(context.getString(R.string.feature_call_blocking));
|
||||
if (contactsDenied) features.add(context.getString(R.string.feature_contacts));
|
||||
|
||||
if (!features.isEmpty()) {
|
||||
String message = context.getString(R.string.denied_permissions_message) + " "
|
||||
+ TextUtils.join(", ", features);
|
||||
|
||||
if (message != null) {
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean havePermission(Context context, String permission) {
|
||||
public static boolean hasCallLogPermission(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
return hasPermission(context, Manifest.permission.READ_CALL_LOG);
|
||||
} else {
|
||||
return true; // TODO: check
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasContactsPermission(Context context) {
|
||||
return hasPermission(context, Manifest.permission.READ_CONTACTS);
|
||||
}
|
||||
|
||||
public static boolean hasPermission(Context context, String permission) {
|
||||
return ContextCompat.checkSelfPermission(context, permission)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ public class Settings {
|
|||
|
||||
private static final String PREF_INCOMING_CALL_NOTIFICATIONS = "incomingCallNotifications";
|
||||
private static final String PREF_BLOCK_CALLS = "blockCalls";
|
||||
private static final String PREF_USE_CONTACTS = "useContacts";
|
||||
private static final String PREF_LAST_UPDATE_TIME = "lastUpdateTime";
|
||||
private static final String PREF_LAST_UPDATE_CHECK_TIME = "lastUpdateCheckTime";
|
||||
|
||||
|
@ -32,6 +33,14 @@ public class Settings {
|
|||
setBoolean(PREF_BLOCK_CALLS, block);
|
||||
}
|
||||
|
||||
public boolean getUseContacts() {
|
||||
return getBoolean(PREF_USE_CONTACTS);
|
||||
}
|
||||
|
||||
public void setUseContacts(boolean use) {
|
||||
setBoolean(PREF_USE_CONTACTS, use);
|
||||
}
|
||||
|
||||
public long getLastUpdateTime() {
|
||||
return getLong(PREF_LAST_UPDATE_TIME, 0);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package dummydomain.yetanothercallblocker;
|
||||
package dummydomain.yetanothercallblocker.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
|
@ -1,11 +1,9 @@
|
|||
package dummydomain.yetanothercallblocker;
|
||||
package dummydomain.yetanothercallblocker.data;
|
||||
|
||||
import android.provider.CallLog;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import dummydomain.yetanothercallblocker.sia.model.NumberInfo;
|
||||
|
||||
public class CallLogItem {
|
||||
|
||||
public enum Type {
|
|
@ -0,0 +1,17 @@
|
|||
package dummydomain.yetanothercallblocker.data;
|
||||
|
||||
public class ContactItem {
|
||||
|
||||
public String displayName;
|
||||
|
||||
public ContactItem(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ContactItem{" +
|
||||
"displayName='" + displayName + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package dummydomain.yetanothercallblocker.data;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.ContactsContract;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import dummydomain.yetanothercallblocker.PermissionHelper;
|
||||
import dummydomain.yetanothercallblocker.Settings;
|
||||
|
||||
public class ContactsHelper {
|
||||
|
||||
private static final String[] PROJECTION = new String[]{
|
||||
ContactsContract.PhoneLookup.DISPLAY_NAME
|
||||
};
|
||||
|
||||
public static ContactsProvider getContactsProvider(Context context, Settings settings) {
|
||||
return number -> {
|
||||
if (settings.getUseContacts() && PermissionHelper.hasContactsPermission(context)) {
|
||||
String contactName = getContactName(context, number);
|
||||
if (!TextUtils.isEmpty(contactName)) return new ContactItem(contactName);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
public static String getContactName(Context context, String number) {
|
||||
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
|
||||
Uri.encode(number));
|
||||
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
try (Cursor cursor = resolver.query(uri, PROJECTION, null, null, null)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return cursor.getString(cursor.getColumnIndex(
|
||||
ContactsContract.PhoneLookup.DISPLAY_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package dummydomain.yetanothercallblocker.data;
|
||||
|
||||
public interface ContactsProvider {
|
||||
ContactItem get(String number);
|
||||
}
|
|
@ -1,23 +1,27 @@
|
|||
package dummydomain.yetanothercallblocker.sia;
|
||||
package dummydomain.yetanothercallblocker.data;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import dummydomain.yetanothercallblocker.sia.model.NumberInfo;
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabase;
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabaseItem;
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.FeaturedDatabase;
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.FeaturedDatabaseItem;
|
||||
|
||||
import static dummydomain.yetanothercallblocker.sia.SiaConstants.*;
|
||||
import static dummydomain.yetanothercallblocker.sia.SiaConstants.SIA_PATH_PREFIX;
|
||||
import static dummydomain.yetanothercallblocker.sia.SiaConstants.SIA_SECONDARY_PATH_PREFIX;
|
||||
|
||||
public class DatabaseSingleton {
|
||||
public class DatabaseSingleton { // TODO: rewrite
|
||||
|
||||
private static final CommunityDatabase COMMUNITY_DATABASE = new CommunityDatabase(SIA_PATH_PREFIX, SIA_SECONDARY_PATH_PREFIX);
|
||||
private static final FeaturedDatabase FEATURED_DATABASE = new FeaturedDatabase(SIA_PATH_PREFIX);
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DatabaseSingleton.class);
|
||||
|
||||
private static ContactsProvider contactsProvider;
|
||||
|
||||
public static CommunityDatabase getCommunityDatabase() {
|
||||
return COMMUNITY_DATABASE;
|
||||
}
|
||||
|
@ -26,6 +30,10 @@ public class DatabaseSingleton {
|
|||
return FEATURED_DATABASE;
|
||||
}
|
||||
|
||||
public static void setContactsProvider(ContactsProvider contactsProvider) {
|
||||
DatabaseSingleton.contactsProvider = contactsProvider;
|
||||
}
|
||||
|
||||
public static NumberInfo getNumberInfo(String number) {
|
||||
LOG.debug("getNumberInfo({}) started", number);
|
||||
// TODO: check number format
|
||||
|
@ -38,9 +46,22 @@ public class DatabaseSingleton {
|
|||
.getDbItemByNumber(number);
|
||||
LOG.trace("getNumberInfo() featuredItem={}", featuredItem);
|
||||
|
||||
ContactItem contactItem = contactsProvider != null ? contactsProvider.get(number) : null;
|
||||
LOG.trace("getNumberInfo() contactItem={}", contactItem);
|
||||
|
||||
NumberInfo numberInfo = new NumberInfo();
|
||||
numberInfo.number = number;
|
||||
if (featuredItem != null) numberInfo.name = featuredItem.getName();
|
||||
|
||||
numberInfo.communityDatabaseItem = communityItem;
|
||||
numberInfo.featuredDatabaseItem = featuredItem;
|
||||
numberInfo.contactItem = contactItem;
|
||||
|
||||
if (contactItem != null && !TextUtils.isEmpty(contactItem.displayName)) {
|
||||
numberInfo.name = contactItem.displayName;
|
||||
} else if (featuredItem != null && !TextUtils.isEmpty(featuredItem.getName())) {
|
||||
numberInfo.name = featuredItem.getName();
|
||||
}
|
||||
LOG.trace("getNumberInfo() name={}", numberInfo.name);
|
||||
|
||||
if (communityItem != null && communityItem.hasRatings()) {
|
||||
if (communityItem.getNegativeRatingsCount() > communityItem.getPositiveRatingsCount()
|
||||
|
@ -54,8 +75,6 @@ public class DatabaseSingleton {
|
|||
numberInfo.rating = NumberInfo.Rating.NEUTRAL;
|
||||
}
|
||||
}
|
||||
numberInfo.communityDatabaseItem = communityItem;
|
||||
|
||||
LOG.trace("getNumberInfo() rating={}", numberInfo.rating);
|
||||
|
||||
return numberInfo;
|
|
@ -1,6 +1,7 @@
|
|||
package dummydomain.yetanothercallblocker.sia.model;
|
||||
package dummydomain.yetanothercallblocker.data;
|
||||
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabaseItem;
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.FeaturedDatabaseItem;
|
||||
|
||||
public class NumberInfo {
|
||||
|
||||
|
@ -8,10 +9,18 @@ public class NumberInfo {
|
|||
POSITIVE, NEUTRAL, NEGATIVE, UNKNOWN
|
||||
}
|
||||
|
||||
// id
|
||||
public String number;
|
||||
public String name;
|
||||
public boolean known;
|
||||
public Rating rating = Rating.UNKNOWN;
|
||||
|
||||
// info from various sources
|
||||
public CommunityDatabaseItem communityDatabaseItem;
|
||||
public FeaturedDatabaseItem featuredDatabaseItem;
|
||||
public ContactItem contactItem;
|
||||
|
||||
// computed rating
|
||||
public Rating rating = Rating.UNKNOWN;
|
||||
|
||||
// name (for convenience)
|
||||
public String name;
|
||||
|
||||
}
|
|
@ -2,9 +2,35 @@ package dummydomain.yetanothercallblocker.sia.model;
|
|||
|
||||
public class CommunityReview {
|
||||
|
||||
public enum Rating {
|
||||
|
||||
UNKNOWN(0),
|
||||
POSITIVE(1),
|
||||
NEGATIVE(2),
|
||||
NEUTRAL(3);
|
||||
|
||||
private int id;
|
||||
|
||||
Rating(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static Rating getById(int id) {
|
||||
for (Rating rating : Rating.values()) {
|
||||
if (rating.getId() == id) return rating;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int id;
|
||||
|
||||
private NumberRating rating;
|
||||
private Rating rating;
|
||||
private NumberCategory category;
|
||||
private String author;
|
||||
private String title;
|
||||
|
@ -18,11 +44,11 @@ public class CommunityReview {
|
|||
this.id = id;
|
||||
}
|
||||
|
||||
public NumberRating getRating() {
|
||||
public Rating getRating() {
|
||||
return rating;
|
||||
}
|
||||
|
||||
public void setRating(NumberRating rating) {
|
||||
public void setRating(Rating rating) {
|
||||
this.rating = rating;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ public class CommunityReviewsLoader {
|
|||
|
||||
CommunityReview review = new CommunityReview();
|
||||
review.setId(item.getInt("id"));
|
||||
review.setRating(NumberRating.getById(item.getInt("rating")));
|
||||
review.setRating(CommunityReview.Rating.getById(item.getInt("rating")));
|
||||
review.setCategory(NumberCategory.getById(item.getInt("category_id")));
|
||||
review.setAuthor(item.getString("nick"));
|
||||
review.setTitle(item.getString("title"));
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package dummydomain.yetanothercallblocker.sia.model;
|
||||
|
||||
public enum NumberRating {
|
||||
|
||||
UNKNOWN(0),
|
||||
POSITIVE(1),
|
||||
NEGATIVE(2),
|
||||
NEUTRAL(3);
|
||||
|
||||
private int id;
|
||||
|
||||
NumberRating(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static NumberRating getById(int id) {
|
||||
for (NumberRating rating : values()) {
|
||||
if (rating.getId() == id) return rating;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -10,7 +10,7 @@ import java.nio.ByteBuffer;
|
|||
import java.util.UUID;
|
||||
|
||||
import dummydomain.yetanothercallblocker.App;
|
||||
import dummydomain.yetanothercallblocker.sia.DatabaseSingleton;
|
||||
import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
|
||||
import dummydomain.yetanothercallblocker.sia.SiaSettings;
|
||||
|
||||
public class Utils {
|
||||
|
|
|
@ -17,11 +17,11 @@ import dummydomain.yetanothercallblocker.App;
|
|||
import dummydomain.yetanothercallblocker.NotificationHelper;
|
||||
import dummydomain.yetanothercallblocker.R;
|
||||
import dummydomain.yetanothercallblocker.Settings;
|
||||
import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
|
||||
import dummydomain.yetanothercallblocker.event.MainDbDownloadFinishedEvent;
|
||||
import dummydomain.yetanothercallblocker.event.MainDbDownloadingEvent;
|
||||
import dummydomain.yetanothercallblocker.event.SecondaryDbUpdateFinished;
|
||||
import dummydomain.yetanothercallblocker.event.SecondaryDbUpdatingEvent;
|
||||
import dummydomain.yetanothercallblocker.sia.DatabaseSingleton;
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.DbManager;
|
||||
|
||||
import static dummydomain.yetanothercallblocker.EventUtils.postEvent;
|
||||
|
|
|
@ -11,9 +11,9 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import dummydomain.yetanothercallblocker.App;
|
||||
import dummydomain.yetanothercallblocker.Settings;
|
||||
import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
|
||||
import dummydomain.yetanothercallblocker.event.SecondaryDbUpdateFinished;
|
||||
import dummydomain.yetanothercallblocker.event.SecondaryDbUpdatingEvent;
|
||||
import dummydomain.yetanothercallblocker.sia.DatabaseSingleton;
|
||||
|
||||
import static dummydomain.yetanothercallblocker.EventUtils.postEvent;
|
||||
import static dummydomain.yetanothercallblocker.EventUtils.postStickyEvent;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
|
||||
</vector>
|
|
@ -18,13 +18,16 @@
|
|||
android:src="@drawable/ic_call_missed_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_number"
|
||||
android:id="@+id/item_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLength="25"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="?attr/textAppearanceListItem"
|
||||
tools:text="+12345678901" />
|
||||
|
||||
|
|
|
@ -8,11 +8,15 @@
|
|||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/featuredName"
|
||||
android:id="@+id/name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_horizontal"
|
||||
android:maxLines="3"
|
||||
android:textAlignment="center"
|
||||
android:textSize="20sp"
|
||||
tools:text="Featured company name" />
|
||||
tools:text="Featured company or contact name" />
|
||||
|
||||
<include
|
||||
layout="@layout/reviews_summary"
|
||||
|
|
|
@ -19,6 +19,12 @@
|
|||
android:onClick="onAutoUpdatesChanged"
|
||||
android:title="@string/auto_updates" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_use_contacts"
|
||||
android:checkable="true"
|
||||
android:onClick="onUseContactsChanged"
|
||||
android:title="@string/use_contacts" />
|
||||
|
||||
<item
|
||||
android:onClick="onOpenDebugActivity"
|
||||
android:title="@string/open_debug_activity" />
|
||||
|
|
|
@ -43,6 +43,6 @@
|
|||
<string name="notification_blocked_call">Блокировано</string>
|
||||
<string name="notification_channel_name_positive">С хорошей оценкой</string>
|
||||
<string name="notification_channel_name_blocked_info">Показать причину блокировки</string>
|
||||
<string name="notification_channel_name_positive_known">Из моего списка контактов</string>
|
||||
<string name="notification_channel_name_known">Из моего списка контактов</string>
|
||||
<string name="notification_incoming_call_positive">Одобрено</string>
|
||||
</resources>
|
|
@ -5,7 +5,7 @@
|
|||
<string name="notification_channel_group_name_blocked_calls">Blocked calls</string>
|
||||
<string name="notification_channel_group_name_tasks">Tasks</string>
|
||||
|
||||
<string name="notification_channel_name_positive_known">Known positive calls</string>
|
||||
<string name="notification_channel_name_known">Known 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>
|
||||
|
@ -13,6 +13,7 @@
|
|||
<string name="notification_channel_name_blocked_info">Blocked info</string>
|
||||
<string name="notification_channel_name_tasks">Tasks</string>
|
||||
|
||||
<string name="notification_incoming_call_contact">Contact</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>
|
||||
|
@ -50,9 +51,10 @@
|
|||
<string name="title_activity_reviews">Reviews</string>
|
||||
<string name="reviews_loading">Loading reviews…</string>
|
||||
|
||||
<string name="denied_permissions_message_blocking_and_info">Blocking and notification functionality may not work due to denied permissions</string>
|
||||
<string name="denied_permissions_message_info">Notification functionality may not work due to denied permissions</string>
|
||||
<string name="denied_permissions_message_blocking">Blocking functionality may not work due to denied permissions</string>
|
||||
<string name="denied_permissions_message">Due to denied permissions following features may not work:</string>
|
||||
<string name="feature_info">notifications</string>
|
||||
<string name="feature_call_blocking">call blocking</string>
|
||||
<string name="feature_contacts">using contacts</string>
|
||||
|
||||
<string name="call_log_permission_message">Grant \"Phone\" permission to see recent calls</string>
|
||||
|
||||
|
@ -71,6 +73,7 @@
|
|||
<string name="incoming_call_notifications">Incoming call notifications</string>
|
||||
<string name="block_calls">Block unwanted calls</string>
|
||||
<string name="auto_updates">Auto-update database</string>
|
||||
<string name="use_contacts">Use contacts</string>
|
||||
|
||||
<string name="open_debug_activity">Open debug screen</string>
|
||||
<string name="debug_activity_label">Debug</string>
|
||||
|
|
Loading…
Reference in New Issue