Contacts and refactoring

This commit is contained in:
xynngh 2020-05-07 17:46:32 +04:00
parent 0fa0e13da2
commit 28b7a9ca55
30 changed files with 352 additions and 130 deletions

View File

@ -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"

View File

@ -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() {

View File

@ -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() + "'";
}
}
}

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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());

View File

@ -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;
}

View File

@ -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);
}

View File

@ -1,4 +1,4 @@
package dummydomain.yetanothercallblocker;
package dummydomain.yetanothercallblocker.data;
import android.content.Context;
import android.database.Cursor;

View File

@ -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 {

View File

@ -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 + '\'' +
'}';
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,5 @@
package dummydomain.yetanothercallblocker.data;
public interface ContactsProvider {
ContactItem get(String number);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"));

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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>

View File

@ -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" />

View File

@ -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"

View File

@ -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" />

View File

@ -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>

View File

@ -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>