Change blocking behavior in Direct Boot mode

This commit is contained in:
xynngh 2020-11-07 19:56:01 +04:00
parent f816f2637d
commit 099fd546db
13 changed files with 235 additions and 4 deletions

View File

@ -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/)).

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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">&lt;ничего&gt;</string>
<string name="open_blacklist_activity">Чёрный список</string>
<string name="title_blacklist_activity">Чёрный список</string>
<plurals name="blacklist_item_stats">

View File

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

View File

@ -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">&lt;nothing&gt;</string>
<string name="open_blacklist_activity">Blacklist</string>
<string name="title_blacklist_activity">Blacklist</string>

View File

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