Change blocking behavior in Direct Boot mode
This commit is contained in:
parent
f816f2637d
commit
099fd546db
|
@ -14,6 +14,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
### Changed
|
||||
|
||||
- Changed blocking behavior in Direct Boot mode: blacklisted numbers are not blocked by default.
|
||||
See #22 for details.
|
||||
- \[Internal\] Settings refactoring.
|
||||
- Updated Croatian translation thanks to Milo Ivir (@milotype).
|
||||
- Updated French translation thanks to J. Lavoie ([@Edanas](https://hosted.weblate.org/user/Edanas/)).
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.text.TextUtils;
|
|||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.preference.MultiSelectListPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
@ -16,6 +17,7 @@ import java.util.regex.Pattern;
|
|||
|
||||
import dummydomain.yetanothercallblocker.utils.DebuggingUtils;
|
||||
import dummydomain.yetanothercallblocker.utils.FileUtils;
|
||||
import dummydomain.yetanothercallblocker.utils.SystemUtils;
|
||||
|
||||
public class AdvancedSettingsFragment extends BaseSettingsFragment {
|
||||
|
||||
|
@ -37,6 +39,17 @@ public class AdvancedSettingsFragment extends BaseSettingsFragment {
|
|||
|
||||
@Override
|
||||
protected void initScreen() {
|
||||
Preference blockInLimitedModePref =
|
||||
requirePreference(Settings.PREF_BLOCK_IN_LIMITED_MODE);
|
||||
if (SystemUtils.isFileBasedEncryptionEnabled()) {
|
||||
blockInLimitedModePref.setSummaryProvider(
|
||||
(Preference.SummaryProvider<MultiSelectListPreference>) preference ->
|
||||
getString(R.string.block_in_limited_mode_summary) + ".\n"
|
||||
+ UiUtils.getSummary(requireContext(), preference));
|
||||
} else {
|
||||
blockInLimitedModePref.setVisible(false);
|
||||
}
|
||||
|
||||
String countryCodesExplanationSummary = getString(R.string.country_codes_info_summary)
|
||||
+ ". " + getString(R.string.country_codes_info_summary_addition,
|
||||
App.getSettings().getCachedAutoDetectedCountryCode());
|
||||
|
|
|
@ -4,6 +4,10 @@ import android.content.Context;
|
|||
import android.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.core.util.Supplier;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class GenericSettings {
|
||||
|
||||
protected final Context context;
|
||||
|
@ -64,6 +68,19 @@ public class GenericSettings {
|
|||
pref.edit().putString(key, value).apply();
|
||||
}
|
||||
|
||||
public Set<String> getStringSet(String key, Set<String> defValue) {
|
||||
return pref.getStringSet(key, defValue);
|
||||
}
|
||||
|
||||
public Set<String> getStringSet(String key, Supplier<Set<String>> defValueSupplier) {
|
||||
Set<String> val = pref.getStringSet(key, null);
|
||||
return val != null ? val : defValueSupplier.get();
|
||||
}
|
||||
|
||||
public void setStringSet(String key, Set<String> value) {
|
||||
pref.edit().putStringSet(key, value).apply();
|
||||
}
|
||||
|
||||
public boolean isSet(String key) {
|
||||
return pref.contains(key);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,10 @@ import androidx.preference.PreferenceManager;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import dummydomain.yetanothercallblocker.data.CountryHelper;
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.DbManager;
|
||||
|
@ -28,6 +31,7 @@ public class Settings extends GenericSettings {
|
|||
public static final String PREF_NOTIFICATIONS_KNOWN = "showNotificationsForKnownCallers";
|
||||
public static final String PREF_NOTIFICATIONS_UNKNOWN = "showNotificationsForUnknownCallers";
|
||||
public static final String PREF_NOTIFICATIONS_BLOCKED = "showNotificationsForBlockedCalls";
|
||||
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_COUNTRY_CODE_OVERRIDE = "countryCodeOverride";
|
||||
|
@ -40,6 +44,9 @@ public class Settings extends GenericSettings {
|
|||
public static final String PREF_CALL_LOG_GROUPING_CONSECUTIVE = "consecutive";
|
||||
public static final String PREF_CALL_LOG_GROUPING_DAY = "day";
|
||||
|
||||
public static final String PREF_BLOCK_IN_LIMITED_MODE_RATING = "rating";
|
||||
public static final String PREF_BLOCK_IN_LIMITED_MODE_BLACKLIST = "blacklist";
|
||||
|
||||
static final String SYS_PREFERENCES_VERSION = "__preferencesVersion";
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Settings.class);
|
||||
|
@ -201,6 +208,24 @@ public class Settings extends GenericSettings {
|
|||
setBoolean(PREF_NOTIFICATIONS_BLOCKED, show);
|
||||
}
|
||||
|
||||
public boolean isBlockingByRatingInLimitedModeAllowed() {
|
||||
return getBlockInLimitedMode().contains(PREF_BLOCK_IN_LIMITED_MODE_RATING);
|
||||
}
|
||||
|
||||
public boolean isBlockingBlacklistedInLimitedModeAllowed() {
|
||||
return getBlockInLimitedMode().contains(PREF_BLOCK_IN_LIMITED_MODE_BLACKLIST);
|
||||
}
|
||||
|
||||
public Set<String> getBlockInLimitedMode() {
|
||||
return getStringSet(PREF_BLOCK_IN_LIMITED_MODE, () ->
|
||||
new HashSet<>(Arrays.asList(context.getResources()
|
||||
.getStringArray(R.array.block_in_limited_mode_default_values))));
|
||||
}
|
||||
|
||||
public void setBlockInLimitedMode(Set<String> value) {
|
||||
setStringSet(PREF_BLOCK_IN_LIMITED_MODE, value);
|
||||
}
|
||||
|
||||
public long getLastUpdateTime() {
|
||||
return getLong(PREF_LAST_UPDATE_TIME, 0);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
package dummydomain.yetanothercallblocker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.preference.MultiSelectListPreference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class UiUtils {
|
||||
|
||||
|
@ -14,4 +21,34 @@ public class UiUtils {
|
|||
return ResourcesCompat.getColor(context.getResources(), colorResId, context.getTheme());
|
||||
}
|
||||
|
||||
public static String getSummary(@NonNull Context context,
|
||||
@NonNull MultiSelectListPreference preference) {
|
||||
List<String> selectedEntries = getSelectedEntries(preference);
|
||||
|
||||
String valuesString = selectedEntries.isEmpty()
|
||||
? context.getString(R.string.selected_value_nothing)
|
||||
: TextUtils.join(", ", selectedEntries);
|
||||
|
||||
return context.getResources().getQuantityString(R.plurals.selected_values,
|
||||
selectedEntries.size(), valuesString);
|
||||
}
|
||||
|
||||
public static List<String> getSelectedEntries(MultiSelectListPreference preference) {
|
||||
CharSequence[] entries = preference.getEntries();
|
||||
CharSequence[] entryValues = preference.getEntryValues();
|
||||
Set<String> values = preference.getValues();
|
||||
|
||||
if (values.isEmpty()) return Collections.emptyList();
|
||||
|
||||
List<String> result = new ArrayList<>(values.size());
|
||||
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
if (values.contains(entryValues[i].toString())) {
|
||||
result.add(entries[i].toString());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import dummydomain.yetanothercallblocker.sia.network.OkHttpClientFactory;
|
|||
import dummydomain.yetanothercallblocker.sia.network.WebService;
|
||||
import dummydomain.yetanothercallblocker.sia.utils.Utils;
|
||||
import dummydomain.yetanothercallblocker.utils.DeferredInit;
|
||||
import dummydomain.yetanothercallblocker.utils.SystemUtils;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
import static dummydomain.yetanothercallblocker.data.SiaConstants.SIA_PATH_PREFIX;
|
||||
|
@ -134,8 +135,17 @@ public class Config {
|
|||
settings::setBlacklistIsNotEmpty, blacklistDao);
|
||||
YacbHolder.setBlacklistService(blacklistService);
|
||||
|
||||
ContactsProvider contactsProvider = number ->
|
||||
settings.getUseContacts() ? ContactsHelper.getContact(context, number) : null;
|
||||
ContactsProvider contactsProvider = new ContactsProvider() {
|
||||
@Override
|
||||
public ContactItem get(String number) {
|
||||
return settings.getUseContacts() ? ContactsHelper.getContact(context, number) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInLimitedMode() {
|
||||
return !SystemUtils.isUserUnlocked(context);
|
||||
}
|
||||
};
|
||||
|
||||
NumberInfoService numberInfoService = new NumberInfoService(
|
||||
settings, NumberUtils::isHiddenNumber, NumberUtils::normalizeNumber,
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package dummydomain.yetanothercallblocker.data;
|
||||
|
||||
public interface ContactsProvider {
|
||||
|
||||
ContactItem get(String number);
|
||||
|
||||
boolean isInLimitedMode();
|
||||
|
||||
}
|
||||
|
|
|
@ -136,17 +136,38 @@ public class NumberInfoService {
|
|||
}
|
||||
|
||||
if (numberInfo.rating == NumberInfo.Rating.NEGATIVE
|
||||
&& settings.getBlockNegativeSiaNumbers()) {
|
||||
&& settings.getBlockNegativeSiaNumbers()
|
||||
&& canBlock(NumberInfo.BlockingReason.SIA_RATING)) {
|
||||
return NumberInfo.BlockingReason.SIA_RATING;
|
||||
}
|
||||
|
||||
if (numberInfo.blacklistItem != null && settings.getBlockBlacklisted()) {
|
||||
if (numberInfo.blacklistItem != null && settings.getBlockBlacklisted()
|
||||
&& canBlock(NumberInfo.BlockingReason.BLACKLISTED)) {
|
||||
return NumberInfo.BlockingReason.BLACKLISTED;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean canBlock(NumberInfo.BlockingReason reason) {
|
||||
if (contactsProvider == null || !contactsProvider.isInLimitedMode()) return true;
|
||||
|
||||
if (reason == NumberInfo.BlockingReason.SIA_RATING
|
||||
&& settings.isBlockingByRatingInLimitedModeAllowed()) {
|
||||
LOG.trace("canBlock() allowed: " + reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (reason == NumberInfo.BlockingReason.BLACKLISTED
|
||||
&& settings.isBlockingBlacklistedInLimitedModeAllowed()) {
|
||||
LOG.trace("canBlock() allowed: " + reason);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG.trace("canBlock() not allowed: " + reason);
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean shouldBlock(NumberInfo numberInfo) {
|
||||
return numberInfo.blockingReason != null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package dummydomain.yetanothercallblocker.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.core.os.UserManagerCompat;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class SystemUtils {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SystemUtils.class);
|
||||
|
||||
private static Boolean fileBasedEncryptionEnabled;
|
||||
private static boolean userUnlocked;
|
||||
|
||||
public static boolean isFileBasedEncryptionEnabled() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return false;
|
||||
|
||||
Boolean enabled = fileBasedEncryptionEnabled;
|
||||
if (enabled == null) {
|
||||
enabled = fileBasedEncryptionEnabled = isFileBasedEncryptionEnabledInternal();
|
||||
}
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
private static Boolean isFileBasedEncryptionEnabledInternal() {
|
||||
try {
|
||||
@SuppressLint("PrivateApi")
|
||||
Class<?> cls = Class.forName("android.os.SystemProperties");
|
||||
Method get = cls.getMethod("get", String.class);
|
||||
|
||||
String type = (String) get.invoke(null, "ro.crypto.type");
|
||||
if (!TextUtils.equals(type, "file")) return false;
|
||||
|
||||
String state = (String) get.invoke(null, "ro.crypto.state");
|
||||
return TextUtils.equals(state, "encrypted");
|
||||
} catch (Exception e) {
|
||||
LOG.warn("isFileBasedEncryptionEnabledInternal()", e);
|
||||
}
|
||||
|
||||
return true; // *assume* it is enabled, if the check fails
|
||||
}
|
||||
|
||||
public static boolean isUserUnlocked(Context context) {
|
||||
if (userUnlocked) return true;
|
||||
|
||||
if (UserManagerCompat.isUserUnlocked(context)) {
|
||||
userUnlocked = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -110,6 +110,10 @@
|
|||
<string name="blocked_call_notifications_disable_confirmation">Всё равно выключить</string>
|
||||
<string name="notification_settings">Настройки уведомлений</string>
|
||||
<string name="settings_screen_advanced">Продвинутые настройки</string>
|
||||
<string name="block_in_limited_mode">Блокировать в режиме Direct Boot</string>
|
||||
<string name="block_in_limited_mode_summary">Типы блокировки, разрешённые в режиме Direct Boot (контакты не могут надёжно выполнять роль белого списка в этом режиме)</string>
|
||||
<string name="block_in_limited_mode_rating">По отзывам</string>
|
||||
<string name="block_in_limited_mode_blacklist">Из чёрного списока</string>
|
||||
<string name="settings_category_country_codes">Коды страны</string>
|
||||
<string name="country_codes_info">Объяснение</string>
|
||||
<string name="country_codes_info_summary">Коды страны используются в запросах к сторонним серверам. Эти коды отправляются, для того чтобы имитировать поведение официального приложения. Если коды соответствуют вашей настоящей стране (которая может быть определена по вашему IP-адресу), то запросы будут выглядеть наиболее неприметными. По умолчанию используется автоопределение (по информации мобильной сети или выбранной локали), которое должно устраивать большинство пользователей, но вы можете задать эти коды вручную. Ожидаются двухбуквенные ISO 3166 коды (такие как RU)</string>
|
||||
|
@ -134,6 +138,12 @@
|
|||
<item quantity="few">%1$d выбрано</item>
|
||||
<item quantity="many">%1$d выбрано</item>
|
||||
</plurals>
|
||||
<plurals name="selected_values">
|
||||
<item quantity="one">Выбраны: %s</item>
|
||||
<item quantity="few">Выбраны: %s</item>
|
||||
<item quantity="many">Выбраны: %s</item>
|
||||
</plurals>
|
||||
<string name="selected_value_nothing"><ничего></string>
|
||||
<string name="open_blacklist_activity">Чёрный список</string>
|
||||
<string name="title_blacklist_activity">Чёрный список</string>
|
||||
<plurals name="blacklist_item_stats">
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<item>2</item>
|
||||
<item>-1</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="call_log_grouping_entries">
|
||||
<item>@string/call_log_grouping_none</item>
|
||||
<item>@string/call_log_grouping_consecutive</item>
|
||||
|
@ -20,4 +21,16 @@
|
|||
<item>consecutive</item>
|
||||
<item>day</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="block_in_limited_mode_entries">
|
||||
<item>@string/block_in_limited_mode_rating</item>
|
||||
<item>@string/block_in_limited_mode_blacklist</item>
|
||||
</string-array>
|
||||
<string-array name="block_in_limited_mode_values">
|
||||
<item>rating</item>
|
||||
<item>blacklist</item>
|
||||
</string-array>
|
||||
<string-array name="block_in_limited_mode_default_values">
|
||||
<item>rating</item>
|
||||
</string-array>
|
||||
</resources>
|
|
@ -128,6 +128,10 @@
|
|||
<string name="call_log_grouping_day">Non-consecutive in a day</string>
|
||||
|
||||
<string name="settings_screen_advanced">Advanced settings</string>
|
||||
<string name="block_in_limited_mode">Block in Direct Boot mode</string>
|
||||
<string name="block_in_limited_mode_summary">Allowed kinds of blocking in Direct Boot mode (Contacts can\'t be reliably whitelisted in this mode)</string>
|
||||
<string name="block_in_limited_mode_rating">By rating</string>
|
||||
<string name="block_in_limited_mode_blacklist">Blacklisted</string>
|
||||
<string name="settings_category_country_codes">Country codes</string>
|
||||
<string name="country_codes_info">Explanation</string>
|
||||
<string name="country_codes_info_summary">Country codes are used in requests to 3rd party servers. These codes are set to mimic the behavior of the official 3rd party app. If the codes match your real country (may be detected by your IP address) then your requests will look the most inconspicuous. Auto-detection is used by default (based on mobile network information or system locale), which should be fine for most users, but you can set the codes manually. ISO 3166 2-letter codes (like US) are expected</string>
|
||||
|
@ -151,6 +155,11 @@
|
|||
<item quantity="one">%1$d selected</item>
|
||||
<item quantity="other">%1$d selected</item>
|
||||
</plurals>
|
||||
<plurals name="selected_values">
|
||||
<item quantity="one">Selected: %s</item>
|
||||
<item quantity="other">Selected: %s</item>
|
||||
</plurals>
|
||||
<string name="selected_value_nothing"><nothing></string>
|
||||
|
||||
<string name="open_blacklist_activity">Blacklist</string>
|
||||
<string name="title_blacklist_activity">Blacklist</string>
|
||||
|
|
|
@ -13,6 +13,14 @@
|
|||
android:targetPackage="@string/app_id" />
|
||||
</Preference>
|
||||
|
||||
<MultiSelectListPreference
|
||||
android:defaultValue="@array/block_in_limited_mode_default_values"
|
||||
android:entries="@array/block_in_limited_mode_entries"
|
||||
android:entryValues="@array/block_in_limited_mode_values"
|
||||
android:key="blockInLimitedMode"
|
||||
android:summary="@string/block_in_limited_mode_summary"
|
||||
android:title="@string/block_in_limited_mode" />
|
||||
|
||||
<PreferenceCategory
|
||||
app:key="categoryCountryCodes"
|
||||
app:title="@string/settings_category_country_codes">
|
||||
|
|
Loading…
Reference in New Issue