parent
69945a9ce9
commit
3c2479dba5
|
@ -56,8 +56,10 @@ Protecting the user's privacy is the first concern during development. No person
|
|||
The only known possible data leaks are the following:
|
||||
|
||||
* Database update procedure leaks user's IP address to the update servers.
|
||||
The request also includes current database version (base or updated).
|
||||
The request also includes current database version (base or updated)
|
||||
and a country code (either auto-detected or set manually).
|
||||
* Online review requests leak user's IP address coupled with the phone number in question.
|
||||
The request also includes country codes (either auto-detected or set manually).
|
||||
Shouldn't be a big deal unless you request it for numbers in your phone book.
|
||||
If the "use contacts" feature is enabled, a confirmation dialog is shown if online reviews are requested for a number present in your phone book.
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ dependencies {
|
|||
implementation 'org.conscrypt:conscrypt-android:2.4.0'
|
||||
//noinspection GradleDependency: 3.12.* is the latest version compatible with Android <5
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.12.12'
|
||||
implementation 'com.gitlab.xynngh:LibPhoneNumberInfo:551b0a2f6b'
|
||||
implementation 'com.gitlab.xynngh:LibPhoneNumberInfo:6d073c991c'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
|
|
|
@ -45,6 +45,18 @@ public class GenericSettings {
|
|||
pref.edit().putLong(key, value).apply();
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
return getString(key, null);
|
||||
}
|
||||
|
||||
public String getString(String key, String defValue) {
|
||||
return pref.getString(key, defValue);
|
||||
}
|
||||
|
||||
public void setString(String key, String value) {
|
||||
pref.edit().putString(key, value).apply();
|
||||
}
|
||||
|
||||
public boolean isSet(String key) {
|
||||
return pref.contains(key);
|
||||
}
|
||||
|
|
|
@ -97,7 +97,8 @@ public class ReviewsActivity extends AppCompatActivity {
|
|||
= new AsyncTask<String, Void, List<CommunityReview>>() {
|
||||
@Override
|
||||
protected List<CommunityReview> doInBackground(String... params) {
|
||||
return DatabaseSingleton.getCommunityReviewsLoader().loadReviews(params[0]);
|
||||
return DatabaseSingleton.getCommunityReviewsLoader()
|
||||
.loadReviews(params[0], App.getSettings().getCountryCodeForReviews());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package dummydomain.yetanothercallblocker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import dummydomain.yetanothercallblocker.data.CountryHelper;
|
||||
|
||||
public class Settings extends GenericSettings {
|
||||
|
||||
public static final String PREF_INCOMING_CALL_NOTIFICATIONS = "incomingCallNotifications";
|
||||
|
@ -13,11 +16,15 @@ public class Settings extends GenericSettings {
|
|||
public static final String PREF_NOTIFICATIONS_UNKNOWN = "showNotificationsForUnknownCallers";
|
||||
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";
|
||||
public static final String PREF_COUNTRY_CODE_FOR_REVIEWS_OVERRIDE = "countryCodeForReviewsOverride";
|
||||
|
||||
private static final String SYS_PREFERENCES_VERSION = "__preferencesVersion";
|
||||
|
||||
private static final int PREFERENCES_VERSION = 1;
|
||||
|
||||
private volatile String cachedAutoDetectedCountryCode;
|
||||
|
||||
Settings(Context context) {
|
||||
super(context, PreferenceManager.getDefaultSharedPreferences(context));
|
||||
}
|
||||
|
@ -108,4 +115,46 @@ public class Settings extends GenericSettings {
|
|||
setLong(PREF_LAST_UPDATE_CHECK_TIME, timestamp);
|
||||
}
|
||||
|
||||
public String getCountryCodeOverride() {
|
||||
return getString(PREF_COUNTRY_CODE_OVERRIDE);
|
||||
}
|
||||
|
||||
public void setCountryCodeOverride(String code) {
|
||||
setString(PREF_COUNTRY_CODE_OVERRIDE, code);
|
||||
}
|
||||
|
||||
public String getCountryCodeForReviewsOverride() {
|
||||
return getString(PREF_COUNTRY_CODE_FOR_REVIEWS_OVERRIDE);
|
||||
}
|
||||
|
||||
public void setCountryCodeForReviewsOverride(String code) {
|
||||
setString(PREF_COUNTRY_CODE_FOR_REVIEWS_OVERRIDE, code);
|
||||
}
|
||||
|
||||
public String getCountryCode() {
|
||||
String override = getCountryCodeOverride();
|
||||
if (!TextUtils.isEmpty(override)) return override.toUpperCase();
|
||||
|
||||
return getCachedAutoDetectedCountryCode();
|
||||
}
|
||||
|
||||
public String getCountryCodeForReviews() {
|
||||
String override = getCountryCodeForReviewsOverride();
|
||||
if (!TextUtils.isEmpty(override)) return override.toUpperCase();
|
||||
|
||||
String code = getCachedAutoDetectedCountryCode();
|
||||
return !TextUtils.isEmpty(code) ? code : "US";
|
||||
}
|
||||
|
||||
public String getCachedAutoDetectedCountryCode() {
|
||||
String code = cachedAutoDetectedCountryCode;
|
||||
if (code == null) {
|
||||
code = CountryHelper.detectCountry(context);
|
||||
if (TextUtils.isEmpty(code)) code = "";
|
||||
|
||||
cachedAutoDetectedCountryCode = code;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,34 +3,43 @@ package dummydomain.yetanothercallblocker;
|
|||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import dummydomain.yetanothercallblocker.work.UpdateScheduler;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
public class SettingsActivity extends AppCompatActivity
|
||||
implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.settings_activity);
|
||||
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings, new SettingsFragment())
|
||||
.commit();
|
||||
if (savedInstanceState == null) {
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings, new SettingsFragment())
|
||||
.commit();
|
||||
}
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
|
@ -38,6 +47,21 @@ public class SettingsActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat,
|
||||
PreferenceScreen preferenceScreen) {
|
||||
SettingsFragment fragment = new SettingsFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, preferenceScreen.getKey());
|
||||
fragment.setArguments(args);
|
||||
|
||||
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||
ft.replace(R.id.settings, fragment, preferenceScreen.getKey());
|
||||
ft.addToBackStack(preferenceScreen.getKey());
|
||||
ft.commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
|
@ -68,6 +92,8 @@ public class SettingsActivity extends AppCompatActivity {
|
|||
private static final String PREF_USE_CALL_SCREENING_SERVICE = "useCallScreeningService";
|
||||
private static final String PREF_AUTO_UPDATE_ENABLED = "autoUpdateEnabled";
|
||||
private static final String PREF_CATEGORY_NOTIFICATIONS = "categoryNotifications";
|
||||
private static final String PREF_SCREEN_ADVANCED = "screenAdvanced";
|
||||
private static final String PREF_COUNTRY_CODES_INFO = "countryCodesInfo";
|
||||
|
||||
private final UpdateScheduler updateScheduler = UpdateScheduler.get(App.getInstance());
|
||||
|
||||
|
@ -75,6 +101,28 @@ public class SettingsActivity extends AppCompatActivity {
|
|||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey);
|
||||
|
||||
initRootScreen(rootKey);
|
||||
initAdvancedScreen(rootKey);
|
||||
|
||||
PreferenceScreen preferenceScreen = getPreferenceScreen();
|
||||
int count = preferenceScreen.getPreferenceCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
Preference preference = preferenceScreen.getPreference(i);
|
||||
preference.setIconSpaceReserved(false);
|
||||
if (preference instanceof PreferenceGroup) {
|
||||
PreferenceGroup group = (PreferenceGroup) preference;
|
||||
int nestedCount = group.getPreferenceCount();
|
||||
for (int k = 0; k < nestedCount; k++) {
|
||||
Preference nested = group.getPreference(k);
|
||||
nested.setIconSpaceReserved(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initRootScreen(String rootKey) {
|
||||
if (rootKey != null) return;
|
||||
|
||||
SwitchPreferenceCompat incomingCallNotificationPref =
|
||||
requireNonNull(findPreference(Settings.PREF_INCOMING_CALL_NOTIFICATIONS));
|
||||
incomingCallNotificationPref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
|
@ -136,21 +184,46 @@ public class SettingsActivity extends AppCompatActivity {
|
|||
Preference category = requireNonNull(findPreference(PREF_CATEGORY_NOTIFICATIONS));
|
||||
category.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
PreferenceScreen preferenceScreen = getPreferenceScreen();
|
||||
int count = preferenceScreen.getPreferenceCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
Preference preference = preferenceScreen.getPreference(i);
|
||||
preference.setIconSpaceReserved(false);
|
||||
if (preference instanceof PreferenceGroup) {
|
||||
PreferenceGroup group = (PreferenceGroup) preference;
|
||||
int nestedCount = group.getPreferenceCount();
|
||||
for (int k = 0; k < nestedCount; k++) {
|
||||
Preference nested = group.getPreference(k);
|
||||
nested.setIconSpaceReserved(false);
|
||||
}
|
||||
private void initAdvancedScreen(String rootKey) {
|
||||
if (!PREF_SCREEN_ADVANCED.equals(rootKey)) return;
|
||||
|
||||
String countryCodesExplanationSummary = getString(R.string.country_codes_info_summary)
|
||||
+ ". " + getString(R.string.country_codes_info_summary_addition,
|
||||
App.getSettings().getCachedAutoDetectedCountryCode());
|
||||
|
||||
Preference countryCodesInfoPreference
|
||||
= requireNonNull(findPreference(PREF_COUNTRY_CODES_INFO));
|
||||
countryCodesInfoPreference.setSummary(countryCodesExplanationSummary);
|
||||
countryCodesInfoPreference.setOnPreferenceClickListener(preference -> {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.settings_category_country_codes)
|
||||
.setMessage(countryCodesExplanationSummary)
|
||||
.setNegativeButton(R.string.back, null)
|
||||
.show();
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference.OnPreferenceChangeListener countryCodeChangeListener
|
||||
= (preference, newValue) -> {
|
||||
String value = (String) newValue;
|
||||
if (TextUtils.isEmpty(value) || Pattern.matches("^[a-zA-Z]{2}$", value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Toast.makeText(getActivity(), R.string.country_code_incorrect_format,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
};
|
||||
|
||||
EditTextPreference countryCodePreference
|
||||
= requireNonNull(findPreference(Settings.PREF_COUNTRY_CODE_OVERRIDE));
|
||||
countryCodePreference.setOnPreferenceChangeListener(countryCodeChangeListener);
|
||||
|
||||
EditTextPreference countryCodeForReviewsPreference
|
||||
= requireNonNull(findPreference(Settings.PREF_COUNTRY_CODE_FOR_REVIEWS_OVERRIDE));
|
||||
countryCodeForReviewsPreference.setOnPreferenceChangeListener(countryCodeChangeListener);
|
||||
}
|
||||
|
||||
public void updateCallScreeningPreference() {
|
||||
|
|
|
@ -3,6 +3,8 @@ package dummydomain.yetanothercallblocker.data;
|
|||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import dummydomain.yetanothercallblocker.PermissionHelper;
|
||||
import dummydomain.yetanothercallblocker.sia.Settings;
|
||||
import dummydomain.yetanothercallblocker.sia.SettingsImpl;
|
||||
|
@ -14,6 +16,7 @@ import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabase;
|
|||
import dummydomain.yetanothercallblocker.sia.model.database.DbManager;
|
||||
import dummydomain.yetanothercallblocker.sia.model.database.FeaturedDatabase;
|
||||
import dummydomain.yetanothercallblocker.sia.network.WebService;
|
||||
import dummydomain.yetanothercallblocker.sia.utils.Utils;
|
||||
|
||||
import static dummydomain.yetanothercallblocker.data.SiaConstants.SIA_PATH_PREFIX;
|
||||
import static dummydomain.yetanothercallblocker.data.SiaConstants.SIA_PROPERTIES;
|
||||
|
@ -22,9 +25,17 @@ import static dummydomain.yetanothercallblocker.data.SiaConstants.SIA_SECONDARY_
|
|||
public class Config {
|
||||
|
||||
private static class WSParameterProvider extends WebService.DefaultWSParameterProvider {
|
||||
dummydomain.yetanothercallblocker.Settings settings;
|
||||
SiaMetadata siaMetadata;
|
||||
CommunityDatabase communityDatabase;
|
||||
|
||||
volatile String appId;
|
||||
volatile long appIdTimestamp;
|
||||
|
||||
void setSettings(dummydomain.yetanothercallblocker.Settings settings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
void setSiaMetadata(SiaMetadata siaMetadata) {
|
||||
this.siaMetadata = siaMetadata;
|
||||
}
|
||||
|
@ -35,7 +46,18 @@ public class Config {
|
|||
|
||||
@Override
|
||||
public String getAppId() {
|
||||
return "qQq0O9nCRNy_aVdPgU9WOA";
|
||||
String appId = this.appId;
|
||||
if (appId != null && System.nanoTime() >
|
||||
appIdTimestamp + TimeUnit.MINUTES.toNanos(5)) {
|
||||
appId = null;
|
||||
}
|
||||
|
||||
if (appId == null) {
|
||||
this.appId = appId = Utils.generateAppId();
|
||||
appIdTimestamp = System.nanoTime();
|
||||
}
|
||||
|
||||
return appId;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,6 +74,11 @@ public class Config {
|
|||
public int getDbVersion() {
|
||||
return communityDatabase.getEffectiveDbVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCountry() {
|
||||
return siaMetadata.getCountry(settings.getCountryCode()).code;
|
||||
}
|
||||
}
|
||||
|
||||
public static void init(Context context, dummydomain.yetanothercallblocker.Settings settings) {
|
||||
|
@ -60,6 +87,8 @@ public class Config {
|
|||
= new SettingsImpl(new AndroidProperties(context, SIA_PROPERTIES));
|
||||
|
||||
WSParameterProvider wsParameterProvider = new WSParameterProvider();
|
||||
wsParameterProvider.setSettings(settings);
|
||||
|
||||
WebService webService = new WebService(wsParameterProvider);
|
||||
|
||||
DatabaseSingleton.setDbManager(new DbManager(storage, SIA_PATH_PREFIX));
|
||||
|
@ -80,8 +109,7 @@ public class Config {
|
|||
DatabaseSingleton.setFeaturedDatabase(new FeaturedDatabase(
|
||||
storage, AbstractDatabase.Source.ANY, SIA_PATH_PREFIX));
|
||||
|
||||
DatabaseSingleton.setCommunityReviewsLoader(
|
||||
new CommunityReviewsLoader(webService, "US"));
|
||||
DatabaseSingleton.setCommunityReviewsLoader(new CommunityReviewsLoader(webService));
|
||||
|
||||
DatabaseSingleton.setContactsProvider(number -> {
|
||||
if (settings.getUseContacts() && PermissionHelper.hasContactsPermission(context)) {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package dummydomain.yetanothercallblocker.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.core.os.ConfigurationCompat;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CountryHelper {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CountryHelper.class);
|
||||
|
||||
public static String detectCountry(Context context) {
|
||||
try {
|
||||
TelephonyManager tm = (TelephonyManager) context
|
||||
.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
|
||||
if (tm != null) {
|
||||
String countryCode = tm.getNetworkCountryIso();
|
||||
if (!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase();
|
||||
|
||||
countryCode = tm.getSimCountryIso();
|
||||
if (!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase();
|
||||
}
|
||||
|
||||
String countryCode = ConfigurationCompat
|
||||
.getLocales(context.getResources().getConfiguration())
|
||||
.get(0).getCountry();
|
||||
if (countryCode.length() == 2) return countryCode;
|
||||
} catch (Exception e) {
|
||||
LOG.warn("detectCountry()", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -84,4 +84,14 @@
|
|||
<string name="show_notifications_for_known_callers_summary">Показывать уведомления для известных звонящих (номеров из телефонной книги)</string>
|
||||
<string name="show_notifications_for_unknown_callers">Уведомления для неизвестных звонящих</string>
|
||||
<string name="show_notifications_for_unknown_callers_summary">Показывать уведомления для неизвестных звонящих (отсутствующих в телефонной книге и в базе номеров)</string>
|
||||
<string name="settings_screen_advanced">Продвинутые настройки</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>
|
||||
<string name="country_codes_info_summary_addition">Автоопределено: %s</string>
|
||||
<string name="country_code_override">Код страны</string>
|
||||
<string name="country_code_override_summary">Код страны, используемый во всех запросах. Оставьте пустым для автоопределения</string>
|
||||
<string name="country_code_for_reviews_override">Код страны для отзывов</string>
|
||||
<string name="country_code_for_reviews_override_summary">Код страны, используемый для запроса онлайн-отзывов. Предназначен для представления страны звонящего. Оставьте пустым для автоопределения</string>
|
||||
<string name="country_code_incorrect_format">Некорректный формат кода страны. Значение не обновлено</string>
|
||||
</resources>
|
|
@ -98,6 +98,17 @@
|
|||
<string name="use_contacts">Use contacts</string>
|
||||
<string name="use_contacts_summary">Numbers present in the phone book are never blocked and the contact name is displayed next to/instead of a number throughout the app</string>
|
||||
|
||||
<string name="settings_screen_advanced">Advanced settings</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>
|
||||
<string name="country_codes_info_summary_addition">Auto-detected: %s</string>
|
||||
<string name="country_code_override">Country code</string>
|
||||
<string name="country_code_override_summary">Country code used in all requests. Leave empty for auto-detection</string>
|
||||
<string name="country_code_for_reviews_override">Country code for reviews</string>
|
||||
<string name="country_code_for_reviews_override_summary">Country code used in requests for online reviews. Meant to represent the country of the caller. Leave empty for auto-detection</string>
|
||||
<string name="country_code_incorrect_format">Incorrect country code format. Value is not updated</string>
|
||||
|
||||
<string name="open_debug_activity">Open debug screen</string>
|
||||
<string name="debug_activity_label">Debug</string>
|
||||
<string name="debug_query_db">Query DB</string>
|
||||
|
|
|
@ -42,4 +42,28 @@
|
|||
app:title="@string/show_notifications_for_unknown_callers" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/settings_screen_advanced">
|
||||
<PreferenceScreen
|
||||
app:key="screenAdvanced"
|
||||
app:title="@string/settings_screen_advanced">
|
||||
<PreferenceCategory
|
||||
app:key="categoryCountryCodes"
|
||||
app:title="@string/settings_category_country_codes">
|
||||
<Preference
|
||||
app:key="countryCodesInfo"
|
||||
app:persistent="false"
|
||||
app:summary="@string/country_codes_info_summary"
|
||||
app:title="@string/country_codes_info" />
|
||||
<EditTextPreference
|
||||
app:key="countryCodeOverride"
|
||||
app:summary="@string/country_code_override_summary"
|
||||
app:title="@string/country_code_override" />
|
||||
<EditTextPreference
|
||||
app:key="countryCodeForReviewsOverride"
|
||||
app:summary="@string/country_code_for_reviews_override_summary"
|
||||
app:title="@string/country_code_for_reviews_override" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
|
Loading…
Reference in New Issue