[DO NOT MERGE] Add setup screen

Also the usual fixes and whatnot

Note the setup is rather broken right now as something else is overwriting the pref right away

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/27/head
androidacy-user 3 years ago
parent 20d51c5b9c
commit 2c675577cb

@ -29,7 +29,7 @@
<application <application
android:name=".MainApplication" android:name=".MainApplication"
android:allowBackup="true" android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/full_backup_content" android:fullBackupContent="@xml/full_backup_content"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"

@ -26,6 +26,8 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.SearchView;
import androidx.cardview.widget.CardView; import androidx.cardview.widget.CardView;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
@ -51,6 +53,7 @@ import com.fox2code.mmm.utils.ExternalHelper;
import com.fox2code.mmm.utils.Http; import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.IntentHelper; import com.fox2code.mmm.utils.IntentHelper;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.materialswitch.MaterialSwitch;
import com.google.android.material.progressindicator.LinearProgressIndicator; import com.google.android.material.progressindicator.LinearProgressIndicator;
import org.chromium.net.ExperimentalCronetEngine; import org.chromium.net.ExperimentalCronetEngine;
@ -61,6 +64,7 @@ import org.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.util.Objects;
import eightbitlab.com.blurview.BlurView; import eightbitlab.com.blurview.BlurView;
@ -168,7 +172,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
moduleViewListBuilder.addNotification(NotificationType.MAGISK_OUTDATED); moduleViewListBuilder.addNotification(NotificationType.MAGISK_OUTDATED);
if (!MainApplication.isShowcaseMode()) if (!MainApplication.isShowcaseMode())
moduleViewListBuilder.addNotification(NotificationType.INSTALL_FROM_STORAGE); moduleViewListBuilder.addNotification(NotificationType.INSTALL_FROM_STORAGE);
ensurePermissions();
ModuleManager.getINSTANCE().scan(); ModuleManager.getINSTANCE().scan();
ModuleManager.getINSTANCE().runAfterScan(moduleViewListBuilder::appendInstalledModules); ModuleManager.getINSTANCE().runAfterScan(moduleViewListBuilder::appendInstalledModules);
this.commonNext(); this.commonNext();
@ -195,6 +198,22 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
// Fix insets not being accounted for correctly // Fix insets not being accounted for correctly
updateScreenInsets(getResources().getConfiguration()); updateScreenInsets(getResources().getConfiguration());
}); });
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
showSetupBox();
// Wait for pref_first_launch to be false
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
while (prefs.getBoolean("pref_first_launch", true)) {
try {
//noinspection BusyWait
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ensurePermissions();
Log.i(TAG, "Scanning for modules!"); Log.i(TAG, "Scanning for modules!");
if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Initialize Update"); if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Initialize Update");
final int max = ModuleManager.getINSTANCE().getUpdatableModuleCount(); final int max = ModuleManager.getINSTANCE().getUpdatableModuleCount();
@ -260,6 +279,16 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
String lastEventId = preferences.getString("lastEventId", ""); String lastEventId = preferences.getString("lastEventId", "");
if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Last Event ID: " + lastEventId); if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Last Event ID: " + lastEventId);
if (!lastEventId.equals("")) { if (!lastEventId.equals("")) {
try {
ExperimentalCronetEngine cronetEngine = new ExperimentalCronetEngine.Builder(this).build();
CronetURLStreamHandlerFactory cronetURLStreamHandlerFactory = new CronetURLStreamHandlerFactory(cronetEngine);
URL.setURLStreamHandlerFactory(cronetURLStreamHandlerFactory);
} catch (Exception e) {
if (BuildConfig.DEBUG) {
Log.w(TAG, "Failed to setup cronet HTTPURLConnection factory", e);
Log.w(TAG, "This might mean the factory is already set");
}
}
// Three edit texts for the user to enter their email, name and a description of the issue // Three edit texts for the user to enter their email, name and a description of the issue
EditText email = new EditText(this); EditText email = new EditText(this);
email.setHint(R.string.email); email.setHint(R.string.email);
@ -320,9 +349,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
if (connection.getResponseCode() == 200) { if (connection.getResponseCode() == 200) {
runOnUiThread(() -> Toast.makeText(this, R.string.sentry_dialogue_success, Toast.LENGTH_LONG).show()); runOnUiThread(() -> Toast.makeText(this, R.string.sentry_dialogue_success, Toast.LENGTH_LONG).show());
} else { } else {
runOnUiThread(() -> Toast.makeText(this, runOnUiThread(() -> Toast.makeText(this, R.string.sentry_dialogue_failed_toast, Toast.LENGTH_LONG).show());
R.string.sentry_dialogue_failed_toast,
Toast.LENGTH_LONG).show());
} }
} catch (IOException | JSONException ignored) { } catch (IOException | JSONException ignored) {
// Show a toast if the user feedback could not be submitted // Show a toast if the user feedback could not be submitted
@ -619,4 +646,53 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
} }
} }
} }
// Method to show a setup box on first launch
@RequiresApi(api = Build.VERSION_CODES.M)
@SuppressLint({"InflateParams", "RestrictedApi", "UnspecifiedImmutableFlag", "ApplySharedPref"})
private void showSetupBox() {
// Check if this is the first launch
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("pref_first_launch", true)) {
MainApplication.getBootSharedPreferences().edit().putBoolean("mm_first_scan", false).commit();
// Show setup box
runOnUiThread(() -> {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setCancelable(false);
builder.setTitle(R.string.setup_title);
builder.setView(getLayoutInflater().inflate(R.layout.setup_box, null));
// For now, we'll just have the positive button save the preferences and dismiss the dialog
builder.setPositiveButton(R.string.setup_button, (dialog, which) -> {
// Set the preferences
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.edit().putBoolean("pref_background_update_check",
((MaterialSwitch) Objects.requireNonNull(((AlertDialog) dialog).findViewById(R.id.setup_background_update_check))).isChecked()).commit();
prefs.edit().putBoolean("pref_crash_reporting", ((MaterialSwitch) Objects.requireNonNull(((AlertDialog) dialog).findViewById(R.id.setup_crash_reporting))).isChecked()).commit();
prefs.edit().putBoolean("pref_androidacy_repo_enabled", ((MaterialSwitch) Objects.requireNonNull(((AlertDialog) dialog).findViewById(R.id.setup_androidacy_repo))).isChecked()).commit();
prefs.edit().putBoolean("pref_magisk_alt_repo_enabled", ((MaterialSwitch) Objects.requireNonNull(((AlertDialog) dialog).findViewById(R.id.setup_magisk_alt_repo))).isChecked()).commit();
if (BuildConfig.DEBUG) {
Log.d("MainActivity", String.format("Background update check: %s, Crash reporting: %s, Androidacy repo: %s, Magisk alt repo: %s",
prefs.getBoolean("pref_background_update_check", false),
prefs.getBoolean("pref_crash_reporting", false),
prefs.getBoolean("pref_androidacy_repo_enabled", false),
prefs.getBoolean("pref_magisk_alt_repo_enabled", false)));
}
// Set pref_first_launch to false
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("pref_first_launch",
false).commit();
// Restart the app
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
});
builder.setNegativeButton(R.string.setup_button_skip, (dialog, which) -> {
// Set pref_first_launch to false
PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("pref_first_launch",
false).commit();
dialog.dismiss();
});
builder.show();
});
}
}
} }

@ -64,8 +64,7 @@ public enum NotificationType implements NotificationTypeCst {
NO_INTERNET(R.string.fail_internet, R.drawable.ic_baseline_cloud_off_24) { NO_INTERNET(R.string.fail_internet, R.drawable.ic_baseline_cloud_off_24) {
@Override @Override
public boolean shouldRemove() { public boolean shouldRemove() {
return AppUpdateManager.getAppUpdateManager().isLastCheckSuccess() || return RepoManager.getINSTANCE().hasConnectivity();
RepoManager.getINSTANCE().hasConnectivity();
} }
}, },
REPO_UPDATE_FAILED(R.string.repo_update_failed, R.drawable.ic_baseline_cloud_off_24) { REPO_UPDATE_FAILED(R.string.repo_update_failed, R.drawable.ic_baseline_cloud_off_24) {

@ -202,9 +202,12 @@ public final class RepoManager extends SyncManager {
if (repoData == null) { if (repoData == null) {
if (ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT.equals(url) || if (ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT.equals(url) ||
ANDROIDACY_MAGISK_REPO_ENDPOINT.equals(url)) { ANDROIDACY_MAGISK_REPO_ENDPOINT.equals(url)) {
if (this.androidacyRepoData != null) //noinspection ReplaceNullCheck
if (this.androidacyRepoData != null) {
return this.androidacyRepoData; return this.androidacyRepoData;
} else {
return this.addAndroidacyRepoData(); return this.addAndroidacyRepoData();
}
} else { } else {
return this.addRepoData(url, fallBackName); return this.addRepoData(url, fallBackName);
} }
@ -289,7 +292,6 @@ public final class RepoManager extends SyncManager {
HttpURLConnection urlConnection = (HttpURLConnection) new URL( HttpURLConnection urlConnection = (HttpURLConnection) new URL(
"https://connectivitycheck.gstatic.com/generate_204").openConnection(); "https://connectivitycheck.gstatic.com/generate_204").openConnection();
urlConnection.setInstanceFollowRedirects(false); urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(1000);
urlConnection.setReadTimeout(1000); urlConnection.setReadTimeout(1000);
urlConnection.setUseCaches(false); urlConnection.setUseCaches(false);
urlConnection.getInputStream().close(); urlConnection.getInputStream().close();

@ -9,6 +9,8 @@ import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -61,6 +63,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.internal.TextWatcherAdapter; import com.google.android.material.internal.TextWatcherAdapter;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.MaterialAutoCompleteTextView; import com.google.android.material.textfield.MaterialAutoCompleteTextView;
import com.google.common.hash.Hashing;
import com.mikepenz.aboutlibraries.LibsBuilder; import com.mikepenz.aboutlibraries.LibsBuilder;
import com.topjohnwu.superuser.internal.UiThreadHandler; import com.topjohnwu.superuser.internal.UiThreadHandler;
@ -461,9 +464,24 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
openFragment(libsBuilder.supportFragment(), R.string.licenses); openFragment(libsBuilder.supportFragment(), R.string.licenses);
return true; return true;
}); });
findPreference("pref_pkg_info").setSummary(BuildConfig.APPLICATION_ID + // Determine if this is an official build based on the signature
" v" + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")" + boolean isOfficial = false;
getRepackageState()); // State may not be "I am just running from myself as myself" try {
// Get the signature of the key used to sign the app
@SuppressLint("PackageManagerGetSignatures") Signature[] signatures = requireContext().getPackageManager().getPackageInfo(requireContext().getPackageName(), PackageManager.GET_SIGNATURES).signatures;
String officialSignatureHash =
"7bec7c4462f4aac616612d9f56a023ee3046e83afa956463b5fab547fd0a0be6";
String ourSignatureHash = Hashing.sha256().hashBytes(signatures[0].toByteArray()).toString();
isOfficial = ourSignatureHash.equals(officialSignatureHash);
} catch (PackageManager.NameNotFoundException ignored) {
}
String flavor = BuildConfig.FLAVOR;
String type = BuildConfig.BUILD_TYPE;
// Set the summary of pref_pkg_info to something like Github-debug v1.0 (123) (Official)
String pkgInfo = getString(R.string.pref_pkg_info_summary, flavor + "-" + type,
BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, isOfficial ?
getString(R.string.official) : getString(R.string.unofficial));
findPreference("pref_pkg_info").setSummary(pkgInfo);
} }
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")

@ -54,7 +54,6 @@ import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
import okhttp3.ResponseBody; import okhttp3.ResponseBody;
import okhttp3.dnsoverhttps.DnsOverHttps; import okhttp3.dnsoverhttps.DnsOverHttps;
import okhttp3.logging.HttpLoggingInterceptor;
import okio.BufferedSink; import okio.BufferedSink;
public class Http { public class Http {
@ -159,6 +158,12 @@ public class Http {
} }
builder.setStoragePath(mainApplication.getCacheDir().getAbsolutePath() + "/cronet"); builder.setStoragePath(mainApplication.getCacheDir().getAbsolutePath() + "/cronet");
builder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 10 * 1024 * 1024); builder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 10 * 1024 * 1024);
// Add quic hint
builder.addQuicHint("github.com", 443, 443);
builder.addQuicHint("githubusercontent.com", 443, 443);
builder.addQuicHint("jsdelivr.net", 443, 443);
builder.addQuicHint("androidacy.com", 443, 443);
builder.addQuicHint("sentry.io", 443, 443);
CronetEngine engine = builder.build(); CronetEngine engine = builder.build();
httpclientBuilder.addInterceptor(CronetInterceptor.newBuilder(engine).build()); httpclientBuilder.addInterceptor(CronetInterceptor.newBuilder(engine).build());
} catch (Exception e) { } catch (Exception e) {
@ -172,13 +177,6 @@ public class Http {
httpClient = followRedirects(httpclientBuilder, true).build(); httpClient = followRedirects(httpclientBuilder, true).build();
followRedirects(httpclientBuilder, false).build(); followRedirects(httpclientBuilder, false).build();
httpclientBuilder.dns(fallbackDNS); httpclientBuilder.dns(fallbackDNS);
if (BuildConfig.DEBUG) {
// Enable logging
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(s -> Log.d(TAG, s));
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpclientBuilder.addInterceptor(logging);
Log.d(TAG, "OkHttp logging enabled");
}
httpClientDoH = followRedirects(httpclientBuilder, true).build(); httpClientDoH = followRedirects(httpclientBuilder, true).build();
followRedirects(httpclientBuilder, false).build(); followRedirects(httpclientBuilder, false).build();
httpclientBuilder.cache(new Cache(new File(mainApplication.getCacheDir(), "http_cache"), 16L * 1024L * 1024L)); // 16Mib of cache httpclientBuilder.cache(new Cache(new File(mainApplication.getCacheDir(), "http_cache"), 16L * 1024L * 1024L)); // 16Mib of cache
@ -343,24 +341,49 @@ public class Http {
} }
public static void ensureCacheDirs(MainActivity mainActivity) { public static void ensureCacheDirs(MainActivity mainActivity) {
// Recursively ensure cache dirs for webview exist under our cache dir
File cacheDir = mainActivity.getCacheDir(); File cacheDir = mainActivity.getCacheDir();
File cacheDir2 = new File(cacheDir, "HTTP Cache"); File webviewCacheDir = new File(cacheDir, "WebView");
if (!cacheDir2.exists()) { if (!webviewCacheDir.exists()) {
if (!cacheDir2.mkdirs()) { if (!webviewCacheDir.mkdirs()) {
Log.e(TAG, "Failed to create cache dir"); Log.e(TAG, "Failed to create webview cache dir");
}
}
File webviewCacheDirCache = new File(webviewCacheDir, "Default");
if (!webviewCacheDirCache.exists()) {
if (!webviewCacheDirCache.mkdirs()) {
Log.e(TAG, "Failed to create webview cache dir");
}
}
File webviewCacheDirCacheCodeCache = new File(webviewCacheDirCache, "HTTP Cache");
if (!webviewCacheDirCacheCodeCache.exists()) {
if (!webviewCacheDirCacheCodeCache.mkdirs()) {
Log.e(TAG, "Failed to create webview cache dir");
}
}
File webviewCacheDirCacheCodeCacheIndex = new File(webviewCacheDirCacheCodeCache, "Code Cache");
if (!webviewCacheDirCacheCodeCacheIndex.exists()) {
if (!webviewCacheDirCacheCodeCacheIndex.mkdirs()) {
Log.e(TAG, "Failed to create webview cache dir");
}
}
File webviewCacheDirCacheCodeCacheIndexIndex = new File(webviewCacheDirCacheCodeCacheIndex, "Index");
if (!webviewCacheDirCacheCodeCacheIndexIndex.exists()) {
if (!webviewCacheDirCacheCodeCacheIndexIndex.mkdirs()) {
Log.e(TAG, "Failed to create webview cache dir");
} }
} }
// Ensure js and wasm cache dirs // Create the js and wasm dirs
File jsCacheDir = new File(cacheDir2, "js"); File webviewCacheDirCacheCodeCacheIndexIndexJs = new File(webviewCacheDirCacheCodeCache, "js");
if (!jsCacheDir.exists()) { if (!webviewCacheDirCacheCodeCacheIndexIndexJs.exists()) {
if (!jsCacheDir.mkdirs()) { if (!webviewCacheDirCacheCodeCacheIndexIndexJs.mkdirs()) {
Log.e(TAG, "Failed to create js cache dir"); Log.e(TAG, "Failed to create webview cache dir");
} }
} }
File wasmCacheDir = new File(cacheDir2, "wasm"); File webviewCacheDirCacheCodeCacheIndexIndexWasm = new File(webviewCacheDirCacheCodeCache, "wasm");
if (!wasmCacheDir.exists()) { if (!webviewCacheDirCacheCodeCacheIndexIndexWasm.exists()) {
if (!wasmCacheDir.mkdirs()) { if (!webviewCacheDirCacheCodeCacheIndexIndexWasm.mkdirs()) {
Log.e(TAG, "Failed to create wasm cache dir"); Log.e(TAG, "Failed to create webview cache dir");
} }
} }
} }

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="fill"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:scrollbars="vertical">
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/setup_message"
android:textSize="14sp" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="6dp"
android:text="@string/repos"
android:textSize="18sp" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/setup_androidacy_repo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:checked="true"
android:key="pref_androidacy_repo_enabled"
android:text="@string/setup_androidacy_repo"
android:textSize="14sp" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/setup_magisk_alt_repo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:checked="false"
android:key="pref_magisk_alt_repo_enabled"
android:text="@string/setup_magisk_alt_repo"
android:textSize="14sp" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="2dp"
android:checked="true"
android:text="@string/setup_custom_repos"
android:textSize="12sp" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="6dp"
android:text="@string/misc"
android:textSize="18sp" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/setup_crash_reporting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:checked="true"
android:key="pref_crash_reporting_enabled"
android:text="@string/setup_crash_reporting"
android:textSize="14sp" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/setup_background_update_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:checked="true"
android:key="pref_background_update_check"
android:text="@string/setup_background_update_check"
android:textSize="14sp" />
</LinearLayout>
</ScrollView>
</LinearLayout>

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----

@ -208,8 +208,8 @@
by adding some information about the error below.\nName and email are optional but will by adding some information about the error below.\nName and email are optional but will
allow us to contact you if needed for more information.</string> allow us to contact you if needed for more information.</string>
<string name="sentry_dialogue_title">Oops! Looks like the app closed unexpectedly.</string> <string name="sentry_dialogue_title">Oops! Looks like the app closed unexpectedly.</string>
<string name="name">Name</string> <string name="name">Your name</string>
<string name="email">Email</string> <string name="email">Your email</string>
<string name="additional_info">Tell us what happened</string> <string name="additional_info">Tell us what happened</string>
<string name="submit">Submit</string> <string name="submit">Submit</string>
<string name="sentry_dialogue_failed_toast">Could not submit feedback due to an error</string> <string name="sentry_dialogue_failed_toast">Could not submit feedback due to an error</string>
@ -218,4 +218,19 @@
<string name="sentry_dialogue_no_description">Could not submit feedback as no <string name="sentry_dialogue_no_description">Could not submit feedback as no
description was provided</string> description was provided</string>
<string name="go_to_online_repo">Scroll to online repo</string> <string name="go_to_online_repo">Scroll to online repo</string>
<string name="pref_pkg_info_summary">%1$s v%2$s (%3$o) | %4$s Build</string>
<string name="official">Official</string>
<string name="unofficial">Unofficial</string>
<string name="setup_title">First time setup</string>
<string name="setup_message">Looks like this is your first time opening the app.\nPick
the basic options you desire below. You can change them later in settings.</string>
<string name="setup_button">Finish setup</string>
<string name="setup_background_update_check">Allow us to check for updates in the background. May use more battery.</string>
<string name="setup_androidacy_repo">Enable the Androidacy repo, which features user reviews, automatic virus scans, fast updates, a wide selection, and is backed by Androidacy.</string>
<string name="setup_magisk_alt_repo">Enable the Magisk Alternative Repo. Less rules and reviewing than the original. Fully hosted on GitHub.</string>
<string name="setup_crash_reporting">Enable automatic crash reporting and performance monitoring. Crash reports are anonymized with all personal info removed, and are onlly accessible to the developers. Uses sentry.io.</string>
<string name="setup_custom_repos">You can add custom repos later in settings.</string>
<string name="repos">Repos</string>
<string name="misc">Misc settings</string>
<string name="setup_button_skip">Skip</string>
</resources> </resources>

@ -8,20 +8,27 @@
</domain-config> </domain-config>
<domain-config cleartextTrafficPermitted="false"> <domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">github.com</domain> <domain includeSubdomains="true">github.com</domain>
<domain includeSubdomains="true">githubusercontent.com</domain>
<trust-anchors> <trust-anchors>
<certificates src="@raw/gh_root_ca" /> <certificates src="@raw/gh_root_ca" />
</trust-anchors> </trust-anchors>
</domain-config> </domain-config>
<domain-config cleartextTrafficPermitted="false"> <domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">githubusercontent.com</domain> <domain includeSubdomains="true">gstatic.com</domain>
<trust-anchors> <trust-anchors>
<certificates src="@raw/gh_root_ca" /> <certificates src="@raw/gstatic_root_ca" />
</trust-anchors> </trust-anchors>
</domain-config> </domain-config>
<domain-config cleartextTrafficPermitted="false"> <domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">gstatic.com</domain> <domain includeSubdomains="true">ingest.sentry.io</domain>
<trust-anchors> <trust-anchors>
<certificates src="@raw/gstatic_root_ca" /> <certificates src="@raw/sentry_root_ca" />
</trust-anchors>
</domain-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="false">sentry.io</domain>
<trust-anchors>
<certificates src="@raw/sentry_io_root_ca" />
</trust-anchors> </trust-anchors>
</domain-config> </domain-config>
</network-security-config> </network-security-config>

@ -25,9 +25,9 @@ public class SentryMain {
@SuppressLint({"RestrictedApi", "UnspecifiedImmutableFlag"}) @SuppressLint({"RestrictedApi", "UnspecifiedImmutableFlag"})
public static void initialize(final MainApplication mainApplication) { public static void initialize(final MainApplication mainApplication) {
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
SharedPreferences.Editor editor = mainApplication.getSharedPreferences( SharedPreferences.Editor editor = mainApplication.getSharedPreferences("sentry", Context.MODE_PRIVATE).edit();
"sentry", Context.MODE_PRIVATE).edit();
editor.putString("lastExitReason", "crash"); editor.putString("lastExitReason", "crash");
editor.putLong("lastExitTime", System.currentTimeMillis());
editor.apply(); editor.apply();
// If we just let the default uncaught exception handler handle the // If we just let the default uncaught exception handler handle the
// exception, the app will hang and never close. // exception, the app will hang and never close.
@ -56,9 +56,7 @@ public class SentryMain {
// With this callback, you can modify the event or, when returning null, also discard the event. // With this callback, you can modify the event or, when returning null, also discard the event.
options.setBeforeSend((event, hint) -> { options.setBeforeSend((event, hint) -> {
// Save lastEventId to private shared preferences // Save lastEventId to private shared preferences
SharedPreferences sharedPreferences = MainApplication.getINSTANCE().getSharedPreferences( SharedPreferences sharedPreferences = MainApplication.getINSTANCE().getSharedPreferences("sentry", Context.MODE_PRIVATE);
"sentry",
Context.MODE_PRIVATE);
String lastEventId = Objects.requireNonNull(event.getEventId()).toString(); String lastEventId = Objects.requireNonNull(event.getEventId()).toString();
SharedPreferences.Editor editor = sharedPreferences.edit(); SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("lastEventId", lastEventId); editor.putString("lastEventId", lastEventId);

Loading…
Cancel
Save