add module update exclusions

closes #272

also, updates dependencies and starts work on #170

also cleans up code some

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/284/head
androidacy-user 1 year ago
parent d9ebb2a2c4
commit c17db63594

@ -57,6 +57,9 @@ android {
versionCode 64
versionName "2.0.0-beta.1"
archivesBaseName = "FoxMMM-v$versionName"
vectorDrawables {
useSupportLibrary true
}
}
splits {
@ -281,15 +284,15 @@ configurations {
dependencies {
// UI
implementation 'androidx.appcompat:appcompat:1.6.0'
implementation "androidx.activity:activity-ktx:1.7.0-alpha03"
implementation "androidx.activity:activity-ktx:1.7.0-alpha04"
implementation 'androidx.emoji2:emoji2:1.2.0'
implementation 'androidx.emoji2:emoji2-views-helper:1.2.0'
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.webkit:webkit:1.5.0'
implementation 'com.google.android.material:material:1.7.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.insets:insets:1.3.0"
implementation 'com.github.Dimezis:BlurView:version-2.0.2'
@ -338,6 +341,17 @@ dependencies {
implementation "com.google.devtools.ksp:symbol-processing-api:1.8.0-1.0.8"
implementation "androidx.security:security-crypto:1.1.0-alpha04"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
implementation 'androidx.activity:activity-compose:1.6.1'
implementation platform('androidx.compose:compose-bom:2022.10.00')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
androidTestImplementation platform('androidx.compose:compose-bom:2022.10.00')
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
}
if (hasSentryConfig) {
@ -361,13 +375,22 @@ android {
}
buildFeatures {
viewBinding true
compose true
}
kotlinOptions {
jvmTarget=JavaVersion.VERSION_11
jvmTarget= '1.8'
}
//noinspection GrDeprecatedAPIUsage
buildToolsVersion '33.0.1'
composeOptions {
kotlinCompilerExtensionVersion '1.3.2'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}

@ -23,9 +23,9 @@
<application
android:name=".MainApplication"
android:enableOnBackInvokedCallback="true"
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
android:fullBackupContent="@xml/full_backup_content"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
@ -39,9 +39,12 @@
tools:replace="android:supportsRtl"
tools:targetApi="tiramisu">
<activity
android:name=".CrashHandler"
android:process=":crash"
android:name=".UpdateActivity"
android:exported="false" />
<activity
android:name=".CrashHandler"
android:exported="false"
android:process=":crash" />
<activity
android:name=".SetupActivity"
android:exported="false"

@ -1,6 +1,8 @@
package com.fox2code.mmm;
public class Constants {
@SuppressWarnings("unused")
public enum Constants {
;
public static final int MAGISK_VER_CODE_FLAT_MODULES = 19000;
public static final int MAGISK_VER_CODE_UTIL_INSTALL = 20400;
public static final int MAGISK_VER_CODE_PATH_SUPPORT = 21000;

@ -214,7 +214,7 @@ public enum NotificationType implements NotificationTypeCst {
}
NotificationType(@StringRes int textId, int iconId, int backgroundAttr, int foregroundAttr,
View.OnClickListener onClickListener, boolean special) {
View.OnClickListener onClickListener, @SuppressWarnings("SameParameterValue") boolean special) {
this.textId = textId;
this.iconId = iconId;
this.backgroundAttr = backgroundAttr;

@ -0,0 +1,14 @@
package com.fox2code.mmm;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class UpdateActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_update);
}
}

@ -16,8 +16,11 @@ import java.util.Collection;
* Class made to expose some manager functions to xposed modules.
* It will not be obfuscated on release builds
*/
@SuppressWarnings("unused")
@Keep
public class XHooks {
public enum XHooks {
;
@Keep
public static void onRepoManagerInitialize() {
// Call addXRepo here if you are an XPosed module

@ -41,6 +41,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import timber.log.Timber;
@ -149,13 +151,17 @@ public final class AndroidacyActivity extends FoxActivity {
webSettings.setUserAgentString(Http.getAndroidacyUA());
webSettings.setDomStorageEnabled(true);
webSettings.setJavaScriptEnabled(true);
// Disable cache
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
webSettings.setAllowFileAccess(false);
webSettings.setAllowContentAccess(false);
webSettings.setAllowFileAccessFromFileURLs(false);
webSettings.setAllowUniversalAccessFromFileURLs(false);
webSettings.setMediaPlaybackRequiresUserGesture(false);
// Attempt at fixing CloudFlare captcha.
if (WebViewFeature.isFeatureSupported(WebViewFeature.REQUESTED_WITH_HEADER_CONTROL)) {
WebSettingsCompat.setRequestedWithHeaderMode(webSettings, WebSettingsCompat.REQUESTED_WITH_HEADER_MODE_NO_HEADER);
if (WebViewFeature.isFeatureSupported(WebViewFeature.REQUESTED_WITH_HEADER_ALLOW_LIST)) {
Set<String> allowList = new HashSet<>();
allowList.add("https://*.androidacy.com");
WebSettingsCompat.setRequestedWithHeaderOriginAllowList(webSettings, allowList);
}
this.webView.setWebViewClient(new WebViewClientCompat() {

@ -56,7 +56,7 @@ public final class AndroidacyRepoData extends RepoData {
public final String ClientID = BuildConfig.ANDROIDACY_CLIENT_ID;
private final boolean testMode;
private final String host;
public SharedPreferences cachedPreferences = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0);
public final SharedPreferences cachedPreferences = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0);
public String memberLevel;
// Avoid spamming requests to Androidacy
private long androidacyBlockade = 0;

@ -7,7 +7,8 @@ import androidx.annotation.Nullable;
import com.fox2code.mmm.BuildConfig;
public class AndroidacyUtil {
public enum AndroidacyUtil {
;
public static final String REFERRER = "utm_source=FoxMMM&utm_medium=app";
public static boolean isAndroidacyLink(@Nullable Uri uri) {

@ -36,6 +36,7 @@ import java.nio.charset.StandardCharsets;
import timber.log.Timber;
@SuppressWarnings("SameReturnValue")
@Keep
public class AndroidacyWebAPI {
public static final int COMPAT_UNSUPPORTED = 0;

@ -7,10 +7,10 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationChannelCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.work.Constraints;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.NetworkType;
@ -52,6 +52,9 @@ public class BackgroundUpdateChecker extends Worker {
for (LocalModuleInfo localModuleInfo : ModuleManager.getINSTANCE().getModules().values()) {
if ("twrp-keep".equals(localModuleInfo.id))
continue;
// exclude all modules with id's stored in the pref pref_background_update_check_excludes
if (MainApplication.getSharedPreferences().getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id))
continue;
RepoModule repoModule = repoModules.get(localModuleInfo.id);
localModuleInfo.checkModuleUpdate();
if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode && !PropUtils.isNullString(localModuleInfo.updateVersion)) {
@ -61,20 +64,20 @@ public class BackgroundUpdateChecker extends Worker {
}
}
if (moduleUpdateCount != 0) {
postNotification(context, moduleUpdateCount);
postNotification(context, moduleUpdateCount, false);
}
});
}
public static void postNotification(Context context, int updateCount) {
public static void postNotification(Context context, int updateCount, boolean test) {
if (!easterEggActive)
easterEggActive = new Random().nextInt(100) <= updateCount;
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID).setContentTitle(context.getString(easterEggActive ? R.string.notification_update_title_easter_egg : R.string.notification_update_title).replace("%i", String.valueOf(updateCount))).setContentText(context.getString(R.string.notification_update_subtitle)).setSmallIcon(R.drawable.ic_baseline_extension_24).setPriority(NotificationCompat.PRIORITY_HIGH).setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK), PendingIntent.FLAG_IMMUTABLE)).setAutoCancel(true);
if (ActivityCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
if (ContextCompat.checkSelfPermission(MainApplication.getINSTANCE(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
return;
}
// check if app is in foreground. if so, don't show notification
if (MainApplication.getINSTANCE().isInForeground())
if (MainApplication.getINSTANCE().isInForeground() && !test)
return;
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, builder.build());
}

@ -57,7 +57,6 @@ import java.util.zip.ZipInputStream;
import timber.log.Timber;
@SuppressWarnings("IOStreamConstructor")
public class InstallerActivity extends FoxActivity {
public LinearProgressIndicator progressIndicator;
public ExtendedFloatingActionButton rebootFloatingButton;

@ -4,7 +4,8 @@ import java.util.ArrayList;
import timber.log.Timber;
public class MarkdownUrlLinker {
public enum MarkdownUrlLinker {
;
public static String urlLinkify(String url) {
int index = url.indexOf("https://");

@ -123,6 +123,8 @@ public class RepoData extends XRepo {
if (this.enabled) {
try {
this.metaDataCache = ModuleListCache.getRepoModulesAsJson(this.id);
// log count of modules in the database
Timber.d("RepoData: " + this.id + ". modules in database: " + this.metaDataCache.length());
// load repo metadata from ReposList unless it's a built-in repo
if (RepoManager.isBuiltInRepo(this.id)) {
this.name = this.defaultName;

@ -11,6 +11,7 @@ import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
@ -51,6 +52,8 @@ import com.fox2code.mmm.R;
import com.fox2code.mmm.androidacy.AndroidacyRepoData;
import com.fox2code.mmm.background.BackgroundUpdateChecker;
import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.manager.LocalModuleInfo;
import com.fox2code.mmm.manager.ModuleManager;
import com.fox2code.mmm.module.ActionButtonType;
import com.fox2code.mmm.repo.CustomRepoData;
import com.fox2code.mmm.repo.CustomRepoManager;
@ -66,6 +69,7 @@ import com.fox2code.rosettax.LanguageActivity;
import com.fox2code.rosettax.LanguageSwitcher;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.internal.TextWatcherAdapter;
import com.google.android.material.snackbar.BaseTransientBottomBar;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.MaterialAutoCompleteTextView;
import com.mikepenz.aboutlibraries.LibsBuilder;
@ -78,9 +82,12 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Locale;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import io.realm.Realm;
import io.realm.RealmConfiguration;
@ -395,11 +402,11 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
findPreference("pref_use_magisk_install_command").setVisible(false);
}
Preference debugNotification = findPreference("pref_background_update_check_debug");
Preference updateCheckExcludes = findPreference("pref_background_update_check_excludes");
debugNotification.setEnabled(MainApplication.isBackgroundUpdateCheckEnabled());
debugNotification.setVisible(MainApplication.isDeveloper() && !MainApplication.isWrapped());
debugNotification.setVisible(MainApplication.isDeveloper() && !MainApplication.isWrapped());
debugNotification.setVisible(MainApplication.isDeveloper() && !MainApplication.isWrapped() && MainApplication.isBackgroundUpdateCheckEnabled());
debugNotification.setOnPreferenceClickListener(preference -> {
BackgroundUpdateChecker.postNotification(this.requireContext(), new Random().nextInt(4) + 2);
BackgroundUpdateChecker.postNotification(this.requireContext(), new Random().nextInt(4) + 2, true);
return true;
});
Preference backgroundUpdateCheck = findPreference("pref_background_update_check");
@ -426,29 +433,57 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
});
backgroundUpdateCheck.setSummary(R.string.background_update_check_permission_required);
}
EditTextPreference updateCheckExcludes = findPreference("pref_background_update_check_excludes");
updateCheckExcludes.setVisible(MainApplication.isBackgroundUpdateCheckEnabled() && !MainApplication.isWrapped());
backgroundUpdateCheck.setOnPreferenceChangeListener((preference, newValue) -> {
boolean enabled = Boolean.parseBoolean(String.valueOf(newValue));
debugNotification.setEnabled(enabled);
debugNotification.setVisible(MainApplication.isDeveloper() && !MainApplication.isWrapped() && enabled);
updateCheckExcludes.setEnabled(enabled);
updateCheckExcludes.setVisible(enabled && !MainApplication.isWrapped());
if (!enabled) {
BackgroundUpdateChecker.onMainActivityResume(this.requireContext());
}
return true;
});
// updateCheckExcludes is an EditTextPreference. on change, validate it contains only alphanumerical and , - _ characters
updateCheckExcludes.setOnPreferenceChangeListener((preference, newValue) -> {
String value = String.valueOf(newValue);
// strip whitespace
value = value.replaceAll("\\s", "");
if (value.matches("^[a-zA-Z0-9,\\-_]*$")) {
return true;
} else {
new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.invalid_excludes).setMessage(R.string.invalid_characters_message).setPositiveButton(R.string.ok, (dialog, which) -> {
}).show();
return false;
// updateCheckExcludes saves to pref_background_update_check_excludes as a stringset. On clicking, it should open a dialog with a list of all installed modules
updateCheckExcludes.setOnPreferenceClickListener(preference -> {
Collection<LocalModuleInfo> localModuleInfos = ModuleManager.getINSTANCE().getModules().values();
String[] moduleNames = new String[localModuleInfos.size()];
boolean[] checkedItems = new boolean[localModuleInfos.size()];
int i = 0;
for (LocalModuleInfo localModuleInfo : localModuleInfos) {
moduleNames[i] = localModuleInfo.name;
SharedPreferences sharedPreferences = MainApplication.getSharedPreferences();
// 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
checkedItems[i] = stringSet.contains(localModuleInfo.id);
Timber.d("name: %s, checked: %s", moduleNames[i], checkedItems[i]);
i++;
}
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();
Set<String> stringSet = new HashSet<>(sharedPreferences.getStringSet("pref_background_update_check_excludes", new HashSet<>()));
// get id from name
String id;
if (localModuleInfos.stream().anyMatch(localModuleInfo -> localModuleInfo.name.equals(moduleNames[which]))) {
//noinspection OptionalGetWithoutIsPresent
id = localModuleInfos.stream().filter(localModuleInfo -> localModuleInfo.name.equals(moduleNames[which])).findFirst().get().id;
} else {
id = "";
}
if (!id.isEmpty()) {
if (isChecked) {
stringSet.add(id);
} else {
stringSet.remove(id);
}
}
sharedPreferences.edit().putStringSet("pref_background_update_check_excludes", stringSet).apply();
}).setPositiveButton(R.string.ok, (dialog, which) -> {
}).show();
return true;
});
final LibsBuilder libsBuilder = new LibsBuilder().withShowLoadingProgress(false).withLicenseShown(true).withAboutMinimalDesign(false);
ClipboardManager clipboard = (ClipboardManager) requireContext().getSystemService(Context.CLIPBOARD_SERVICE);
@ -851,7 +886,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// validate the api key client side first. should be 64 characters long, and only allow alphanumeric characters
if (!newValue.toString().matches("[a-zA-Z0-9]{64}")) {
// Show snack bar with error
Snackbar.make(requireView(), R.string.api_key_mismatch, Snackbar.LENGTH_LONG).show();
Snackbar.make(requireView(), R.string.api_key_mismatch, BaseTransientBottomBar.LENGTH_LONG).show();
// Restore the original api key
prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]);
prefAndroidacyRepoApiKey.performClick();
@ -863,7 +898,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// get original api key
String apiKey = String.valueOf(newValue);
// Show snack bar with indeterminate progress
Snackbar.make(requireView(), R.string.checking_api_key, Snackbar.LENGTH_INDEFINITE).setAction(R.string.cancel, v -> {
Snackbar.make(requireView(), R.string.checking_api_key, BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(R.string.cancel, v -> {
// Restore the original api key
prefAndroidacyRepoApiKey.setText(originalApiKeyRef[0]);
}).show();
@ -873,7 +908,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
if (apiKey.isEmpty()) {
MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().remove("pref_androidacy_api_token").apply();
new Handler(Looper.getMainLooper()).post(() -> {
Snackbar.make(requireView(), R.string.api_key_removed, Snackbar.LENGTH_SHORT).show();
Snackbar.make(requireView(), R.string.api_key_removed, BaseTransientBottomBar.LENGTH_SHORT).show();
// Show dialog to restart app with ok button
new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.restart).setCancelable(false).setMessage(R.string.api_key_restart).setNeutralButton(android.R.string.ok, (dialog, which) -> {
// User clicked OK button
@ -893,7 +928,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
// If key < 64 chars, it's not valid
if (apiKey.length() < 64) {
new Handler(Looper.getMainLooper()).post(() -> {
Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show();
Snackbar.make(requireView(), R.string.api_key_invalid, BaseTransientBottomBar.LENGTH_SHORT).show();
// Save the original key
MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().putString("pref_androidacy_api_token", originalApiKeyRef[0]).apply();
// Re-show the dialog with an error
@ -904,7 +939,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
} else {
// If the key is the same as the original, just show a snack bar
if (apiKey.equals(originalApiKeyRef[0])) {
new Handler(Looper.getMainLooper()).post(() -> Snackbar.make(requireView(), R.string.api_key_unchanged, Snackbar.LENGTH_SHORT).show());
new Handler(Looper.getMainLooper()).post(() -> Snackbar.make(requireView(), R.string.api_key_unchanged, BaseTransientBottomBar.LENGTH_SHORT).show());
return;
}
boolean valid = false;
@ -921,7 +956,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().putString("pref_androidacy_api_token", apiKey).apply();
// Snackbar with success and restart button
new Handler(Looper.getMainLooper()).post(() -> {
Snackbar.make(requireView(), R.string.api_key_valid, Snackbar.LENGTH_SHORT).show();
Snackbar.make(requireView(), R.string.api_key_valid, BaseTransientBottomBar.LENGTH_SHORT).show();
// Show dialog to restart app with ok button
new MaterialAlertDialogBuilder(this.requireContext()).setTitle(R.string.restart).setCancelable(false).setMessage(R.string.api_key_restart).setNeutralButton(android.R.string.ok, (dialog, which) -> {
// User clicked OK button
@ -939,7 +974,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
});
} else {
new Handler(Looper.getMainLooper()).post(() -> {
Snackbar.make(requireView(), R.string.api_key_invalid, Snackbar.LENGTH_SHORT).show();
Snackbar.make(requireView(), R.string.api_key_invalid, BaseTransientBottomBar.LENGTH_SHORT).show();
// Save the original key
MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit().putString("pref_androidacy_api_token", originalApiKeyRef[0]).apply();
// Re-show the dialog with an error
@ -1027,7 +1062,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
});
builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel());
AlertDialog alertDialog = builder.show();
final Button positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
final Button positiveButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
input.setValidator(new AutoCompleteTextView.Validator() {
@Override
public boolean isValid(CharSequence charSequence) {
@ -1086,7 +1121,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
preference.setOnPreferenceChangeListener((p, newValue) -> {
p.setTitle(((Boolean) newValue) ? R.string.repo_enabled : R.string.repo_disabled);
// Show snackbar telling the user to refresh the modules list or restart the app
Snackbar.make(requireView(), R.string.repo_enabled_changed, Snackbar.LENGTH_LONG).show();
Snackbar.make(requireView(), R.string.repo_enabled_changed, BaseTransientBottomBar.LENGTH_LONG).show();
return true;
});
}

@ -14,7 +14,9 @@ import eightbitlab.com.blurview.BlurView;
import eightbitlab.com.blurview.RenderEffectBlur;
import eightbitlab.com.blurview.RenderScriptBlur;
public class BlurUtils {
public enum BlurUtils {
;
public static void setupBlur(BlurView blurView, Activity activity, @IdRes int viewId) {
setupBlur(blurView, activity, activity.findViewById(viewId));
}

@ -14,8 +14,10 @@ import androidx.appcompat.widget.LinearLayoutCompat;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
// ProgressDialog is deprecated because it's an bad UX pattern, but sometimes we have no other choice...
public class BudgetProgressDialog {
public static AlertDialog build(Context context, String title, String message) {
public enum BudgetProgressDialog {
;
public static AlertDialog build(Context context, String title, String message) {
Resources r = context.getResources();
int padding = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, r.getDisplayMetrics()));
LinearLayoutCompat v = new LinearLayoutCompat(context);
@ -39,10 +41,6 @@ public class BudgetProgressDialog {
return build(context, context.getString(title), message);
}
public static AlertDialog build(Context context, String title, int message) {
return build(context, title, context.getString(message));
}
public static AlertDialog build(Context context, int title, int message) {
return build(context, title, context.getString(message));
}

@ -39,7 +39,8 @@ import java.net.URISyntaxException;
import timber.log.Timber;
public class IntentHelper {
public enum IntentHelper {
;
private static final String EXTRA_TAB_SESSION =
"android.support.customtabs.extra.SESSION";
private static final String EXTRA_TAB_COLOR_SCHEME =

@ -9,7 +9,8 @@ import com.fox2code.mmm.MainActivity;
import java.util.concurrent.ThreadLocalRandom;
public class ProcessHelper {
public enum ProcessHelper {
;
private static final int sPendingIntentId = ThreadLocalRandom.current().nextInt(100, 1000000 + 1);
public static void restartApplicationProcess(Context context) {

@ -3,8 +3,6 @@ package com.fox2code.mmm.utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.fox2code.mmm.repo.RepoManager;
/**
* Manager that want both to be thread safe and not to worry about thread safety
* {@link #scan()} and {@link #update(UpdateListener)} can be called from multiple

@ -62,14 +62,20 @@ public class AddCookiesInterceptor implements Interceptor {
// ensure the cookie applies to the current domain
if (cookie.contains("domain=")) {
// match from the start of the string to the first semicolon
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 {
builder.addHeader("Cookie", cookie);
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 {
builder.addHeader("Cookie", cookie);
}
} catch (
Exception ignored) {
}
} else {
builder.addHeader("Cookie", cookie);
}
}

@ -29,7 +29,8 @@ import java.util.zip.ZipOutputStream;
import timber.log.Timber;
public class Files {
public enum Files {
;
private static final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
// stolen from https://stackoverflow.com/a/25005243

@ -35,7 +35,8 @@ import timber.log.Timber;
*/
// Note: This code is MIT because I took it from another unpublished project I had
// I might upstream this to MicroG at some point
public class GMSProviderInstaller {
public enum GMSProviderInstaller {
;
private static boolean called = false;
public static void installIfNeeded(final Context context) {

@ -7,7 +7,8 @@ import java.util.regex.Pattern;
import timber.log.Timber;
public class Hashes {
public enum Hashes {
;
private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();
private static final Pattern nonAlphaNum = Pattern.compile("[^a-zA-Z0-9]");

@ -56,7 +56,8 @@ import okhttp3.dnsoverhttps.DnsOverHttps;
import okio.BufferedSink;
import timber.log.Timber;
public class Http {
public enum Http {
;
private static final OkHttpClient httpClient;
private static final OkHttpClient httpClientDoH;
private static final OkHttpClient httpClientWithCache;

@ -22,11 +22,13 @@ import java.util.Locale;
import timber.log.Timber;
public class PropUtils {
public enum PropUtils {
;
private static final HashMap<String, String> moduleSupportsFallbacks = new HashMap<>();
private static final HashMap<String, String> moduleConfigsFallbacks = new HashMap<>();
private static final HashMap<String, Integer> moduleMinApiFallbacks = new HashMap<>();
private static final HashMap<String, String> moduleUpdateJsonFallbacks = new HashMap<>();
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private static final HashSet<String> moduleMTTRebornFallback = new HashSet<>();
private static final HashSet<String> moduleImportantProp = new HashSet<>(Arrays.asList(
"id", "name", "version", "versionCode"

@ -17,7 +17,8 @@ import io.sentry.android.core.SentryAndroid;
import io.sentry.android.fragment.FragmentLifecycleIntegration;
import io.sentry.android.timber.SentryTimberIntegration;
public class SentryMain {
public enum SentryMain {
;
public static final boolean IS_SENTRY_INSTALLED = true;
private static boolean sentryEnabled = false;

@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp"
android:height="24dp" android:autoMirrored="true"
android:tint="?attr/colorControlNormal" android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10 10,-4.5 10,-10S17.5,2 12,2zM4,12c0,-4.4 3.6,-8 8,-8 1.9,0 3.6,0.6 4.9,1.7L5.7,16.9C4.6,15.6 4,13.9 4,12zM12,20c-1.9,0 -3.6,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20,10.2 20,12c0,4.4 -3.6,8 -8,8z"/>
</vector>

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UpdateActivity">
<!-- Activity used to download and install app updates -->
<TextView
android:id="@+id/update_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"
android:text="@string/update_title"
android:textAppearance="?attr/textAppearanceHeadline6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/update_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:text="@string/update_message"
android:textAppearance="?attr/textAppearanceBody2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/update_title" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/update_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/update_subtitle" />
<TextView
android:id="@+id/update_progress_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:textAppearance="?attr/textAppearanceBody2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/update_progress" />
<com.google.android.material.button.MaterialButton
android:id="@+id/update_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"
android:text="@string/update_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/update_progress_text" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"

@ -1,4 +1,4 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<resources>
<style name="Theme.MagiskModuleManager"
parent="Theme.MagiskModuleManager.Dark" />
</resources>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<resources>
<string name="app_name">Trình quản lý Mô-đun Magisk của Fox</string>
<string name="app_name_short">Quản lý MM</string>
<string name="fail_root_magisk">Thất bại khi lấy quyền truy cập Root hoặc Magisk</string>

@ -322,8 +322,13 @@
<string name="reset_app_confirmation">This is going to completely wipe app data, but will not effect modules.</string>
<string name="error_adding">Failed to add custom repo</string>
<string name="api_key_mismatch">API key is in an invalid format</string>
<string name="notification_update_ignore_desc">Comma separated list of modules to exclude from update checks</string>
<string name="notification_update_ignore_desc">List of module IDs to exclude from update checks</string>
<string name="notification_update_ignore_pref">Exclude modules</string>
<string name="pref_category_updates">Updates</string><string name="invalid_excludes">Invalid input</string>
<string name="invalid_characters_message">The list of modules you input is invalid. Please only input valid module id\'s separated by commas</string>
<string name="background_update_check_excludes">Modules to exclude from automatic update checks</string>
<string name="title_activity_update">In-app Updater</string>
<string name="update_title">Update app</string>
<string name="update_message">An update is available for FoxMMM. Please wait while we download and install it.</string>
<string name="update_button">Please wait...</string>
</resources>

@ -44,9 +44,8 @@
app:title="@string/notification_update_pref" />
<!-- Ignore updates for preference. Used to ignore updates for specific modules -->
<EditTextPreference
app:defaultValue=""
app:icon="@drawable/ic_baseline_notifications_24"
<Preference
app:icon="@drawable/baseline_block_24"
app:key="pref_background_update_check_excludes"
app:singleLineTitle="false"
app:summary="@string/notification_update_ignore_desc"

@ -100,7 +100,7 @@ Note²: For `minMagisk`, `XX.Y` is parsed as `XXY00`, so you can just put the Ma
(For example `- Hello world` will be transformed to `[*] Hello world`, do not apply to modules installed from storage)
Note: Fox's Mmm use fallback
[here](../app/src/main/java/com/fox2code/mmm/utils/PropUtils.java#L36)
[here](../app/src/main/java/com/fox2code/mmm/utils/io/PropUtils.java#L36)
for some modules
Theses values are only used if not defined in the `module.prop` files
So the original module maker can still override them

Loading…
Cancel
Save