rework http

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/299/head
androidacy-user 1 year ago
parent c4a4ec5287
commit 888b624ff6

@ -302,13 +302,13 @@ dependencies {
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.webkit:webkit:1.6.0'
implementation 'com.google.android.material:material:1.8.0'
implementation "dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0"
implementation 'dev.rikka.rikkax.layoutinflater:layoutinflater:1.3.0'
implementation "dev.rikka.rikkax.insets:insets:1.3.0"
implementation 'com.github.Dimezis:BlurView:version-2.0.2'
implementation 'com.github.KieronQuinn:MonetCompat:0.4.1'
implementation 'com.github.Fox2Code:FoxCompat:0.2.0'
// Update the version code in the root build.gradle
implementation "com.mikepenz:aboutlibraries:10.6.0"
implementation 'com.mikepenz:aboutlibraries:10.6.1'
// Utils
implementation 'androidx.work:work-runtime:2.8.0'
@ -326,7 +326,7 @@ dependencies {
implementation 'com.github.Fox2Code:AndroidANSI:1.0.1'
// sentry
implementation platform('io.sentry:sentry-bom:6.15.0')
implementation platform('io.sentry:sentry-bom:6.16.0')
implementation "io.sentry:sentry-android"
implementation "io.sentry:sentry-android-timber"
implementation "io.sentry:sentry-android-fragment"

@ -66,8 +66,7 @@
tools:targetApi="tiramisu">
<activity
android:name=".UpdateActivity"
android:exported="false"
android:process=":update" />
android:exported="false" />
<activity
android:name=".CrashHandler"
android:exported="false"
@ -75,7 +74,6 @@
<activity
android:name=".SetupActivity"
android:exported="false"
android:process=":setup"
android:label="@string/title_activity_setup"
android:theme="@style/Theme.MagiskModuleManager.NoActionBar" />
@ -96,6 +94,7 @@
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true"
@ -106,6 +105,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".installer.InstallerActivity"
android:exported="false"
@ -119,7 +119,9 @@
<action android:name="${applicationId}.intent.action.INSTALL_MODULE_INTERNAL" />
</intent-filter>
-->
</activity> <!-- We can handle zip files -->
</activity>
<!-- We can handle zip files -->
<activity
android:name=".utils.ZipFileOpener"
android:exported="true"
@ -134,11 +136,13 @@
<data android:scheme="content" />
</intent-filter>
</activity>
<activity
android:name=".markdown.MarkdownActivity"
android:exported="false"
android:parentActivityName=".MainActivity"
android:theme="@style/Theme.MagiskModuleManager" />
<activity
android:name=".androidacy.AndroidacyActivity"
android:exported="false"
@ -151,6 +155,7 @@
</intent-filter>
-->
</activity>
<activity
android:name="com.mikepenz.aboutlibraries.ui.LibsActivity"
tools:node="remove" />
@ -167,6 +172,7 @@
android:name="androidx.work.WorkManagerInitializer"
tools:node="remove" />
</provider>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.file-provider"

@ -1,7 +1,7 @@
package com.fox2code.mmm;
import com.fox2code.mmm.utils.io.Files;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import org.json.JSONObject;
@ -149,38 +149,18 @@ public class AppUpdateManager {
int value = 0;
for (String arg : line.substring(i + 1).split(",")) {
switch (arg) {
default:
break;
case "lowQuality":
value |= FLAG_COMPAT_LOW_QUALITY;
break;
case "noExt":
value |= FLAG_COMPAT_NO_EXT;
break;
case "magiskCmd":
value |= FLAG_COMPAT_MAGISK_CMD;
break;
case "need32bit":
value |= FLAG_COMPAT_NEED_32BIT;
break;
case "malware":
value |= FLAG_COMPAT_MALWARE;
break;
case "noANSI":
value |= FLAG_COMPAT_NO_ANSI;
break;
case "forceANSI":
value |= FLAG_COMPAT_FORCE_ANSI;
break;
case "forceHide":
value |= FLAG_COMPAT_FORCE_HIDE;
break;
case "mmtReborn":
value |= FLAG_COMPAT_MMT_REBORN;
break;
case "wrapper":
value |= FLAG_COMPAT_ZIP_WRAPPER;
break;
default -> {
}
case "lowQuality" -> value |= FLAG_COMPAT_LOW_QUALITY;
case "noExt" -> value |= FLAG_COMPAT_NO_EXT;
case "magiskCmd" -> value |= FLAG_COMPAT_MAGISK_CMD;
case "need32bit" -> value |= FLAG_COMPAT_NEED_32BIT;
case "malware" -> value |= FLAG_COMPAT_MALWARE;
case "noANSI" -> value |= FLAG_COMPAT_NO_ANSI;
case "forceANSI" -> value |= FLAG_COMPAT_FORCE_ANSI;
case "forceHide" -> value |= FLAG_COMPAT_FORCE_HIDE;
case "mmtReborn" -> value |= FLAG_COMPAT_MMT_REBORN;
case "wrapper" -> value |= FLAG_COMPAT_ZIP_WRAPPER;
}
}
compatDataId.put(line.substring(0, i), value);

@ -37,8 +37,12 @@ public class CrashHandler extends FoxActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crash_handler);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// per process webview data dir
WebView.setDataDirectorySuffix(FoxApplication.getProcessName());
try {
// per process webview data dir
WebView.setDataDirectorySuffix(FoxApplication.getProcessName());
} catch (Exception e) {
Timber.e(e, "CrashHandler.onCreate: Failed to set webview data directory suffix");
}
}
// set crash_details MaterialTextView to the exception passed in the intent or unknown if null
// convert stacktrace from array to string, and pretty print it (first line is the exception, the rest is the stacktrace, with each line indented by 4 spaces)

@ -22,7 +22,6 @@ import android.util.TypedValue;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.webkit.WebView;
import android.widget.CheckBox;
import android.widget.Toast;
@ -38,7 +37,6 @@ import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.fox2code.foxcompat.app.FoxActivity;
import com.fox2code.foxcompat.app.FoxApplication;
import com.fox2code.foxcompat.view.FoxDisplay;
import com.fox2code.mmm.background.BackgroundUpdateChecker;
import com.fox2code.mmm.installer.InstallerInitializer;
@ -49,7 +47,7 @@ import com.fox2code.mmm.module.ModuleViewListBuilder;
import com.fox2code.mmm.repo.RepoManager;
import com.fox2code.mmm.settings.SettingsActivity;
import com.fox2code.mmm.utils.ExternalHelper;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.progressindicator.LinearProgressIndicator;
@ -58,6 +56,7 @@ import org.chromium.net.CronetEngine;
import java.io.File;
import java.net.URL;
import java.util.Map;
import java.util.Objects;
import timber.log.Timber;
@ -101,14 +100,6 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
@Override
protected void onCreate(Bundle savedInstanceState) {
this.initMode = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// per process webview data dir
try {
WebView.setDataDirectorySuffix(FoxApplication.getProcessName());
} catch (IllegalStateException e) {
Timber.d("Could not set webview data dir, possibly already set or webview already initialized");
}
}
// Ensure HTTP Cache directories are created
Http.ensureCacheDirs(this);
if (!urlFactoryInstalled) {
@ -138,6 +129,13 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
}
BackgroundUpdateChecker.onMainActivityCreate(this);
super.onCreate(savedInstanceState);
// log all shared preferences that are present
// first, get every shared preference
Map<String, ?> allPrefs = MainApplication.getPreferences("mmm").getAll();
// then, log them
for (Map.Entry<String, ?> entry : allPrefs.entrySet()) {
Timber.d("Shared preference: %s = %s", entry.getKey(), entry.getValue());
}
if (!isOfficial) {
Timber.w("You may be running an untrusted build.");
// Show a toast to warn the user
@ -296,7 +294,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
// On every preferences change, log the change if debug is enabled
if (BuildConfig.DEBUG) {
// Log all preferences changes
MainApplication.getSharedPreferences("mmm").registerOnSharedPreferenceChangeListener((prefs, key) -> Timber.i("onSharedPreferenceChanged: " + key + " = " + prefs.getAll().get(key)));
MainApplication.getPreferences("mmm").registerOnSharedPreferenceChangeListener((prefs, key) -> Timber.i("onSharedPreferenceChanged: " + key + " = " + prefs.getAll().get(key)));
}
Timber.i("Scanning for modules!");
@ -719,7 +717,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
if (BuildConfig.DEBUG)
Timber.i("Checking if we need to run setup");
// Check if this is the first launch using prefs and if doSetupRestarting was passed in the intent
SharedPreferences prefs = MainApplication.getSharedPreferences("mmm");
SharedPreferences prefs = MainApplication.getPreferences("mmm");
boolean firstLaunch = !Objects.equals(prefs.getString("last_shown_setup", null), "v1");
// First launch
// this is intentionally separate from the above if statement, because it needs to be checked even if the first launch check is true due to some weird edge cases

@ -29,15 +29,13 @@ import com.fox2code.foxcompat.app.internal.FoxProcessExt;
import com.fox2code.foxcompat.view.FoxThemeWrapper;
import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.utils.io.GMSProviderInstaller;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.sentry.SentryMain;
import com.fox2code.rosettax.LanguageSwitcher;
import com.google.common.hash.Hashing;
import com.topjohnwu.superuser.Shell;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
@ -109,26 +107,13 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
intent.putExtra("secret", secret);
}
public static SharedPreferences getSharedPreferences(String name) {
// for debugging, log timing
long start = SystemClock.elapsedRealtime();
// log what's requesting the shared preferences
// get caller if debug build
if (BuildConfig.DEBUG) {
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[3];
String methodName = e.getMethodName();
String className = e.getClassName();
Timber.d("Shared preferences %s requested by %s.%s", name, className, methodName);
}
public static SharedPreferences getPreferences(String name) {
// encryptedSharedPreferences is used
Context context = getINSTANCE().getApplicationContext();
Context context = getINSTANCE();
try {
MasterKey masterKey = new MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build();
SharedPreferences mSharedPrefs = EncryptedSharedPreferences.create(context, name, masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
Timber.d("Encrypted shared preferences %s took %dms", name, SystemClock.elapsedRealtime() - start);
return mSharedPrefs;
} catch (GeneralSecurityException | IOException e) {
return EncryptedSharedPreferences.create(context, name, masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
} catch (Exception e) {
Timber.e(e, "Failed to create encrypted shared preferences");
return context.getSharedPreferences(name, Context.MODE_PRIVATE);
}
@ -145,56 +130,56 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
}
public static boolean isShowcaseMode() {
return getSharedPreferences("mmm").getBoolean("pref_showcase_mode", false);
return getPreferences("mmm").getBoolean("pref_showcase_mode", false);
}
public static boolean shouldPreventReboot() {
return getSharedPreferences("mmm").getBoolean("pref_prevent_reboot", true);
return getPreferences("mmm").getBoolean("pref_prevent_reboot", true);
}
public static boolean isShowIncompatibleModules() {
return getSharedPreferences("mmm").getBoolean("pref_show_incompatible", false);
return getPreferences("mmm").getBoolean("pref_show_incompatible", false);
}
public static boolean isForceDarkTerminal() {
return getSharedPreferences("mmm").getBoolean("pref_force_dark_terminal", false);
return getPreferences("mmm").getBoolean("pref_force_dark_terminal", false);
}
public static boolean isTextWrapEnabled() {
return getSharedPreferences("mmm").getBoolean("pref_wrap_text", false);
return getPreferences("mmm").getBoolean("pref_wrap_text", false);
}
public static boolean isDohEnabled() {
return getSharedPreferences("mmm").getBoolean("pref_dns_over_https", true);
return getPreferences("mmm").getBoolean("pref_dns_over_https", true);
}
public static boolean isMonetEnabled() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && getSharedPreferences("mmm").getBoolean("pref_enable_monet", true);
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && getPreferences("mmm").getBoolean("pref_enable_monet", true);
}
public static boolean isBlurEnabled() {
return getSharedPreferences("mmm").getBoolean("pref_enable_blur", false);
return getPreferences("mmm").getBoolean("pref_enable_blur", false);
}
public static boolean isDeveloper() {
if (BuildConfig.DEBUG) return true;
return getSharedPreferences("mmm").getBoolean("developer", false);
return getPreferences("mmm").getBoolean("developer", false);
}
public static boolean isDisableLowQualityModuleFilter() {
return getSharedPreferences("mmm").getBoolean("pref_disable_low_quality_module_filter", false) && isDeveloper();
return getPreferences("mmm").getBoolean("pref_disable_low_quality_module_filter", false) && isDeveloper();
}
public static boolean isUsingMagiskCommand() {
return InstallerInitializer.peekMagiskVersion() >= Constants.MAGISK_VER_CODE_INSTALL_COMMAND && getSharedPreferences("mmm").getBoolean("pref_use_magisk_install_command", false) && isDeveloper();
return InstallerInitializer.peekMagiskVersion() >= Constants.MAGISK_VER_CODE_INSTALL_COMMAND && getPreferences("mmm").getBoolean("pref_use_magisk_install_command", false) && isDeveloper();
}
public static boolean isBackgroundUpdateCheckEnabled() {
return !wrapped && getSharedPreferences("mmm").getBoolean("pref_background_update_check", true);
return !wrapped && getPreferences("mmm").getBoolean("pref_background_update_check", true);
}
public static boolean isAndroidacyTestMode() {
return isDeveloper() && getSharedPreferences("mmm").getBoolean("pref_androidacy_test_mode", false);
return isDeveloper() && getPreferences("mmm").getBoolean("pref_androidacy_test_mode", false);
}
public static boolean isFirstBoot() {
@ -202,15 +187,15 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
}
public static void setHasGottenRootAccess(boolean bool) {
getSharedPreferences("mmm").edit().putBoolean("has_root_access", bool).apply();
getPreferences("mmm").edit().putBoolean("has_root_access", bool).apply();
}
public static boolean isCrashReportingEnabled() {
return SentryMain.IS_SENTRY_INSTALLED && getSharedPreferences("mmm").getBoolean("pref_crash_reporting", BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING);
return SentryMain.IS_SENTRY_INSTALLED && getPreferences("mmm").getBoolean("pref_crash_reporting", BuildConfig.DEFAULT_ENABLE_CRASH_REPORTING);
}
public static SharedPreferences getBootSharedPreferences() {
return getSharedPreferences("mmm_boot");
return getPreferences("mmm_boot");
}
public static MainApplication getINSTANCE() {
@ -246,7 +231,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
@StyleRes int themeResId;
String theme;
boolean monet = isMonetEnabled();
switch (theme = getSharedPreferences("mmm").getString("pref_theme", "system")) {
switch (theme = getPreferences("mmm").getString("pref_theme", "system")) {
default:
Timber.w("Unknown theme id: %s", theme);
case "system":
@ -368,9 +353,9 @@ public class MainApplication extends FoxApplication implements androidx.work.Con
isOfficial = Arrays.asList(officialSignatureHashArray).contains(ourSignatureHash);
} catch (PackageManager.NameNotFoundException ignored) {
}
SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm");
SharedPreferences sharedPreferences = MainApplication.getPreferences("mmm");
// We are only one process so it's ok to do this
SharedPreferences bootPrefs = MainApplication.getSharedPreferences("mmm_boot");
SharedPreferences bootPrefs = MainApplication.getPreferences("mmm_boot");
long lastBoot = System.currentTimeMillis() - SystemClock.elapsedRealtime();
long lastBootPrefs = bootPrefs.getLong("last_boot", 0);
if (lastBootPrefs == 0 || Math.abs(lastBoot - lastBootPrefs) > 100) {

@ -13,7 +13,7 @@ import com.fox2code.mmm.module.ModuleViewListBuilder;
import com.fox2code.mmm.repo.RepoManager;
import com.fox2code.mmm.utils.IntentHelper;
import com.fox2code.mmm.utils.io.Files;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import java.io.BufferedReader;
import java.io.File;

@ -3,14 +3,12 @@ package com.fox2code.mmm;
import static com.fox2code.mmm.utils.IntentHelper.getActivity;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.view.WindowManager;
@ -30,9 +28,9 @@ import com.google.android.material.materialswitch.MaterialSwitch;
import com.topjohnwu.superuser.internal.UiThreadHandler;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import io.realm.Realm;
import io.realm.RealmConfiguration;
@ -60,7 +58,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
createFiles();
disableUpdateActivityForFdroidFlavor();
// Set theme
SharedPreferences prefs = MainApplication.getSharedPreferences("mmm");
SharedPreferences prefs = MainApplication.getPreferences("mmm");
switch (prefs.getString("theme", "system")) {
case "light" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Light);
case "dark" -> setTheme(R.style.Theme_MagiskModuleManager_Monet_Dark);
@ -155,9 +153,9 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
});
// Set up the buttons
// Setup button
MaterialButton setupButton = view.findViewById(R.id.setup_continue);
MaterialButton setupButton = view.findViewById(R.id.setup_finish);
setupButton.setOnClickListener(v -> {
// Set first launch to false
Timber.i("Setup button clicked");
// get instance of editor
SharedPreferences.Editor editor = prefs.edit();
// Set the Automatic update check pref
@ -178,39 +176,31 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
editor.putString("last_shown_setup", "v1");
// Commit the changes
editor.commit();
// Sleep for 1 second to allow the user to see the changes
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// Log the changes if debug
if (BuildConfig.DEBUG) {
Timber.d("Automatic update check: %s", prefs.getBoolean("pref_background_update_check", false));
Timber.i("Crash reporting: %s", prefs.getBoolean("pref_crash_reporting", false));
Timber.i("Androidacy repo: %s", androidacyRepo);
Timber.i("Magisk Alt repo: %s", magiskAltRepo);
// log last shown setup
Timber.i("Last shown setup: %s", prefs.getString("last_shown_setup", "v0"));
}
// Log the changes
Timber.d("Setup finished. Preferences: %s", prefs.getAll());
Timber.d("Androidacy repo: %s", androidacyRepo);
Timber.d("Magisk Alt repo: %s", magiskAltRepo);
// log last shown setup
Timber.d("Last shown setup: %s", prefs.getString("last_shown_setup", "v0"));
// Restart the activity
MainActivity.doSetupRestarting = true;
PendingIntent intent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_IMMUTABLE);
try {
intent.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("doSetupRestarting", true);
startActivity(intent);
finish();
});
// Cancel button
MaterialButton cancelButton = view.findViewById(R.id.setup_cancel);
cancelButton.setText(R.string.cancel);
cancelButton.setOnClickListener(v -> {
Timber.i("Cancel button clicked");
// Set first launch to false and restart the activity
prefs.edit().putString("last_shown_setup", "v1").commit();
MainActivity.doSetupRestarting = true;
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("doSetupRestarting", true);
startActivity(intent);
finish();
});
@ -225,7 +215,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
return theme;
}
// Set the theme
SharedPreferences prefs = MainApplication.getSharedPreferences("mmm");
SharedPreferences prefs = MainApplication.getPreferences("mmm");
String themePref = prefs.getString("pref_theme", "system");
switch (themePref) {
case "light" -> {
@ -318,32 +308,14 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
}
public void createFiles() {
try {
String cookieFileName = "cookies";
// initial set of cookies, only really used to create the file
String initialCookie = "is_foxmmm=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=production-api.androidacy.com; SameSite=None; Secure;|foxmmm_version=" + BuildConfig.VERSION_CODE + "; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/; domain=production-api.androidacy.com; SameSite=None; Secure;";
File cookieFile = new File(MainApplication.getINSTANCE().getFilesDir(), cookieFileName);
// if the file exists, delete it
if (cookieFile.exists()) {
if (!cookieFile.delete()) {
Timber.e("Failed to delete cookie file");
throw new IllegalStateException("Failed to create cookie file. This probably means that the app doesn't have permission to write to the files directory");
}
}
// create the file
if (!cookieFile.createNewFile()) {
Timber.e("Failed to create cookie file");
throw new IllegalStateException("Failed to create cookie file. This probably means that the app doesn't have permission to write to the files directory");
}
// create the file output stream
FileOutputStream fileOutputStream = new FileOutputStream(cookieFile);
// write the initial cookie to the file
fileOutputStream.write(Base64.encode(initialCookie.getBytes(), Base64.DEFAULT));
// close the file output stream
fileOutputStream.close();
} catch (IOException e) {
Timber.e(e);
}
// create cookie prefs
SharedPreferences cookiePrefs = MainApplication.getPreferences("cookies");
// add is_foxmmm cookie to the universal cookie stringset
Set<String> universalCookies = cookiePrefs.getStringSet("universal", new HashSet<>());
// silence the warning by copying the set to a new set
Set<String> universalCookies2 = new HashSet<>(universalCookies);
universalCookies2.add("is_foxmmm=true");
cookiePrefs.edit().putStringSet("universal", universalCookies2).apply();
// we literally only use these to create the http cache folders
File httpCacheDir = MainApplication.getINSTANCE().getDataDirWithPath("cache/WebView/Default/HTTP Cache/Code Cache/wasm");
File httpCacheDir2 = MainApplication.getINSTANCE().getDataDirWithPath("cache/WebView/Default/HTTP Cache/Code Cache/js");

@ -12,7 +12,7 @@ import androidx.core.content.FileProvider;
import com.fox2code.foxcompat.app.FoxActivity;
import com.fox2code.foxcompat.app.FoxApplication;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.google.android.material.textview.MaterialTextView;
@ -36,10 +36,6 @@ public class UpdateActivity extends FoxActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_update);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// per process webview data dir
WebView.setDataDirectorySuffix(FoxApplication.getProcessName());
}
// Get the progress bar and make it indeterminate for now
LinearProgressIndicator progressIndicator = findViewById(R.id.update_progress);
progressIndicator.setIndeterminate(true);

@ -5,7 +5,6 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.webkit.ConsoleMessage;
@ -29,14 +28,13 @@ import androidx.webkit.WebViewClientCompat;
import androidx.webkit.WebViewFeature;
import com.fox2code.foxcompat.app.FoxActivity;
import com.fox2code.foxcompat.app.FoxApplication;
import com.fox2code.mmm.BuildConfig;
import com.fox2code.mmm.Constants;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.R;
import com.fox2code.mmm.XHooks;
import com.fox2code.mmm.utils.IntentHelper;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import java.io.ByteArrayInputStream;
@ -74,10 +72,6 @@ public final class AndroidacyActivity extends FoxActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
this.moduleFile = new File(this.getCacheDir(), "module.zip");
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// per process webview data dir
WebView.setDataDirectorySuffix(FoxApplication.getProcessName());
}
Intent intent = this.getIntent();
Uri uri;
if (!MainApplication.checkSecret(intent) || (uri = intent.getData()) == null) {
@ -119,6 +113,13 @@ public final class AndroidacyActivity extends FoxActivity {
device_id = AndroidacyRepoData.generateDeviceId();
url = url + "&device_id=" + device_id;
}
// check if client_id is present
String client_id = uri.getQueryParameter("client_id");
if (client_id == null) {
// get from shared preferences
client_id = BuildConfig.ANDROIDACY_CLIENT_ID;
url = url + "&client_id=" + client_id;
}
boolean allowInstall = intent.getBooleanExtra(Constants.EXTRA_ANDROIDACY_ALLOW_INSTALL, false);
String title = intent.getStringExtra(Constants.EXTRA_ANDROIDACY_ACTIONBAR_TITLE);
String config = intent.getStringExtra(Constants.EXTRA_ANDROIDACY_ACTIONBAR_CONFIG);

@ -18,8 +18,8 @@ import com.fox2code.mmm.manager.ModuleInfo;
import com.fox2code.mmm.repo.RepoData;
import com.fox2code.mmm.repo.RepoManager;
import com.fox2code.mmm.repo.RepoModule;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.HttpException;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.io.net.HttpException;
import com.fox2code.mmm.utils.io.PropUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.topjohnwu.superuser.Shell;
@ -44,7 +44,7 @@ import timber.log.Timber;
@SuppressWarnings("KotlinInternalInJava")
public final class AndroidacyRepoData extends RepoData {
public static String token = MainApplication.getSharedPreferences("androidacy").getString("pref_androidacy_api_token", null);
public static String token = MainApplication.getPreferences("androidacy").getString("pref_androidacy_api_token", null);
static {
HttpUrl.Builder OK_HTTP_URL_BUILDER = new HttpUrl.Builder().scheme("https");
@ -55,7 +55,7 @@ public final class AndroidacyRepoData extends RepoData {
@SuppressWarnings("unused")
public final String ClientID = BuildConfig.ANDROIDACY_CLIENT_ID;
public final SharedPreferences cachedPreferences = MainApplication.getSharedPreferences("androidacy");
public final SharedPreferences cachedPreferences = MainApplication.getPreferences("androidacy");
private final boolean testMode;
private final String host;
public String[][] userInfo = new String[][]{{"role", null}, {"permissions", null}};
@ -89,7 +89,7 @@ public final class AndroidacyRepoData extends RepoData {
// limiting and fraud detection.
public static String generateDeviceId() {
// Try to get the device ID from the shared preferences
SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("androidacy");
SharedPreferences sharedPreferences = MainApplication.getPreferences("androidacy");
String deviceIdPref = sharedPreferences.getString("device_id", null);
if (deviceIdPref != null) {
return deviceIdPref;
@ -145,7 +145,7 @@ public final class AndroidacyRepoData extends RepoData {
public boolean isValidToken(String token) throws IOException {
String deviceId = generateDeviceId();
try {
byte[] resp = Http.doHttpGet("https://" + this.host + "/auth/me?token=" + token + "&device_id=" + deviceId, false);
byte[] resp = Http.doHttpGet("https://" + this.host + "/auth/me?token=" + token + "&device_id=" + deviceId + "&client_id=" + BuildConfig.ANDROIDACY_CLIENT_ID, false);
// response is JSON
JSONObject jsonObject = new JSONObject(new String(resp));
memberLevel = jsonObject.getString("role");
@ -182,7 +182,7 @@ public final class AndroidacyRepoData extends RepoData {
protected boolean prepare() {
// If ANDROIDACY_CLIENT_ID is not set or is empty, disable this repo and return
if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) {
SharedPreferences.Editor editor = MainApplication.getSharedPreferences("mmm").edit();
SharedPreferences.Editor editor = MainApplication.getPreferences("mmm").edit();
editor.putBoolean("pref_androidacy_repo_enabled", false);
editor.apply();
return false;

@ -60,7 +60,7 @@ public class BackgroundUpdateChecker extends Worker {
static void doCheck(Context context) {
// first, check if the user has enabled background update checking
if (!MainApplication.getSharedPreferences("mmm").getBoolean("pref_background_update_check", false)) {
if (!MainApplication.getPreferences("mmm").getBoolean("pref_background_update_check", false)) {
return;
}
if (MainApplication.getINSTANCE().isInForeground()) {
@ -68,7 +68,7 @@ public class BackgroundUpdateChecker extends Worker {
return;
}
// next, check if user requires wifi
if (MainApplication.getSharedPreferences("mmm").getBoolean("pref_background_update_check_wifi", true)) {
if (MainApplication.getPreferences("mmm").getBoolean("pref_background_update_check_wifi", true)) {
// check if wifi is connected
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
Network networkInfo = connectivityManager.getActiveNetwork();
@ -107,7 +107,7 @@ public class BackgroundUpdateChecker extends Worker {
continue;
// exclude all modules with id's stored in the pref pref_background_update_check_excludes
try {
if (MainApplication.getSharedPreferences("mmm").getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id))
if (MainApplication.getPreferences("mmm").getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id))
continue;
} catch (
Exception ignored) {
@ -127,7 +127,7 @@ public class BackgroundUpdateChecker extends Worker {
}
});
// check for app updates
if (MainApplication.getSharedPreferences("mmm").getBoolean("pref_background_update_check_app", false)) {
if (MainApplication.getPreferences("mmm").getBoolean("pref_background_update_check_app", false)) {
try {
boolean shouldUpdate = AppUpdateManager.getAppUpdateManager().checkUpdate(true);
if (shouldUpdate) {
@ -209,7 +209,7 @@ public class BackgroundUpdateChecker extends Worker {
public static void onMainActivityCreate(Context context) {
// Refuse to run if first_launch pref is not false
if (!Objects.equals(MainApplication.getSharedPreferences("mmm").getString("last_shown_setup", null), "v1"))
if (!Objects.equals(MainApplication.getPreferences("mmm").getString("last_shown_setup", null), "v1"))
return;
// create notification channel group
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

@ -31,7 +31,7 @@ import com.fox2code.mmm.utils.FastException;
import com.fox2code.mmm.utils.IntentHelper;
import com.fox2code.mmm.utils.io.Files;
import com.fox2code.mmm.utils.io.Hashes;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.io.PropUtils;
import com.fox2code.mmm.utils.sentry.SentryBreadcrumb;
import com.fox2code.mmm.utils.sentry.SentryMain;

@ -2,7 +2,7 @@ package com.fox2code.mmm.manager;
import com.fox2code.mmm.markdown.MarkdownUrlLinker;
import com.fox2code.mmm.utils.FastException;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.io.PropUtils;
import org.json.JSONObject;

@ -55,7 +55,7 @@ public final class ModuleManager extends SyncManager {
protected void scanInternal(@NonNull UpdateListener updateListener) {
// if last_shown_setup is not "v1", then refuse to continue
if (!MainApplication.getSharedPreferences("mmm").getString("last_shown_setup", "").equals("v1")) {
if (!MainApplication.getPreferences("mmm").getString("last_shown_setup", "").equals("v1")) {
return;
}
boolean firstScan = this.bootPrefs.getBoolean("mm_first_scan", true);

@ -24,7 +24,7 @@ import com.fox2code.mmm.R;
import com.fox2code.mmm.XHooks;
import com.fox2code.mmm.utils.BlurUtils;
import com.fox2code.mmm.utils.IntentHelper;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@ -263,46 +263,27 @@ public class MarkdownActivity extends FoxActivity {
}
private String parseAndroidVersion(int version) {
switch (version) {
case Build.VERSION_CODES.JELLY_BEAN:
return "4.1 JellyBean";
case Build.VERSION_CODES.JELLY_BEAN_MR1:
return "4.2 JellyBean";
case Build.VERSION_CODES.JELLY_BEAN_MR2:
return "4.3 JellyBean";
case Build.VERSION_CODES.KITKAT:
return "4.4 KitKat";
case Build.VERSION_CODES.KITKAT_WATCH:
return "4.4 KitKat Watch";
case Build.VERSION_CODES.LOLLIPOP:
return "5.0 Lollipop";
case Build.VERSION_CODES.LOLLIPOP_MR1:
return "5.1 Lollipop";
case Build.VERSION_CODES.M:
return "6.0 Marshmallow";
case Build.VERSION_CODES.N:
return "7.0 Nougat";
case Build.VERSION_CODES.N_MR1:
return "7.1 Nougat";
case Build.VERSION_CODES.O:
return "8.0 Oreo";
case Build.VERSION_CODES.O_MR1:
return "8.1 Oreo";
case Build.VERSION_CODES.P:
return "9.0 Pie";
case Build.VERSION_CODES.Q:
return "10 (Q)";
case Build.VERSION_CODES.R:
return "11 (R)";
case Build.VERSION_CODES.S:
return "12 (S)";
case Build.VERSION_CODES.S_V2:
return "12L";
case Build.VERSION_CODES.TIRAMISU:
return "13 Tiramisu";
default:
return "Sdk: " + version;
}
return switch (version) {
case Build.VERSION_CODES.JELLY_BEAN -> "4.1 JellyBean";
case Build.VERSION_CODES.JELLY_BEAN_MR1 -> "4.2 JellyBean";
case Build.VERSION_CODES.JELLY_BEAN_MR2 -> "4.3 JellyBean";
case Build.VERSION_CODES.KITKAT -> "4.4 KitKat";
case Build.VERSION_CODES.KITKAT_WATCH -> "4.4 KitKat Watch";
case Build.VERSION_CODES.LOLLIPOP -> "5.0 Lollipop";
case Build.VERSION_CODES.LOLLIPOP_MR1 -> "5.1 Lollipop";
case Build.VERSION_CODES.M -> "6.0 Marshmallow";
case Build.VERSION_CODES.N -> "7.0 Nougat";
case Build.VERSION_CODES.N_MR1 -> "7.1 Nougat";
case Build.VERSION_CODES.O -> "8.0 Oreo";
case Build.VERSION_CODES.O_MR1 -> "8.1 Oreo";
case Build.VERSION_CODES.P -> "9.0 Pie";
case Build.VERSION_CODES.Q -> "10 (Q)";
case Build.VERSION_CODES.R -> "11 (R)";
case Build.VERSION_CODES.S -> "12 (S)";
case Build.VERSION_CODES.S_V2 -> "12L";
case Build.VERSION_CODES.TIRAMISU -> "13 Tiramisu";
default -> "Sdk: " + version;
};
}
@Override

@ -15,7 +15,7 @@ import com.fox2code.mmm.manager.LocalModuleInfo;
import com.fox2code.mmm.manager.ModuleInfo;
import com.fox2code.mmm.repo.RepoModule;
import com.fox2code.mmm.utils.IntentHelper;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.io.PropUtils;
import java.util.Comparator;

@ -2,7 +2,7 @@ package com.fox2code.mmm.repo;
import android.content.SharedPreferences;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import org.json.JSONException;
import org.json.JSONObject;

@ -1,22 +1,20 @@
package com.fox2code.mmm.repo;
import android.content.Context;
import android.content.SharedPreferences;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.utils.io.PropUtils;
public class CustomRepoManager {
private static final boolean AUTO_RECOMPILE = true;
public static final int MAX_CUSTOM_REPOS = 5;
private final MainApplication mainApplication;
private static final boolean AUTO_RECOMPILE = true;
private final RepoManager repoManager;
private final String[] customRepos;
private int customReposCount;
boolean dirty;
private int customReposCount;
@SuppressWarnings("unused")
CustomRepoManager(MainApplication mainApplication, RepoManager repoManager) {
this.mainApplication = mainApplication;
this.repoManager = repoManager;
this.customRepos = new String[MAX_CUSTOM_REPOS];
this.customReposCount = 0;
@ -45,8 +43,8 @@ public class CustomRepoManager {
}
private SharedPreferences getSharedPreferences() {
return this.mainApplication.getSharedPreferences(
"mmm_custom_repos", Context.MODE_PRIVATE);
return MainApplication.getPreferences(
"mmm_custom_repos");
}
public CustomRepoData addRepo(String repo) {

@ -2,7 +2,6 @@ package com.fox2code.mmm.repo;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
@ -21,7 +20,7 @@ import com.fox2code.mmm.manager.ModuleInfo;
import com.fox2code.mmm.utils.SyncManager;
import com.fox2code.mmm.utils.io.Files;
import com.fox2code.mmm.utils.io.Hashes;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.io.PropUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@ -148,7 +147,7 @@ public final class RepoManager extends SyncManager {
@SuppressWarnings("StatementWithEmptyBody")
private void populateDefaultCache(RepoData repoData) {
// if last_shown_setup is not "v1", them=n refuse to continue
if (!MainApplication.getSharedPreferences("mmm").getString("last_shown_setup", "").equals("v1")) {
if (!MainApplication.getPreferences("mmm").getString("last_shown_setup", "").equals("v1")) {
return;
}
// make sure repodata is not null
@ -372,7 +371,7 @@ public final class RepoManager extends SyncManager {
private RepoData addRepoData(String url, String fallBackName) {
String id = internalIdOfUrl(url);
File cacheRoot = new File(this.mainApplication.getDataDir(), "repos/" + id);
SharedPreferences sharedPreferences = this.mainApplication.getSharedPreferences("mmm_" + id, Context.MODE_PRIVATE);
SharedPreferences sharedPreferences = MainApplication.getPreferences("mmm_" + id);
RepoData repoData = id.startsWith("repo_") ? new CustomRepoData(url, cacheRoot, sharedPreferences) : new RepoData(url, cacheRoot, sharedPreferences);
if (fallBackName != null && !fallBackName.isEmpty()) {
repoData.defaultName = fallBackName;
@ -395,7 +394,7 @@ public final class RepoManager extends SyncManager {
private AndroidacyRepoData addAndroidacyRepoData() {
// cache dir is actually under app data
File cacheRoot = this.mainApplication.getDataDirWithPath("realms/repos/androidacy_repo");
SharedPreferences sharedPreferences = this.mainApplication.getSharedPreferences("mmm_androidacy_repo", Context.MODE_PRIVATE);
SharedPreferences sharedPreferences = MainApplication.getPreferences("mmm_androidacy_repo");
AndroidacyRepoData repoData = new AndroidacyRepoData(cacheRoot, sharedPreferences, MainApplication.isAndroidacyTestMode());
this.repoData.put(ANDROIDACY_MAGISK_REPO_ENDPOINT, repoData);
this.repoData.put(ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT, repoData);

@ -1,7 +1,7 @@
package com.fox2code.mmm.repo;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.realm.ModuleListCache;
import com.fox2code.mmm.utils.realm.ReposList;

@ -66,7 +66,7 @@ import com.fox2code.mmm.repo.RepoManager;
import com.fox2code.mmm.utils.ExternalHelper;
import com.fox2code.mmm.utils.IntentHelper;
import com.fox2code.mmm.utils.ProcessHelper;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.fox2code.mmm.utils.realm.ReposList;
import com.fox2code.mmm.utils.sentry.SentryMain;
import com.fox2code.rosettax.LanguageActivity;
@ -457,7 +457,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// set the box to unchecked
((SwitchPreferenceCompat) backgroundUpdateCheck).setChecked(false);
// ensure that the preference is false
MainApplication.getSharedPreferences("mmm").edit().putBoolean("pref_background_update_check", false).apply();
MainApplication.getPreferences("mmm").edit().putBoolean("pref_background_update_check", false).apply();
new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.permission_notification_title).setMessage(R.string.permission_notification_message).setPositiveButton(R.string.ok, (dialog, which) -> {
// Open the app settings
Intent intent = new Intent();
@ -494,7 +494,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
int i = 0;
for (LocalModuleInfo localModuleInfo : localModuleInfos) {
moduleNames[i] = localModuleInfo.name;
SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm");
SharedPreferences sharedPreferences = MainApplication.getPreferences("mmm");
// get the stringset pref_background_update_check_excludes
Set<String> stringSet = sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>());
// Stringset uses id, we show name
@ -504,7 +504,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
}
new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.background_update_check_excludes).setMultiChoiceItems(moduleNames, checkedItems, (dialog, which, isChecked) -> {
// get the stringset pref_background_update_check_excludes
SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm");
SharedPreferences sharedPreferences = MainApplication.getPreferences("mmm");
Set<String> stringSet = new HashSet<>(sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>()));
// get id from name
String id;
@ -592,11 +592,11 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
if (devModeStep == 2) {
devModeStep = 0;
if (MainApplication.isDeveloper() && !BuildConfig.DEBUG) {
MainApplication.getSharedPreferences("mmm").edit().putBoolean("developer", false).apply();
MainApplication.getPreferences("mmm").edit().putBoolean("developer", false).apply();
Toast.makeText(getContext(), // Tell the user something changed
R.string.dev_mode_disabled, Toast.LENGTH_SHORT).show();
} else {
MainApplication.getSharedPreferences("mmm").edit().putBoolean("developer", true).apply();
MainApplication.getPreferences("mmm").edit().putBoolean("developer", true).apply();
Toast.makeText(getContext(), // Tell the user something changed
R.string.dev_mode_enabled, Toast.LENGTH_SHORT).show();
}
@ -808,7 +808,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// Use MaterialAlertDialogBuilder
new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.warning).setCancelable(false).setMessage(R.string.androidacy_test_mode_warning).setPositiveButton(android.R.string.ok, (dialog, which) -> {
// User clicked OK button
MainApplication.getSharedPreferences("mmm").edit().putBoolean("androidacy_test_mode", true).apply();
MainApplication.getPreferences("mmm").edit().putBoolean("androidacy_test_mode", true).apply();
// Check the switch
Intent mStartActivity = new Intent(requireContext(), MainActivity.class);
mStartActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
@ -826,10 +826,10 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
SwitchPreferenceCompat switchPreferenceCompat = (SwitchPreferenceCompat) androidacyTestMode;
switchPreferenceCompat.setChecked(false);
// There's probably a better way to do this than duplicate code but I'm too lazy to figure it out
MainApplication.getSharedPreferences("mmm").edit().putBoolean("androidacy_test_mode", false).apply();
MainApplication.getPreferences("mmm").edit().putBoolean("androidacy_test_mode", false).apply();
}).show();
} else {
MainApplication.getSharedPreferences("mmm").edit().putBoolean("androidacy_test_mode", false).apply();
MainApplication.getPreferences("mmm").edit().putBoolean("androidacy_test_mode", false).apply();
// Show dialog to restart app with ok button
new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.warning).setCancelable(false).setMessage(R.string.androidacy_test_mode_disable_warning).setNeutralButton(android.R.string.ok, (dialog, which) -> {
// User clicked OK button

@ -26,7 +26,7 @@ import com.fox2code.mmm.androidacy.AndroidacyActivity;
import com.fox2code.mmm.installer.InstallerActivity;
import com.fox2code.mmm.markdown.MarkdownActivity;
import com.fox2code.mmm.utils.io.Files;
import com.fox2code.mmm.utils.io.Http;
import com.fox2code.mmm.utils.io.net.Http;
import com.topjohnwu.superuser.CallbackList;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFileInputStream;

@ -1,86 +0,0 @@
package com.fox2code.mmm.utils.io;
import android.content.Context;
import android.util.Base64;
import androidx.annotation.NonNull;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.regex.Pattern;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import timber.log.Timber;
public class AddCookiesInterceptor implements Interceptor {
private final Context context;
public AddCookiesInterceptor(Context context) {
this.context = context;
}
@NonNull
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
String cookieFileName = "cookies";
String[] cookies;
// cookies are split by | so we can split the string into an array of cookies
File cookieFile = new File(context.getFilesDir(), cookieFileName);
if (cookieFile.exists()) {
// read the file into a string
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(cookieFile)));
char[] buf = new char[1024];
int read;
StringBuilder stringBuilder = new StringBuilder();
while ((read = bufferedReader.read(buf)) != -1) {
stringBuilder.append(buf, 0, read);
}
bufferedReader.close();
String cookieString = stringBuilder.toString();
// base64 decode the string
cookieString = Arrays.toString(Base64.decode(cookieString, Base64.DEFAULT));
// split the string into an array of cookies
cookies = cookieString.split("\\|");
} else {
cookies = new String[0];
}
for (String cookie : cookies) {
// ensure the cookie applies to the current domain
if (cookie.contains("domain=")) {
// match from the start of the string to the first semicolon
try {
Pattern pattern = Pattern.compile("domain=([^;]+)");
String domain = pattern.matcher(cookie).group(1);
if (domain != null && !chain.request().url().host().contains(domain)) {
//noinspection UnnecessaryContinue
continue;
} else {
// yeet any newlines from the cookie
cookie = cookie.replaceAll("[\\r\\n]", "");
builder.addHeader("Cookie", cookie);
}
} catch (
Exception ignored) {
}
} else {
try {
builder.addHeader("Cookie", cookie);
} catch (Exception e) {
Timber.e(e, "Error adding cookie to request: %s", cookie);
}
}
}
return chain.proceed(builder.build());
}
}

@ -1,99 +0,0 @@
package com.fox2code.mmm.utils.io;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Base64;
import androidx.annotation.NonNull;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import okhttp3.Interceptor;
import okhttp3.Response;
import timber.log.Timber;
public class ReceivedCookiesInterceptor implements Interceptor {
private final Context context;
public ReceivedCookiesInterceptor(Context context) {
this.context = context;
} // AddCookiesInterceptor()
@NonNull
@SuppressLint({"MutatingSharedPrefs", "ApplySharedPref"})
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
StringBuilder cookieBuffer = new StringBuilder();
for (String header : originalResponse.headers("Set-Cookie")) {
cookieBuffer.append(header).append("|");
} // for
int lastPipe = cookieBuffer.lastIndexOf("|");
if (lastPipe > 0) {
cookieBuffer.deleteCharAt(lastPipe);
}
String cookieFileName = "cookies";
String[] cookies = new String[0];
try {
File cookieFile = new File(context.getFilesDir(), cookieFileName);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(cookieFile)));
char[] buf = new char[1024];
int read;
StringBuilder stringBuilder = new StringBuilder();
while ((read = bufferedReader.read(buf)) != -1) {
stringBuilder.append(buf, 0, read);
}
bufferedReader.close();
String cookieString = stringBuilder.toString();
// base64 decode the string
cookieString = Arrays.toString(Base64.decode(cookieString, Base64.DEFAULT));
// split the string into an array of cookies
cookies = cookieString.split("\\|");
} catch (Exception e) {
Timber.e(e, "Error reading cookies from file");
}
// Logic to merge our cookies with received cookies
// We need to check if the cookie we received is already in our file
// If it is, we need to replace it with the new one
// If it isn't, we need to add it to the end of the file
HashSet<String> cookieSet = new HashSet<>(Arrays.asList(cookies));
String[] newCookies = cookieBuffer.toString().split("\\|");
for (String cookie : newCookies) {
cookieSet.remove(cookie);
cookieSet.add(cookie);
}
// convert the set back into a string
StringBuilder newCookieBuffer = new StringBuilder();
for (String cookie : cookieSet) {
newCookieBuffer.append(cookie).append("|");
}
// remove the last pipe
lastPipe = newCookieBuffer.lastIndexOf("|");
if (lastPipe > 0) {
newCookieBuffer.deleteCharAt(lastPipe);
}
// write the new cookies to the file
File cookieFile = new File(context.getFilesDir(), cookieFileName);
FileOutputStream fileOutputStream = new FileOutputStream(cookieFile);
fileOutputStream.write(newCookieBuffer.toString().getBytes());
fileOutputStream.close();
}
return originalResponse;
}
}

@ -1,4 +1,4 @@
package com.fox2code.mmm.utils.io;
package com.fox2code.mmm.utils.io.net;
import android.annotation.SuppressLint;
import android.content.Context;
@ -18,6 +18,7 @@ import com.fox2code.mmm.MainActivity;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.androidacy.AndroidacyUtil;
import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.utils.io.Files;
import com.google.net.cronet.okhttptransport.CronetInterceptor;
import org.chromium.net.CronetEngine;
@ -42,8 +43,6 @@ import java.util.concurrent.TimeUnit;
import io.sentry.android.okhttp.SentryOkHttpInterceptor;
import okhttp3.Cache;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.Dns;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
@ -111,16 +110,14 @@ public enum Http {
return Dns.SYSTEM.lookup(s);
};
httpclientBuilder.dns(dns);
httpclientBuilder.cookieJar(new CDNCookieJar());
WebkitCookieManagerProxy cookieJar = new WebkitCookieManagerProxy();
httpclientBuilder.cookieJar(cookieJar);
dns = new DnsOverHttps.Builder().client(httpclientBuilder.build()).url(Objects.requireNonNull(HttpUrl.parse("https://cloudflare-dns.com/dns-query"))).bootstrapDnsHosts(cloudflareBootstrap).resolvePrivateAddresses(true).build();
} catch (
UnknownHostException |
RuntimeException e) {
Timber.e(e, "Failed to init DoH");
}
// Add cookie support.
httpclientBuilder.addInterceptor(new AddCookiesInterceptor(MainApplication.getINSTANCE().getApplicationContext())); // VERY VERY IMPORTANT
httpclientBuilder.addInterceptor(new ReceivedCookiesInterceptor(MainApplication.getINSTANCE().getApplicationContext())); // VERY VERY IMPORTANT
// User-Agent format was agreed on telegram
if (hasWebView) {
androidacyUA = WebSettings.getDefaultUserAgent(mainApplication).replace("wv", "") + " FoxMMM/" + BuildConfig.VERSION_CODE;
@ -182,7 +179,6 @@ public enum Http {
}
// Fallback DNS cache responses in case request fail but already succeeded once in the past
fallbackDNS = new FallBackDNS(mainApplication, dns, "github.com", "api.github.com", "raw.githubusercontent.com", "camo.githubusercontent.com", "user-images.githubusercontent.com", "cdn.jsdelivr.net", "img.shields.io", "magisk-modules-repo.github.io", "www.androidacy.com", "api.androidacy.com", "production-api.androidacy.com");
httpclientBuilder.cookieJar(new CDNCookieJar(cookieManager));
httpclientBuilder.dns(Dns.SYSTEM);
httpClient = followRedirects(httpclientBuilder, true).build();
followRedirects(httpclientBuilder, false).build();
@ -414,95 +410,6 @@ public enum Http {
void onUpdate(int downloaded, int total, boolean done);
}
/**
* Cookie jar that allow CDN cookies, reset on app relaunch
* Note: An argument can be made that it allow tracking but
* caching is a better attack vector for tracking, this system
* only exist to improve CDN response time, any other cookies
* that are not CDN related are just simply ignored.
* <p>
* Note: CDNCookies are only stored in RAM unlike https cache
*/
private static class CDNCookieJar implements CookieJar {
private final HashMap<String, Cookie> cookieMap = new HashMap<>();
private final boolean androidacySupport;
private final CookieManager cookieManager;
private List<Cookie> androidacyCookies;
private CDNCookieJar() {
this.androidacySupport = false;
this.cookieManager = null;
}
private CDNCookieJar(CookieManager cookieManager) {
this.androidacySupport = true;
this.cookieManager = cookieManager;
if (cookieManager == null) {
this.androidacyCookies = Collections.emptyList();
}
}
@NonNull
@Override
public List<Cookie> loadForRequest(@NonNull HttpUrl httpUrl) {
if (!httpUrl.isHttps())
return Collections.emptyList();
if (this.androidacySupport && httpUrl.host().endsWith(".androidacy.com")) {
if (this.cookieManager == null)
return this.androidacyCookies;
String cookies = this.cookieManager.getCookie(httpUrl.uri().toString());
if (cookies == null || cookies.isEmpty())
return Collections.emptyList();
String[] splitCookies = cookies.split(";");
ArrayList<Cookie> cookieList = new ArrayList<>(splitCookies.length);
for (String cookie : splitCookies) {
cookieList.add(Cookie.parse(httpUrl, cookie));
}
return cookieList;
}
Cookie cookies = cookieMap.get(httpUrl.url().getHost());
return cookies == null || cookies.expiresAt() < System.currentTimeMillis() ? Collections.emptyList() : Collections.singletonList(cookies);
}
@Override
public void saveFromResponse(@NonNull HttpUrl httpUrl, @NonNull List<Cookie> cookies) {
if (!httpUrl.isHttps())
return;
if (this.androidacySupport && httpUrl.host().endsWith(".androidacy.com")) {
if (this.cookieManager == null) {
if (httpUrl.host().equals(".androidacy.com") || !cookies.isEmpty())
this.androidacyCookies = cookies;
return;
}
for (Cookie cookie : cookies) {
this.cookieManager.setCookie(httpUrl.uri().toString(), cookie.toString());
}
return;
}
String host = httpUrl.url().getHost();
Iterator<Cookie> cookieIterator = cookies.iterator();
Cookie cdnCookie = cookieMap.get(host);
while (cookieIterator.hasNext()) {
Cookie cookie = cookieIterator.next();
if (host.equals(cookie.domain()) && cookie.secure() && cookie.httpOnly() && cookie.expiresAt() < (System.currentTimeMillis() + 1000 * 60 * 60 * 48)) {
if (cdnCookie != null && !cdnCookie.name().equals(cookie.name())) {
cookieMap.remove(host);
cdnCookie = null;
break;
} else {
cdnCookie = cookie;
}
}
}
if (cdnCookie == null) {
cookieMap.remove(host);
} else {
cookieMap.put(host, cdnCookie);
}
}
}
/**
* FallBackDNS store successful DNS request to return them later
* can help make the app to work later when the current DNS system

@ -1,4 +1,4 @@
package com.fox2code.mmm.utils.io;
package com.fox2code.mmm.utils.io.net;
import androidx.annotation.Keep;
@ -23,14 +23,10 @@ public final class HttpException extends IOException {
}
public boolean shouldTimeout() {
switch (errorCode) {
case 419:
case 429:
case 503:
return true;
default:
return false;
}
return switch (errorCode) {
case 419, 429, 503 -> true;
default -> false;
};
}
public static boolean shouldTimeout(Exception exception) {

@ -0,0 +1,127 @@
package com.fox2code.mmm.utils.io.net;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.CookieStore;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import timber.log.Timber;
public class WebkitCookieManagerProxy extends CookieManager implements CookieJar {
private final android.webkit.CookieManager webkitCookieManager;
public WebkitCookieManagerProxy() {
this(null, null);
}
WebkitCookieManagerProxy(CookieStore ignoredStore, CookiePolicy cookiePolicy) {
super(null, cookiePolicy);
this.webkitCookieManager = android.webkit.CookieManager.getInstance();
}
@Override
public void put(URI uri, Map<String, List<String>> responseHeaders)
throws IOException {
// make sure our args are valid
if ((uri == null) || (responseHeaders == null))
return;
// save our url once
String url = uri.toString();
// go over the headers
for (String headerKey : responseHeaders.keySet()) {
// ignore headers which aren't cookie related
if ((headerKey == null)
|| !(headerKey.equalsIgnoreCase("Set-Cookie2") || headerKey
.equalsIgnoreCase("Set-Cookie")))
continue;
// process each of the headers
for (String headerValue : Objects.requireNonNull(responseHeaders.get(headerKey))) {
webkitCookieManager.setCookie(url, headerValue);
}
}
}
@Override
public Map<String, List<String>> get(URI uri,
Map<String, List<String>> requestHeaders) throws IOException {
// make sure our args are valid
if ((uri == null) || (requestHeaders == null))
throw new IllegalArgumentException("Argument is null");
// save our url once
String url = uri.toString();
// prepare our response
Map<String, List<String>> res = new java.util.HashMap<>();
// get the cookie
String cookie = webkitCookieManager.getCookie(url);
// return it
if (cookie != null) {
res.put("Cookie", List.of(cookie));
}
return res;
}
@Override
public CookieStore getCookieStore() {
// we don't want anyone to work with this cookie store directly
throw new UnsupportedOperationException();
}
@Override
public void saveFromResponse(@NonNull HttpUrl url, List<Cookie> cookies) {
HashMap<String, List<String>> generatedResponseHeaders = new HashMap<>();
ArrayList<String> cookiesList = new ArrayList<>();
for (Cookie c : cookies) {
// toString correctly generates a normal cookie string
cookiesList.add(c.toString());
}
generatedResponseHeaders.put("Set-Cookie", cookiesList);
try {
put(url.uri(), generatedResponseHeaders);
} catch (IOException e) {
Timber.e(e, "Error adding cookies through okhttp");
}
}
@NonNull
@Override
public List<Cookie> loadForRequest(HttpUrl url) {
ArrayList<Cookie> cookieArrayList = new ArrayList<>();
try {
Map<String, List<String>> cookieList = get(url.uri(), new HashMap<>());
// Format here looks like: "Cookie":["cookie1=val1;cookie2=val2;"]
for (List<String> ls : cookieList.values()) {
for (String s : ls) {
String[] cookies = s.split(";");
for (String cookie : cookies) {
Cookie c = Cookie.parse(url, cookie);
cookieArrayList.add(c);
}
}
}
} catch (IOException e) {
Timber.e(e, "error making cookie!");
}
return cookieArrayList;
}
}

@ -28,7 +28,7 @@ public class SentryMain {
@SuppressLint({"RestrictedApi", "UnspecifiedImmutableFlag"})
public static void initialize(final MainApplication mainApplication) {
// If first_launch pref is not false, refuse to initialize Sentry
SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("sentry");
SharedPreferences sharedPreferences = MainApplication.getPreferences("sentry");
if (!Objects.equals(sharedPreferences.getString("last_shown_setup", null), "v1")) {
return;
}
@ -81,7 +81,7 @@ public class SentryMain {
// With this callback, you can modify the event or, when returning null, also discard the event.
options.setBeforeSend((event, hint) -> {
// Save lastEventId to private shared preferences
SharedPreferences sentryPrefs = MainApplication.getSharedPreferences("sentry");
SharedPreferences sentryPrefs = MainApplication.getPreferences("sentry");
String lastEventId = Objects.requireNonNull(event.getEventId()).toString();
SharedPreferences.Editor editor = sentryPrefs.edit();
editor.putString("lastEventId", lastEventId);

@ -4,6 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:id="setupScrollView"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".SetupActivity"
@ -194,14 +195,14 @@
android:layout_height="wrap_content"
android:layout_marginEnd="2dp"
android:layout_weight="1"
android:text="@string/cancel" android:textColor="#5D4037" />
android:text="@string/cancel" />
<com.google.android.material.button.MaterialButton
android:id="@+id/setup_continue"
android:id="@+id/setup_finish"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/finish" android:textColor="#5D4037" />
android:text="@string/finish" />
</LinearLayout>
</LinearLayout>

@ -8,7 +8,7 @@ buildscript {
url 'https://jitpack.io'
}
}
project.ext.latestAboutLibsRelease = "10.5.2"
project.ext.latestAboutLibsRelease = "10.6.1"
project.ext.sentryConfigFile = new File(rootDir, "sentry.properties").getAbsoluteFile()
project.ext.hasSentryConfig = sentryConfigFile.exists()
project.ext.sentryCli = [
@ -16,7 +16,7 @@ buildscript {
flavorAware: true
]
project.ext.kotlin_version = "1.8.0"
project.ext.sentry_version = "6.14.0"
project.ext.sentry_version = "6.16.0"
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10"

Loading…
Cancel
Save