misc fixes

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/299/head
androidacy-user 1 year ago
parent 0ac2779bb0
commit c7cc320028

@ -128,6 +128,10 @@ android {
dimension "type"
buildConfigField "boolean", "ENABLE_AUTO_UPDATER", "true"
buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "true"
buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING_PII", "true"
buildConfigField "boolean", "DEFAULT_ENABLE_ANALYTICS", "true" // unused for now
buildConfigField "boolean", "DEFAULT_ENABLE_ANALYTICS_PII", "true" // unused for now
buildConfigField "boolean", "ENABLE_PROTECTION", "true"
if (hasSentryConfig) {
Properties properties = new Properties()
try (FileInputStream fis = new FileInputStream(sentryConfigFile)) {
@ -182,6 +186,8 @@ android {
// Disable crash reporting for F-Droid flavor by default
buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING", "false"
buildConfigField "boolean", "DEFAULT_ENABLE_CRASH_REPORTING_PII", "true"
buildConfigField "boolean", "ENABLE_PROTECTION", "true"
if (hasSentryConfig) {
Properties properties = new Properties()

@ -60,6 +60,8 @@
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="@bool/lang_support_rtl"
android:testOnly="false"
android:appCategory="productivity"
android:memtagMode="async"
android:theme="@style/Theme.MagiskModuleManager"
android:usesCleartextTraffic="false"
android:extractNativeLibs="true"

@ -315,17 +315,6 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
@Override
public void onCreate() {
// init timber
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
} else {
if (isCrashReportingEnabled()) {
//noinspection UnstableApiUsage
Timber.plant(new SentryTimberTree(Sentry.getCurrentHub(), SentryLevel.ERROR, SentryLevel.ERROR));
} else {
Timber.plant(new ReleaseTree());
}
}
supportedLocales.add("ar");
// supportedLocales.add("ar_SA");
supportedLocales.add("bs");
@ -356,27 +345,41 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
supportedLocales.add("en");
if (INSTANCE == null) INSTANCE = this;
relPackageName = this.getPackageName();
Timber.d("Starting FoxMMM version " + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + "), commit " + BuildConfig.COMMIT_HASH);
super.onCreate();
// Update SSL Ciphers if update is possible
GMSProviderInstaller.installIfNeeded(this);
SentryMain.initialize(this);
// init timber
if (BuildConfig.DEBUG) {
Timber.d("Initializing FoxMMM");
Timber.d("Started from background: %s", !isInForeground());
Timber.d("FoxMMM is running in debug mode");
Timber.d("Initializing Realm");
Timber.plant(new Timber.DebugTree());
} else {
if (isCrashReportingEnabled()) {
//noinspection UnstableApiUsage
Timber.plant(new SentryTimberTree(Sentry.getCurrentHub(), SentryLevel.ERROR, SentryLevel.ERROR));
} else {
Timber.plant(new ReleaseTree());
}
}
Timber.i("Starting FoxMMM version %s (%d) - commit %s", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, BuildConfig.COMMIT_HASH);
// Update SSL Ciphers if update is possible
GMSProviderInstaller.installIfNeeded(this);
Timber.d("Initializing FoxMMM");
Timber.d("Started from background: %s", !isInForeground());
Timber.d("FoxMMM is running in debug mode");
Timber.d("Initializing Realm");
Realm.init(this);
Timber.d("Initialized Realm");
// Determine if this is an official build based on the signature
try {
// Get the signature of the key used to sign the app
@SuppressLint("PackageManagerGetSignatures") Signature[] signatures = this.getPackageManager().getPackageInfo(this.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
@SuppressWarnings("SpellCheckingInspection") String[] officialSignatureHashArray = new String[]{"7bec7c4462f4aac616612d9f56a023ee3046e83afa956463b5fab547fd0a0be6", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"};
String ourSignatureHash = Hashing.sha256().hashBytes(signatures[0].toByteArray()).toString();
isOfficial = Arrays.asList(officialSignatureHashArray).contains(ourSignatureHash);
@SuppressLint("PackageManagerGetSignatures") Signature[] s = this.getPackageManager().getPackageInfo(this.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
@SuppressWarnings("SpellCheckingInspection") String[] osh = new String[]{"7bec7c4462f4aac616612d9f56a023ee3046e83afa956463b5fab547fd0a0be6", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"};
String oosh = Hashing.sha256().hashBytes(s[0].toByteArray()).toString();
isOfficial = Arrays.asList(osh).contains(oosh);
} catch (PackageManager.NameNotFoundException ignored) {
}
// hide this behind a buildconfig flag for now, but crash the app if it's not an official build and not debug
if (BuildConfig.ENABLE_PROTECTION && !isOfficial && !BuildConfig.DEBUG) {
throw new RuntimeException("This is not an official build of FoxMMM");
}
SharedPreferences sharedPreferences = MainApplication.getPreferences("mmm");
// We are only one process so it's ok to do this
SharedPreferences bootPrefs = MainApplication.getPreferences("mmm_boot");
@ -407,7 +410,6 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
Timber.i("Emoji compat loaded!");
}, "Emoji compat init.").start();
}
SentryMain.initialize(this);
if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) {
Timber.w("Androidacy client id is empty! Please set it in androidacy.properties. Will not enable Androidacy.");
SharedPreferences.Editor editor = sharedPreferences.edit();
@ -529,8 +531,8 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
} catch (KeyStoreException | NoSuchAlgorithmException
| CertificateException | IOException e) {
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException |
IOException e) {
Timber.v("Failed to open the keystore.");
throw new RuntimeException(e);
}
@ -540,9 +542,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
// create a cipher that uses AES encryption -- we'll use this to encrypt our key
Cipher cipher;
try {
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES
+ "/" + KeyProperties.BLOCK_MODE_CBC
+ "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
Timber.e("Failed to create a cipher.");
throw new RuntimeException(e);
@ -551,19 +551,12 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
// generate secret key
KeyGenerator keyGenerator;
try {
keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore");
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
Timber.e("Failed to access the key generator.");
throw new RuntimeException(e);
}
KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder(
"realm_key",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build();
KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder("realm_key", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT).setBlockModes(KeyProperties.BLOCK_MODE_CBC).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7).build();
try {
keyGenerator.init(keySpec);
} catch (InvalidAlgorithmParameterException e) {
@ -578,33 +571,25 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
byte[] initializationVector;
byte[] encryptedKeyForRealm;
try {
SecretKey secretKey =
(SecretKey) keyStore.getKey("realm_key", null);
SecretKey secretKey = (SecretKey) keyStore.getKey("realm_key", null);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
encryptedKeyForRealm = cipher.doFinal(realmKey);
initializationVector = cipher.getIV();
} catch (InvalidKeyException | UnrecoverableKeyException
| NoSuchAlgorithmException | KeyStoreException
| BadPaddingException | IllegalBlockSizeException e) {
} catch (InvalidKeyException | UnrecoverableKeyException | NoSuchAlgorithmException |
KeyStoreException | BadPaddingException | IllegalBlockSizeException e) {
Timber.e("Failed encrypting the key with the secret key.");
throw new RuntimeException(e);
}
// keep the encrypted key in shared preferences
// to persist it across application runs
byte[] initializationVectorAndEncryptedKey =
new byte[Integer.BYTES +
initializationVector.length +
encryptedKeyForRealm.length];
byte[] initializationVectorAndEncryptedKey = new byte[Integer.BYTES + initializationVector.length + encryptedKeyForRealm.length];
ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey);
buffer.order(ByteOrder.BIG_ENDIAN);
buffer.putInt(initializationVector.length);
buffer.put(initializationVector);
buffer.put(encryptedKeyForRealm);
Timber.d("Created all keys successfully.");
MainApplication.getPreferences("realm_key").edit()
.putString("iv_and_encrypted_key",
Base64.encodeToString(initializationVectorAndEncryptedKey, Base64.NO_WRAP))
.apply();
MainApplication.getPreferences("realm_key").edit().putString("iv_and_encrypted_key", Base64.encodeToString(initializationVectorAndEncryptedKey, Base64.NO_WRAP)).apply();
Timber.d("Saved the encrypted key in shared preferences.");
return realmKey; // pass to a realm configuration via encryptionKey()
}
@ -623,18 +608,15 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
} catch (KeyStoreException | NoSuchAlgorithmException
| CertificateException | IOException e) {
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException |
IOException e) {
Timber.e("Failed to open the keystore.");
throw new RuntimeException(e);
}
Timber.v("Keystore opened.");
// access the encrypted key that's stored in shared preferences
byte[] initializationVectorAndEncryptedKey = Base64.decode(MainApplication
.getPreferences("realm_key")
.getString("iv_and_encrypted_key", null), Base64.DEFAULT);
Timber.d("Retrieved the encrypted key from shared preferences. Key length: %d",
initializationVectorAndEncryptedKey.length);
byte[] initializationVectorAndEncryptedKey = Base64.decode(MainApplication.getPreferences("realm_key").getString("iv_and_encrypted_key", null), Base64.DEFAULT);
Timber.d("Retrieved the encrypted key from shared preferences. Key length: %d", initializationVectorAndEncryptedKey.length);
ByteBuffer buffer = ByteBuffer.wrap(initializationVectorAndEncryptedKey);
buffer.order(ByteOrder.BIG_ENDIAN);
// extract the length of the initialization vector from the buffer
@ -643,17 +625,13 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
byte[] initializationVector = new byte[initializationVectorLength];
buffer.get(initializationVector);
// extract the encrypted key
byte[] encryptedKey = new byte[initializationVectorAndEncryptedKey.length
- Integer.BYTES
- initializationVectorLength];
byte[] encryptedKey = new byte[initializationVectorAndEncryptedKey.length - Integer.BYTES - initializationVectorLength];
buffer.get(encryptedKey);
Timber.d("Got key from shared preferences.");
// create a cipher that uses AES encryption to decrypt our key
Cipher cipher;
try {
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES
+ "/" + KeyProperties.BLOCK_MODE_CBC
+ "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
Timber.e("Failed to create cipher.");
throw new RuntimeException(e);
@ -661,18 +639,16 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
// decrypt the encrypted key with the secret key stored in the keystore
byte[] decryptedKey;
try {
final SecretKey secretKey =
(SecretKey) keyStore.getKey("realm_key", null);
final IvParameterSpec initializationVectorSpec =
new IvParameterSpec(initializationVector);
final SecretKey secretKey = (SecretKey) keyStore.getKey("realm_key", null);
final IvParameterSpec initializationVectorSpec = new IvParameterSpec(initializationVector);
cipher.init(Cipher.DECRYPT_MODE, secretKey, initializationVectorSpec);
decryptedKey = cipher.doFinal(encryptedKey);
} catch (InvalidKeyException e) {
Timber.e("Failed to decrypt. Invalid key.");
throw new RuntimeException(e);
} catch (UnrecoverableKeyException | NoSuchAlgorithmException
| BadPaddingException | KeyStoreException
| IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
} catch (UnrecoverableKeyException | NoSuchAlgorithmException | BadPaddingException |
KeyStoreException | IllegalBlockSizeException |
InvalidAlgorithmParameterException e) {
Timber.e("Failed to decrypt the encrypted realm key with the secret key.");
throw new RuntimeException(e);
}

@ -72,6 +72,8 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
View view = binding.getRoot();
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).setChecked(BuildConfig.ENABLE_AUTO_UPDATER);
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).setChecked(BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING);
// pref_crash_reporting_pii
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting_pii))).setChecked(BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING_PII);
// assert that both switches match the build config on debug builds
if (BuildConfig.DEBUG) {
assert ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).isChecked() == BuildConfig.ENABLE_AUTO_UPDATER;
@ -84,6 +86,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
if (BuildConfig.DEBUG) {
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Automatic update Check: %s", isChecked));
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Crash Reporting: %s", isChecked));
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting_pii))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Crash Reporting PII: %s", isChecked));
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_androidacy_repo))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Androidacy Repo: %s", isChecked));
((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_magisk_alt_repo))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Magisk Alt Repo: %s", isChecked));
}
@ -163,6 +166,8 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
editor.putBoolean("pref_background_update_check", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).isChecked());
// Set the crash reporting pref
editor.putBoolean("pref_crash_reporting", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).isChecked());
// Set the crash reporting PII pref
editor.putBoolean("pref_crash_reporting_pii", ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting_pii))).isChecked());
Timber.d("Saving preferences");
// Set the repos in the ReposList realm db
RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").encryptionKey(MainApplication.getINSTANCE().getExistingKey()).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build();

@ -23,6 +23,7 @@ import com.fox2code.mmm.utils.io.Hashes;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.io.PropUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import java.io.File;
import java.nio.charset.StandardCharsets;
@ -32,6 +33,8 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import javax.net.ssl.SSLException;
import timber.log.Timber;
public final class RepoManager extends SyncManager {
@ -338,11 +341,24 @@ public final class RepoManager extends SyncManager {
// If we can't, we don't have internet connection
Timber.d("Checking internet connection...");
// this url is actually hosted by Cloudflare and is not dependent on Androidacy servers being up
byte[] resp = new byte[0];
byte[] resp;
try {
resp = Http.doHttpGet("https://production-api.androidacy.com/cdn-cgi/trace", false);
} catch (Exception e) {
Timber.e(e, "Failed to check internet connection. Assuming no internet connection.");
// check if it's a security or ssl exception
if (e instanceof SSLException || e instanceof SecurityException) {
// if it is, user installed a certificate that blocks the connection
// show a snackbar to inform the user
Activity context = MainApplication.getINSTANCE().getLastCompatActivity();
new Handler(Looper.getMainLooper()).post(() -> {
if (context != null) {
Snackbar.make(context.findViewById(android.R.id.content), R.string.certificate_error, Snackbar.LENGTH_LONG).show();
}
});
}
this.hasInternet = false;
return;
}
// get the response body
String response = new String(resp, StandardCharsets.UTF_8);

@ -23,7 +23,7 @@ public class SentryMain {
/**
* Initialize Sentry
* Sentry is used for crash reporting and performance monitoring. The SDK is explcitly configured not to send PII, and server side scrubbing of sensitive data is enabled (which also removes IP addresses)
* Sentry is used for crash reporting and performance monitoring.
*/
@SuppressLint({"RestrictedApi", "UnspecifiedImmutableFlag"})
public static void initialize(final MainApplication mainApplication) {
@ -54,8 +54,11 @@ public class SentryMain {
SentryAndroid.init(mainApplication, options -> {
// If crash reporting is disabled, stop here.
if (!MainApplication.isCrashReportingEnabled()) {
sentryEnabled = false; // Set sentry state to disabled
options.setDsn("");
} else {
// get pref_crash_reporting_pii pref
boolean crashReportingPii = sharedPreferences.getBoolean("crashReportingPii", false);
sentryEnabled = true; // Set sentry state to enabled
options.addIntegration(new FragmentLifecycleIntegration(mainApplication, true, true));
// Enable automatic activity lifecycle breadcrumbs
@ -71,15 +74,20 @@ public class SentryMain {
options.addInAppInclude("com.fox2code.mmm.debug");
options.addInAppInclude("com.fox2code.mmm.fdroid");
options.addInAppExclude("com.fox2code.mmm.utils.sentry.SentryMain");
// Sentry sends ABSOLUTELY NO Personally Identifiable Information (PII) by default.
// Already set to false by default, just set it again to make peoples feel safer.
options.setSendDefaultPii(false);
// Respect user preference for sending PII. default is true on non fdroid builds, false on fdroid builds
options.setSendDefaultPii(crashReportingPii);
options.enableAllAutoBreadcrumbs(true);
// in-app screenshots are only sent if the app crashes, and it only shows the last activity. so no, we won't see your, ahem, "private" stuff
options.setAttachScreenshot(true);
// It just tell if sentry should ping the sentry dsn to tell the app is running. Useful for performance and profiling.
options.setEnableAutoSessionTracking(true);
// A screenshot of the app itself is only sent if the app crashes, and it only shows the last activity
// Add a callback that will be used before the event is sent to Sentry.
// With this callback, you can modify the event or, when returning null, also discard the event.
options.setBeforeSend((event, hint) -> {
// in the rare event that crash reporting has been disabled since we started the app, we don't want to send the crash report
if (!MainApplication.isCrashReportingEnabled()) {
return null;
}
// Save lastEventId to private shared preferences
SharedPreferences sentryPrefs = MainApplication.getPreferences("sentry");
String lastEventId = Objects.requireNonNull(event.getEventId()).toString();

@ -147,7 +147,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="@string/crash_reporting_headline"
android:text="@string/pref_category_privacy"
android:textAppearance="@android:style/TextAppearance.Material.Headline" />
<com.google.android.material.materialswitch.MaterialSwitch
@ -170,6 +170,27 @@
android:text="@string/setup_crash_reporting_summary"
android:textAppearance="@android:style/TextAppearance.Material.Small" />
<!-- pii toggle -->
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/setup_crash_reporting_pii"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:checked="false"
android:key="pref_crash_reporting_pii"
android:text="@string/setup_crash_reporting_pii"
android:textSize="18sp" />
<!-- Small summary for above switch -->
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:drawableStart="@drawable/ic_baseline_info_24"
android:drawablePadding="8dp"
android:text="@string/setup_crash_reporting_pii_summary"
android:textAppearance="@android:style/TextAppearance.Material.Small" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"

@ -386,4 +386,10 @@
<string name="cache_clear_failed">Failed to clear cache</string>
<string name="clear_cache_dialogue_title">Clear app cache?</string>
<string name="clear_cache_dialogue_message">This will clear app cache. Your preferences will be saved, but the app may take longer to do some operations temporarily.</string>
<string name="certificate_error">The server certificate could not be verified. Please make sure nothing is intercepting HTTPS connections for FoxMMM.</string>
<string name="pref_category_privacy">Privacy</string>
<string name="crash_reporting_pii_desc">Allows sending additional information in crash reports, some of which may contain personally identifiable information such as IP address and device identifiers.</string>
<string name="crash_reporting_pii">Send additional information</string>
<string name="setup_crash_reporting_pii">Send additional info in crash reports.</string>
<string name="setup_crash_reporting_pii_summary"> This may include device identifiers and IP addresses. No data will be used for any other purpose besides analyzing crashes and improving performance.</string>
</resources>

@ -1,4 +1,5 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- Custom repos has been announced, check https://github.com/Fox2Code/FoxMagiskModuleManager/issues/131 -->
<PreferenceCategory app:title="@string/pref_category_repos">
@ -55,9 +56,9 @@
<!-- require wifi -->
<SwitchPreferenceCompat
app:defaultValue="true"
app:dependency="pref_background_update_check"
app:icon="@drawable/baseline_network_wifi_24"
app:key="pref_background_update_check_wifi"
app:dependency="pref_background_update_check"
app:singleLineTitle="false"
app:summary="@string/notification_update_wifi_pref"
app:title="@string/notification_update_wifi_desc" />
@ -71,9 +72,9 @@
app:title="@string/notification_update_ignore_pref" />
<Preference
app:icon="@drawable/baseline_notification_important_24"
app:key="pref_background_update_check_debug"
app:singleLineTitle="false"
app:icon="@drawable/baseline_notification_important_24"
app:title="@string/notification_update_debug_pref" />
<!-- For debugging: launch update activity with download action -->
@ -170,6 +171,8 @@
app:singleLineTitle="false"
app:summary="@string/prevent_reboot_desc"
app:title="@string/prevent_reboot_pref" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/pref_category_privacy">
<!-- Crash reporting -->
<SwitchPreferenceCompat
app:defaultValue="true"
@ -178,6 +181,14 @@
app:singleLineTitle="false"
app:summary="@string/crash_reporting_desc"
app:title="@string/crash_reporting" />
<!-- allow pii in crash reports -->
<SwitchPreferenceCompat
app:defaultValue="false"
app:icon="@drawable/ic_baseline_bug_report_24"
app:key="pref_crash_reporting_pii"
app:singleLineTitle="false"
app:summary="@string/crash_reporting_pii_desc"
app:title="@string/crash_reporting_pii" />
<!-- Purposely crash the app -->
<Preference
app:icon="@drawable/ic_baseline_bug_report_24"
@ -195,8 +206,8 @@
app:icon="@drawable/ic_baseline_delete_24"
app:key="pref_clear_cache"
app:singleLineTitle="false"
app:title="@string/clear_app_cache"
app:summary="@string/clear_app_cache_desc" />
app:summary="@string/clear_app_cache_desc"
app:title="@string/clear_app_cache" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/pref_category_info">

Loading…
Cancel
Save