From 91ddb35296bd298afec164ba72377b03170aaf0e Mon Sep 17 00:00:00 2001 From: xynngh Date: Thu, 3 Jun 2021 23:24:52 +0400 Subject: [PATCH] Add DB filtering --- CHANGELOG.md | 1 + .../DbFilteringSettingsFragment.java | 81 +++++++++++++++++++ .../yetanothercallblocker/Settings.java | 45 +++++++++++ .../yetanothercallblocker/data/Config.java | 3 + .../data/NumberFilter.java | 50 ++++++++++++ .../preference/IntEditTextPreference.java | 54 +++++++++++++ .../utils/DbFilteringUtils.java | 40 +++++++++ .../work/TaskService.java | 10 +++ app/src/main/res/values-ru/strings.xml | 17 ++++ app/src/main/res/values/strings.xml | 19 +++++ app/src/main/res/xml/advanced_preferences.xml | 27 +++++-- .../main/res/xml/db_filtering_preferences.xml | 46 +++++++++++ 12 files changed, 385 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/dummydomain/yetanothercallblocker/DbFilteringSettingsFragment.java create mode 100644 app/src/main/java/dummydomain/yetanothercallblocker/data/NumberFilter.java create mode 100644 app/src/main/java/dummydomain/yetanothercallblocker/preference/IntEditTextPreference.java create mode 100644 app/src/main/java/dummydomain/yetanothercallblocker/utils/DbFilteringUtils.java create mode 100644 app/src/main/res/xml/db_filtering_preferences.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 9942588..e4a3f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added +- \[Advanced\] Options for database filtering to save storage space. - Vietnamese translation thanks to bruh ([@quangtrung02hn16](https://hosted.weblate.org/user/quangtrung02hn16/)). - Esperanto translation of the app description thanks to phlostically ([@phlostically](https://hosted.weblate.org/user/phlostically/)). diff --git a/app/src/main/java/dummydomain/yetanothercallblocker/DbFilteringSettingsFragment.java b/app/src/main/java/dummydomain/yetanothercallblocker/DbFilteringSettingsFragment.java new file mode 100644 index 0000000..91305f3 --- /dev/null +++ b/app/src/main/java/dummydomain/yetanothercallblocker/DbFilteringSettingsFragment.java @@ -0,0 +1,81 @@ +package dummydomain.yetanothercallblocker; + +import android.text.TextUtils; + +import androidx.appcompat.app.AlertDialog; +import androidx.preference.EditTextPreference; + +import java.util.ArrayList; +import java.util.List; + +import dummydomain.yetanothercallblocker.data.YacbHolder; +import dummydomain.yetanothercallblocker.utils.DbFilteringUtils; +import dummydomain.yetanothercallblocker.work.TaskService; + +import static dummydomain.yetanothercallblocker.Settings.PREF_DB_FILTERING_PREFIXES_TO_KEEP; + +public class DbFilteringSettingsFragment extends BaseSettingsFragment { + + private static final String PREF_SCREEN_DB_FILTERING = "dbFiltering"; + private static final String PREF_INFO = "dbFilteringInfo"; + private static final String PREF_FILTER_DB = "dbFilteringFilterDb"; + + private final Settings settings = App.getSettings(); + + @Override + protected String getScreenKey() { + return PREF_SCREEN_DB_FILTERING; + } + + @Override + protected int getPreferencesResId() { + return R.xml.db_filtering_preferences; + } + + @Override + protected void initScreen() { + requirePreference(PREF_INFO).setOnPreferenceClickListener(pref -> { + new AlertDialog.Builder(requireActivity()) + .setTitle(R.string.settings_screen_db_filtering) + .setMessage(pref.getSummary()) + .setNegativeButton(R.string.back, null) + .show(); + return true; + }); + + setPrefChangeListener(PREF_DB_FILTERING_PREFIXES_TO_KEEP, (pref, newValue) -> { + String value = (String) newValue; + + List prefixes = new ArrayList<>(); + for (String prefix : DbFilteringUtils.parsePrefixes(value)) { + prefixes.add("+" + prefix); + } + String formattedPrefixes = TextUtils.join(",", prefixes); + + if (!TextUtils.equals(formattedPrefixes, value)) { + ((EditTextPreference) pref).setText(formattedPrefixes); + return false; + } + + return true; + }); + + requirePreference(PREF_FILTER_DB).setOnPreferenceClickListener(preference -> { + updateFilter(); + TaskService.start(requireContext(), TaskService.TASK_FILTER_DB); + return true; + }); + } + + @Override + public void onStop() { + super.onStop(); + + updateFilter(); + } + + private void updateFilter() { + YacbHolder.getDbManager().setNumberFilter(DbFilteringUtils.getNumberFilter(settings)); + } + +} diff --git a/app/src/main/java/dummydomain/yetanothercallblocker/Settings.java b/app/src/main/java/dummydomain/yetanothercallblocker/Settings.java index 1bbef93..f91dfe3 100644 --- a/app/src/main/java/dummydomain/yetanothercallblocker/Settings.java +++ b/app/src/main/java/dummydomain/yetanothercallblocker/Settings.java @@ -34,6 +34,11 @@ public class Settings extends GenericSettings { public static final String PREF_BLOCK_IN_LIMITED_MODE = "blockInLimitedMode"; public static final String PREF_LAST_UPDATE_TIME = "lastUpdateTime"; public static final String PREF_LAST_UPDATE_CHECK_TIME = "lastUpdateCheckTime"; + public static final String PREF_DB_FILTERING_ENABLED = "dbFilteringEnabled"; + public static final String PREF_DB_FILTERING_PREFIXES_TO_KEEP = "dbFilteringPrefixesToKeep"; + public static final String PREF_DB_FILTERING_THOROUGH = "dbFilteringThorough"; + public static final String PREF_DB_FILTERING_KEEP_SHORT_NUMBERS = "dbFilteringKeepShortNumbers"; + public static final String PREF_DB_FILTERING_KEEP_SHORT_NUMBERS_MAX_LENGTH = "dbFilteringKeepShortNumbersMaxLength"; public static final String PREF_COUNTRY_CODE_OVERRIDE = "countryCodeOverride"; public static final String PREF_COUNTRY_CODE_FOR_REVIEWS_OVERRIDE = "countryCodeForReviewsOverride"; public static final String PREF_DATABASE_DOWNLOAD_URL = "databaseDownloadUrl"; @@ -242,6 +247,46 @@ public class Settings extends GenericSettings { setLong(PREF_LAST_UPDATE_CHECK_TIME, timestamp); } + public boolean isDbFilteringEnabled() { + return getBoolean(PREF_DB_FILTERING_ENABLED); + } + + public void setDbFilteringEnabled(boolean enabled) { + setBoolean(PREF_DB_FILTERING_ENABLED, enabled); + } + + public String getDbFilteringPrefixesToKeep() { + return getString(PREF_DB_FILTERING_PREFIXES_TO_KEEP); + } + + public void setDbFilteringPrefixesToKeep(String prefixes) { + setString(PREF_DB_FILTERING_PREFIXES_TO_KEEP, prefixes); + } + + public boolean isDbFilteringThorough() { + return getBoolean(PREF_DB_FILTERING_THOROUGH, true); + } + + public void setDbFilteringThorough(boolean thorough) { + setBoolean(PREF_DB_FILTERING_THOROUGH, thorough); + } + + public boolean getDbFilteringKeepShortNumbers() { + return getBoolean(PREF_DB_FILTERING_KEEP_SHORT_NUMBERS, true); + } + + public void setDbFilteringKeepShortNumbers(boolean keep) { + setBoolean(PREF_DB_FILTERING_KEEP_SHORT_NUMBERS, keep); + } + + public int getDbFilteringKeepShortNumbersMaxLength() { + return getInt(PREF_DB_FILTERING_KEEP_SHORT_NUMBERS_MAX_LENGTH, 5); + } + + public void setDbFilteringKeepShortNumbersMaxLength(int length) { + setInt(PREF_DB_FILTERING_KEEP_SHORT_NUMBERS_MAX_LENGTH, length); + } + public String getCountryCodeOverride() { return getString(PREF_COUNTRY_CODE_OVERRIDE); } diff --git a/app/src/main/java/dummydomain/yetanothercallblocker/data/Config.java b/app/src/main/java/dummydomain/yetanothercallblocker/data/Config.java index 066ddec..ba9fec9 100644 --- a/app/src/main/java/dummydomain/yetanothercallblocker/data/Config.java +++ b/app/src/main/java/dummydomain/yetanothercallblocker/data/Config.java @@ -22,6 +22,7 @@ import dummydomain.yetanothercallblocker.sia.network.DbUpdateRequester; import dummydomain.yetanothercallblocker.sia.network.OkHttpClientFactory; import dummydomain.yetanothercallblocker.sia.network.WebService; import dummydomain.yetanothercallblocker.sia.utils.Utils; +import dummydomain.yetanothercallblocker.utils.DbFilteringUtils; import dummydomain.yetanothercallblocker.utils.DeferredInit; import dummydomain.yetanothercallblocker.utils.SystemUtils; import okhttp3.OkHttpClient; @@ -117,6 +118,8 @@ public class Config { new DbDownloader(okHttpClientFactory), new DbUpdateRequester(webService), communityDatabase)); + YacbHolder.getDbManager().setNumberFilter(DbFilteringUtils.getNumberFilter(settings)); + YacbHolder.setCommunityReviewsLoader(new CommunityReviewsLoader(webService)); YacbDaoSessionFactory daoSessionFactory = new YacbDaoSessionFactory(context, "YACB"); diff --git a/app/src/main/java/dummydomain/yetanothercallblocker/data/NumberFilter.java b/app/src/main/java/dummydomain/yetanothercallblocker/data/NumberFilter.java new file mode 100644 index 0000000..75a54f0 --- /dev/null +++ b/app/src/main/java/dummydomain/yetanothercallblocker/data/NumberFilter.java @@ -0,0 +1,50 @@ +package dummydomain.yetanothercallblocker.data; + +import java.util.List; + +public class NumberFilter + implements dummydomain.yetanothercallblocker.sia.model.database.NumberFilter { + + private final String[] prefixesToKeep; + private final int keepLength; + private final boolean detailed; + + public NumberFilter(List prefixesToKeep, boolean detailed, int keepLength) { + this.prefixesToKeep = prefixesToKeep.toArray(new String[0]); + this.detailed = detailed; + this.keepLength = keepLength; + } + + @Override + public boolean keepPrefix(String prefix) { + for (String prefixToKeep : prefixesToKeep) { + if (prefixMatch(prefix, prefixToKeep)) return true; + } + return false; + } + + @Override + public boolean keepNumber(String number) { + if (detailed && keepLength > 0 && number.length() <= keepLength) return true; + + for (String prefixToKeep : prefixesToKeep) { + if (number.startsWith(prefixToKeep)) return true; + } + + return false; + } + + @Override + public boolean isDetailed() { + return detailed; + } + + private boolean prefixMatch(String prefix, String keepPrefix) { + if (prefix.length() < keepPrefix.length()) { + return keepPrefix.startsWith(prefix); + } else { + return prefix.startsWith(keepPrefix); + } + } + +} diff --git a/app/src/main/java/dummydomain/yetanothercallblocker/preference/IntEditTextPreference.java b/app/src/main/java/dummydomain/yetanothercallblocker/preference/IntEditTextPreference.java new file mode 100644 index 0000000..2bdab58 --- /dev/null +++ b/app/src/main/java/dummydomain/yetanothercallblocker/preference/IntEditTextPreference.java @@ -0,0 +1,54 @@ +package dummydomain.yetanothercallblocker.preference; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.InputType; +import android.text.TextUtils; +import android.util.AttributeSet; + +import androidx.preference.EditTextPreference; + +public class IntEditTextPreference extends EditTextPreference { + + public IntEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + setListener(); + } + + public IntEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setListener(); + } + + public IntEditTextPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setListener(); + } + + public IntEditTextPreference(Context context) { + super(context); + setListener(); + } + + @Override + protected Object onGetDefaultValue(TypedArray a, int index) { + return a.getInt(index, 0); + } + + @Override + protected void onSetInitialValue(Object defaultValue) { + int defaultInt = defaultValue != null ? (int) defaultValue : 0; + setText(String.valueOf(getPersistedInt(defaultInt))); + } + + @Override + protected boolean persistString(String value) { + return persistInt(!TextUtils.isEmpty(value) ? Integer.parseInt(value) : 0); + } + + private void setListener() { + setOnBindEditTextListener(editText -> editText.setInputType(InputType.TYPE_CLASS_NUMBER)); + } + +} diff --git a/app/src/main/java/dummydomain/yetanothercallblocker/utils/DbFilteringUtils.java b/app/src/main/java/dummydomain/yetanothercallblocker/utils/DbFilteringUtils.java new file mode 100644 index 0000000..9bf1d39 --- /dev/null +++ b/app/src/main/java/dummydomain/yetanothercallblocker/utils/DbFilteringUtils.java @@ -0,0 +1,40 @@ +package dummydomain.yetanothercallblocker.utils; + +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import dummydomain.yetanothercallblocker.Settings; +import dummydomain.yetanothercallblocker.data.NumberFilter; + +public class DbFilteringUtils { + + public static NumberFilter getNumberFilter(Settings settings) { + if (!settings.isDbFilteringEnabled()) return null; + + return new NumberFilter(getPrefixesToKeep(settings), + settings.isDbFilteringThorough(), + settings.getDbFilteringKeepShortNumbers() + ? settings.getDbFilteringKeepShortNumbersMaxLength() : 0); + } + + public static List getPrefixesToKeep(Settings settings) { + return parsePrefixes(settings.getDbFilteringPrefixesToKeep()); + } + + public static List parsePrefixes(String prefixesString) { + if (TextUtils.isEmpty(prefixesString)) return Collections.emptyList(); + + List prefixList = new ArrayList<>(); + + for (String prefix : prefixesString.split("[,;]")) { + prefix = prefix.replaceAll("[^0-9]", ""); + if (!prefix.isEmpty() && !prefixList.contains(prefix)) prefixList.add(prefix); + } + + return prefixList; + } + +} diff --git a/app/src/main/java/dummydomain/yetanothercallblocker/work/TaskService.java b/app/src/main/java/dummydomain/yetanothercallblocker/work/TaskService.java index 42ac528..d9b4bec 100644 --- a/app/src/main/java/dummydomain/yetanothercallblocker/work/TaskService.java +++ b/app/src/main/java/dummydomain/yetanothercallblocker/work/TaskService.java @@ -28,6 +28,7 @@ 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"; + public static final String TASK_FILTER_DB = "filter_db"; private static final Logger LOG = LoggerFactory.getLogger(TaskService.class); @@ -61,6 +62,11 @@ public class TaskService extends IntentService { updateSecondaryDb(); break; + case TASK_FILTER_DB: + updateNotification(getString(R.string.filtering_db)); + filterDb(); + break; + default: LOG.warn("Unknown action: " + action); break; @@ -102,4 +108,8 @@ public class TaskService extends IntentService { new DbUpdater().update(); } + private void filterDb() { + YacbHolder.getDbManager().filterDb(); + } + } diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 7023e03..f7a3e6a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -214,4 +214,21 @@ Перевести приложение на Weblate Получить поддержку / сообщить о проблеме Это приложение распространяется под лицензией AGPL-3.0-only. + База данных + Фильтрация базы данных + Пояснение + Эта функция позволят сэкономить место в хранилище путём удаления неприменимых к вам данных (таких как номера из других регионов и стран). Она не влияет на общую скорость работы приложения (например, на блокировку вызовов). Фильтрация происходит во время начальной загрузки базы и во время её обновления. Существующие данные (загруженные до включения фильтрации) могут быть отфильтрованы с помощью опции \"Отфильтровать базу\" ниже. Отключение фильтрации или изменение её параметров не восстанавливает удалённые данные - для этого вам потребуется загрузить их заново + Фильтрация включена + Включает фильтрацию во время загрузки и обновления базы + Префиксы для сохранения + Список (разделённый запятыми) префиксов номеров, которые нужно сохранить. Префиксы должны быть в международной формате (например, +7999). Например, если вам звонят только номера начинающиеся на +7, введите сюда +7 + Тщательная фильтрация + Если включена, фильтрация проходит по всем блокам данных и удаляет номера, не подходящие по префиксам. В противном случае удаляются только целые блоки (оставляя некоторые лишние номера в сохраняемых блоках). Рекомендуется оставить включённым. Требуется для сохранения коротких номеров + Сохранять короткие номера + Сохранять номера, короче указанной длины, вне зависимости от префиксов + Длина номеров для сохранения + Максимальная длина (включительная) коротких номеров, которые нужно сохранить вне зависимости от префиксов. Например, значение 5 сохранит номера длиной 5 цифр и короче + Отфильтровать базу + Запустить фильтрацию базы сейчас (чтобы отфильтровать существующие данные) + Фильтрация базы номеров… \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5cf10d4..937284d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -128,6 +128,7 @@ Non-consecutive in a day Advanced settings + Database Block in Direct Boot mode Allowed kinds of blocking in Direct Boot mode (Contacts can\'t be reliably used as a whitelist in this mode) By rating @@ -149,6 +150,24 @@ Save logcat output on crash (in addition to a basic stacktrace) Export logcat Export and share logcat contents with an app of your choosing (for example with an email client). The reports may contain sensitive data (phone numbers, contact names). Only the chosen app will have access to this data + + Database filtering + Explanation + This feature allows to save storage space by removing some data that is not applicable to you (like numbers belonging to different regions or countries). It doesn\'t affect general app performance (like call blocking). Filtering occurs during the initial database download and during database update. Existing data (downloaded prior to enabling the filtering) may be filtered using the \"Filter the database\" option below. Disabling the feature or changing its parameters doesn\'t restore removed data - you need to re-download the database for that + Filtering enabled + Enables the filtering during the database download and update + Prefixes to keep + A comma-separated list of prefixes of numbers to keep. The prefixes should be in the international format (like +1234). E.g., if you only receive calls from numbers starting with +1, put +1 here + Thorough filtering + If enabled, it goes through every block of data and removes numbers not matching the prefixes. Otherwise, it only removes whole unwanted blocks (leaving some unwanted numbers in wanted blocks). It is recommended to leave this on. Required for keeping short numbers + Keep short numbers + Keep numbers shorter than the specified length regardless of prefixes + Length of numbers to keep + The maximum length (inclusive) of short numbers to keep regardless of prefixes. E.g. the value of 5 will keep numbers with length of 5 digits or shorter + Filter the database + Run the database filtering now (to filter the existing data) + Filtering DB… + ]]> Save diff --git a/app/src/main/res/xml/advanced_preferences.xml b/app/src/main/res/xml/advanced_preferences.xml index 5ab061a..1fd903a 100644 --- a/app/src/main/res/xml/advanced_preferences.xml +++ b/app/src/main/res/xml/advanced_preferences.xml @@ -4,14 +4,25 @@ app:key="screenAdvanced" app:title="@string/settings_screen_advanced"> - - - + + + + + + + + + + + + + + + + + + + + + + + + + +