Refactoring

Use foreground IntentService for DB loading and update.
Add EventBus.
Some other changes.
This commit is contained in:
xynngh 2020-05-06 13:15:34 +04:00
parent 3f6778cbc7
commit cf32cf41cb
19 changed files with 421 additions and 118 deletions

View File

@ -8,6 +8,12 @@ android {
targetSdkVersion 28
versionCode 3040
versionName "0.3.4"
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex: 'dummydomain.yetanothercallblocker.EventBusIndex']
}
}
}
buildTypes {
release {
@ -25,6 +31,8 @@ android {
}
dependencies {
def eventbus_version = '3.2.0'
implementation 'org.slf4j:slf4j-api:1.7.30'
implementation 'com.github.tony19:logback-android:2.0.0'
//noinspection GradleDependency: 3.12.* is the latest version compatible with Android <5
@ -35,4 +43,6 @@ dependencies {
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.work:work-runtime:2.3.4'
implementation "org.greenrobot:eventbus:$eventbus_version"
annotationProcessor "org.greenrobot:eventbus-annotation-processor:$eventbus_version"
}

View File

@ -19,3 +19,14 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keepattributes *Annotation*
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# And if you use AsyncExecutor:
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}

View File

@ -45,6 +45,8 @@
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
<service android:name=".work.TaskService" />
</application>
</manifest>

View File

@ -2,6 +2,8 @@ package dummydomain.yetanothercallblocker;
import android.app.Application;
import org.greenrobot.eventbus.EventBus;
public class App extends Application {
private static App instance;
@ -12,6 +14,14 @@ public class App extends Application {
instance = this;
EventBus.builder()
.throwSubscriberException(BuildConfig.DEBUG)
.sendNoSubscriberEvent(false)
.addIndex(new EventBusIndex())
.installDefaultEventBus();
EventHandler.create(this);
NotificationHelper.createNotificationChannels(this);
}

View File

@ -8,10 +8,16 @@ import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import dummydomain.yetanothercallblocker.event.SecondaryDbUpdateFinished;
import dummydomain.yetanothercallblocker.sia.DatabaseSingleton;
import dummydomain.yetanothercallblocker.sia.model.NumberCategory;
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabaseItem;
import dummydomain.yetanothercallblocker.sia.model.database.FeaturedDatabaseItem;
import dummydomain.yetanothercallblocker.work.TaskService;
public class DebugActivity extends AppCompatActivity {
@ -22,6 +28,26 @@ public class DebugActivity extends AppCompatActivity {
hideSummary();
}
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onSecondaryDbUpdateFinished(SecondaryDbUpdateFinished event) {
setResult(getString(R.string.debug_update_result,
DatabaseSingleton.getCommunityDatabase().getEffectiveDbVersion()));
}
public void onQueryDbButtonClick(View view) {
setResult("");
hideSummary();
@ -90,20 +116,7 @@ public class DebugActivity extends AppCompatActivity {
setResult("");
hideSummary();
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
DatabaseSingleton.getCommunityDatabase().updateSecondaryDb();
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
setResult(DebugActivity.this.getString(R.string.debug_update_result,
DatabaseSingleton.getCommunityDatabase().getEffectiveDbVersion()));
}
}.execute();
TaskService.start(this, TaskService.TASK_UPDATE_SECONDARY_DB);
}
private String getNumber() {

View File

@ -0,0 +1,36 @@
package dummydomain.yetanothercallblocker;
import android.content.Context;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.SubscriberExceptionEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EventHandler {
private static final Logger LOG = LoggerFactory.getLogger(EventHandler.class);
private static EventHandler instance;
private Context context;
public static EventHandler create(Context context) {
EventHandler instance = new EventHandler(context);
EventBus.getDefault().register(instance);
return EventHandler.instance = instance;
}
private EventHandler(Context context) {
this.context = context;
}
@Subscribe
public void onSubscriberExceptionEvent(SubscriberExceptionEvent event) {
LOG.warn("onSubscriberExceptionEvent()", event.throwable);
}
}

View File

@ -16,18 +16,27 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SwitchCompat;
import androidx.recyclerview.widget.RecyclerView;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
import java.util.List;
import dummydomain.yetanothercallblocker.event.MainDbDownloadFinishedEvent;
import dummydomain.yetanothercallblocker.event.MainDbDownloadingEvent;
import dummydomain.yetanothercallblocker.sia.DatabaseSingleton;
import dummydomain.yetanothercallblocker.sia.model.NumberInfo;
import dummydomain.yetanothercallblocker.sia.model.database.DbManager;
import dummydomain.yetanothercallblocker.work.TaskService;
public class MainActivity extends AppCompatActivity {
private CallLogItemRecyclerViewAdapter callLogAdapter;
private List<CallLogItem> callLogItems = new ArrayList<>();
private AsyncTask<Void, Void, Boolean> checkMainDbTask;
private AsyncTask<Void, Void, List<CallLogItem>> loadCallLogTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -53,22 +62,6 @@ public class MainActivity extends AppCompatActivity {
if (isChecked) Updater.scheduleAutoUpdateWorker();
else Updater.cancelAutoUpdateWorker();
});
@SuppressLint("StaticFieldLeak")
AsyncTask<Void, Void, Boolean> noDbTask = new AsyncTask<Void, Void, Boolean>() {
@Override
protected Boolean doInBackground(Void... voids) {
return DatabaseSingleton.getCommunityDatabase().isOperational();
}
@Override
protected void onPostExecute(Boolean result) {
updateNoDbUi(result ? UpdateUiState.HIDDEN : UpdateUiState.NO_DB);
}
};
noDbTask.execute();
PermissionHelper.checkPermissions(this);
}
@Override
@ -91,29 +84,74 @@ public class MainActivity extends AppCompatActivity {
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
startCheckMainDbTask();
PermissionHelper.checkPermissions(this);
loadCallLog();
}
public void onDownloadDbClick(View view) {
// TODO: use service
@Override
protected void onStop() {
EventBus.getDefault().unregister(this);
updateNoDbUi(UpdateUiState.DOWNLOADING_DB);
super.onStop();
}
@Override
protected void onDestroy() {
cancelCheckMainDbTask();
cancelLoadingCallLogTask();
super.onDestroy();
}
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onMainDbDownloadFinished(MainDbDownloadFinishedEvent event) {
loadCallLog();
}
private void startCheckMainDbTask() {
cancelCheckMainDbTask();
@SuppressLint("StaticFieldLeak")
AsyncTask<Void, Void, Boolean> dlTask = new AsyncTask<Void, Void, Boolean>() {
AsyncTask<Void, Void, Boolean> checkMainDbTask = this.checkMainDbTask
= new AsyncTask<Void, Void, Boolean>() {
@Override
protected Boolean doInBackground(Void... voids) {
return DbManager.downloadMainDb()
&& DatabaseSingleton.getCommunityDatabase().reload()
&& DatabaseSingleton.getFeaturedDatabase().reload();
return DatabaseSingleton.getCommunityDatabase().isOperational();
}
@Override
protected void onPostExecute(Boolean result) {
updateNoDbUi(result ? UpdateUiState.HIDDEN : UpdateUiState.ERROR);
if (!result && EventBus.getDefault().getStickyEvent(MainDbDownloadingEvent.class) == null) {
showNoMainDbDialog();
}
}
};
dlTask.execute();
checkMainDbTask.execute();
}
private void cancelCheckMainDbTask() {
if (checkMainDbTask != null) {
checkMainDbTask.cancel(true);
checkMainDbTask = null;
}
}
private void showNoMainDbDialog() {
new AlertDialog.Builder(this)
.setTitle(R.string.no_main_db_title)
.setMessage(R.string.no_main_db_text)
.setPositiveButton(R.string.download_main_db,
(d, w) -> downloadMainDb())
.setNegativeButton(R.string.no, null)
.show();
}
public void downloadMainDb() {
TaskService.start(this, TaskService.TASK_DOWNLOAD_MAIN_DB);
}
public void onOpenDebugActivity(MenuItem item) {
@ -147,24 +185,16 @@ public class MainActivity extends AppCompatActivity {
builder.show();
}
enum UpdateUiState {HIDDEN, NO_DB, DOWNLOADING_DB, ERROR}
private void updateNoDbUi(UpdateUiState state) {
findViewById(R.id.noDbText).setVisibility(state == UpdateUiState.NO_DB ? View.VISIBLE : View.GONE);
findViewById(R.id.downloadDbButton).setVisibility(state == UpdateUiState.NO_DB ? View.VISIBLE : View.GONE);
findViewById(R.id.downloadingDbText).setVisibility(state == UpdateUiState.DOWNLOADING_DB ? View.VISIBLE : View.GONE);
findViewById(R.id.dbCouldNotBeDownloaded).setVisibility(state == UpdateUiState.ERROR ? View.VISIBLE : View.GONE);
}
private void loadCallLog() {
if (!PermissionHelper.havePermission(this, Manifest.permission.READ_CALL_LOG)) {
setCallLogVisibility(false);
return;
}
new AsyncTask<Void, Void, List<CallLogItem>>() {
cancelLoadingCallLogTask();
@SuppressLint("StaticFieldLeak")
AsyncTask<Void, Void, List<CallLogItem>> loadCallLogTask = this.loadCallLogTask
= new AsyncTask<Void, Void, List<CallLogItem>>() {
@Override
protected List<CallLogItem> doInBackground(Void... voids) {
List<CallLogItem> items = CallLogHelper.getRecentCalls(MainActivity.this, 10);
@ -186,7 +216,15 @@ public class MainActivity extends AppCompatActivity {
setCallLogVisibility(true);
}
}.execute();
};
loadCallLogTask.execute();
}
private void cancelLoadingCallLogTask() {
if (loadCallLogTask != null) {
loadCallLogTask.cancel(true);
loadCallLogTask = null;
}
}
private void setCallLogVisibility(boolean visible) {

View File

@ -8,6 +8,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.core.app.NotificationCompat;
@ -26,15 +27,19 @@ public class NotificationHelper {
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;
public static final int NOTIFICATION_ID_TASKS = 3;
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_GROUP_ID_TASKS = "tasks";
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 final String CHANNEL_ID_TASKS = "tasks";
public static void showIncomingCallNotification(Context context, NumberInfo numberInfo) {
Notification notification = createIncomingCallNotification(context, numberInfo);
@ -171,6 +176,11 @@ public class NotificationHelper {
context.getString(R.string.notification_channel_group_name_blocked_calls));
notificationManager.createNotificationChannelGroup(channelGroupBlocked);
NotificationChannelGroup channelGroupTasks = new NotificationChannelGroup(
CHANNEL_GROUP_ID_TASKS,
context.getString(R.string.notification_channel_group_name_tasks));
notificationManager.createNotificationChannelGroup(channelGroupTasks);
List<NotificationChannel> channels = new ArrayList<>();
NotificationChannel channel;
@ -217,6 +227,13 @@ public class NotificationHelper {
channel.setGroup(channelGroupBlocked.getId());
channels.add(channel);
channel = new NotificationChannel(
CHANNEL_ID_TASKS, context.getString(R.string.notification_channel_name_tasks),
NotificationManager.IMPORTANCE_LOW
);
channel.setGroup(channelGroupTasks.getId());
channels.add(channel);
notificationManager.createNotificationChannels(channels);
}
}

View File

@ -1,16 +1,18 @@
package dummydomain.yetanothercallblocker;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import java.util.List;
@ -22,7 +24,8 @@ public class ReviewsActivity extends AppCompatActivity {
private static final String PARAM_NUMBER = "param_number";
private CustomListViewAdapter listViewAdapter;
private RecyclerView reviewsList;
private AsyncTask<Void, Void, List<CommunityReview>> loadReviewsTask;
public static Intent getNumberIntent(Context context, String number) {
Intent intent = new Intent(context, ReviewsActivity.class);
@ -47,12 +50,14 @@ public class ReviewsActivity extends AppCompatActivity {
setText(getString(R.string.reviews_loading));
listViewAdapter = new CustomListViewAdapter();
reviewsList = findViewById(R.id.reviews_list);
RecyclerView reviewsList = findViewById(R.id.reviews_list);
reviewsList.setLayoutManager(new LinearLayoutManager(this));
reviewsList.setAdapter(listViewAdapter);
reviewsList.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
new AsyncTask<Void, Void, List<CommunityReview>>() {
@SuppressLint("StaticFieldLeak")
AsyncTask<Void, Void, List<CommunityReview>> asyncTask = loadReviewsTask
= new AsyncTask<Void, Void, List<CommunityReview>>() {
@Override
protected List<CommunityReview> doInBackground(Void... voids) {
return CommunityReviewsLoader.loadReviews(paramNumber);
@ -63,7 +68,22 @@ public class ReviewsActivity extends AppCompatActivity {
setText("");
handleReviews(reviews);
}
}.execute();
};
asyncTask.execute();
}
@Override
protected void onDestroy() {
cancelReviewsLoadingTask();
super.onDestroy();
}
private void cancelReviewsLoadingTask() {
if (loadReviewsTask != null) {
loadReviewsTask.cancel(true);
loadReviewsTask = null;
}
}
private void setText(String text) {

View File

@ -1,13 +1,17 @@
package dummydomain.yetanothercallblocker;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import org.greenrobot.eventbus.EventBus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import dummydomain.yetanothercallblocker.event.SecondaryDbUpdateFinished;
import dummydomain.yetanothercallblocker.event.SecondaryDbUpdatingEvent;
import dummydomain.yetanothercallblocker.sia.DatabaseSingleton;
public class UpdateWorker extends Worker {
@ -23,7 +27,18 @@ public class UpdateWorker extends Worker {
public Result doWork() {
LOG.info("doWork() started");
DatabaseSingleton.getCommunityDatabase().updateSecondaryDb();
EventBus bus = EventBus.getDefault();
SecondaryDbUpdatingEvent sticky = new SecondaryDbUpdatingEvent();
bus.postSticky(sticky);
try {
DatabaseSingleton.getCommunityDatabase().updateSecondaryDb();
} finally {
bus.removeStickyEvent(sticky);
}
bus.post(new SecondaryDbUpdateFinished());
LOG.info("doWork() finished");
return Result.success();

View File

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

View File

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

View File

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

View File

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

View File

@ -33,9 +33,20 @@ public class DbManager {
if (DbDownloader.download(url, FileUtils.getDataDirPath() + tmpUpdateDir)) {
LOG.debug("downloadMainDb() downloaded and unpacked");
new File(dataDir, siaDir).renameTo(new File(dataDir, oldDir));
new File(dataDir, tmpUpdateDir).renameTo(new File(dataDir, siaDir));
File old = new File(dataDir, siaDir);
if (old.exists() && !old.renameTo(new File(dataDir, oldDir))) {
LOG.warn("downloadMainDb() couldn't rename sia to old");
return false;
}
if (!new File(dataDir, tmpUpdateDir).renameTo(new File(dataDir, siaDir))) {
LOG.warn("downloadMainDb() couldn't rename tmp to sia");
return false;
}
FileUtils.delete(dataDir, oldDir);
LOG.debug("downloadMainDb() folders moved");
return true;
} else {

View File

@ -0,0 +1,139 @@
package dummydomain.yetanothercallblocker.work;
import android.app.IntentService;
import android.app.Notification;
import android.content.Context;
import android.content.Intent;
import android.os.Process;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import org.greenrobot.eventbus.EventBus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dummydomain.yetanothercallblocker.NotificationHelper;
import dummydomain.yetanothercallblocker.R;
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;
public class TaskService extends IntentService {
public static final String TASK_DOWNLOAD_MAIN_DB = "download_main_db";
public static final String TASK_UPDATE_SECONDARY_DB = "update_secondary_db";
private static final Logger LOG = LoggerFactory.getLogger(TaskService.class);
public static void start(Context context, String task) {
Intent intent = new Intent(context, TaskService.class);
intent.setAction(task);
ContextCompat.startForegroundService(context, intent);
}
public TaskService() {
super(TaskService.class.getSimpleName());
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
String action = intent != null ? intent.getAction() : null;
startForeground(NotificationHelper.NOTIFICATION_ID_TASKS, createNotification(null));
try {
if (!TextUtils.isEmpty(action)) {
switch (action) {
case TASK_DOWNLOAD_MAIN_DB:
updateNotification(getString(R.string.main_db_downloading));
downloadMainDb();
break;
case TASK_UPDATE_SECONDARY_DB:
updateNotification(getString(R.string.secondary_db_updating));
updateSecondaryDb();
break;
default:
LOG.warn("Unknown action: " + action);
break;
}
}
} finally {
stopForeground(true);
}
}
private Notification createNotification(String title) {
if (title == null) title = getString(R.string.notification_background_operation);
NotificationCompat.Builder builder = new NotificationCompat.Builder(
getApplicationContext(), NotificationHelper.CHANNEL_ID_TASKS)
.setSmallIcon(R.drawable.ic_file_download_black_24dp);
builder.setContentTitle(title);
return builder.build();
}
private void updateNotification(String title) {
NotificationManagerCompat notificationManager = NotificationManagerCompat
.from(getApplicationContext());
notificationManager.notify(NotificationHelper.NOTIFICATION_ID_TASKS,
createNotification(title));
}
private void downloadMainDb() {
MainDbDownloadingEvent sticky = new MainDbDownloadingEvent();
postSticky(sticky);
try {
DbManager.downloadMainDb();
DatabaseSingleton.getCommunityDatabase().reload();
DatabaseSingleton.getFeaturedDatabase().reload();
} finally {
removeSticky(sticky);
}
post(new MainDbDownloadFinishedEvent());
}
private void updateSecondaryDb() {
SecondaryDbUpdatingEvent sticky = new SecondaryDbUpdatingEvent();
postSticky(sticky);
try {
DatabaseSingleton.getCommunityDatabase().updateSecondaryDb();
} finally {
removeSticky(sticky);
}
post(new SecondaryDbUpdateFinished());
}
private void post(Object event) {
bus().post(event);
}
private void postSticky(Object event) {
bus().postSticky(event);
}
private void removeSticky(Object event) {
bus().removeStickyEvent(event);
}
private EventBus bus() {
return EventBus.getDefault();
}
}

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="#FF000000"
android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z" />
</vector>

View File

@ -26,54 +26,6 @@
android:paddingRight="@dimen/item_padding"
android:showDividers="middle">
<TextView
android:id="@+id/noDbText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginStart="@dimen/text_margin"
android:layout_marginLeft="@dimen/text_margin"
android:paddingTop="@dimen/item_padding"
android:text="@string/no_main_db"
android:textAlignment="textStart"
android:textColor="@color/rateNegative"
android:visibility="gone" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/downloadDbButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/text_margin"
android:layout_marginBottom="@dimen/text_margin"
android:onClick="onDownloadDbClick"
android:text="@string/download_main_db"
android:visibility="gone" />
<TextView
android:id="@+id/downloadingDbText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginStart="@dimen/text_margin"
android:layout_marginLeft="@dimen/text_margin"
android:paddingTop="@dimen/item_padding"
android:text="@string/downloading_db"
android:textAlignment="textStart"
android:visibility="gone" />
<TextView
android:id="@+id/dbCouldNotBeDownloaded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginStart="@dimen/text_margin"
android:layout_marginLeft="@dimen/text_margin"
android:paddingTop="@dimen/item_padding"
android:text="@string/db_could_not_be_downloaded"
android:textAlignment="textStart"
android:textColor="@color/rateNegative"
android:visibility="gone" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/notificationsEnabledSwitch"
android:layout_width="match_parent"

View File

@ -3,6 +3,7 @@
<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_group_name_tasks">Tasks</string>
<string name="notification_channel_name_positive_known">Known positive calls</string>
<string name="notification_channel_name_positive">Positive calls</string>
@ -10,6 +11,7 @@
<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_channel_name_tasks">Tasks</string>
<string name="notification_incoming_call_positive">Positive call</string>
<string name="notification_incoming_call_neutral">Neutral call</string>
@ -20,6 +22,8 @@
<string name="notification_incoming_call_text_description">Negative: %d, positive: %d, neutral: %d</string>
<string name="notification_background_operation">Performing background operation…</string>
<string name="sia_category_none">None</string>
<string name="sia_category_telemarketer">Telemarketer</string>
<string name="sia_category_dept_collector">Debt collector</string>
@ -47,18 +51,22 @@
<string name="reviews_loading">Loading reviews…</string>
<string name="general_settings">General settings</string>
<string name="no_main_db">Database is not present! For the app to function offline you need to download DB. Press the button below to do it.</string>
<string name="downloading_db">Downloading DB…</string>
<string name="db_could_not_be_downloaded">Database couldn\'t be downloaded</string>
<string name="no_main_db_title">Download main database</string>
<string name="no_main_db_text">Database is not present! For the app to perform most of its functions you need to download DB. It will take around 25 MB of traffic.</string>
<string name="download_main_db">Download database</string>
<string name="main_db_downloading">Downloading DB…</string>
<string name="secondary_db_updating">Updating DB…</string>
<string name="recent_calls">Recent calls</string>
<string name="online_reviews">Online reviews</string>
<string name="back">Back</string>
<string name="no">No</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="download_main_db">Download database</string>
<string name="open_debug_activity">Open debug screen</string>
<string name="debug_activity_label">Debug</string>