diff --git a/app/build.gradle b/app/build.gradle index 0c526e8..42d5938 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -288,7 +288,7 @@ dependencies { implementation 'com.github.topjohnwu.libsu:io:5.0.1' implementation 'com.github.Fox2Code:RosettaX:1.0.9' implementation 'com.github.Fox2Code:AndroidANSI:1.0.1' - implementation 'io.sentry:sentry-android:6.12.1' + implementation "io.sentry:sentry-android:$sentry_version" // Markdown implementation "io.noties.markwon:core:4.6.2" @@ -302,6 +302,12 @@ dependencies { //implementation "androidx.appcompat:appcompat:${versions.appCompat}" implementation 'androidx.core:core-ktx:1.9.0' + + implementation "io.sentry:sentry-kotlin-extensions:$sentry_version" + + // timber + implementation 'com.jakewharton.timber:timber:5.0.1' + implementation "io.sentry:sentry-android-timber:$sentry_version" } if (hasSentryConfig) { diff --git a/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java b/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java index da0d11b..ec90d5d 100644 --- a/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java +++ b/app/src/main/java/com/fox2code/mmm/AppUpdateManager.java @@ -1,7 +1,5 @@ package com.fox2code.mmm; -import android.util.Log; - import com.fox2code.mmm.utils.io.Files; import com.fox2code.mmm.utils.io.Http; @@ -17,6 +15,8 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.HashMap; +import timber.log.Timber; + // See https://docs.github.com/en/rest/reference/repos#releases public class AppUpdateManager { public static final int FLAG_COMPAT_LOW_QUALITY = 0x0001; @@ -29,10 +29,8 @@ public class AppUpdateManager { public static final int FLAG_COMPAT_FORCE_HIDE = 0x0080; public static final int FLAG_COMPAT_MMT_REBORN = 0x0100; public static final int FLAG_COMPAT_ZIP_WRAPPER = 0x0200; - private static final String TAG = "AppUpdateManager"; private static final AppUpdateManager INSTANCE = new AppUpdateManager(); private static final String RELEASES_API_URL = "https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/releases"; - private static final String COMPAT_API_URL = "https://api.github.com/repos/Fox2Code/FoxMagiskModuleManager/issues/4"; private final HashMap compatDataId = new HashMap<>(); private final Object updateLock = new Object(); private final File compatFile; @@ -120,15 +118,15 @@ public class AppUpdateManager { this.preReleaseNewer = false; } if (BuildConfig.DEBUG) - Log.d(TAG, "Latest release: " + latestRelease); + Timber.d("Latest release: %s", latestRelease); if (BuildConfig.DEBUG) - Log.d(TAG, "Latest pre-release: " + latestPreRelease); + Timber.d("Latest pre-release: %s", latestPreRelease); if (BuildConfig.DEBUG) - Log.d(TAG, "Latest pre-release newer: " + preReleaseNewer); + Timber.d("Latest pre-release newer: %s", preReleaseNewer); this.lastChecked = System.currentTimeMillis(); } catch ( Exception ioe) { - Log.e("AppUpdateManager", "Failed to check releases", ioe); + Timber.e(ioe); } } return this.peekShouldUpdate(); @@ -138,11 +136,12 @@ public class AppUpdateManager { compatDataId.clear(); try { Files.write(compatFile, new byte[0]); - } catch (IOException e) { + } catch ( + IOException e) { e.printStackTrace(); } // There once lived an implementation that used a GitHub API to get the compatibility flags. It was removed because it was too slow and the API was rate limited. - Log.w(TAG, "Remote compatibility data flags are not implemented."); + Timber.w("Remote compatibility data flags are not implemented."); } public boolean peekShouldUpdate() { diff --git a/app/src/main/java/com/fox2code/mmm/MainActivity.java b/app/src/main/java/com/fox2code/mmm/MainActivity.java index 7fffabb..0c7e31c 100644 --- a/app/src/main/java/com/fox2code/mmm/MainActivity.java +++ b/app/src/main/java/com/fox2code/mmm/MainActivity.java @@ -1,5 +1,7 @@ package com.fox2code.mmm; +import static com.fox2code.mmm.MainApplication.isOfficial; + import android.Manifest; import android.annotation.SuppressLint; import android.content.Intent; @@ -13,13 +15,13 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; -import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.widget.CheckBox; import android.widget.TextView; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.widget.SearchView; @@ -56,9 +58,9 @@ import org.chromium.net.urlconnection.CronetURLStreamHandlerFactory; import java.net.URL; import eightbitlab.com.blurview.BlurView; +import timber.log.Timber; public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRefreshListener, SearchView.OnQueryTextListener, SearchView.OnCloseListener, OverScrollManager.OverScrollHelper { - private static final String TAG = "MainActivity"; private static final int PRECISION = 10000; public static boolean doSetupNowRunning = true; public final ModuleViewListBuilder moduleViewListBuilder; @@ -104,12 +106,12 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe URL.setURLStreamHandlerFactory(cronetURLStreamHandlerFactory); } catch ( Error e) { - Log.e(TAG, "Failed to install Cronet URLStreamHandlerFactory"); + Timber.e("Failed to install Cronet URLStreamHandlerFactory"); } urlFactoryInstalled = true; } catch ( Throwable t) { - Log.e(TAG, "Failed to install CronetURLStreamHandlerFactory - other"); + Timber.e("Failed to install CronetURLStreamHandlerFactory - other"); } } if (doSetupRestarting) { @@ -117,6 +119,11 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe } BackgroundUpdateChecker.onMainActivityCreate(this); super.onCreate(savedInstanceState); + if (!isOfficial) { + Timber.w("You may be running an untrusted build."); + // Show a toast to warn the user + Toast.makeText(this, R.string.not_official_build, Toast.LENGTH_LONG).show(); + } if (!MainApplication.getSharedPreferences().getBoolean("first_time_user", true)) { this.setActionBarExtraMenuButton(R.drawable.ic_baseline_settings_24, v -> { IntentHelper.startActivity(this, SettingsActivity.class); @@ -181,7 +188,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe InstallerInitializer.tryGetMagiskPathAsync(new InstallerInitializer.Callback() { @Override public void onPathReceived(String path) { - Log.i(TAG, "Got magisk path: " + path); + Timber.i("Got magisk path: %s", path); if (InstallerInitializer.peekMagiskVersion() < Constants.MAGISK_VER_CODE_INSTALL_COMMAND) moduleViewListBuilder.addNotification(NotificationType.MAGISK_OUTDATED); if (!MainApplication.isShowcaseMode()) @@ -193,21 +200,19 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe @Override public void onFailure(int error) { - Log.i(TAG, "Failed to get magisk path!"); + Timber.i("Failed to get magisk path!"); moduleViewListBuilder.addNotification(InstallerInitializer.getErrorNotification()); this.commonNext(); } public void commonNext() { if (BuildConfig.DEBUG) { - Log.d(TAG, "Common next"); + Timber.d("Common next"); moduleViewListBuilder.addNotification(NotificationType.DEBUG); } updateScreenInsets(); // Fix an edge case if (waitInitialSetupFinished()) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Initial setup not finished, waiting..."); - } + Timber.d("waiting..."); return; } swipeRefreshBlocker = System.currentTimeMillis() + 5_000L; @@ -225,22 +230,22 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe // On every preferences change, log the change if debug is enabled if (BuildConfig.DEBUG) { - Log.d("PrefsListener", "onCreate: Preferences: " + MainApplication.getSharedPreferences().getAll()); + Timber.d("onCreate: Preferences: %s", MainApplication.getSharedPreferences().getAll()); // Log all preferences changes - MainApplication.getSharedPreferences().registerOnSharedPreferenceChangeListener((prefs, key) -> Log.i("PrefsListener", "onSharedPreferenceChanged: " + key + " = " + prefs.getAll().get(key))); + MainApplication.getSharedPreferences().registerOnSharedPreferenceChangeListener((prefs, key) -> Timber.i("onSharedPreferenceChanged: " + key + " = " + prefs.getAll().get(key))); } - Log.i(TAG, "Scanning for modules!"); + Timber.i("Scanning for modules!"); if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Initialize Update"); + Timber.i("Initialize Update"); final int max = ModuleManager.getINSTANCE().getUpdatableModuleCount(); if (RepoManager.getINSTANCE().getCustomRepoManager().needUpdate()) { - Log.w(TAG, "Need update on create?"); + Timber.w("Need update on create?"); } if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Check Update Compat"); + Timber.i("Check Update Compat"); AppUpdateManager.getAppUpdateManager().checkUpdateCompat(); if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Check Update"); + Timber.i("Check Update"); RepoManager.getINSTANCE().update(value -> runOnUiThread(max == 0 ? () -> progressIndicator.setProgressCompat((int) (value * PRECISION), true) : () -> progressIndicator.setProgressCompat((int) (value * PRECISION * 0.75F), true))); NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder); // Add debug notification for debug builds @@ -255,11 +260,11 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe // Compatibility data still needs to be updated AppUpdateManager appUpdateManager = AppUpdateManager.getAppUpdateManager(); if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Check App Update"); + Timber.i("Check App Update"); if (BuildConfig.ENABLE_AUTO_UPDATER && appUpdateManager.checkUpdate(true)) moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE); if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Check Json Update"); + Timber.i("Check Json Update"); if (max != 0) { int current = 0; @@ -267,12 +272,12 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe for (LocalModuleInfo localModuleInfo : ModuleManager.getINSTANCE().getModules().values()) { if (localModuleInfo.updateJson != null) { if (BuildConfig.DEBUG) - Log.i("NoodleDebug", localModuleInfo.id); + Timber.i(localModuleInfo.id); try { localModuleInfo.checkModuleUpdate(); } catch ( Exception e) { - Log.e("MainActivity", "Failed to fetch update of: " + localModuleInfo.id, e); + Timber.e(e); } current++; final int currentTmp = current; @@ -289,11 +294,11 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe updateScreenInsets(getResources().getConfiguration()); }); if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Apply"); + Timber.i("Apply"); RepoManager.getINSTANCE().runAfterUpdate(moduleViewListBuilder::appendRemoteModules); moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter); - Log.i(TAG, "Finished app opening state!"); + Timber.i("Finished app opening state!"); // noodleDebug.unbind(); } }, true); @@ -331,7 +336,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe //this.actionBarBlur.invalidate(); this.overScrollInsetTop = combinedBarsHeight; this.overScrollInsetBottom = bottomInset; - Log.i(TAG, "( " + bottomInset + ", " + this.searchCard.getHeight() + ")"); + Timber.i("(" + this.searchCard.getHeight() + ")"); } private void updateBlurState() { @@ -360,7 +365,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe if (this.initMode) return; this.initMode = true; - Log.i(TAG, "Item Before"); + Timber.i("Item Before"); this.searchView.setQuery("", false); this.searchView.clearFocus(); this.searchView.setIconified(true); @@ -368,7 +373,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe this.updateScreenInsets(); this.updateBlurState(); this.moduleViewListBuilder.setQuery(null); - Log.i(TAG, "Item After"); + Timber.i("Item After"); this.moduleViewListBuilder.refreshNotificationsUI(this.moduleViewAdapter); InstallerInitializer.tryGetMagiskPathAsync(new InstallerInitializer.Callback() { @Override @@ -399,7 +404,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe } public void commonNext() { - Log.i(TAG, "Common Before"); + Timber.i("Common Before"); if (MainApplication.isShowcaseMode()) moduleViewListBuilder.addNotification(NotificationType.SHOWCASE_MODE); NotificationType.NEED_CAPTCHA_ANDROIDACY.autoAdd(moduleViewListBuilder); @@ -414,7 +419,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe progressIndicator.setMax(PRECISION); }); if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Check Update"); + Timber.i("Check Update"); RepoManager.getINSTANCE().update(value -> runOnUiThread(() -> progressIndicator.setProgressCompat((int) (value * PRECISION), true))); runOnUiThread(() -> { progressIndicator.setProgressCompat(PRECISION, true); @@ -422,11 +427,11 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe }); } if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Apply"); + Timber.i("Apply"); RepoManager.getINSTANCE().runAfterUpdate(moduleViewListBuilder::appendRemoteModules); - Log.i(TAG, "Common Before applyTo"); + Timber.i("Common Before applyTo"); moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter); - Log.i(TAG, "Common After"); + Timber.i("Common After"); } }); this.initMode = false; @@ -444,7 +449,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe return; // Do not double scan } if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Refresh"); + Timber.i("Refresh"); this.progressIndicator.setVisibility(View.VISIBLE); this.progressIndicator.setProgressCompat(0, false); this.swipeRefreshBlocker = System.currentTimeMillis() + 5_000L; @@ -463,22 +468,22 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe // Compatibility data still needs to be updated AppUpdateManager appUpdateManager = AppUpdateManager.getAppUpdateManager(); if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Check App Update"); + Timber.i("Check App Update"); if (BuildConfig.ENABLE_AUTO_UPDATER && appUpdateManager.checkUpdate(true)) moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE); if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Check Json Update"); + Timber.i("Check Json Update"); if (max != 0) { int current = 0; for (LocalModuleInfo localModuleInfo : ModuleManager.getINSTANCE().getModules().values()) { if (localModuleInfo.updateJson != null) { if (BuildConfig.DEBUG) - Log.i("NoodleDebug", localModuleInfo.id); + Timber.i(localModuleInfo.id); try { localModuleInfo.checkModuleUpdate(); } catch ( Exception e) { - Log.e("MainActivity", "Failed to fetch update of: " + localModuleInfo.id, e); + Timber.e(e); } current++; final int currentTmp = current; @@ -488,7 +493,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe } } if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Apply"); + Timber.i("Apply"); runOnUiThread(() -> { this.progressIndicator.setVisibility(View.GONE); this.swipeRefreshLayout.setRefreshing(false); @@ -547,18 +552,18 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe @SuppressLint("RestrictedApi") private void ensurePermissions() { if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Ensure Permissions"); + Timber.i("Ensure Permissions"); // First, check if user has said don't ask again by checking if pref_dont_ask_again_notification_permission is true if (!PreferenceManager.getDefaultSharedPreferences(this).getBoolean("pref_dont_ask_again_notification_permission", false)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Request Notification Permission"); + Timber.i("Request Notification Permission"); if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.POST_NOTIFICATIONS)) { // Show a dialog explaining why we need this permission, which is to show // notifications for updates runOnUiThread(() -> { if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Show Notification Permission Dialog"); + Timber.i("Show Notification Permission Dialog"); MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); builder.setTitle(R.string.permission_notification_title); builder.setMessage(R.string.permission_notification_message); @@ -582,16 +587,17 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe }); builder.show(); if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Show Notification Permission Dialog Done"); + Timber.i("Show Notification Permission Dialog Done"); }); } else { // Request the permission if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Request Notification Permission"); + Timber.i("Request Notification Permission"); this.requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 0); if (BuildConfig.DEBUG) { // Log if granted via onRequestPermissionsResult - Log.i("NoodleDebug", "Request Notification Permission Done. Result: " + (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED)); + boolean granted = ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED; + Timber.i( "Request Notification Permission Done. Result: %s", granted); } doSetupNowRunning = false; } @@ -630,7 +636,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe } } else { if (BuildConfig.DEBUG) - Log.i("NoodleDebug", "Notification Permission Already Granted or Don't Ask Again"); + Timber.i("Notification Permission Already Granted or Don't Ask Again"); doSetupNowRunning = false; } } @@ -639,17 +645,17 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe @SuppressLint({"InflateParams", "RestrictedApi", "UnspecifiedImmutableFlag", "ApplySharedPref"}) private void checkShowInitialSetup() { if (BuildConfig.DEBUG) - Log.i("SetupWizard", "Checking if we need to run setup"); + Timber.i("Checking if we need to run setup"); // Check if this is the first launch SharedPreferences prefs = MainApplication.getSharedPreferences(); boolean firstLaunch = prefs.getBoolean("first_time_user", true); if (BuildConfig.DEBUG) - Log.i("SetupWizard", "First launch: " + firstLaunch); + Timber.i("First launch: %s", firstLaunch); if (firstLaunch) { doSetupNowRunning = true; // Launch setup wizard if (BuildConfig.DEBUG) - Log.i("SetupWizard", "Launching setup wizard"); + Timber.i("Launching setup wizard"); // Show setup activity Intent intent = new Intent(this, SetupActivity.class); finish(); @@ -664,7 +670,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe */ private boolean waitInitialSetupFinished() { if (BuildConfig.DEBUG) - Log.i("SetupWizard", "waitInitialSetupFinished"); + Timber.i("waitInitialSetupFinished"); if (doSetupNowRunning) updateScreenInsets(); // Fix an edge case try { diff --git a/app/src/main/java/com/fox2code/mmm/MainApplication.java b/app/src/main/java/com/fox2code/mmm/MainApplication.java index 47a97bf..3890df5 100644 --- a/app/src/main/java/com/fox2code/mmm/MainApplication.java +++ b/app/src/main/java/com/fox2code/mmm/MainApplication.java @@ -1,7 +1,9 @@ package com.fox2code.mmm; import android.annotation.SuppressLint; +import android.app.ActivityManager; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -11,7 +13,6 @@ import android.content.res.Resources; import android.os.Build; import android.os.SystemClock; import android.util.Log; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.StyleRes; @@ -36,6 +37,7 @@ import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.Random; @@ -45,6 +47,9 @@ import io.noties.markwon.html.HtmlPlugin; import io.noties.markwon.image.ImagesPlugin; import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler; import io.realm.Realm; +import io.sentry.Sentry; +import io.sentry.SentryLevel; +import timber.log.Timber; public class MainApplication extends FoxApplication implements androidx.work.Configuration.Provider { private static final String timeFormatString = "dd MMM yyyy"; // Example: 13 july 2001 @@ -56,7 +61,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con public static boolean isOfficial = false; // Warning! Locales that are't exist will crash the app // Anything that is commented out is supported but the translation is not complete to at least 60% - public static HashSet supportedLocales = new HashSet<>(); + public static final HashSet supportedLocales = new HashSet<>(); private static Locale timeFormatLocale = Resources.getSystem().getConfiguration().getLocales().get(0); private static SimpleDateFormat timeFormat = new SimpleDateFormat(timeFormatString, timeFormatLocale); private static SharedPreferences bootSharedPreferences; @@ -215,7 +220,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con boolean monet = isMonetEnabled(); switch (theme = getSharedPreferences().getString("pref_theme", "system")) { default: - Log.w("MainApplication", "Unknown theme id: " + theme); + Timber.w("Unknown theme id: %s", theme); case "system": themeResId = monet ? R.style.Theme_MagiskModuleManager_Monet : R.style.Theme_MagiskModuleManager; break; @@ -230,7 +235,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con break; case "transparent_light": if (monet) { - Log.w("MainApplication", "Monet is not supported for transparent theme"); + Timber.tag("MainApplication").w("Monet is not supported for transparent theme"); } themeResId = R.style.Theme_MagiskModuleManager_Transparent_Light; break; @@ -276,6 +281,9 @@ public class MainApplication extends FoxApplication implements androidx.work.Con @Override public void onCreate() { + // init timber + if (BuildConfig.DEBUG) Timber.plant(new Timber.DebugTree()); + else Timber.plant(new ReleaseTree()); // supportedLocales.add("ar"); // supportedLocales.add("ar_SA"); supportedLocales.add("cs"); @@ -303,18 +311,16 @@ public class MainApplication extends FoxApplication implements androidx.work.Con if (INSTANCE == null) INSTANCE = this; relPackageName = this.getPackageName(); - if (BuildConfig.DEBUG) { - Log.d("MainApplication", "Starting FoxMMM version " + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + "), commit " + BuildConfig.COMMIT_HASH); - } + Timber.d("Starting FoxMMM version " + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + "), commit " + BuildConfig.COMMIT_HASH); super.onCreate(); - - if (BuildConfig.DEBUG) { - Log.d("MainApplication", "FoxMMM is running in debug mode"); - } if (BuildConfig.DEBUG) { - Log.d("MainApplication", "Initializing Realm"); + Timber.d("Initializing FoxMMM"); + Timber.d("Started from background: %s", !isInForeground()); + Timber.d("FoxMMM is running in debug mode"); + Timber.d("Initializing Realm"); } Realm.init(this); + Timber.d("Initialized Realm"); // Determine if this is an official build based on the signature try { // Get the signature of the key used to sign the app @@ -325,11 +331,6 @@ public class MainApplication extends FoxApplication implements androidx.work.Con } catch ( PackageManager.NameNotFoundException ignored) { } - if (!isOfficial) { - Log.w("MainApplication", "This is not an official build of FoxMMM. This warning can be safely ignored if this is expected, otherwise you may be running an untrusted build."); - // Show a toast to warn the user - Toast.makeText(this, R.string.not_official_build, Toast.LENGTH_LONG).show(); - } SharedPreferences sharedPreferences = MainApplication.getSharedPreferences(); // We are only one process so it's ok to do this SharedPreferences bootPrefs = MainApplication.bootSharedPreferences = this.getSharedPreferences("mmm_boot", MODE_PRIVATE); @@ -357,14 +358,14 @@ public class MainApplication extends FoxApplication implements androidx.work.Con fontRequestEmojiCompatConfig.setMetadataLoadStrategy(EmojiCompat.LOAD_STRATEGY_MANUAL); EmojiCompat emojiCompat = EmojiCompat.init(fontRequestEmojiCompatConfig); new Thread(() -> { - Log.i("MainApplication", "Loading emoji compat..."); + Timber.i("Loading emoji compat..."); emojiCompat.load(); - Log.i("MainApplication", "Emoji compat loaded!"); + Timber.i("Emoji compat loaded!"); }, "Emoji compat init.").start(); } SentryMain.initialize(this); if (Objects.equals(BuildConfig.ANDROIDACY_CLIENT_ID, "")) { - Log.w("MainApplication", "Androidacy client id is empty! Please set it in androidacy.properties. Will not enable Androidacy."); + Timber.w("Androidacy client id is empty! Please set it in androidacy.properties. Will not enable Androidacy."); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putBoolean("pref_androidacy_repo_enabled", false); editor.apply(); @@ -408,7 +409,7 @@ public class MainApplication extends FoxApplication implements androidx.work.Con if (!dataDir.exists()) { if (!dataDir.mkdirs()) { if (BuildConfig.DEBUG) - Log.w("MainApplication", "Failed to create directory " + dataDir); + Timber.w("Failed to create directory %s", dataDir); } } return dataDir; @@ -427,31 +428,69 @@ public class MainApplication extends FoxApplication implements androidx.work.Con if (children != null) { for (String s : children) { if (BuildConfig.DEBUG) - Log.w("MainApplication", "Deleting " + s); + Timber.w("Deleting %s", s); if (!s.equals("lib")) { if (!new File(cacheDir, s).delete()) { if (BuildConfig.DEBUG) - Log.w("MainApplication", "Failed to delete " + s); + Timber.w("Failed to delete %s", s); } } } } } if (BuildConfig.DEBUG) - Log.w("MainApplication", "Deleting cache dir"); + Timber.w("Deleting cache dir"); this.deleteSharedPreferences("mmm_boot"); this.deleteSharedPreferences("mmm"); this.deleteSharedPreferences("sentry"); this.deleteSharedPreferences("androidacy"); if (BuildConfig.DEBUG) - Log.w("MainApplication", "Deleting shared prefs"); + Timber.w("Deleting shared prefs"); this.getPackageManager().clearPackagePreferredActivities(this.getPackageName()); if (BuildConfig.DEBUG) - Log.w("MainApplication", "Done clearing app data"); + Timber.w("Done clearing app data"); } catch ( Exception e) { - Log.e("MainApplication", "Failed to clear app data", e); + Timber.e(e); } } + public boolean isInForeground() { + // determine if the app is in the foreground + ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE); + if (activityManager != null) { + List appProcesses = activityManager.getRunningAppProcesses(); + if (appProcesses != null) { + for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { + if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { + for (String activeProcess : appProcess.pkgList) { + if (activeProcess.equals(this.getPackageName())) { + return true; + } + } + return false; + } + } + } + } else { + Timber.e("Failed to get activity manager"); + } + return false; + } + + private static class ReleaseTree extends Timber.Tree { + @SuppressWarnings("StatementWithEmptyBody") + @Override + protected void log(int priority, String tag, @NonNull String message, Throwable t) { + if (priority == Log.VERBOSE || priority == Log.DEBUG) { + // silently ignore + } else if (priority == Log.INFO) { + Sentry.captureMessage(message, SentryLevel.INFO); + } else if (priority == Log.WARN) { + Sentry.captureMessage(message, SentryLevel.WARNING); + } else if (priority == Log.ERROR) { + Sentry.captureException(t); + } + } + } } diff --git a/app/src/main/java/com/fox2code/mmm/NotificationType.java b/app/src/main/java/com/fox2code/mmm/NotificationType.java index af042a2..0ef4c20 100644 --- a/app/src/main/java/com/fox2code/mmm/NotificationType.java +++ b/app/src/main/java/com/fox2code/mmm/NotificationType.java @@ -1,6 +1,5 @@ package com.fox2code.mmm; -import android.util.Log; import android.view.View; import android.widget.Toast; @@ -24,8 +23,9 @@ import java.io.InputStreamReader; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import timber.log.Timber; + interface NotificationTypeCst { - String TAG = "NotificationType"; } public enum NotificationType implements NotificationTypeCst { @@ -119,7 +119,7 @@ public enum NotificationType implements NotificationTypeCst { } if (needPatch(d)) { if (d.exists() && !d.delete()) - Log.w(TAG, "Failed to delete non module zip"); + Timber.w("Failed to delete non module zip"); Toast.makeText(compatActivity, R.string.invalid_format, Toast.LENGTH_SHORT).show(); } else { @@ -131,7 +131,7 @@ public enum NotificationType implements NotificationTypeCst { } } catch (IOException ignored) { if (d.exists() && !d.delete()) - Log.w(TAG, "Failed to delete invalid module"); + Timber.w("Failed to delete invalid module"); Toast.makeText(compatActivity, R.string.invalid_format, Toast.LENGTH_SHORT).show(); } @@ -152,7 +152,7 @@ public enum NotificationType implements NotificationTypeCst { } }; - public static boolean needPatch(File target) throws IOException { + public static boolean needPatch(File target) { try (ZipFile zipFile = new ZipFile(target)) { boolean validEntries = zipFile.getEntry("module.prop") != null; // ensure there's no anykernel.sh diff --git a/app/src/main/java/com/fox2code/mmm/SetupActivity.java b/app/src/main/java/com/fox2code/mmm/SetupActivity.java index 81ec47e..dd05fd1 100644 --- a/app/src/main/java/com/fox2code/mmm/SetupActivity.java +++ b/app/src/main/java/com/fox2code/mmm/SetupActivity.java @@ -7,7 +7,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; -import android.util.Log; import android.view.View; import android.view.WindowManager; @@ -33,6 +32,7 @@ import java.util.Objects; import io.realm.Realm; import io.realm.RealmConfiguration; import io.realm.RealmResults; +import timber.log.Timber; public class SetupActivity extends FoxActivity implements LanguageActivity { @@ -91,10 +91,10 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_magisk_alt_repo))).setChecked(BuildConfig.ENABLED_REPOS.contains("magisk_alt_repo")); // On debug builds, log when a switch is toggled if (BuildConfig.DEBUG) { - ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).setOnCheckedChangeListener((buttonView, isChecked) -> Log.i("SetupWizard", "Background Update Check: " + isChecked)); - ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).setOnCheckedChangeListener((buttonView, isChecked) -> Log.i("SetupWizard", "Crash Reporting: " + isChecked)); - ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_androidacy_repo))).setOnCheckedChangeListener((buttonView, isChecked) -> Log.i("SetupWizard", "Androidacy Repo: " + isChecked)); - ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_magisk_alt_repo))).setOnCheckedChangeListener((buttonView, isChecked) -> Log.i("SetupWizard", "Magisk Alt Repo: " + isChecked)); + ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_background_update_check))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Background Update Check: %s", isChecked)); + ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_crash_reporting))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Crash Reporting: %s", isChecked)); + ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_androidacy_repo))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Androidacy Repo: %s", isChecked)); + ((MaterialSwitch) Objects.requireNonNull(view.findViewById(R.id.setup_magisk_alt_repo))).setOnCheckedChangeListener((buttonView, isChecked) -> Timber.i("Magisk Alt Repo: %s", isChecked)); } // Setup popup dialogue for the setup_theme_button MaterialButton themeButton = view.findViewById(R.id.setup_theme_button); @@ -213,8 +213,8 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { } // Log the changes if debug if (BuildConfig.DEBUG) { - Log.d("SetupWizard", "Background update check: " + prefs.getBoolean("pref_background_update_check", false)); - Log.i("SetupWizard", "Crash reporting: " + prefs.getBoolean("pref_crash_reporting", false)); + Timber.d("Background update check: %s", prefs.getBoolean("pref_background_update_check", false)); + Timber.i("Crash reporting: %s", prefs.getBoolean("pref_crash_reporting", false)); } // Restart the activity MainActivity.doSetupRestarting = true; @@ -274,9 +274,7 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { // creates the realm database private void createRealmDatabase() { - if (BuildConfig.DEBUG) { - Log.d("Realm", "Creating Realm databases"); - } + Timber.d("Creating Realm databases"); // create the realm database for ModuleListCache RealmConfiguration config = new RealmConfiguration.Builder().name("ModuleListCache.realm").schemaVersion(1).build(); // do a dummy write to create the database @@ -333,20 +331,20 @@ public class SetupActivity extends FoxActivity implements LanguageActivity { realm1.commitTransaction(); realm1.close(); if (BuildConfig.DEBUG) { - Log.d("Realm", "Realm databases created"); + Timber.d("Realm databases created"); // log each database Realm realm2 = Realm.getInstance(config); RealmResults moduleListCaches = realm2.where(ModuleListCache.class).findAll(); - Log.d("Realm", "ModuleListCache.realm"); + Timber.d("ModuleListCache.realm"); for (ModuleListCache moduleListCache : moduleListCaches) { - Log.d("Realm", moduleListCache.toString()); + Timber.d(moduleListCache.toString()); } realm2.close(); Realm realm3 = Realm.getInstance(config2); RealmResults reposLists = realm3.where(ReposList.class).findAll(); - Log.d("Realm", "ReposList.realm"); + Timber.d("ReposList.realm"); for (ReposList reposList : reposLists) { - Log.d("Realm", reposList.toString()); + Timber.d(reposList.toString()); } realm3.close(); } diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java index 18426c4..27526c9 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyActivity.java @@ -6,7 +6,6 @@ import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; -import android.util.Log; import android.view.View; import android.webkit.ConsoleMessage; import android.webkit.ValueCallback; @@ -32,8 +31,8 @@ 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.io.Http; import com.fox2code.mmm.utils.IntentHelper; +import com.fox2code.mmm.utils.io.Http; import com.google.android.material.progressindicator.LinearProgressIndicator; import java.io.ByteArrayInputStream; @@ -43,11 +42,12 @@ import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.HashMap; +import timber.log.Timber; + /** * Per Androidacy repo implementation agreement, no request of this WebView shall be modified. */ public final class AndroidacyActivity extends FoxActivity { - private static final String TAG = "AndroidacyActivity"; static { if (BuildConfig.DEBUG) { @@ -72,18 +72,18 @@ public final class AndroidacyActivity extends FoxActivity { Intent intent = this.getIntent(); Uri uri; if (!MainApplication.checkSecret(intent) || (uri = intent.getData()) == null) { - Log.w(TAG, "Impersonation detected"); + Timber.w("Impersonation detected"); this.forceBackPressed(); return; } String url = uri.toString(); if (!AndroidacyUtil.isAndroidacyLink(url, uri)) { - Log.w(TAG, "Calling non androidacy link in secure WebView: " + url); + Timber.w("Calling non androidacy link in secure WebView: %s", url); this.forceBackPressed(); return; } if (!Http.hasWebView()) { - Log.w(TAG, "No WebView found to load url: " + url); + Timber.w("No WebView found to load url: %s", url); this.forceBackPressed(); return; } @@ -167,7 +167,7 @@ public final class AndroidacyActivity extends FoxActivity { if (request.isForMainFrame() && !AndroidacyUtil.isAndroidacyLink(request.getUrl())) { if (downloadMode || backOnResume) return true; - Log.i(TAG, "Exiting WebView " + AndroidacyUtil.hideToken(request.getUrl().toString())); + Timber.i("Exiting WebView %s", AndroidacyUtil.hideToken(request.getUrl().toString())); IntentHelper.openUri(view.getContext(), request.getUrl().toString()); return true; } @@ -229,19 +229,19 @@ public final class AndroidacyActivity extends FoxActivity { if (BuildConfig.DEBUG) { switch (consoleMessage.messageLevel()) { case TIP: - Log.v(TAG, consoleMessage.message()); + Timber.v(consoleMessage.message()); break; case LOG: - Log.i(TAG, consoleMessage.message()); + Timber.i(consoleMessage.message()); break; case WARNING: - Log.w(TAG, consoleMessage.message()); + Timber.w(consoleMessage.message()); break; case ERROR: - Log.e(TAG, consoleMessage.message()); + Timber.e(consoleMessage.message()); break; case DEBUG: - Log.d(TAG, consoleMessage.message()); + Timber.d(consoleMessage.message()); break; } } @@ -278,7 +278,7 @@ public final class AndroidacyActivity extends FoxActivity { return; } else if (moduleId != null) { // Download module - Log.i(TAG, "megaIntercept failure. Forcing onBackPress"); + Timber.i("megaIntercept failure. Forcing onBackPress"); this.onBackPressed(); } } @@ -286,7 +286,7 @@ public final class AndroidacyActivity extends FoxActivity { androidacyWebAPI.downloadMode = false; } this.backOnResume = true; - Log.i(TAG, "Exiting WebView " + AndroidacyUtil.hideToken(downloadUrl)); + Timber.i("Exiting WebView %s", AndroidacyUtil.hideToken(downloadUrl)); for (String prefix : new String[]{"https://production-api.androidacy.com/downloads/", "https://staging-api.androidacy.com/magisk/downloads/"}) { if (downloadUrl.startsWith(prefix)) { return; @@ -304,7 +304,7 @@ public final class AndroidacyActivity extends FoxActivity { headers.put("Accept-Language", this.getResources().getConfiguration().locale.toLanguageTag()); if (BuildConfig.DEBUG) { headers.put("X-Debug", "true"); - Log.i(TAG, "Debug mode enabled for webview using URL: " + url + " with headers: " + headers); + Timber.i("Debug mode enabled for webview using URL: " + url + " with headers: " + headers); } this.webView.loadUrl(url, headers); } @@ -364,13 +364,13 @@ public final class AndroidacyActivity extends FoxActivity { if (pageUrl == null || fileUrl == null) return false; if (this.isFileUrl(fileUrl)) { - Log.i(TAG, "megaIntercept(" + AndroidacyUtil.hideToken(pageUrl) + ", " + AndroidacyUtil.hideToken(fileUrl) + ")"); + Timber.i("megaIntercept(%s", AndroidacyUtil.hideToken(AndroidacyUtil.hideToken(fileUrl) )); } else return false; final AndroidacyWebAPI androidacyWebAPI = this.androidacyWebAPI; String moduleId = AndroidacyUtil.getModuleId(fileUrl); if (moduleId == null) { - Log.i(TAG, "No module id?"); + Timber.i("No module id?"); // Re-open the page this.webView.loadUrl(pageUrl + "&force_refresh=" + System.currentTimeMillis()); } diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java index e8370e2..ef08a95 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyRepoData.java @@ -4,7 +4,6 @@ import android.annotation.SuppressLint; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; -import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; @@ -40,11 +39,11 @@ import java.util.Objects; import io.realm.Realm; import io.realm.RealmConfiguration; import okhttp3.HttpUrl; +import timber.log.Timber; @SuppressWarnings("KotlinInternalInJava") public final class AndroidacyRepoData extends RepoData { - private static final String TAG = "AndroidacyRepoData"; public static String token = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", null); static { @@ -150,7 +149,7 @@ public final class AndroidacyRepoData extends RepoData { if (status.equals("success")) { return true; } else { - Log.w(TAG, "Invalid token, resetting..."); + Timber.w("Invalid token, resetting..."); // Remove saved preference SharedPreferences.Editor editor = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit(); editor.remove("pref_androidacy_api_token"); @@ -160,7 +159,7 @@ public final class AndroidacyRepoData extends RepoData { } catch ( HttpException e) { if (e.getErrorCode() == 401) { - Log.w(TAG, "Invalid token, resetting..."); + Timber.w("Invalid token, resetting..."); // Remove saved preference SharedPreferences.Editor editor = MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).edit(); editor.remove("pref_androidacy_api_token"); @@ -210,7 +209,7 @@ public final class AndroidacyRepoData extends RepoData { } } catch ( Exception e) { - Log.e(TAG, "Failed to ping server", e); + Timber.e(e, "Failed to ping server"); return false; } String deviceId = generateDeviceId(); @@ -225,7 +224,7 @@ public final class AndroidacyRepoData extends RepoData { if (token != null && !this.isValidToken(token)) { token = null; } else { - Log.i(TAG, "Using cached token"); + Timber.i("Using cached token"); } } else if (!this.isValidToken(token)) { if (BuildConfig.DEBUG) { @@ -236,14 +235,14 @@ public final class AndroidacyRepoData extends RepoData { } catch ( IOException e) { if (HttpException.shouldTimeout(e)) { - Log.e(TAG, "We are being rate limited!", e); + Timber.e(e, "We are being rate limited!"); this.androidacyBlockade = time + 3_600_000L; } return false; } if (token == null) { try { - Log.i(TAG, "Requesting new token..."); + Timber.i("Requesting new token..."); // POST json request to https://produc/tion-api.androidacy.com/auth/register token = new String(Http.doHttpPost("https://" + this.host + "/auth/register", "{\"device_id\":\"" + deviceId + "\"}", false)); // Parse token @@ -253,14 +252,14 @@ public final class AndroidacyRepoData extends RepoData { memberLevel = jsonObject.getString("role"); } catch ( JSONException e) { - Log.e(TAG, "Failed to parse token", e); + Timber.e(e, "Failed to parse token"); // Show a toast Toast.makeText(MainApplication.getINSTANCE(), R.string.androidacy_failed_to_parse_token, Toast.LENGTH_LONG).show(); return false; } // Ensure token is valid if (!isValidToken(token)) { - Log.e(TAG, "Failed to validate token"); + Timber.e("Failed to validate token"); // Show a toast Toast.makeText(MainApplication.getINSTANCE(), R.string.androidacy_failed_to_validate_token, Toast.LENGTH_LONG).show(); return false; @@ -272,10 +271,10 @@ public final class AndroidacyRepoData extends RepoData { } catch ( Exception e) { if (HttpException.shouldTimeout(e)) { - Log.e(TAG, "We are being rate limited!", e); + Timber.e(e, "We are being rate limited!"); this.androidacyBlockade = time + 3_600_000L; } - Log.e(TAG, "Failed to get a new token", e); + Timber.e(e, "Failed to get a new token"); return false; } } @@ -286,9 +285,7 @@ public final class AndroidacyRepoData extends RepoData { @Override protected List populate(JSONObject jsonObject) throws JSONException, NoSuchAlgorithmException { - if (BuildConfig.DEBUG) { - Log.d(TAG, "AndroidacyRepoData populate start"); - } + Timber.d("AndroidacyRepoData populate start"); if (!jsonObject.getString("status").equals("success")) throw new JSONException("Response is not a success!"); String name = jsonObject.optString("name", "Androidacy Modules Repo"); @@ -409,12 +406,12 @@ public final class AndroidacyRepoData extends RepoData { return url; if (this.testMode) { if (url.startsWith("https://production-api.androidacy.com/")) { - Log.e(TAG, "Got non test mode url: " + AndroidacyUtil.hideToken(url)); + Timber.e("Got non test mode url: %s", AndroidacyUtil.hideToken(url)); url = "https://staging-api.androidacy.com/" + url.substring(38); } } else { if (url.startsWith("https://staging-api.androidacy.com/")) { - Log.e(TAG, "Got test mode url: " + AndroidacyUtil.hideToken(url)); + Timber.e("Got test mode url: %s", AndroidacyUtil.hideToken(url)); url = "https://production-api.androidacy.com/" + url.substring(35); } } diff --git a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java index 1d058a5..d8f2ce9 100644 --- a/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java +++ b/app/src/main/java/com/fox2code/mmm/androidacy/AndroidacyWebAPI.java @@ -5,7 +5,6 @@ import android.content.res.Resources; import android.graphics.Color; import android.net.Uri; import android.os.Build; -import android.util.Log; import android.util.TypedValue; import android.view.View; import android.webkit.JavascriptInterface; @@ -26,20 +25,21 @@ import com.fox2code.mmm.manager.ModuleInfo; import com.fox2code.mmm.manager.ModuleManager; import com.fox2code.mmm.repo.RepoModule; import com.fox2code.mmm.utils.ExternalHelper; +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.IntentHelper; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; +import timber.log.Timber; + @Keep public class AndroidacyWebAPI { public static final int COMPAT_UNSUPPORTED = 0; public static final int COMPAT_DOWNLOAD = 1; - private static final String TAG = "AndroidacyWebAPI"; private static final int MAX_COMPAT_MODE = 1; private final AndroidacyActivity activity; private final boolean allowInstall; @@ -63,7 +63,7 @@ public class AndroidacyWebAPI { void openNativeModuleDialogRaw(String moduleUrl, String moduleId, String installTitle, String checksum, boolean canInstall) { if (BuildConfig.DEBUG) - Log.d(TAG, "ModuleDialog, downloadUrl: " + AndroidacyUtil.hideToken(moduleUrl) + ", moduleId: " + moduleId + ", installTitle: " + installTitle + ", checksum: " + checksum + ", canInstall: " + canInstall); + Timber.d("ModuleDialog, downloadUrl: " + AndroidacyUtil.hideToken(moduleUrl) + ", moduleId: " + moduleId + ", installTitle: " + installTitle + ", checksum: " + checksum + ", canInstall: " + canInstall); this.downloadMode = false; RepoModule repoModule = AndroidacyRepoData.getInstance().moduleHashMap.get(installTitle); String title, description; @@ -113,7 +113,7 @@ public class AndroidacyWebAPI { return this.activity.downloadFileAsync(moduleUrl); } catch ( IOException e) { - Log.e(TAG, "Failed to download module", e); + Timber.e(e, "Failed to download module"); AndroidacyWebAPI.this.activity.runOnUiThread(() -> Toast.makeText(AndroidacyWebAPI.this.activity, R.string.failed_download, Toast.LENGTH_SHORT).show()); return null; } @@ -135,7 +135,7 @@ public class AndroidacyWebAPI { if (this.consumedAction) return; if (BuildConfig.DEBUG) - Log.d(TAG, "Androidacy Compat mode: " + value); + Timber.d("Androidacy Compat mode: %s", value); this.notifiedCompatMode = value; if (value < 0) { value = 0; @@ -173,7 +173,7 @@ public class AndroidacyWebAPI { this.consumedAction = true; this.downloadMode = false; if (BuildConfig.DEBUG) - Log.d(TAG, "Received openUrl request: " + url); + Timber.d("Received openUrl request: %s", url); if (Uri.parse(url).getScheme().equals("https")) { IntentHelper.openUrl(this.activity, url); } @@ -189,7 +189,7 @@ public class AndroidacyWebAPI { this.consumedAction = true; this.downloadMode = false; if (BuildConfig.DEBUG) - Log.d(TAG, "Received openCustomTab request: " + url); + Timber.d("Received openCustomTab request: %s", url); if (Uri.parse(url).getScheme().equals("https")) { IntentHelper.openCustomTab(this.activity, url); } @@ -233,14 +233,14 @@ public class AndroidacyWebAPI { this.consumedAction = true; this.downloadMode = false; if (BuildConfig.DEBUG) - Log.d(TAG, "Received install request: " + moduleUrl + " " + installTitle + " " + checksum); + Timber.d("Received install request: " + moduleUrl + " " + installTitle + " " + checksum); if (!AndroidacyUtil.isAndroidacyLink(moduleUrl)) { this.forceQuitRaw("Non Androidacy module link used on Androidacy"); return; } checksum = Hashes.checkSumFormat(checksum); if (checksum == null || checksum.isEmpty()) { - Log.w(TAG, "Androidacy WebView didn't provided a checksum!"); + Timber.w("Androidacy WebView didn't provided a checksum!"); } else if (!Hashes.checkSumValid(checksum)) { this.forceQuitRaw("Androidacy didn't provided a valid checksum"); return; @@ -284,7 +284,7 @@ public class AndroidacyWebAPI { } checksum = Hashes.checkSumFormat(checksum); if (checksum == null || checksum.isEmpty()) { - Log.w(TAG, "Androidacy WebView didn't provided a checksum!"); + Timber.w("Androidacy WebView didn't provided a checksum!"); } else if (!Hashes.checkSumValid(checksum)) { this.forceQuitRaw("Androidacy didn't provided a valid checksum"); return; diff --git a/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java b/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java index e3d0acd..12dcf0f 100644 --- a/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java +++ b/app/src/main/java/com/fox2code/mmm/background/BackgroundUpdateChecker.java @@ -73,6 +73,9 @@ public class BackgroundUpdateChecker extends Worker { if (ActivityCompat.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()) + return; NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, builder.build()); } diff --git a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java index 9c12463..3821d12 100644 --- a/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java +++ b/app/src/main/java/com/fox2code/mmm/installer/InstallerActivity.java @@ -7,7 +7,6 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Bundle; -import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.WindowManager; @@ -29,10 +28,10 @@ import com.fox2code.mmm.XHooks; import com.fox2code.mmm.androidacy.AndroidacyUtil; import com.fox2code.mmm.module.ActionButtonType; 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.IntentHelper; import com.fox2code.mmm.utils.io.PropUtils; import com.fox2code.mmm.utils.sentry.SentryBreadcrumb; import com.fox2code.mmm.utils.sentry.SentryMain; @@ -56,9 +55,10 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; +import timber.log.Timber; + @SuppressWarnings("IOStreamConstructor") public class InstallerActivity extends FoxActivity { - private static final String TAG = "InstallerActivity"; public LinearProgressIndicator progressIndicator; public ExtendedFloatingActionButton rebootFloatingButton; public InstallerTerminal installerTerminal; @@ -68,12 +68,14 @@ public class InstallerActivity extends FoxActivity { private boolean canceled; private boolean warnReboot; + private static final HashSet extracted = new HashSet<>(); + @Override protected void onCreate(Bundle savedInstanceState) { this.warnReboot = false; this.moduleCache = new File(this.getCacheDir(), "installer"); if (!this.moduleCache.exists() && !this.moduleCache.mkdirs()) - Log.e(TAG, "Failed to mkdir module cache dir!"); + Timber.e("Failed to mkdir module cache dir!"); super.onCreate(savedInstanceState); this.setDisplayHomeAsUpEnabled(true); setActionBarBackground(null); @@ -91,7 +93,7 @@ public class InstallerActivity extends FoxActivity { // Should we allow 3rd part app to install modules? if (Constants.INTENT_INSTALL_INTERNAL.equals(intent.getAction())) { if (!MainApplication.checkSecret(intent)) { - Log.e(TAG, "Security check failed!"); + Timber.e("Security check failed!"); this.forceBackPressed(); return; } @@ -109,7 +111,7 @@ public class InstallerActivity extends FoxActivity { this.forceBackPressed(); return; } - Log.i(TAG, "Install link: " + target); + Timber.i("Install link: %s", target); // Note: Sentry only send this info on crash. if (MainApplication.isCrashReportingEnabled()) { SentryBreadcrumb breadcrumb = new SentryBreadcrumb(); @@ -158,13 +160,13 @@ public class InstallerActivity extends FoxActivity { new File(this.moduleCache, "module.zip") : new File(target); if (urlMode && moduleCache.exists() && !moduleCache.delete() && !new SuFile(moduleCache.getAbsolutePath()).delete()) - Log.e(TAG, "Failed to delete module cache"); + Timber.e("Failed to delete module cache"); String errMessage = "Failed to download module zip"; // Set this to the error message if it's a HTTP error byte[] rawModule; boolean androidacyBlame = false; // In case Androidacy mess-up again... yeah screw you too jk jk try { - Log.i(TAG, (urlMode ? "Downloading: " : "Loading: ") + target); + Timber.i("%s%s", (urlMode ? "Downloading: " : "Loading: "), target); rawModule = urlMode ? Http.doHttpGet(target, (progress, max, done) -> { if (max <= 0 && this.progressIndicator.isIndeterminate()) return; @@ -181,7 +183,7 @@ public class InstallerActivity extends FoxActivity { if (this.canceled) return; androidacyBlame = urlMode && AndroidacyUtil.isAndroidacyFileUrl(target); if (checksum != null && !checksum.isEmpty()) { - Log.i(TAG, "Checking for checksum: " + checksum); + Timber.i("Checking for checksum: %s", checksum); this.runOnUiThread(() -> this.installerTerminal.addLine("- Checking file integrity")); if (!Hashes.checkSumMatch(rawModule, checksum)) { this.setInstallStateFinished(false, @@ -244,7 +246,7 @@ public class InstallerActivity extends FoxActivity { } else { errMessage = "Failed to patch module zip"; this.runOnUiThread(() -> this.installerTerminal.addLine("- Patching " + name)); - Log.i(TAG, "Patching: " + moduleCache.getName()); + Timber.i("Patching: %s", moduleCache.getName()); try (OutputStream outputStream = new FileOutputStream(moduleCache)) { Files.patchModuleSimple(rawModule, outputStream); outputStream.flush(); @@ -257,7 +259,7 @@ public class InstallerActivity extends FoxActivity { errMessage = "Failed to install module zip"; this.doInstall(moduleCache, noExtensions, rootless); } catch (IOException e) { - Log.e(TAG, errMessage, e); + Timber.e(e); if (androidacyBlame) { errMessage += " (" + e.getLocalizedMessage() + ")"; } @@ -267,14 +269,13 @@ public class InstallerActivity extends FoxActivity { rawModule = null; // Because reference is kept when calling setInstallStateFinished if ("Failed to install module zip".equals(errMessage)) throw e; // Ignore if in installation state. - Log.e(TAG, "Module too large", e); + Timber.e(e); this.setInstallStateFinished(false, "! Module is too large to be loaded on this device", ""); } }, "Module install Thread").start(); } - @Keep private void doInstall(File file, boolean noExtensions, boolean rootless) { if (this.canceled) return; @@ -282,7 +283,7 @@ public class InstallerActivity extends FoxActivity { this.setOnBackPressedCallback(DISABLE_BACK_BUTTON); this.setDisplayHomeAsUpEnabled(false); }); - Log.i(TAG, "Installing: " + moduleCache.getName()); + Timber.i("Installing: %s", moduleCache.getName()); InstallerController installerController = new InstallerController( this.progressIndicator, this.installerTerminal, file.getAbsoluteFile(), noExtensions); @@ -306,12 +307,12 @@ public class InstallerActivity extends FoxActivity { } } } catch (Exception e) { - Log.i(TAG, "Failed ot extract install script via java code", e); + Timber.i(e); } installerMonitor = new InstallerMonitor(installScript); installJob = Shell.cmd("export MMM_EXT_SUPPORT=1", "export MMM_USER_LANGUAGE=" + this.getResources() - .getConfiguration().locale.toLanguageTag(), + .getConfiguration().getLocales().get(0).toLanguageTag(), "export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME, "export MMM_TEXT_WRAP=" + (this.textWrap ? "1" : "0"), AnsiConstants.ANSI_CMD_SUPPORT, @@ -469,7 +470,7 @@ public class InstallerActivity extends FoxActivity { else this.installerTerminal.enableAnsi(); installJob = Shell.cmd(arch32, "export MMM_EXT_SUPPORT=1", "export MMM_USER_LANGUAGE=" + this.getResources() - .getConfiguration().locale.toLanguageTag(), + .getConfiguration().getLocales().get(0).toLanguageTag(), "export MMM_APP_VERSION=" + BuildConfig.VERSION_NAME, "export MMM_TEXT_WRAP=" + (this.textWrap ? "1" : "0"), this.installerTerminal.isAnsiEnabled() ? @@ -495,7 +496,7 @@ public class InstallerActivity extends FoxActivity { SentryMain.addSentryBreadcrumb(breadcrumb); } if (mmtReborn && magiskCmdLine) { - Log.w(TAG, "mmtReborn and magiskCmdLine may not work well together"); + Timber.w("mmtReborn and magiskCmdLine may not work well together"); } } boolean success = installJob.exec().isSuccess(); @@ -515,6 +516,105 @@ public class InstallerActivity extends FoxActivity { installerController.getSupportLink()); } + private File extractInstallScript(String script) { + File compatInstallScript = new File(this.moduleCache, script); + if (!compatInstallScript.exists() || compatInstallScript.length() == 0 || + !extracted.contains(script)) { + try { + Files.write(compatInstallScript, Files.readAllBytes( + this.getAssets().open(script))); + extracted.add(script); + } catch (IOException e) { + if (compatInstallScript.delete()) + extracted.remove(script); + Timber.e(e); + return null; + } + } + return compatInstallScript; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + int keyCode = event.getKeyCode(); + if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || + keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) return true; + return super.dispatchKeyEvent(event); + } + + @SuppressLint("RestrictedApi") + @SuppressWarnings("SameParameterValue") + private void setInstallStateFinished(boolean success, String message, String optionalLink) { + this.installerTerminal.disableAnsi(); + if (success && toDelete != null && !toDelete.delete()) { + SuFile suFile = new SuFile(toDelete.getAbsolutePath()); + if (suFile.exists() && !suFile.delete()) + Timber.w("Failed to delete zip file"); + else toDelete = null; + } else toDelete = null; + this.runOnUiThread(() -> { + this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, 0); + // Set the back press to finish the activity and return to the main activity + this.setOnBackPressedCallback(a -> { + this.finishAndRemoveTask(); + startActivity(new Intent(this, MainActivity.class)); + return true; + }); + this.setDisplayHomeAsUpEnabled(true); + this.progressIndicator.setVisibility(View.GONE); + + // This should be improved ? + String reboot_cmd = "/system/bin/svc power reboot || /system/bin/reboot || setprop sys.powerctl reboot"; + this.rebootFloatingButton.setOnClickListener(_view -> { + if (this.warnReboot || MainApplication.shouldPreventReboot()) { + MaterialAlertDialogBuilder builder = + new MaterialAlertDialogBuilder(this); + + builder + .setTitle(R.string.install_terminal_reboot_now) + .setMessage(R.string.install_terminal_reboot_now_message) + .setCancelable(false) + .setIcon(R.drawable.ic_reboot_24) + .setPositiveButton(R.string.ok, (x, y) -> Shell.cmd(reboot_cmd).submit()) + .setNegativeButton(R.string.no, (x, y) -> x.dismiss()).show(); + } else { + Shell.cmd(reboot_cmd).submit(); + } + }); + this.rebootFloatingButton.setVisibility(View.VISIBLE); + + if (message != null && !message.isEmpty()) + this.installerTerminal.addLine(message); + if (optionalLink != null && !optionalLink.isEmpty()) { + this.setActionBarExtraMenuButton(ActionButtonType.supportIconForUrl(optionalLink), + menu -> { + IntentHelper.openUrl(this, optionalLink); + return true; + }); + } else if (success) { + final Intent intent = this.getIntent(); + final String config = MainApplication.checkSecret(intent) ? + intent.getStringExtra(Constants.EXTRA_INSTALL_CONFIG) : null; + if (config != null && !config.isEmpty()) { + String configPkg = IntentHelper.getPackageOfConfig(config); + try { + XHooks.checkConfigTargetExists(this, configPkg, config); + this.setActionBarExtraMenuButton(R.drawable.ic_baseline_app_settings_alt_24, menu -> { + IntentHelper.openConfig(this, config); + return true; + }); + } catch (PackageManager.NameNotFoundException e) { + Timber.w("Config package \"" + + configPkg + "\" missing for installer view"); + this.installerTerminal.addLine(String.format( + this.getString(R.string.install_terminal_config_missing), + configPkg)); + } + } + } + }); + } + public static class InstallerController extends CallbackList { private final LinearProgressIndicator progressIndicator; private final InstallerTerminal terminal; @@ -538,7 +638,7 @@ public class InstallerActivity extends FoxActivity { @Override public void onAddElement(String s) { if (!this.enabled) return; - Log.i(TAG, "MSG: " + s); + Timber.i("MSG: %s", s); if ("#!useExt".equals(s.trim()) && !this.noExtension) { this.useExt = true; return; @@ -668,14 +768,6 @@ public class InstallerActivity extends FoxActivity { } } - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - int keyCode = event.getKeyCode(); - if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || - keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) return true; - return super.dispatchKeyEvent(event); - } - public static class InstallerMonitor extends CallbackList { private static final String DEFAULT_ERR = "! Install failed"; private final String installScriptErr; @@ -691,7 +783,7 @@ public class InstallerActivity extends FoxActivity { @Override public void onAddElement(String s) { - Log.i(TAG, "Monitor: " + s); + Timber.i("Monitor: %s", s); this.lastCommand = s; } @@ -710,107 +802,15 @@ public class InstallerActivity extends FoxActivity { SuFile moduleUpdate = new SuFile("/data/adb/modules_update/" + module); if (moduleUpdate.exists()) { if (!moduleUpdate.deleteRecursive()) - Log.e(TAG, "Failed to delete failed update"); + Timber.e("Failed to delete failed update"); return "Error: " + installScriptErr.substring(i + 1); } } else if (this.forCleanUp != null) { SuFile moduleUpdate = new SuFile("/data/adb/modules_update/" + this.forCleanUp); if (moduleUpdate.exists() && !moduleUpdate.deleteRecursive()) - Log.e(TAG, "Failed to delete failed update"); + Timber.e("Failed to delete failed update"); } return DEFAULT_ERR; } } - - private static final HashSet extracted = new HashSet<>(); - private File extractInstallScript(String script) { - File compatInstallScript = new File(this.moduleCache, script); - if (!compatInstallScript.exists() || compatInstallScript.length() == 0 || - !extracted.contains(script)) { - try { - Files.write(compatInstallScript, Files.readAllBytes( - this.getAssets().open(script))); - extracted.add(script); - } catch (IOException e) { - if (compatInstallScript.delete()) - extracted.remove(script); - Log.e(TAG, "Failed to extract " + script, e); - return null; - } - } - return compatInstallScript; - } - - @SuppressLint("RestrictedApi") - @SuppressWarnings("SameParameterValue") - private void setInstallStateFinished(boolean success, String message, String optionalLink) { - this.installerTerminal.disableAnsi(); - if (success && toDelete != null && !toDelete.delete()) { - SuFile suFile = new SuFile(toDelete.getAbsolutePath()); - if (suFile.exists() && !suFile.delete()) - Log.w(TAG, "Failed to delete zip file"); - else toDelete = null; - } else toDelete = null; - this.runOnUiThread(() -> { - this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, 0); - // Set the back press to finish the activity and return to the main activity - this.setOnBackPressedCallback(a -> { - this.finishAndRemoveTask(); - startActivity(new Intent(this, MainActivity.class)); - return true; - }); - this.setDisplayHomeAsUpEnabled(true); - this.progressIndicator.setVisibility(View.GONE); - - // This should be improved ? - String reboot_cmd = "/system/bin/svc power reboot || /system/bin/reboot || setprop sys.powerctl reboot"; - this.rebootFloatingButton.setOnClickListener(_view -> { - if (this.warnReboot || MainApplication.shouldPreventReboot()) { - MaterialAlertDialogBuilder builder = - new MaterialAlertDialogBuilder(this); - - builder - .setTitle(R.string.install_terminal_reboot_now) - .setMessage(R.string.install_terminal_reboot_now_message) - .setCancelable(false) - .setIcon(R.drawable.ic_reboot_24) - .setPositiveButton(R.string.ok, (x, y) -> Shell.cmd(reboot_cmd).submit()) - .setNegativeButton(R.string.no, (x, y) -> x.dismiss()).show(); - } else { - Shell.cmd(reboot_cmd).submit(); - } - }); - this.rebootFloatingButton.setVisibility(View.VISIBLE); - - if (message != null && !message.isEmpty()) - this.installerTerminal.addLine(message); - if (optionalLink != null && !optionalLink.isEmpty()) { - this.setActionBarExtraMenuButton(ActionButtonType.supportIconForUrl(optionalLink), - menu -> { - IntentHelper.openUrl(this, optionalLink); - return true; - }); - } else if (success) { - final Intent intent = this.getIntent(); - final String config = MainApplication.checkSecret(intent) ? - intent.getStringExtra(Constants.EXTRA_INSTALL_CONFIG) : null; - if (config != null && !config.isEmpty()) { - String configPkg = IntentHelper.getPackageOfConfig(config); - try { - XHooks.checkConfigTargetExists(this, configPkg, config); - this.setActionBarExtraMenuButton(R.drawable.ic_baseline_app_settings_alt_24, menu -> { - IntentHelper.openConfig(this, config); - return true; - }); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Config package \"" + - configPkg + "\" missing for installer view"); - this.installerTerminal.addLine(String.format( - this.getString(R.string.install_terminal_config_missing), - configPkg)); - } - } - } - }); - } } diff --git a/app/src/main/java/com/fox2code/mmm/installer/InstallerInitializer.java b/app/src/main/java/com/fox2code/mmm/installer/InstallerInitializer.java index b052b31..a67b38e 100644 --- a/app/src/main/java/com/fox2code/mmm/installer/InstallerInitializer.java +++ b/app/src/main/java/com/fox2code/mmm/installer/InstallerInitializer.java @@ -1,7 +1,6 @@ package com.fox2code.mmm.installer; import android.content.Context; -import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -16,8 +15,9 @@ import com.topjohnwu.superuser.Shell; import java.io.File; import java.util.ArrayList; +import timber.log.Timber; + public class InstallerInitializer extends Shell.Initializer { - private static final String TAG = "InstallerInitializer"; private static final File MAGISK_SBIN = new File("/sbin/magisk"); private static final File MAGISK_SYSTEM = @@ -30,7 +30,6 @@ public class InstallerInitializer extends Shell.Initializer { private static int MAGISK_VERSION_CODE; private static boolean HAS_RAMDISK; - public static final int ERROR_OK = 0; public static final int ERROR_NO_PATH = 1; public static final int ERROR_NO_SU = 2; public static final int ERROR_OTHER = 3; @@ -98,10 +97,10 @@ public class InstallerInitializer extends Shell.Initializer { error = ERROR_NO_PATH; } catch (NoShellException e) { error = ERROR_NO_SU; - Log.w(TAG, "Device doesn't have root!", e); + Timber.w(e); } catch (Throwable e) { error = ERROR_OTHER; - Log.e(TAG, "Something bad happened", e); + Timber.e(e); } if (forceCheck) { InstallerInitializer.MAGISK_PATH = MAGISK_PATH; @@ -138,9 +137,9 @@ public class InstallerInitializer extends Shell.Initializer { return null; } MAGISK_PATH = output.size() < 3 ? "" : output.get(2); - Log.i(TAG, "Magisk runtime path: " + MAGISK_PATH); + Timber.i("Magisk runtime path: %s", MAGISK_PATH); MAGISK_VERSION_CODE = Integer.parseInt(output.get(1)); - Log.i(TAG, "Magisk version code: " + MAGISK_VERSION_CODE); + Timber.i("Magisk version code: %s", MAGISK_VERSION_CODE); if (MAGISK_VERSION_CODE >= Constants.MAGISK_VER_CODE_FLAT_MODULES && MAGISK_VERSION_CODE < Constants.MAGISK_VER_CODE_PATH_SUPPORT && (MAGISK_PATH.isEmpty() || !new File(MAGISK_PATH).exists())) { @@ -149,7 +148,7 @@ public class InstallerInitializer extends Shell.Initializer { if (MAGISK_PATH.length() != 0 && Files.existsSU(new File(MAGISK_PATH))) { InstallerInitializer.MAGISK_PATH = MAGISK_PATH; } else { - Log.e(TAG, "Failed to get Magisk path (Got " + MAGISK_PATH + ")"); + Timber.e("Failed to get Magisk path (Got " + MAGISK_PATH + ")"); MAGISK_PATH = null; } InstallerInitializer.MAGISK_VERSION_CODE = MAGISK_VERSION_CODE; diff --git a/app/src/main/java/com/fox2code/mmm/manager/LocalModuleInfo.java b/app/src/main/java/com/fox2code/mmm/manager/LocalModuleInfo.java index bcc980e..8155e29 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/LocalModuleInfo.java +++ b/app/src/main/java/com/fox2code/mmm/manager/LocalModuleInfo.java @@ -1,7 +1,5 @@ package com.fox2code.mmm.manager; -import android.util.Log; - import com.fox2code.mmm.markdown.MarkdownUrlLinker; import com.fox2code.mmm.utils.FastException; import com.fox2code.mmm.utils.io.Http; @@ -12,6 +10,8 @@ import org.json.JSONObject; import java.io.IOException; import java.nio.charset.StandardCharsets; +import timber.log.Timber; + public class LocalModuleInfo extends ModuleInfo { public String updateVersion; public long updateVersionCode = Long.MIN_VALUE; @@ -48,7 +48,7 @@ public class LocalModuleInfo extends ModuleInfo { this.updateVersion = PropUtils.shortenVersionName( this.updateVersion.trim(), this.updateVersionCode); if (this.updateChangeLog.length() > 1000) - this.updateChangeLog = this.updateChangeLog.substring(0, 1000); + this.updateChangeLog = this.updateChangeLog.substring(1000); this.updateChangeLog = MarkdownUrlLinker.urlLinkify(this.updateChangeLog); } catch (Exception e) { this.updateVersion = null; @@ -56,8 +56,7 @@ public class LocalModuleInfo extends ModuleInfo { this.updateZipUrl = null; this.updateChangeLog = ""; this.updateChecksum = null; - Log.w("LocalModuleInfo", - "Failed update checking for module: " + this.id, e); + Timber.w(e, "Failed update checking for module: %s", this.id); } } } diff --git a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java index f2fc109..c475bab 100644 --- a/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java +++ b/app/src/main/java/com/fox2code/mmm/manager/ModuleManager.java @@ -1,15 +1,14 @@ package com.fox2code.mmm.manager; import android.content.SharedPreferences; -import android.util.Log; import androidx.annotation.NonNull; import com.fox2code.mmm.BuildConfig; import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.installer.InstallerInitializer; -import com.fox2code.mmm.utils.io.PropUtils; import com.fox2code.mmm.utils.SyncManager; +import com.fox2code.mmm.utils.io.PropUtils; import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.io.SuFile; import com.topjohnwu.superuser.io.SuFileInputStream; @@ -21,14 +20,14 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Iterator; +import timber.log.Timber; + public final class ModuleManager extends SyncManager { // New method is not really effective, this flag force app to use old method public static final boolean FORCE_NEED_FALLBACK = true; - private static final String TAG = "ModuleManager"; private static final int FLAG_MM_INVALID = ModuleInfo.FLAG_METADATA_INVALID; private static final int FLAG_MM_UNPROCESSED = ModuleInfo.FLAG_CUSTOM_INTERNAL; - private static final int FLAGS_KEEP_INIT = FLAG_MM_UNPROCESSED | - ModuleInfo.FLAGS_MODULE_ACTIVE | ModuleInfo.FLAG_MODULE_UPDATING_ONLY; + private static final int FLAGS_KEEP_INIT = FLAG_MM_UNPROCESSED | ModuleInfo.FLAGS_MODULE_ACTIVE | ModuleInfo.FLAG_MODULE_UPDATING_ONLY; private static final int FLAGS_RESET_UPDATE = FLAG_MM_INVALID | FLAG_MM_UNPROCESSED; private static final ModuleManager INSTANCE = new ModuleManager(); private final HashMap moduleInfos; @@ -65,17 +64,18 @@ public final class ModuleManager extends SyncManager { } String modulesPath = InstallerInitializer.peekModulesPath(); String[] modules = new SuFile("/data/adb/modules").list(); - boolean needFallback = FORCE_NEED_FALLBACK || - modulesPath == null || !new SuFile(modulesPath).exists(); + boolean needFallback = FORCE_NEED_FALLBACK || modulesPath == null || !new SuFile(modulesPath).exists(); if (!FORCE_NEED_FALLBACK && needFallback) { - Log.e(TAG, "Failed to detect modules folder, using fallback instead."); + Timber.e("using fallback instead."); } - if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Scan"); + if (BuildConfig.DEBUG) + Timber.d("Scan"); if (modules != null) { for (String module : modules) { if (!new SuFile("/data/adb/modules/" + module).isDirectory()) continue; // Ignore non directory files inside modules folder - if (BuildConfig.DEBUG) Log.d("NoodleDebug", module); + if (BuildConfig.DEBUG) + Timber.d(module); LocalModuleInfo moduleInfo = moduleInfos.get(module); if (moduleInfo == null) { moduleInfo = new LocalModuleInfo(module); @@ -93,8 +93,7 @@ public final class ModuleManager extends SyncManager { if (new SuFile("/data/adb/modules/" + module + "/remove").exists()) { moduleInfo.flags |= ModuleInfo.FLAG_MODULE_UNINSTALLING; } - if ((firstScan && !needFallback && new SuFile(modulesPath, module).exists()) || - bootPrefs.getBoolean("module_" + moduleInfo.id + "_active", false)) { + if ((firstScan && !needFallback && new SuFile(modulesPath, module).exists()) || bootPrefs.getBoolean("module_" + moduleInfo.id + "_active", false)) { moduleInfo.flags |= ModuleInfo.FLAG_MODULE_ACTIVE; if (firstScan) { editor.putBoolean("module_" + moduleInfo.id + "_active", true); @@ -102,29 +101,28 @@ public final class ModuleManager extends SyncManager { } else if (!needFallback) { moduleInfo.flags &= ~ModuleInfo.FLAG_MODULE_ACTIVE; } - if ((moduleInfo.flags & ModuleInfo.FLAGS_MODULE_ACTIVE) != 0 - && (new SuFile("/data/adb/modules/" + module + "/system").exists() || - new SuFile("/data/adb/modules/" + module + "/vendor").exists() || - new SuFile("/data/adb/modules/" + module + "/zygisk").exists() || - new SuFile("/data/adb/modules/" + module + "/riru").exists())) { + if ((moduleInfo.flags & ModuleInfo.FLAGS_MODULE_ACTIVE) != 0 && (new SuFile("/data/adb/modules/" + module + "/system").exists() || new SuFile("/data/adb/modules/" + module + "/vendor").exists() || new SuFile("/data/adb/modules/" + module + "/zygisk").exists() || new SuFile("/data/adb/modules/" + module + "/riru").exists())) { moduleInfo.flags |= ModuleInfo.FLAG_MODULE_HAS_ACTIVE_MOUNT; } try { - PropUtils.readProperties(moduleInfo, - "/data/adb/modules/" + module + "/module.prop", true); - } catch (Exception e) { - if (BuildConfig.DEBUG) Log.d(TAG, "Failed to parse metadata!", e); + PropUtils.readProperties(moduleInfo, "/data/adb/modules/" + module + "/module.prop", true); + } catch ( + Exception e) { + if (BuildConfig.DEBUG) + Timber.d(e); moduleInfo.flags |= FLAG_MM_INVALID; } } } - if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Scan update"); + if (BuildConfig.DEBUG) + Timber.d("Scan update"); String[] modules_update = new SuFile("/data/adb/modules_update").list(); if (modules_update != null) { for (String module : modules_update) { if (!new SuFile("/data/adb/modules_update/" + module).isDirectory()) continue; // Ignore non directory files inside modules folder - if (BuildConfig.DEBUG) Log.d("NoodleDebug", module); + if (BuildConfig.DEBUG) + Timber.d(module); LocalModuleInfo moduleInfo = moduleInfos.get(module); if (moduleInfo == null) { moduleInfo = new LocalModuleInfo(module); @@ -133,21 +131,23 @@ public final class ModuleManager extends SyncManager { moduleInfo.flags &= ~FLAGS_RESET_UPDATE; moduleInfo.flags |= ModuleInfo.FLAG_MODULE_UPDATING; try { - PropUtils.readProperties(moduleInfo, - "/data/adb/modules_update/" + module + "/module.prop", true); - } catch (Exception e) { - if (BuildConfig.DEBUG) Log.d(TAG, "Failed to parse metadata!", e); + PropUtils.readProperties(moduleInfo, "/data/adb/modules_update/" + module + "/module.prop", true); + } catch ( + Exception e) { + if (BuildConfig.DEBUG) + Timber.d(e); moduleInfo.flags |= FLAG_MM_INVALID; } } } - if (BuildConfig.DEBUG) Log.d("NoodleDebug", "Finalize scan"); + if (BuildConfig.DEBUG) + Timber.d("Finalize scan"); this.updatableModuleCount = 0; - Iterator moduleInfoIterator = - this.moduleInfos.values().iterator(); + Iterator moduleInfoIterator = this.moduleInfos.values().iterator(); while (moduleInfoIterator.hasNext()) { LocalModuleInfo moduleInfo = moduleInfoIterator.next(); - if (BuildConfig.DEBUG) Log.d("NoodleDebug", moduleInfo.id); + if (BuildConfig.DEBUG) + Timber.d(moduleInfo.id); if ((moduleInfo.flags & FLAG_MM_UNPROCESSED) != 0) { moduleInfoIterator.remove(); continue; // Don't process fallbacks if unreferenced @@ -161,8 +161,7 @@ public final class ModuleManager extends SyncManager { moduleInfo.updateChangeLog = ""; } if (moduleInfo.name == null || (moduleInfo.name.equals(moduleInfo.id))) { - moduleInfo.name = Character.toUpperCase(moduleInfo.id.charAt(0)) + - moduleInfo.id.substring(1).replace('_', ' '); + moduleInfo.name = Character.toUpperCase(moduleInfo.id.charAt(0)) + moduleInfo.id.substring(1).replace('_', ' '); } if (moduleInfo.version == null || moduleInfo.version.trim().isEmpty()) { moduleInfo.version = "v" + moduleInfo.versionCode; @@ -186,7 +185,8 @@ public final class ModuleManager extends SyncManager { } public boolean setEnabledState(ModuleInfo moduleInfo, boolean checked) { - if (moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING) && !checked) return false; + if (moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING) && !checked) + return false; SuFile disable = new SuFile("/data/adb/modules/" + moduleInfo.id + "/disable"); if (checked) { if (disable.exists() && !disable.delete()) { @@ -204,7 +204,8 @@ public final class ModuleManager extends SyncManager { } public boolean setUninstallState(ModuleInfo moduleInfo, boolean checked) { - if (checked && moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING)) return false; + if (checked && moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_UPDATING)) + return false; SuFile disable = new SuFile("/data/adb/modules/" + moduleInfo.id + "/remove"); if (checked) { if (!disable.exists() && !disable.createNewFile()) { @@ -222,26 +223,22 @@ public final class ModuleManager extends SyncManager { } public boolean masterClear(ModuleInfo moduleInfo) { - if (moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_HAS_ACTIVE_MOUNT)) return false; - String escapedId = moduleInfo.id.replace("\\", "\\\\") - .replace("\"", "\\\"").replace(" ", "\\ "); + if (moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_HAS_ACTIVE_MOUNT)) + return false; + String escapedId = moduleInfo.id.replace("\\", "\\\\").replace("\"", "\\\"").replace(" ", "\\ "); try { // Check for module that declare having file outside their own folder. - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( - SuFileInputStream.open("/data/adb/modules/." + moduleInfo.id + "-files"), - StandardCharsets.UTF_8))) { + try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(SuFileInputStream.open("/data/adb/modules/." + moduleInfo.id + "-files"), StandardCharsets.UTF_8))) { String line; while ((line = bufferedReader.readLine()) != null) { line = line.trim().replace(' ', '.'); - if (!line.startsWith("/data/adb/") || line.contains("*") || - line.contains("/../") || line.endsWith("/..") || - line.startsWith("/data/adb/modules") || - line.equals("/data/adb/magisk.db")) continue; - line = line.replace("\\", "\\\\") - .replace("\"", "\\\""); + if (!line.startsWith("/data/adb/") || line.contains("*") || line.contains("/../") || line.endsWith("/..") || line.startsWith("/data/adb/modules") || line.equals("/data/adb/magisk.db")) + continue; + line = line.replace("\\", "\\\\").replace("\"", "\\\""); Shell.cmd("rm -rf \"" + line + "\"").exec(); } } - } catch (IOException ignored) { + } catch ( + IOException ignored) { } Shell.cmd("rm -rf /data/adb/modules/" + escapedId + "/").exec(); Shell.cmd("rm -f /data/adb/modules/." + escapedId + "-files").exec(); diff --git a/app/src/main/java/com/fox2code/mmm/markdown/MarkdownActivity.java b/app/src/main/java/com/fox2code/mmm/markdown/MarkdownActivity.java index c5164cd..7fef234 100644 --- a/app/src/main/java/com/fox2code/mmm/markdown/MarkdownActivity.java +++ b/app/src/main/java/com/fox2code/mmm/markdown/MarkdownActivity.java @@ -8,7 +8,6 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Bundle; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -24,8 +23,8 @@ import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.R; import com.fox2code.mmm.XHooks; import com.fox2code.mmm.utils.BlurUtils; -import com.fox2code.mmm.utils.io.Http; import com.fox2code.mmm.utils.IntentHelper; +import com.fox2code.mmm.utils.io.Http; import com.google.android.material.chip.Chip; import com.google.android.material.chip.ChipGroup; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -36,9 +35,9 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import eightbitlab.com.blurview.BlurView; +import timber.log.Timber; public class MarkdownActivity extends FoxActivity { - private static final String TAG = "MarkdownActivity"; private static final HashMap redirects = new HashMap<>(4); private static final String[] variants = new String[]{ "readme.md", "README.MD", ".github/README.md" @@ -81,7 +80,7 @@ public class MarkdownActivity extends FoxActivity { this.setDisplayHomeAsUpEnabled(true); Intent intent = this.getIntent(); if (!MainApplication.checkSecret(intent)) { - Log.e(TAG, "Impersonation detected!"); + Timber.e("Impersonation detected!"); this.forceBackPressed(); return; } @@ -116,11 +115,11 @@ public class MarkdownActivity extends FoxActivity { return true; }); } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Config package \"" + + Timber.w("Config package \"" + configPkg + "\" missing for markdown view"); } } - Log.i(TAG, "Url for markdown " + url); + Timber.i("Url for markdown %s", url); setContentView(R.layout.markdown_view); final ViewGroup markdownBackground = findViewById(R.id.markdownBackground); final TextView textView = findViewById(R.id.markdownView); @@ -145,11 +144,11 @@ public class MarkdownActivity extends FoxActivity { new Thread(() -> { try { - Log.i(TAG, "Downloading"); + Timber.i("Downloading"); byte[] rawMarkdown = getRawMarkdown(url); - Log.i(TAG, "Encoding"); + Timber.i("Encoding"); String markdown = new String(rawMarkdown, StandardCharsets.UTF_8); - Log.i(TAG, "Done!"); + Timber.i("Done!"); runOnUiThread(() -> { findViewById(R.id.markdownFooter) .setMinimumHeight(this.getNavigationBarHeight()); @@ -160,7 +159,7 @@ public class MarkdownActivity extends FoxActivity { } }); } catch (Exception e) { - Log.e(TAG, "Failed download", e); + Timber.e(e); runOnUiThread(() -> Toast.makeText(this, R.string.failed_download, Toast.LENGTH_SHORT).show()); } diff --git a/app/src/main/java/com/fox2code/mmm/markdown/MarkdownUrlLinker.java b/app/src/main/java/com/fox2code/mmm/markdown/MarkdownUrlLinker.java index d912f60..9f21459 100644 --- a/app/src/main/java/com/fox2code/mmm/markdown/MarkdownUrlLinker.java +++ b/app/src/main/java/com/fox2code/mmm/markdown/MarkdownUrlLinker.java @@ -1,50 +1,44 @@ package com.fox2code.mmm.markdown; -import android.util.Log; - -import com.fox2code.mmm.BuildConfig; - import java.util.ArrayList; +import timber.log.Timber; + public class MarkdownUrlLinker { - private static final String TAG = "MarkdownUrlLinker"; public static String urlLinkify(String url) { int index = url.indexOf("https://"); - if (index == -1) return url; + if (index == -1) + return url; ArrayList linkifyTasks = new ArrayList<>(); int extra = 0; while (index != -1) { int end = url.indexOf(' ', index); - end = end == -1 ? url.indexOf('\n', index) : - Math.min(url.indexOf('\n', index), end); - if (end == -1) end = url.length(); - if (index == 0 || - '\n' == url.charAt(index - 1) || - ' ' == url.charAt(index - 1)) { + end = end == -1 ? url.indexOf('\n', index) : Math.min(url.indexOf('\n', index), end); + if (end == -1) + end = url.length(); + if (index == 0 || '\n' == url.charAt(index - 1) || ' ' == url.charAt(index - 1)) { int endDomain = url.indexOf('/', index + 9); char endCh = url.charAt(end - 1); - if (endDomain != -1 && endDomain < end && - endCh != '>' && endCh != ')' && endCh != ']') { + if (endDomain != -1 && endDomain < end && endCh != '>' && endCh != ')' && endCh != ']') { linkifyTasks.add(new LinkifyTask(index, end)); extra += (end - index) + 4; - if (BuildConfig.DEBUG) { Log.d(TAG, "Linkify url: " + url.substring(index, end)); - } + Timber.d("Linkify url: %s", url.substring(end)); } } index = url.indexOf("https://", end); } - if (linkifyTasks.isEmpty()) return url; + if (linkifyTasks.isEmpty()) + return url; LinkifyTask prev = LinkifyTask.NULL; StringBuilder stringBuilder = new StringBuilder(url.length() + extra); for (LinkifyTask linkifyTask : linkifyTasks) { - stringBuilder.append(url, prev.end, linkifyTask.start) - .append('[').append(url, linkifyTask.start, linkifyTask.end) - .append("](").append(url, linkifyTask.start, linkifyTask.end).append(')'); + stringBuilder.append(url, prev.end, linkifyTask.start).append('[').append(url, linkifyTask.start, linkifyTask.end).append("](").append(url, linkifyTask.start, linkifyTask.end).append(')'); prev = linkifyTask; } - if (prev.end != url.length()) stringBuilder.append(url, prev.end, url.length()); - Log.i(TAG, "Added Markdown link to " + linkifyTasks.size() + " urls"); + if (prev.end != url.length()) + stringBuilder.append(url, prev.end, url.length()); + Timber.i("Added Markdown link to " + linkifyTasks.size() + " urls"); return stringBuilder.toString(); } diff --git a/app/src/main/java/com/fox2code/mmm/module/ActionButtonType.java b/app/src/main/java/com/fox2code/mmm/module/ActionButtonType.java index df81b3e..f90bb08 100644 --- a/app/src/main/java/com/fox2code/mmm/module/ActionButtonType.java +++ b/app/src/main/java/com/fox2code/mmm/module/ActionButtonType.java @@ -4,7 +4,6 @@ import android.annotation.SuppressLint; import android.content.Context; import android.net.Uri; import android.text.Spanned; -import android.util.Log; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; @@ -27,6 +26,7 @@ import com.google.android.material.chip.Chip; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import io.noties.markwon.Markwon; +import timber.log.Timber; @SuppressLint("UseCompatLoadingForDrawables") public enum ActionButtonType { @@ -95,7 +95,7 @@ public enum ActionButtonType { } else { builder.setMessage(desc); } - Log.i("Test", "URL: " + updateZipUrl); + Timber.i("URL: %s", updateZipUrl); builder.setNegativeButton(R.string.download_module, (x, y) -> IntentHelper.openCustomTab(button.getContext(), updateZipUrl)); if (hasRoot) { builder.setPositiveButton(moduleHolder.hasUpdate() ? R.string.update_module : R.string.install_module, (x, y) -> { @@ -132,9 +132,9 @@ public enum ActionButtonType { doActionLong(button, moduleHolder); return; } - Log.i("ActionButtonType", Integer.toHexString(moduleHolder.moduleInfo.flags)); + Timber.i(Integer.toHexString(moduleHolder.moduleInfo.flags)); if (!ModuleManager.getINSTANCE().setUninstallState(moduleHolder.moduleInfo, !moduleHolder.hasFlag(ModuleInfo.FLAG_MODULE_UNINSTALLING))) { - Log.e("ActionButtonType", "Failed to switch uninstalled state!"); + Timber.e("Failed to switch uninstalled state!"); } update(button, moduleHolder); } @@ -144,17 +144,22 @@ public enum ActionButtonType { // Actually a module having mount is the only issue when deleting module if (moduleHolder.moduleInfo.hasFlag(ModuleInfo.FLAG_MODULE_HAS_ACTIVE_MOUNT)) return false; // We can't trust active flag on first boot - new AlertDialog.Builder(button.getContext()).setTitle(R.string.master_delete).setPositiveButton(R.string.master_delete_yes, (v, i) -> { + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(button.getContext()); + builder.setTitle(R.string.master_delete); + builder.setPositiveButton(R.string.master_delete_yes, (dialog, which) -> { String moduleId = moduleHolder.moduleInfo.id; if (!ModuleManager.getINSTANCE().masterClear(moduleHolder.moduleInfo)) { Toast.makeText(button.getContext(), R.string.master_delete_fail, Toast.LENGTH_SHORT).show(); } else { moduleHolder.moduleInfo = null; FoxActivity.getFoxActivity(button).refreshUI(); - Log.e("ActionButtonType", "Cleared: " + moduleId); + Timber.e("Cleared: %s", moduleId); } - }).setNegativeButton(R.string.master_delete_no, (v, i) -> { - }).create().show(); + }); + builder.setNegativeButton(R.string.master_delete_no, (v, i) -> { + }); + builder.create(); + builder.show(); return true; } }, CONFIG() { diff --git a/app/src/main/java/com/fox2code/mmm/module/ModuleHolder.java b/app/src/main/java/com/fox2code/mmm/module/ModuleHolder.java index 5d35137..3dc7b2d 100644 --- a/app/src/main/java/com/fox2code/mmm/module/ModuleHolder.java +++ b/app/src/main/java/com/fox2code/mmm/module/ModuleHolder.java @@ -2,7 +2,6 @@ package com.fox2code.mmm.module; import android.content.Context; import android.content.pm.PackageManager; -import android.util.Log; import android.view.View; import androidx.annotation.NonNull; @@ -15,8 +14,8 @@ import com.fox2code.mmm.XHooks; import com.fox2code.mmm.manager.LocalModuleInfo; import com.fox2code.mmm.manager.ModuleInfo; import com.fox2code.mmm.repo.RepoModule; -import com.fox2code.mmm.utils.io.Http; import com.fox2code.mmm.utils.IntentHelper; +import com.fox2code.mmm.utils.io.Http; import com.fox2code.mmm.utils.io.PropUtils; import java.util.Comparator; @@ -24,8 +23,9 @@ import java.util.List; import java.util.Locale; import java.util.Objects; +import timber.log.Timber; + public final class ModuleHolder implements Comparable { - private static final String TAG = "ModuleHolder"; public final String moduleId; public final NotificationType notificationType; @@ -196,7 +196,7 @@ public final class ModuleHolder implements Comparable { XHooks.checkConfigTargetExists(context, pkg, config); buttonTypeList.add(ActionButtonType.CONFIG); } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Config package \"" + pkg + + Timber.w("Config package \"" + pkg + "\" missing for module \"" + this.moduleId + "\""); } } diff --git a/app/src/main/java/com/fox2code/mmm/module/ModuleViewAdapter.java b/app/src/main/java/com/fox2code/mmm/module/ModuleViewAdapter.java index 894df8f..3e57380 100644 --- a/app/src/main/java/com/fox2code/mmm/module/ModuleViewAdapter.java +++ b/app/src/main/java/com/fox2code/mmm/module/ModuleViewAdapter.java @@ -5,7 +5,6 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.Drawable; -import android.util.Log; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; @@ -22,7 +21,6 @@ import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.RecyclerView; import com.fox2code.foxcompat.view.FoxDisplay; -import com.fox2code.mmm.BuildConfig; import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.NotificationType; import com.fox2code.mmm.R; @@ -38,16 +36,21 @@ import com.topjohnwu.superuser.internal.UiThreadHandler; import java.util.ArrayList; import java.util.Objects; +import timber.log.Timber; + public final class ModuleViewAdapter extends RecyclerView.Adapter { private static final boolean DEBUG = false; public final ArrayList moduleHolders = new ArrayList<>(); + private static String formatType(ModuleHolder.Type type) { + return type.name().substring(0, 3) + "_" + type.ordinal(); + } + @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.module_entry, parent, false); + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.module_entry, parent, false); return new ViewHolder(view); } @@ -83,9 +86,9 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter actionButtonsTypes; - private boolean initState; public ModuleHolder moduleHolder; public Drawable background; + private boolean initState; public ViewHolder(@NonNull View itemView) { super(itemView); @@ -117,14 +120,16 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter { - if (this.initState) return; // Skip if non user + if (this.initState) + return; // Skip if non user ModuleHolder moduleHolder = this.moduleHolder; if (moduleHolder != null && moduleHolder.moduleInfo != null) { ModuleInfo moduleInfo = moduleHolder.moduleInfo; @@ -138,23 +143,23 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter { - if (this.initState) return; // Skip if non user + if (this.initState) + return; // Skip if non user ModuleHolder moduleHolder = this.moduleHolder; if (index < this.actionButtonsTypes.size() && moduleHolder != null) { - this.actionButtonsTypes.get(index) - .doAction((Chip) v, moduleHolder); + this.actionButtonsTypes.get(index).doAction((Chip) v, moduleHolder); if (moduleHolder.shouldRemove()) { this.cardView.setVisibility(View.GONE); } } }); this.actionsButtons[i].setOnLongClickListener(v -> { - if (this.initState) return false; // Skip if non user + if (this.initState) + return false; // Skip if non user ModuleHolder moduleHolder = this.moduleHolder; boolean didSomething = false; if (index < this.actionButtonsTypes.size() && moduleHolder != null) { - didSomething = this.actionButtonsTypes.get(index) - .doActionLong((Chip) v, moduleHolder); + didSomething = this.actionButtonsTypes.get(index).doActionLong((Chip) v, moduleHolder); if (moduleHolder.shouldRemove()) { this.cardView.setVisibility(View.GONE); } @@ -190,8 +195,7 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter - localModuleInfo.updateVersionCode) { - this.creditText.setText((localModuleInfo == null || - Objects.equals(moduleInfo.version, localModuleInfo.version) ? - moduleInfo.version : localModuleInfo.version + " (" + - this.getString(R.string.module_last_update) + " " + - moduleInfo.version + ")") + " " + - this.getString(R.string.module_by) + " " + moduleInfo.author); + if (localModuleInfo == null || moduleInfo.versionCode > localModuleInfo.updateVersionCode) { + this.creditText.setText((localModuleInfo == null || Objects.equals(moduleInfo.version, localModuleInfo.version) ? moduleInfo.version : localModuleInfo.version + " (" + this.getString(R.string.module_last_update) + " " + moduleInfo.version + ")") + " " + this.getString(R.string.module_by) + " " + moduleInfo.author); } else { - this.creditText.setText(localModuleInfo.version + ( - (localModuleInfo.updateVersion != null && (Objects.equals( - localModuleInfo.version, localModuleInfo.updateVersion) || - Objects.equals(localModuleInfo.version, - localModuleInfo.updateVersion + " (" + - localModuleInfo.updateVersionCode + ")"))) ? - "" : " (" + this.getString(R.string.module_last_update) + - " " + localModuleInfo.updateVersion + ")") + " " + - this.getString(R.string.module_by) + " " + localModuleInfo.author); + this.creditText.setText(localModuleInfo.version + ((localModuleInfo.updateVersion != null && (Objects.equals(localModuleInfo.version, localModuleInfo.updateVersion) || Objects.equals(localModuleInfo.version, localModuleInfo.updateVersion + " (" + localModuleInfo.updateVersionCode + ")"))) ? "" : " (" + this.getString(R.string.module_last_update) + " " + localModuleInfo.updateVersion + ")") + " " + this.getString(R.string.module_by) + " " + localModuleInfo.author); } if (moduleInfo.description == null || moduleInfo.description.isEmpty()) { this.descriptionText.setText(R.string.no_desc_found); @@ -232,12 +222,7 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter 2 || !hasUpdateText) { - this.moduleLayoutHelper.setMinHeight(Math.max(FoxDisplay.dpToPixel(36F), - this.moduleOptionsHolder.getHeight() - FoxDisplay.dpToPixel(14F))); + this.moduleLayoutHelper.setMinHeight(Math.max(FoxDisplay.dpToPixel(36F), this.moduleOptionsHolder.getHeight() - FoxDisplay.dpToPixel(14F))); } else { this.moduleLayoutHelper.setMinHeight(FoxDisplay.dpToPixel(4F)); } this.cardView.setClickable(false); - if (moduleHolder.isModuleHolder() && - moduleHolder.hasFlag(ModuleInfo.FLAG_MODULE_ACTIVE)) { + if (moduleHolder.isModuleHolder() && moduleHolder.hasFlag(ModuleInfo.FLAG_MODULE_ACTIVE)) { this.titleText.setTypeface(Typeface.DEFAULT_BOLD); } else { this.titleText.setTypeface(Typeface.DEFAULT); @@ -290,9 +270,7 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter { - MaterialAlertDialogBuilder builder = - new MaterialAlertDialogBuilder(_view.getContext()); - builder.setTitle(R.string.low_quality_module) - .setMessage("Actual description for Low-quality module") - .setCancelable(true) - .setPositiveButton(R.string.ok, (x, y) -> x.dismiss()).show(); + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(_view.getContext()); + builder.setTitle(R.string.low_quality_module).setMessage("Actual description for Low-quality module").setCancelable(true).setPositiveButton(R.string.ok, (x, y) -> x.dismiss()).show(); }); // Backup restore // foregroundAttr = R.attr.colorOnError; @@ -373,11 +342,8 @@ public final class ModuleViewAdapter extends RecyclerView.Adapter {}; private final EnumSet notifications = EnumSet.noneOf(NotificationType.class); private final HashMap mappedModuleHolders = new HashMap<>(); @@ -44,9 +43,28 @@ public class ModuleViewListBuilder { this.activity = activity; } + private static void notifySizeChanged(ModuleViewAdapter moduleViewAdapter, + int index, int oldLen, int newLen) { + // Timber.i("A: " + index + " " + oldLen + " " + newLen); + if (oldLen == newLen) { + if (newLen != 0) + moduleViewAdapter.notifyItemRangeChanged(index, newLen); + } else if (oldLen < newLen) { + if (oldLen != 0) + moduleViewAdapter.notifyItemRangeChanged(index, oldLen); + moduleViewAdapter.notifyItemRangeInserted( + index + oldLen, newLen - oldLen); + } else { + if (newLen != 0) + moduleViewAdapter.notifyItemRangeChanged(index, newLen); + moduleViewAdapter.notifyItemRangeRemoved( + index + newLen, oldLen - newLen); + } + } + public void addNotification(NotificationType notificationType) { if (notificationType == null) { - Log.w(TAG, "addNotification(null) called!"); + Timber.w("addNotification(null) called!"); return; } synchronized (this.updateLock) { @@ -61,7 +79,7 @@ public class ModuleViewListBuilder { } ModuleManager moduleManager = ModuleManager.getINSTANCE(); moduleManager.runAfterScan(() -> { - Log.i(TAG, "A1: " + moduleManager.getModules().size()); + Timber.i("A1: %s", moduleManager.getModules().size()); for (LocalModuleInfo moduleInfo : moduleManager.getModules().values()) { ModuleHolder moduleHolder = this.mappedModuleHolders.get(moduleInfo.id); if (moduleHolder == null) { @@ -82,7 +100,7 @@ public class ModuleViewListBuilder { } RepoManager repoManager = RepoManager.getINSTANCE(); repoManager.runAfterUpdate(() -> { - Log.i(TAG, "A2: " + repoManager.getModules().size()); + Timber.i("A2: %s", repoManager.getModules().size()); boolean no32bitSupport = Build.SUPPORTED_32_BIT_ABIS.length == 0; for (RepoModule repoModule : repoManager.getModules().values()) { if (!repoModule.repoData.isEnabled()) continue; @@ -111,6 +129,37 @@ public class ModuleViewListBuilder { } } + public void refreshNotificationsUI(ModuleViewAdapter moduleViewAdapter) { + final int notificationCount = this.notifications.size(); + notifySizeChanged(moduleViewAdapter, 0, + notificationCount, notificationCount); + } + + private boolean matchFilter(ModuleHolder moduleHolder) { + ModuleInfo moduleInfo = moduleHolder.getMainModuleInfo(); + String query = this.query; + String idLw = moduleInfo.id.toLowerCase(Locale.ROOT); + String nameLw = moduleInfo.name.toLowerCase(Locale.ROOT); + String authorLw = moduleInfo.author == null ? "" : + moduleInfo.author.toLowerCase(Locale.ROOT); + if (query.isEmpty() || query.equals(idLw) || + query.equals(nameLw) || query.equals(authorLw)) { + moduleHolder.filterLevel = 0; // Lower = better + return true; + } + if (idLw.contains(query) || nameLw.contains(query)) { + moduleHolder.filterLevel = 1; + return true; + } + if (authorLw.contains(query) || (moduleInfo.description != null && + moduleInfo.description.toLowerCase(Locale.ROOT).contains(query))) { + moduleHolder.filterLevel = 2; + return true; + } + moduleHolder.filterLevel = 3; + return false; + } + public void applyTo(final RecyclerView moduleList,final ModuleViewAdapter moduleViewAdapter) { if (this.updating) return; this.updating = true; @@ -170,14 +219,14 @@ public class ModuleViewListBuilder { } } } - Collections.sort(moduleHolders, this.moduleSorter); + moduleHolders.sort(this.moduleSorter); // Header is always first moduleHolders.add(0, headerFooter[0] = new ModuleHolder(this.headerPx, true)); // Footer is always last moduleHolders.add(headerFooter[1] = new ModuleHolder(this.footerPx, false)); - Log.i(TAG, "Got " + moduleHolders.size() + " entries!"); + Timber.i("Got " + moduleHolders.size() + " entries!"); // Build end } } finally { @@ -251,59 +300,9 @@ public class ModuleViewListBuilder { }); } - public void refreshNotificationsUI(ModuleViewAdapter moduleViewAdapter) { - final int notificationCount = this.notifications.size(); - notifySizeChanged(moduleViewAdapter, 0, - notificationCount, notificationCount); - } - - private boolean matchFilter(ModuleHolder moduleHolder) { - ModuleInfo moduleInfo = moduleHolder.getMainModuleInfo(); - String query = this.query; - String idLw = moduleInfo.id.toLowerCase(Locale.ROOT); - String nameLw = moduleInfo.name.toLowerCase(Locale.ROOT); - String authorLw = moduleInfo.author == null ? "" : - moduleInfo.author.toLowerCase(Locale.ROOT); - if (query.isEmpty() || query.equals(idLw) || - query.equals(nameLw) || query.equals(authorLw)) { - moduleHolder.filterLevel = 0; // Lower = better - return true; - } - if (idLw.contains(query) || nameLw.contains(query)) { - moduleHolder.filterLevel = 1; - return true; - } - if (authorLw.contains(query) || (moduleInfo.description != null && - moduleInfo.description.toLowerCase(Locale.ROOT).contains(query))) { - moduleHolder.filterLevel = 2; - return true; - } - moduleHolder.filterLevel = 3; - return false; - } - - private static void notifySizeChanged(ModuleViewAdapter moduleViewAdapter, - int index, int oldLen, int newLen) { - // Log.i(TAG, "A: " + index + " " + oldLen + " " + newLen); - if (oldLen == newLen) { - if (newLen != 0) - moduleViewAdapter.notifyItemRangeChanged(index, newLen); - } else if (oldLen < newLen) { - if (oldLen != 0) - moduleViewAdapter.notifyItemRangeChanged(index, oldLen); - moduleViewAdapter.notifyItemRangeInserted( - index + oldLen, newLen - oldLen); - } else { - if (newLen != 0) - moduleViewAdapter.notifyItemRangeChanged(index, newLen); - moduleViewAdapter.notifyItemRangeRemoved( - index + newLen, oldLen - newLen); - } - } - public void setQuery(String query) { synchronized (this.queryLock) { - Log.i(TAG, "Query " + this.query + " -> " + query); + Timber.i("Query " + this.query + " -> " + query); this.query = query == null ? "" : query.trim().toLowerCase(Locale.ROOT); } @@ -313,7 +312,7 @@ public class ModuleViewListBuilder { synchronized (this.queryLock) { String newQuery = query == null ? "" : query.trim().toLowerCase(Locale.ROOT); - Log.i(TAG, "Query change " + this.query + " -> " + newQuery); + Timber.i("Query change " + this.query + " -> " + newQuery); if (this.query.equals(newQuery)) return false; this.query = newQuery; diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java index 7fdaf54..ee1ce96 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoData.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoData.java @@ -2,7 +2,6 @@ package com.fox2code.mmm.repo; import android.content.SharedPreferences; import android.net.Uri; -import android.util.Log; import androidx.annotation.NonNull; @@ -32,6 +31,7 @@ import java.util.List; import io.realm.Realm; import io.realm.RealmConfiguration; +import timber.log.Timber; public class RepoData extends XRepo { public final String url; @@ -43,7 +43,7 @@ public class RepoData extends XRepo { private final Object populateLock = new Object(); public long lastUpdate; public String name, website, support, donate, submitModule; - public JSONObject supportedProperties = new JSONObject(); + public final JSONObject supportedProperties = new JSONObject(); protected String defaultName, defaultWebsite, defaultSupport, defaultDonate, defaultSubmitModule; @@ -112,9 +112,7 @@ public class RepoData extends XRepo { .build(); Realm realm = Realm.getInstance(realmConfiguration); ReposList reposList = realm.where(ReposList.class).equalTo("id", this.id).findFirst(); - if (BuildConfig.DEBUG) { - Log.d("RepoData", "RepoData: " + this.id + ". record in database: " + (reposList != null ? reposList.toString() : "none")); - } + Timber.d("RepoData: " + this.id + ". record in database: " + (reposList != null ? reposList.toString() : "none")); this.enabled = (!this.forceHide && reposList != null && reposList.isEnabled()); this.enabled = (!this.forceHide) && MainApplication.getSharedPreferences().getBoolean("pref_" + this.getPreferenceId() + "_enabled", true); this.defaultWebsite = "https://" + Uri.parse(url).getHost() + "/"; @@ -122,7 +120,7 @@ public class RepoData extends XRepo { // load metadata from realm database if (this.enabled) { try { - RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).build(); + RealmConfiguration realmConfiguration2 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); // load metadata from realm database Realm.getInstance(realmConfiguration2); this.metaDataCache = ModuleListCache.getRepoModulesAsJson(this.id); @@ -135,7 +133,7 @@ public class RepoData extends XRepo { this.submitModule = this.defaultSubmitModule; } else { // get everything from ReposList realm database - RealmConfiguration realmConfiguration3 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).build(); + RealmConfiguration realmConfiguration3 = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); // load metadata from realm database Realm.getInstance(realmConfiguration3); this.name = ReposList.getRepo(this.id).getName(); @@ -146,7 +144,7 @@ public class RepoData extends XRepo { } } catch (Exception e) { e.printStackTrace(); - Log.w("RepoData", "Failed to load repo metadata from realm database. If this is a first time install, this is normal."); + Timber.w("If this is a first install, this is normal."); } } } @@ -179,7 +177,7 @@ public class RepoData extends XRepo { } // If module id start with a dot, warn user if (moduleId.charAt(0) == '.') { - Log.w("MMM", "Module ID " + moduleId + " in repo " + this.url + " start with a dot, this is not recommended and may indicate an attempt to hide the module"); + Timber.w("This is not recommended and may indicate an attempt to hide the module"); } long moduleLastUpdate = module.getLong("last_update"); String moduleNotesUrl = module.getString("notes_url"); @@ -283,9 +281,7 @@ public class RepoData extends XRepo { SharedPreferences preferenceManager = MainApplication.getSharedPreferences(); boolean enabled = preferenceManager.getBoolean("pref_" + this.id + "_enabled", this.isEnabledByDefault()); if (this.enabled != enabled) { - if (BuildConfig.DEBUG) { - Log.d("NoodleDebug", "Repo " + this.id + " enable mismatch: " + this.enabled + " vs " + enabled); - } + Timber.d("Repo " + this.id + " enable mismatch: " + this.enabled + " vs " + enabled); this.enabled = enabled; } return this.enabled; @@ -294,9 +290,7 @@ public class RepoData extends XRepo { @Override public void setEnabled(boolean enabled) { this.enabled = enabled && !this.forceHide; - if (BuildConfig.DEBUG) { - Log.d("RepoData", "Repo " + this.id + " enabled: " + this.enabled + " (forced: " + this.forceHide + ") with preferenceID: " + this.getPreferenceId()); - } + Timber.d("Repo " + this.id + " enabled: " + this.enabled + " (forced: " + this.forceHide + ") with preferenceID: " + this.getPreferenceId()); MainApplication.getSharedPreferences().edit().putBoolean("pref_" + this.getPreferenceId() + "_enabled", enabled).apply(); } @@ -306,9 +300,7 @@ public class RepoData extends XRepo { return; } this.forceHide = AppUpdateManager.shouldForceHide(this.id); - if (BuildConfig.DEBUG) { - Log.d("RepoData", "Repo " + this.id + " update enabled: " + this.enabled + " (forced: " + this.forceHide + ") with preferenceID: " + this.getPreferenceId()); - } + Timber.d("Repo " + this.id + " update enabled: " + this.enabled + " (forced: " + this.forceHide + ") with preferenceID: " + this.getPreferenceId()); this.enabled = (!this.forceHide) && MainApplication.getSharedPreferences().getBoolean("pref_" + this.getPreferenceId() + "_enabled", true); } diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java index 4a7c7b5..82fafa6 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoManager.java @@ -6,7 +6,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.Handler; import android.os.Looper; -import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; @@ -40,6 +39,8 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.stream.Collectors; +import timber.log.Timber; + public final class RepoManager extends SyncManager { public static final String MAGISK_REPO = "https://raw.githubusercontent.com/Magisk-Modules-Repo/submission/modules/modules.json"; public static final String MAGISK_REPO_HOMEPAGE = "https://github.com/Magisk-Modules-Repo"; @@ -49,7 +50,6 @@ public final class RepoManager extends SyncManager { public static final String ANDROIDACY_MAGISK_REPO_ENDPOINT = "https://production-api.androidacy.com/magisk/repo"; public static final String ANDROIDACY_TEST_MAGISK_REPO_ENDPOINT = "https://staging-api.androidacy.com/magisk/repo"; public static final String ANDROIDACY_MAGISK_REPO_HOMEPAGE = "https://www.androidacy.com/modules-repo"; - private static final String TAG = "RepoManager"; private static final String MAGISK_REPO_MANAGER = "https://magisk-modules-repo.github.io/submission/modules.json"; private static final Object lock = new Object(); private static final double STEP1 = 0.1D; @@ -174,7 +174,7 @@ public final class RepoManager extends SyncManager { this.modules.put(repoModule.id, repoModule); } } else { - Log.e(TAG, "Detected module with invalid metadata: " + repoModule.repoName + "/" + repoModule.id); + Timber.e("Detected module with invalid metadata: " + repoModule.repoName + "/" + repoModule.id); } } } @@ -232,58 +232,50 @@ public final class RepoManager extends SyncManager { // Attempt to contact connectivitycheck.gstatic.com/generate_204 // If we can't, we don't have internet connection try { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Checking internet connection..."); - } + Timber.d("Checking internet connection..."); // this url is actually hosted by Cloudflare and is not dependent on Androidacy servers being up HttpURLConnection urlConnection = (HttpURLConnection) new URL("https://production-api.androidacy.com/cdn-cgi/trace").openConnection(); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Opened connection to " + urlConnection.getURL()); - } + Timber.d("Opened connection to %s", urlConnection.getURL()); urlConnection.setInstanceFollowRedirects(false); urlConnection.setReadTimeout(1000); urlConnection.setUseCaches(false); urlConnection.getInputStream().close(); // should return a 200 and the content should contain "visit_scheme=https" and ip= - if (BuildConfig.DEBUG) { - Log.d(TAG, "Response code: " + urlConnection.getResponseCode()); - } + Timber.d("Response code: %s", urlConnection.getResponseCode()); // get the response body String responseBody = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())).lines().collect(Collectors.joining("\n")); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Response body: " + responseBody); - } + Timber.d("Response body: %s", responseBody); // check if the response body contains the expected content if (urlConnection.getResponseCode() == 200 && responseBody.contains("visit_scheme=https") && responseBody.contains("ip=")) { this.hasInternet = true; } else { - Log.e(TAG, "Failed to check internet connection"); + Timber.e("Failed to check internet connection"); } } catch ( IOException e) { - Log.e(TAG, "Failed to check internet connection", e); + Timber.e(e); } for (int i = 0; i < repoDatas.length; i++) { if (BuildConfig.DEBUG) - Log.d("RepoManager", "Preparing to fetch: " + repoDatas[i].getName()); + Timber.d("Preparing to fetch: %s", repoDatas[i].getName()); moduleToUpdate += (repoUpdaters[i] = new RepoUpdater(repoDatas[i])).fetchIndex(); updateListener.update(STEP1 / repoDatas.length * (i + 1)); } if (BuildConfig.DEBUG) - Log.d("RepoManager", "Updating meta-data"); + Timber.d("Updating meta-data"); int updatedModules = 0; boolean allowLowQualityModules = MainApplication.isDisableLowQualityModuleFilter(); for (int i = 0; i < repoUpdaters.length; i++) { // Check if the repo is enabled if (!repoUpdaters[i].repoData.isEnabled()) { if (BuildConfig.DEBUG) - Log.d("RepoManager", "Skipping disabled repo: " + repoUpdaters[i].repoData.getName()); + Timber.d("Skipping disabled repo: %s", repoUpdaters[i].repoData.getName()); continue; } List repoModules = repoUpdaters[i].toUpdate(); RepoData repoData = repoDatas[i]; if (BuildConfig.DEBUG) - Log.d("RepoManager", "Registering " + repoData.getName()); + Timber.d("Registering %s", repoData.getName()); for (RepoModule repoModule : repoModules) { try { if (repoModule.propUrl != null && !repoModule.propUrl.isEmpty()) { @@ -305,7 +297,7 @@ public final class RepoManager extends SyncManager { } } catch ( Exception e) { - Log.e(TAG, "Failed to get \"" + repoModule.id + "\" metadata", e); + Timber.e(e); } updatedModules++; updateListener.update(STEP1 + (STEP2 / moduleToUpdate * updatedModules)); @@ -326,20 +318,20 @@ public final class RepoManager extends SyncManager { } } if (BuildConfig.DEBUG) - Log.d("RepoManager", "Finishing update"); + Timber.d("Finishing update"); if (hasInternet) { for (int i = 0; i < repoDatas.length; i++) { // If repo is not enabled, skip if (!repoDatas[i].isEnabled()) { if (BuildConfig.DEBUG) - Log.d("RepoManager", "Skipping " + repoDatas[i].getName() + " because it's disabled"); + Timber.d("Skipping " + repoDatas[i].getName() + " because it's disabled"); continue; } if (BuildConfig.DEBUG) - Log.d("RepoManager", "Finishing: " + repoUpdaters[i].repoData.getName()); + Timber.d("Finishing: %s", repoUpdaters[i].repoData.getName()); this.repoLastSuccess = repoUpdaters[i].finish(); if (!this.repoLastSuccess) { - Log.e(TAG, "Failed to update " + repoUpdaters[i].repoData.getName()); + Timber.e("Failed to update %s", repoUpdaters[i].repoData.getName()); // Show snackbar on main looper and add some bottom padding int finalI = i; Activity context = MainApplication.getINSTANCE().getLastCompatActivity(); @@ -366,7 +358,7 @@ public final class RepoManager extends SyncManager { updateListener.update(STEP1 + STEP2 + (STEP3 / repoDatas.length * (i + 1))); } } - Log.i(TAG, "Got " + this.modules.size() + " modules!"); + Timber.i("Got " + this.modules.size() + " modules!"); updateListener.update(1D); } diff --git a/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java b/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java index 487fed3..71d7cd0 100644 --- a/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java +++ b/app/src/main/java/com/fox2code/mmm/repo/RepoUpdater.java @@ -1,7 +1,5 @@ package com.fox2code.mmm.repo; -import android.util.Log; - import androidx.annotation.NonNull; import com.fox2code.mmm.utils.io.Http; @@ -18,9 +16,9 @@ import java.util.List; import io.realm.Realm; import io.realm.RealmConfiguration; +import timber.log.Timber; public class RepoUpdater { - private static final String TAG = "RepoUpdater"; public final RepoData repoData; public byte[] indexRaw; private List toUpdate; @@ -59,7 +57,7 @@ public class RepoUpdater { return this.toUpdate.size(); } catch ( Exception e) { - Log.e(TAG, "Failed to get manifest of " + this.repoData.id, e); + Timber.e(e); this.indexRaw = null; this.toUpdate = Collections.emptyList(); this.toApply = Collections.emptySet(); @@ -115,12 +113,26 @@ public class RepoUpdater { // both are arrays of modules // try to get modules from "modules" key JSONObject modules = new JSONObject(new String(this.indexRaw, StandardCharsets.UTF_8)); + // check if modules has key "modules" or "data" try { - // get modules - modules = modules.getJSONObject("modules"); + if (modules.has("modules")) { + // get modules from "modules" key + modules = modules.getJSONObject("modules"); + } else if (modules.has("data")) { + // get modules from "data" key + modules = modules.getJSONObject("data"); + } } catch (JSONException e) { - // if it fails, try to get modules from "data" key - modules = modules.getJSONObject("data"); + // there's a possibility that the modules key is an array, so we need to convert it to a json object + // get modules array + JSONObject[] modulesArray = new JSONObject[]{modules}; + // create new json object + modules = new JSONObject(); + // iterate over modules array + for (int i = 0; i < modulesArray.length; i++) { + // put module in json object + modules.put(String.valueOf(i), modulesArray[i]); + } } for (JSONObject module : new JSONObject[]{modules}) { try { @@ -209,12 +221,12 @@ public class RepoUpdater { moduleListCache.setInstalledVersionCode(installedVersionCode); }, () -> { // Transaction was a success. - Log.d(TAG, "onSuccess: Transaction was a success."); + Timber.d("onSuccess: Transaction was a success."); // close realm realm.close(); }, error -> { // Transaction failed and was automatically canceled. - Log.e(TAG, "onError: Transaction failed and was automatically canceled.", error); + Timber.e(error); // close realm realm.close(); }); @@ -223,7 +235,7 @@ public class RepoUpdater { } catch ( JSONException e) { e.printStackTrace(); - Log.w(TAG, "Failed to get module info from module " + module + " in repo " + this.repoData.id + " with error " + e.getMessage()); + Timber.w("Failed to get module info from module " + module + " in repo " + this.repoData.id + " with error " + e.getMessage()); } } } catch ( diff --git a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java index c1df679..c9b03ed 100644 --- a/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java +++ b/app/src/main/java/com/fox2code/mmm/settings/SettingsActivity.java @@ -19,7 +19,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.provider.Settings; -import android.util.Log; import android.view.inputmethod.EditorInfo; import android.widget.AutoCompleteTextView; import android.widget.Button; @@ -61,6 +60,7 @@ 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.realm.ReposList; import com.fox2code.mmm.utils.sentry.SentryMain; import com.fox2code.rosettax.LanguageActivity; import com.fox2code.rosettax.LanguageSwitcher; @@ -84,13 +84,16 @@ import java.util.Locale; import java.util.Objects; import java.util.Random; +import io.realm.Realm; +import io.realm.RealmConfiguration; +import timber.log.Timber; + public class SettingsActivity extends FoxActivity implements LanguageActivity { // Shamelessly adapted from https://github.com/DrKLO/Telegram/blob/2c71f6c92b45386f0c2b25f1442596462404bb39/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java#L1254 public final static int PERFORMANCE_CLASS_LOW = 0; public final static int PERFORMANCE_CLASS_AVERAGE = 1; public final static int PERFORMANCE_CLASS_HIGH = 2; private static final int LANGUAGE_SUPPORT_LEVEL = 1; - private static final String TAG = "SettingsActivity"; private static boolean devModeStepFirstBootIgnore = MainApplication.isDeveloper(); private static int devModeStep = 0; @@ -125,9 +128,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { devicePerformanceClass = PERFORMANCE_CLASS_HIGH; } - if (BuildConfig.DEBUG) { - Log.d(TAG, "getDevicePerformanceClass: androidVersion=" + androidVersion + " cpuCount=" + cpuCount + " memoryClass=" + memoryClass + " maxCpuFreq=" + maxCpuFreq + " devicePerformanceClass=" + devicePerformanceClass); - } + Timber.d("getDevicePerformanceClass: androidVersion=" + androidVersion + " cpuCount=" + cpuCount + " memoryClass=" + memoryClass + " maxCpuFreq=" + maxCpuFreq + " devicePerformanceClass=" + devicePerformanceClass); return devicePerformanceClass; } @@ -176,9 +177,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { ListPreference themePreference = findPreference("pref_theme"); // If transparent theme(s) are set, disable monet if (themePreference.getValue().equals("transparent_light")) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Transparent theme is set, disabling monet"); - } + Timber.d("disabling monet"); findPreference("pref_enable_monet").setEnabled(false); // Toggle monet off ((TwoStatePreference) findPreference("pref_enable_monet")).setChecked(false); @@ -197,17 +196,13 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { // You need to reboot your device at least once to be able to access dev-mode if (devModeStepFirstBootIgnore || !MainApplication.isFirstBoot()) devModeStep = 1; - if (BuildConfig.DEBUG) { - Log.d(TAG, "Theme changed, refreshing activity. New value: " + newValue); - } + Timber.d("refreshing activity. New value: %s", newValue); // Immediately save SharedPreferences.Editor editor = getPreferenceManager().getSharedPreferences().edit(); editor.putString("pref_theme", (String) newValue).apply(); // If theme contains "transparent" then disable monet if (newValue.toString().contains("transparent")) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Transparent theme is being set, disabling monet"); - } + Timber.d("disabling monet"); // Show a dialogue warning the user about issues with transparent themes and // that blur/monet will be disabled new MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.transparent_theme_dialogue_title).setMessage(R.string.transparent_theme_dialogue_message).setPositiveButton(R.string.ok, (dialog, which) -> { @@ -276,9 +271,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Restarting app to save crash reporting preference: " + newValue); - } + Timber.d("Restarting app to save crash reporting preference: %s", newValue); System.exit(0); // Exit app process }); // Do not reverse the change if the user cancels the dialog @@ -358,7 +351,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { int level = this.currentLanguageLevel(); if (level != LANGUAGE_SUPPORT_LEVEL) { - Log.e(TAG, "Detected language level " + level + ", latest is " + LANGUAGE_SUPPORT_LEVEL); + Timber.e("latest is %s", LANGUAGE_SUPPORT_LEVEL); languageSelector.setSummary(R.string.language_support_outdated); } else { String translatedBy = this.getString(R.string.language_translated_by); @@ -375,7 +368,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { } if (!SentryMain.IS_SENTRY_INSTALLED || !BuildConfig.DEBUG || InstallerInitializer.peekMagiskPath() == null) { // Hide the pref_crash option if not in debug mode - stop users from purposely crashing the app - Log.i(TAG, String.format("Sentry installed: %s, debug: %s, magisk path: %s", SentryMain.IS_SENTRY_INSTALLED, BuildConfig.DEBUG, InstallerInitializer.peekMagiskPath())); + Timber.i(InstallerInitializer.peekMagiskPath()); Objects.requireNonNull((Preference) findPreference("pref_test_crash")).setVisible(false); // Find pref_clear_data and set it invisible Objects.requireNonNull((Preference) findPreference("pref_clear_data")).setVisible(false); @@ -398,7 +391,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { return true; }); } else { - Log.e(TAG, String.format("Something is null: %s, %s", findPreference("pref_test_crash"), findPreference("pref_clear_data"))); + Timber.e("Something is null: %s, %s", findPreference("pref_clear_data"), findPreference("pref_test_crash")); } } if (InstallerInitializer.peekMagiskVersion() < Constants.MAGISK_VER_CODE_INSTALL_COMMAND || !MainApplication.isDeveloper()) { @@ -563,7 +556,8 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { fileOutputStream.write((line + "\n").getBytes()); } fileOutputStream.close(); - } catch (IOException e) { + } catch ( + IOException e) { e.printStackTrace(); Toast.makeText(requireContext(), R.string.error_saving_logs, Toast.LENGTH_SHORT).show(); return true; @@ -711,9 +705,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Restarting app to save staging endpoint preference: " + newValue); - } + Timber.d("Restarting app to save staging endpoint preference: %s", newValue); System.exit(0); // Exit app process }).setNegativeButton(android.R.string.cancel, (dialog, which) -> { // User cancelled the dialog @@ -736,15 +728,35 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Restarting app to save staging endpoint preference: " + newValue); - } + Timber.d("Restarting app to save staging endpoint preference: %s", newValue); System.exit(0); // Exit app process }).show(); } return true; }); } + // Get magisk_alt_repo enabled state from realm db + RealmConfiguration realmConfig = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + Realm realm1 = Realm.getInstance(realmConfig); + ReposList reposList = realm1.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst(); + if (reposList != null) { + // Set the switch to the current state + SwitchPreferenceCompat magiskAltRepoEnabled = Objects.requireNonNull(findPreference("pref_magisk_alt_repo_enabled")); + magiskAltRepoEnabled.setChecked(reposList.isEnabled()); + } + // add listener to magisk_alt_repo_enabled switch to update realm db + Preference magiskAltRepoEnabled = Objects.requireNonNull(findPreference("pref_magisk_alt_repo_enabled")); + magiskAltRepoEnabled.setOnPreferenceChangeListener((preference, newValue) -> { + // Update realm db + Realm realm = Realm.getInstance(realmConfig); + realm.executeTransaction(realm2 -> { + ReposList reposList1 = realm2.where(ReposList.class).equalTo("id", "magisk_alt_repo").findFirst(); + if (reposList1 != null) { + reposList1.setEnabled(Boolean.parseBoolean(String.valueOf(newValue))); + } + }); + return true; + }); // Disable toggling the pref_androidacy_repo_enabled on builds without an // ANDROIDACY_CLIENT_ID or where the ANDROIDACY_CLIENT_ID is empty Preference androidacyRepoEnabled = Objects.requireNonNull(findPreference("pref_androidacy_repo_enabled")); @@ -758,13 +770,25 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { // Revert the switch to off SwitchPreferenceCompat switchPreferenceCompat = (SwitchPreferenceCompat) androidacyRepoEnabled; switchPreferenceCompat.setChecked(false); - // Save the preference - MainApplication.getSharedPreferences().edit().putBoolean("pref_androidacy_repo_enabled", false).apply(); + // Disable in realm db + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + Realm realm = Realm.getInstance(realmConfiguration); + realm.executeTransaction(realm2 -> { + ReposList repoRealmResults = realm2.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); + assert repoRealmResults != null; + repoRealmResults.setEnabled(false); + realm2.insertOrUpdate(repoRealmResults); + realm2.close(); + }); return false; }); } - // get if androidacy repo is enabled - boolean androidacyRepoEnabledPref = MainApplication.getSharedPreferences().getBoolean("pref_androidacy_repo_enabled", false); + // get if androidacy repo is enabled from realm db + RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().name("ReposList.realm").allowQueriesOnUiThread(true).allowWritesOnUiThread(true).directory(MainApplication.getINSTANCE().getDataDirWithPath("realms")).schemaVersion(1).build(); + Realm realm = Realm.getInstance(realmConfiguration); + ReposList repoRealmResults = realm.where(ReposList.class).equalTo("id", "androidacy_repo").findFirst(); + assert repoRealmResults != null; + boolean androidacyRepoEnabledPref = repoRealmResults.isEnabled(); if (androidacyRepoEnabledPref) { String[] originalApiKeyRef = new String[]{MainApplication.getINSTANCE().getSharedPreferences("androidacy", 0).getString("pref_androidacy_api_token", "")}; // Get the dummy pref_androidacy_repo_api_token preference with id pref_androidacy_repo_api_token @@ -816,9 +840,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Restarting app to save token preference: " + newValue); - } + Timber.d("Restarting app to save token preference: %s", newValue); System.exit(0); // Exit app process }).show(); }); @@ -866,9 +888,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { mPendingIntent = PendingIntent.getActivity(requireContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); AlarmManager mgr = (AlarmManager) requireContext().getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent); - if (BuildConfig.DEBUG) { - Log.d(TAG, "Restarting app to save token preference: " + newValue); - } + Timber.d("Restarting app to save token preference: %s", newValue); System.exit(0); // Exit app process }).show(); }); @@ -940,7 +960,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { IOException | JSONException | NoSuchAlgorithmException e) { - Log.e(TAG, "Failed to preload repo values", e); + Timber.e(e); } UiThreadHandler.handler.post(() -> updateCustomRepoList(false)); } @@ -986,9 +1006,7 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity { if (preference == null) return; if (!preferenceName.contains("androidacy") && !preferenceName.contains("magisk_alt_repo")) { - if (BuildConfig.DEBUG) { - Log.d(TAG, "Setting preference " + preferenceName + " because it is not the Androidacy repo or the Magisk Alt Repo"); - } + Timber.d("Setting preference " + preferenceName + " because it is not the Androidacy repo or the Magisk Alt Repo"); if (repoData == null || repoData.isForceHide()) { hideRepoData(preferenceName); return; diff --git a/app/src/main/java/com/fox2code/mmm/utils/ExternalHelper.java b/app/src/main/java/com/fox2code/mmm/utils/ExternalHelper.java index a2a4471..067c111 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/ExternalHelper.java +++ b/app/src/main/java/com/fox2code/mmm/utils/ExternalHelper.java @@ -9,7 +9,6 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Bundle; -import android.util.Log; import android.widget.Toast; import androidx.appcompat.app.AlertDialog; @@ -21,44 +20,41 @@ import com.topjohnwu.superuser.internal.UiThreadHandler; import java.util.List; +import timber.log.Timber; + public final class ExternalHelper { - private static final String TAG = "ExternalHelper"; + public static final ExternalHelper INSTANCE = new ExternalHelper(); private static final boolean TEST_MODE = false; - private static final String FOX_MMM_OPEN_EXTERNAL = - "com.fox2code.mmm.utils.intent.action.OPEN_EXTERNAL"; + private static final String FOX_MMM_OPEN_EXTERNAL = "com.fox2code.mmm.utils.intent.action.OPEN_EXTERNAL"; private static final String FOX_MMM_EXTRA_REPO_ID = "extra_repo_id"; - public static final ExternalHelper INSTANCE = new ExternalHelper(); private ComponentName fallback; private CharSequence label; private boolean multi; - private ExternalHelper() {} + private ExternalHelper() { + } public void refreshHelper(Context context) { - Intent intent = new Intent(FOX_MMM_OPEN_EXTERNAL, - Uri.parse("https://fox2code.com/module.zip")); - List resolveInfos = context.getPackageManager() - .queryIntentActivities(intent, PackageManager.GET_RESOLVED_FILTER); + Intent intent = new Intent(FOX_MMM_OPEN_EXTERNAL, Uri.parse("https://fox2code.com/module.zip")); + List resolveInfos = context.getPackageManager().queryIntentActivities(intent, PackageManager.GET_RESOLVED_FILTER); if (resolveInfos == null || resolveInfos.isEmpty()) { - Log.i(TAG, "No external provider installed!"); + Timber.i("No external provider installed!"); label = TEST_MODE ? "External" : null; multi = TEST_MODE; fallback = null; } else { ResolveInfo resolveInfo = resolveInfos.get(0); - Log.i(TAG, "Found external provider: " + resolveInfo.activityInfo.packageName); - fallback = new ComponentName( - resolveInfo.activityInfo.packageName, - resolveInfo.activityInfo.name); + Timber.i("Found external provider: %s", resolveInfo.activityInfo.packageName); + fallback = new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name); label = resolveInfo.loadLabel(context.getPackageManager()); multi = resolveInfos.size() >= 2; } } public boolean openExternal(Context context, Uri uri, String repoId) { - if (label == null) return false; - Bundle param = ActivityOptionsCompat.makeCustomAnimation(context, - android.R.anim.fade_in, android.R.anim.fade_out).toBundle(); + if (label == null) + return false; + Bundle param = ActivityOptionsCompat.makeCustomAnimation(context, android.R.anim.fade_in, android.R.anim.fade_out).toBundle(); Intent intent = new Intent(FOX_MMM_OPEN_EXTERNAL, uri); intent.setFlags(IntentHelper.FLAG_GRANT_URI_PERMISSION); intent.putExtra(FOX_MMM_EXTRA_REPO_ID, repoId); @@ -74,8 +70,9 @@ public final class ExternalHelper { context.startActivity(intent, param); } return true; - } catch (ActivityNotFoundException e) { - Log.e(TAG, "Failed to launch activity", e); + } catch ( + ActivityNotFoundException e) { + Timber.e(e); } if (fallback != null) { if (multi) { @@ -87,27 +84,28 @@ public final class ExternalHelper { try { context.startActivity(intent, param); return true; - } catch (ActivityNotFoundException e) { - Log.e(TAG, "Failed to launch fallback", e); + } catch ( + ActivityNotFoundException e) { + Timber.e(e); } } return false; } public void injectButton(AlertDialog.Builder builder, Supplier uriSupplier, String repoId) { - if (label == null) return; + if (label == null) + return; builder.setNeutralButton(label, (dialog, button) -> { Context context = ((Dialog) dialog).getContext(); new Thread("Async downloader") { @Override public void run() { final Uri uri = uriSupplier.get(); - if (uri == null) return; + if (uri == null) + return; UiThreadHandler.run(() -> { if (!openExternal(context, uri, repoId)) { - Toast.makeText(context, - "Failed to launch external activity", - Toast.LENGTH_SHORT).show(); + Toast.makeText(context, "Failed to launch external activity", Toast.LENGTH_SHORT).show(); } }); } diff --git a/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java b/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java index 84f01f2..93f7ea8 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java +++ b/app/src/main/java/com/fox2code/mmm/utils/IntentHelper.java @@ -10,7 +10,6 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Environment; -import android.util.Log; import android.util.TypedValue; import android.widget.Toast; @@ -38,8 +37,9 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URISyntaxException; +import timber.log.Timber; + public class IntentHelper { - private static final String TAG = "IntentHelper"; private static final String EXTRA_TAB_SESSION = "android.support.customtabs.extra.SESSION"; private static final String EXTRA_TAB_COLOR_SCHEME = @@ -57,7 +57,7 @@ public class IntentHelper { try { startActivity(context, Intent.parseUri(uri, Intent.URI_INTENT_SCHEME), false); } catch (URISyntaxException | ActivityNotFoundException e) { - Log.e(TAG, "Failed launch of " + uri, e); + Timber.e(e); } } else openUrl(context, uri); } @@ -103,7 +103,7 @@ public class IntentHelper { public static void openUrlAndroidacy(Context context, String url, boolean allowInstall, String title,String config) { if (!Http.hasWebView()) { - Log.w(TAG, "Using custom tab for: " + url); + Timber.w("Using custom tab for: %s", url); openCustomTab(context, url); return; } @@ -151,7 +151,7 @@ public class IntentHelper { .to(new CallbackList<>() { @Override public void onAddElement(String str) { - Log.i(TAG, "LSPosed: " + str); + Timber.i("LSPosed: %s", str); } }).submit(); return; @@ -343,7 +343,7 @@ public class IntentHelper { callback.onReceived(destination, null, RESPONSE_ERROR); return; } - Log.i(TAG, "FilePicker returned " + uri); + Timber.i("FilePicker returned %s", uri); if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) { callback.onReceived(destination, uri, RESPONSE_URL); @@ -373,10 +373,10 @@ public class IntentHelper { } outputStream = new FileOutputStream(destination); Files.copy(inputStream, outputStream); - Log.i(TAG, "File saved at " + destination); + Timber.i("File saved at %s", destination); success = true; } catch (Exception e) { - Log.e(TAG, "failed copy of " + uri, e); + Timber.e(e); Toast.makeText(compatActivity, R.string.file_picker_failure, Toast.LENGTH_SHORT).show(); @@ -384,7 +384,7 @@ public class IntentHelper { Files.closeSilently(inputStream); Files.closeSilently(outputStream); if (!success && destination.exists() && !destination.delete()) - Log.e(TAG, "Failed to delete artefact!"); + Timber.e("Failed to delete artefact!"); } callback.onReceived(destination, uri, success ? RESPONSE_FILE : RESPONSE_ERROR); }); diff --git a/app/src/main/java/com/fox2code/mmm/utils/ZipFileOpener.java b/app/src/main/java/com/fox2code/mmm/utils/ZipFileOpener.java index 8448435..5a14f78 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/ZipFileOpener.java +++ b/app/src/main/java/com/fox2code/mmm/utils/ZipFileOpener.java @@ -23,6 +23,8 @@ import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import timber.log.Timber; + public class ZipFileOpener extends FoxActivity { AlertDialog loading = null; @@ -33,13 +35,11 @@ public class ZipFileOpener extends FoxActivity { super.onCreate(savedInstanceState); loading = BudgetProgressDialog.build(this, R.string.loading, R.string.zip_unpacking); new Thread(() -> { - if (BuildConfig.DEBUG) { - Log.d("ZipFileOpener", "onCreate: " + getIntent()); - } + Timber.d("onCreate: %s", getIntent()); File zipFile; Uri uri = getIntent().getData(); if (uri == null) { - Log.e("ZipFileOpener", "onCreate: No data provided"); + Timber.e("onCreate: No data provided"); runOnUiThread(() -> { Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show(); finishAndRemoveTask(); @@ -57,7 +57,7 @@ public class ZipFileOpener extends FoxActivity { zipFile = File.createTempFile("module", ".zip", getCacheDir()); try (InputStream inputStream = getContentResolver().openInputStream(uri); FileOutputStream outputStream = new FileOutputStream(zipFile)) { if (inputStream == null) { - Log.e("ZipFileOpener", "onCreate: Failed to open input stream"); + Timber.e("onCreate: Failed to open input stream"); runOnUiThread(() -> { Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show(); finishAndRemoveTask(); @@ -72,7 +72,7 @@ public class ZipFileOpener extends FoxActivity { } } catch ( Exception e) { - Log.e("ZipFileOpener", "onCreate: Failed to copy zip file", e); + Timber.e(e, "onCreate: Failed to copy zip file"); runOnUiThread(() -> { Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show(); finishAndRemoveTask(); @@ -81,16 +81,14 @@ public class ZipFileOpener extends FoxActivity { } // Ensure zip is not empty if (zipFile.length() == 0) { - Log.e("ZipFileOpener", "onCreate: Zip file is empty"); + Timber.e("onCreate: Zip file is empty"); runOnUiThread(() -> { Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show(); finishAndRemoveTask(); }); return; } else { - if (BuildConfig.DEBUG) { - Log.d("ZipFileOpener", "onCreate: Zip file is " + zipFile.length() + " bytes"); - } + Timber.d("onCreate: Zip file is " + zipFile.length() + " bytes"); } ZipEntry entry; ZipFile zip = null; @@ -100,12 +98,12 @@ public class ZipFileOpener extends FoxActivity { try { zip = new ZipFile(zipFile); if ((entry = zip.getEntry("module.prop")) == null) { - Log.e("ZipFileOpener", "onCreate: Zip file is not a valid magisk module"); + Timber.e("onCreate: Zip file is not a valid magisk module"); if (BuildConfig.DEBUG) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Log.d("ZipFileOpener", "onCreate: Zip file contents: " + zip.stream().map(ZipEntry::getName).reduce((a, b) -> a + ", " + b).orElse("empty")); + Timber.d("onCreate: Zip file contents: %s", zip.stream().map(ZipEntry::getName).reduce((a, b) -> a + ", " + b).orElse("empty")); } else { - Log.d("ZipFileOpener", "onCreate: Zip file contents cannot be listed on this version of android"); + Timber.d("onCreate: Zip file contents cannot be listed on this version of android"); } } runOnUiThread(() -> { @@ -116,7 +114,7 @@ public class ZipFileOpener extends FoxActivity { } } catch ( Exception e) { - Log.e("ZipFileOpener", "onCreate: Failed to open zip file", e); + Timber.e(e, "onCreate: Failed to open zip file"); runOnUiThread(() -> { Toast.makeText(this, R.string.zip_load_failed, Toast.LENGTH_LONG).show(); finishAndRemoveTask(); @@ -125,14 +123,12 @@ public class ZipFileOpener extends FoxActivity { try { zip.close(); } catch (IOException exception) { - Log.e("ZipFileOpener", Log.getStackTraceString(exception)); + Timber.e(Log.getStackTraceString(exception)); } } return; } - if (BuildConfig.DEBUG) { - Log.d("ZipFileOpener", "onCreate: Zip file is valid"); - } + Timber.d("onCreate: Zip file is valid"); String moduleInfo; try { moduleInfo = PropUtils.readModulePropSimple(zip.getInputStream(entry), "name"); @@ -144,7 +140,7 @@ public class ZipFileOpener extends FoxActivity { } } catch ( Exception e) { - Log.e("ZipFileOpener", "onCreate: Failed to load module id", e); + Timber.e(e, "onCreate: Failed to load module id"); runOnUiThread(() -> { Toast.makeText(this, R.string.zip_prop_load_failed, Toast.LENGTH_LONG).show(); finishAndRemoveTask(); @@ -152,14 +148,14 @@ public class ZipFileOpener extends FoxActivity { try { zip.close(); } catch (IOException exception) { - Log.e("ZipFileOpener", Log.getStackTraceString(exception)); + Timber.e(Log.getStackTraceString(exception)); } return; } try { zip.close(); } catch (IOException exception) { - Log.e("ZipFileOpener", Log.getStackTraceString(exception)); + Timber.e(Log.getStackTraceString(exception)); } String finalModuleInfo = moduleInfo; runOnUiThread(() -> { diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/Files.java b/app/src/main/java/com/fox2code/mmm/utils/io/Files.java index b97b8c1..00b1511 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/Files.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/Files.java @@ -27,6 +27,8 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; +import timber.log.Timber; + public class Files { private static final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0; @@ -73,7 +75,7 @@ public class Files { result = new File(uri.getPath()).length(); } } catch (Exception e) { - Log.e("Files", Log.getStackTraceString(e)); + Timber.e(Log.getStackTraceString(e)); return result; } return result; diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/GMSProviderInstaller.java b/app/src/main/java/com/fox2code/mmm/utils/io/GMSProviderInstaller.java index 1ec312d..e7ad262 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/GMSProviderInstaller.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/GMSProviderInstaller.java @@ -26,35 +26,37 @@ package com.fox2code.mmm.utils.io; import android.content.Context; import android.content.pm.PackageManager; -import android.util.Log; -/** Open implementation of ProviderInstaller.installIfNeeded +import timber.log.Timber; + +/** + * Open implementation of ProviderInstaller.installIfNeeded * (Compatible with MicroG even without signature spoofing) */ // 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 { - private static final String TAG = "GMSProviderInstaller"; private static boolean called = false; public static void installIfNeeded(final Context context) { if (context == null) { throw new NullPointerException("Context must not be null"); } - if (called) return; + if (called) + return; called = true; try { // Trust default GMS implementation - Context remote = context.createPackageContext("com.google.android.gms", - Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); - Class cl = remote.getClassLoader().loadClass( - "com.google.android.gms.common.security.ProviderInstallerImpl"); + Context remote = context.createPackageContext("com.google.android.gms", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + Class cl = remote.getClassLoader().loadClass("com.google.android.gms.common.security.ProviderInstallerImpl"); cl.getDeclaredMethod("insertProvider", Context.class).invoke(null, remote); - Log.i(TAG, "Installed GMS security providers!"); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "No GMS Implementation are installed on this device"); - } catch (Exception e) { - Log.w(TAG, "Failed to install the provider of the current GMS Implementation", e); + Timber.i("Installed GMS security providers!"); + } catch ( + PackageManager.NameNotFoundException e) { + Timber.w("No GMS Implementation are installed on this device"); + } catch ( + Exception e) { + Timber.w(e); } } } diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/Hashes.java b/app/src/main/java/com/fox2code/mmm/utils/io/Hashes.java index 00261a4..e8c7f80 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/Hashes.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/Hashes.java @@ -1,18 +1,16 @@ package com.fox2code.mmm.utils.io; -import android.util.Log; - -import java.io.IOException; -import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Locale; import java.util.regex.Pattern; +import timber.log.Timber; + public class Hashes { - private static final String TAG = "Hashes"; private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); private static final Pattern nonAlphaNum = Pattern.compile("[^a-zA-Z0-9]"); + public static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { @@ -24,12 +22,13 @@ public class Hashes { } public static String hashMd5(byte[] input) { - Log.w(TAG, "hashMd5: This method is insecure, use hashSha256 instead"); + Timber.w("hashMd5: This method is insecure, use hashSha256 instead"); try { MessageDigest md = MessageDigest.getInstance("MD5"); return bytesToHex(md.digest(input)); - } catch (NoSuchAlgorithmException e) { + } catch ( + NoSuchAlgorithmException e) { throw new RuntimeException(e); } } @@ -39,7 +38,8 @@ public class Hashes { MessageDigest md = MessageDigest.getInstance("SHA-1"); return bytesToHex(md.digest(input)); - } catch (NoSuchAlgorithmException e) { + } catch ( + NoSuchAlgorithmException e) { throw new RuntimeException(e); } } @@ -49,7 +49,8 @@ public class Hashes { MessageDigest md = MessageDigest.getInstance("SHA-256"); return bytesToHex(md.digest(input)); - } catch (NoSuchAlgorithmException e) { + } catch ( + NoSuchAlgorithmException e) { throw new RuntimeException(e); } } @@ -59,7 +60,8 @@ public class Hashes { MessageDigest md = MessageDigest.getInstance("SHA-512"); return bytesToHex(md.digest(input)); - } catch (NoSuchAlgorithmException e) { + } catch ( + NoSuchAlgorithmException e) { throw new RuntimeException(e); } } @@ -70,58 +72,35 @@ public class Hashes { */ public static boolean checkSumMatch(byte[] data, String checksum) { String hash; - if (checksum == null) return false; + if (checksum == null) + return false; switch (checksum.length()) { case 0: return true; // No checksum case 32: - hash = Hashes.hashMd5(data); break; + hash = Hashes.hashMd5(data); + break; case 40: - hash = Hashes.hashSha1(data); break; + hash = Hashes.hashSha1(data); + break; case 64: - hash = Hashes.hashSha256(data); break; + hash = Hashes.hashSha256(data); + break; case 128: - hash = Hashes.hashSha512(data); break; + hash = Hashes.hashSha512(data); + break; default: - Log.e(TAG, "No hash algorithm for " + - checksum.length() * 8 + "bit checksums"); + Timber.e("No hash algorithm for " + checksum.length() * 8 + "bit checksums"); return false; } - Log.i(TAG, "Checksum result (data: " + hash+ ",expected: " + checksum + ")"); - return hash.equals(checksum.toLowerCase(Locale.ROOT)); - } - - /** - * Check if the checksum match a file by picking the correct - * hashing algorithm depending on the length of the checksum - */ - public static boolean checkSumMatch(InputStream data, String checksum) throws IOException { - String hash; - if (checksum == null) return false; - String checksumAlgorithm = checkSumName(checksum); - if (checksumAlgorithm == null) { - Log.e(TAG, "No hash algorithm for " + - checksum.length() * 8 + "bit checksums"); - return false; - } - try { - MessageDigest md = MessageDigest.getInstance(checksumAlgorithm); - - byte[] bytes = new byte[2048]; - int nRead; - while ((nRead = data.read(bytes)) > 0) { - md.update(bytes, 0, nRead); - } - hash = bytesToHex(md.digest()); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - Log.i(TAG, "Checksum result (data: " + hash + ",expected: " + checksum + ")"); + Timber.i("Checksum result (data: " + hash + ",expected: " + checksum + ")"); return hash.equals(checksum.toLowerCase(Locale.ROOT)); } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") public static boolean checkSumValid(String checksum) { - if (checksum == null) return false; + if (checksum == null) + return false; switch (checksum.length()) { case 0: default: @@ -133,16 +112,19 @@ public class Hashes { final int len = checksum.length(); for (int i = 0; i < len; i++) { char c = checksum.charAt(i); - if (c < '0' || c > 'f') return false; + if (c < '0' || c > 'f') + return false; if (c > '9' && // Easier working with bits - (c & 0b01011111) < 'A') return false; + (c & 0b01011111) < 'A') + return false; } return true; } } public static String checkSumName(String checksum) { - if (checksum == null) return null; + if (checksum == null) + return null; switch (checksum.length()) { case 0: default: @@ -159,7 +141,8 @@ public class Hashes { } public static String checkSumFormat(String checksum) { - if (checksum == null) return null; + if (checksum == null) + return null; // Remove all non-alphanumeric characters return nonAlphaNum.matcher(checksum.trim()).replaceAll(""); } diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/Http.java b/app/src/main/java/com/fox2code/mmm/utils/io/Http.java index 2f5065f..c4bd7e4 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/Http.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/Http.java @@ -7,7 +7,6 @@ import android.net.Uri; import android.os.Build; import android.system.ErrnoException; import android.system.Os; -import android.util.Log; import android.webkit.CookieManager; import android.webkit.WebSettings; @@ -54,9 +53,9 @@ import okhttp3.Response; import okhttp3.ResponseBody; import okhttp3.dnsoverhttps.DnsOverHttps; import okio.BufferedSink; +import timber.log.Timber; public class Http { - private static final String TAG = "Http"; private static final OkHttpClient httpClient; private static final OkHttpClient httpClientDoH; private static final OkHttpClient httpClientWithCache; @@ -72,7 +71,7 @@ public class Http { if (mainApplication == null) { Error error = new Error("Initialized Http too soon!"); error.fillInStackTrace(); - Log.e(TAG, "Initialized Http too soon!", error); + Timber.e(error, "Initialized Http too soon!"); System.out.flush(); System.err.flush(); try { @@ -91,7 +90,7 @@ public class Http { } catch ( Throwable t) { cookieManager = null; - Log.e(TAG, "No WebView support!", t); + Timber.e(t, "No WebView support!"); } hasWebView = cookieManager != null; OkHttpClient.Builder httpclientBuilder = new OkHttpClient.Builder(); @@ -115,7 +114,7 @@ public class Http { } catch ( UnknownHostException | RuntimeException e) { - Log.e(TAG, "Failed to init DoH", e); + Timber.e(e, "Failed to init DoH"); } // Add cookie support. httpclientBuilder.addInterceptor(new AddCookiesInterceptor(MainApplication.getINSTANCE().getApplicationContext())); // VERY VERY IMPORTANT @@ -172,7 +171,7 @@ public class Http { httpclientBuilder.addInterceptor(CronetInterceptor.newBuilder(engine).build()); } catch ( Exception e) { - Log.e(TAG, "Failed to init cronet", e); + Timber.e(e, "Failed to init cronet"); // Gracefully fallback to okhttp } // Fallback DNS cache responses in case request fail but already succeeded once in the past @@ -189,7 +188,7 @@ public class Http { httpClientWithCache = followRedirects(httpclientBuilder, true).build(); httpclientBuilder.dns(fallbackDNS); httpClientWithCacheDoH = followRedirects(httpclientBuilder, true).build(); - Log.i(TAG, "Initialized Http successfully!"); + Timber.i("Initialized Http successfully!"); doh = MainApplication.isDohEnabled(); } @@ -228,15 +227,15 @@ public class Http { public static byte[] doHttpGet(String url, boolean allowCache) throws IOException { if (BuildConfig.DEBUG_HTTP) { // Log, but set all query parameters values to "****" while keeping the keys - Log.d(TAG, "doHttpGet: " + url.replaceAll("=[^&]*", "=****")); + Timber.d("doHttpGet: %s", url.replaceAll("=[^&]*", "=****")); } Response response = (allowCache ? getHttpClientWithCache() : getHttpClient()).newCall(new Request.Builder().url(url).get().build()).execute(); if (BuildConfig.DEBUG_HTTP) { - Log.d(TAG, "doHttpGet: request executed"); + Timber.d("doHttpGet: request executed"); } // 200/204 == success, 304 == cache valid if (response.code() != 200 && response.code() != 204 && (response.code() != 304 || !allowCache)) { - Log.e(TAG, "Failed to fetch " + url.replaceAll("=[^&]*", "=****") + " with code " + response.code()); + Timber.e("Failed to fetch " + url.replaceAll("=[^&]*", "=****") + " with code " + response.code()); checkNeedCaptchaAndroidacy(url, response.code()); // If it's a 401, and an androidacy link, it's probably an invalid token if (response.code() == 401 && AndroidacyUtil.isAndroidacyLink(url)) { @@ -246,7 +245,7 @@ public class Http { throw new HttpException(response.code()); } if (BuildConfig.DEBUG_HTTP) { - Log.d(TAG, "doHttpGet: " + url.replaceAll("=[^&]*", "=****") + " succeeded"); + Timber.d("doHttpGet: " + url.replaceAll("=[^&]*", "=****") + " succeeded"); } ResponseBody responseBody = response.body(); // Use cache api if used cached response @@ -256,7 +255,7 @@ public class Http { responseBody = response.body(); } if (BuildConfig.DEBUG_HTTP) { - Log.d(TAG, "doHttpGet: returning " + responseBody.contentLength() + " bytes"); + Timber.d("doHttpGet: returning " + responseBody.contentLength() + " bytes"); } return responseBody.bytes(); } @@ -268,7 +267,7 @@ public class Http { @SuppressWarnings("resource") private static Object doHttpPostRaw(String url, String data, boolean allowCache) throws IOException { if (BuildConfig.DEBUG) - Log.i(TAG, "POST " + url + " " + data); + Timber.i("POST " + url + " " + data); Response response; response = (allowCache ? getHttpClientWithCache() : getHttpClient()).newCall(new Request.Builder().url(url).post(JsonRequestBody.from(data)).header("Content-Type", "application/json").build()).execute(); if (response.isRedirect()) { @@ -277,7 +276,7 @@ public class Http { // 200/204 == success, 304 == cache valid if (response.code() != 200 && response.code() != 204 && (response.code() != 304 || !allowCache)) { if (BuildConfig.DEBUG) - Log.e(TAG, "Failed to fetch " + url + ", code: " + response.code() + ", body: " + response.body().string()); + Timber.e("Failed to fetch " + url + ", code: " + response.code() + ", body: " + response.body().string()); checkNeedCaptchaAndroidacy(url, response.code()); throw new HttpException(response.code()); } @@ -293,10 +292,10 @@ public class Http { public static byte[] doHttpGet(String url, ProgressListener progressListener) throws IOException { if (BuildConfig.DEBUG) - Log.i("Http", "GET " + url.split("\\?")[0]); + Timber.i("GET %s", url.split("\\?")[0]); Response response = getHttpClient().newCall(new Request.Builder().url(url).get().build()).execute(); if (response.code() != 200 && response.code() != 204) { - Log.e(TAG, "Failed to fetch " + url + ", code: " + response.code()); + Timber.e("Failed to fetch " + url + ", code: " + response.code()); checkNeedCaptchaAndroidacy(url, response.code()); throw new HttpException(response.code()); } @@ -313,7 +312,7 @@ public class Http { final long UPDATE_INTERVAL = 100; long nextUpdate = System.currentTimeMillis() + UPDATE_INTERVAL; long currentUpdate; - Log.i("Http", "Target: " + target + " Divider: " + divider); + Timber.i("Target: " + target + " Divider: " + divider); progressListener.onUpdate(0, (int) (target / divider), false); while (true) { int read = inputStream.read(buff); @@ -343,7 +342,7 @@ public class Http { } public static void setDoh(boolean doh) { - Log.i(TAG, "DoH: " + Http.doh + " -> " + doh); + Timber.i("DoH: " + Http.doh + " -> " + doh); Http.doh = doh; } @@ -357,44 +356,44 @@ public class Http { File webviewCacheDir = new File(cacheDir, "WebView"); if (!webviewCacheDir.exists()) { if (!webviewCacheDir.mkdirs()) { - Log.e(TAG, "Failed to create webview cache dir"); + Timber.e("Failed to create webview cache dir"); } } File webviewCacheDirCache = new File(webviewCacheDir, "Default"); if (!webviewCacheDirCache.exists()) { if (!webviewCacheDirCache.mkdirs()) { - Log.e(TAG, "Failed to create webview cache dir"); + Timber.e("Failed to create webview cache dir"); } } File webviewCacheDirCacheCodeCache = new File(webviewCacheDirCache, "HTTP Cache"); if (!webviewCacheDirCacheCodeCache.exists()) { if (!webviewCacheDirCacheCodeCache.mkdirs()) { - Log.e(TAG, "Failed to create webview cache dir"); + Timber.e("Failed to create webview cache dir"); } } File webviewCacheDirCacheCodeCacheIndex = new File(webviewCacheDirCacheCodeCache, "Code Cache"); if (!webviewCacheDirCacheCodeCacheIndex.exists()) { if (!webviewCacheDirCacheCodeCacheIndex.mkdirs()) { - Log.e(TAG, "Failed to create webview cache dir"); + Timber.e("Failed to create webview cache dir"); } } File webviewCacheDirCacheCodeCacheIndexIndex = new File(webviewCacheDirCacheCodeCacheIndex, "Index"); if (!webviewCacheDirCacheCodeCacheIndexIndex.exists()) { if (!webviewCacheDirCacheCodeCacheIndexIndex.mkdirs()) { - Log.e(TAG, "Failed to create webview cache dir"); + Timber.e("Failed to create webview cache dir"); } } // Create the js and wasm dirs File webviewCacheDirCacheCodeCacheIndexIndexJs = new File(webviewCacheDirCacheCodeCache, "js"); if (!webviewCacheDirCacheCodeCacheIndexIndexJs.exists()) { if (!webviewCacheDirCacheCodeCacheIndexIndexJs.mkdirs()) { - Log.e(TAG, "Failed to create webview cache dir"); + Timber.e("Failed to create webview cache dir"); } } File webviewCacheDirCacheCodeCacheIndexIndexWasm = new File(webviewCacheDirCacheCodeCache, "wasm"); if (!webviewCacheDirCacheCodeCacheIndexIndexWasm.exists()) { if (!webviewCacheDirCacheCodeCacheIndexIndexWasm.mkdirs()) { - Log.e(TAG, "Failed to create webview cache dir"); + Timber.e("Failed to create webview cache dir"); } } } diff --git a/app/src/main/java/com/fox2code/mmm/utils/io/PropUtils.java b/app/src/main/java/com/fox2code/mmm/utils/io/PropUtils.java index 8bf00df..b72ba3c 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/io/PropUtils.java +++ b/app/src/main/java/com/fox2code/mmm/utils/io/PropUtils.java @@ -5,7 +5,6 @@ import static com.fox2code.mmm.AppUpdateManager.getFlagsForModule; import android.os.Build; import android.text.TextUtils; -import android.util.Log; import com.fox2code.mmm.AppUpdateManager; import com.fox2code.mmm.manager.ModuleInfo; @@ -21,6 +20,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Locale; +import timber.log.Timber; + public class PropUtils { private static final HashMap moduleSupportsFallbacks = new HashMap<>(); private static final HashMap moduleConfigsFallbacks = new HashMap<>(); @@ -342,7 +343,7 @@ public class PropUtils { } } } catch (IOException e) { - Log.i("PropUtils", "Failed to get moduleId", e); + Timber.i(e); } return moduleId; } diff --git a/build.gradle b/build.gradle index cf9df6d..5c7893d 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,7 @@ buildscript { flavorAware: true ] project.ext.kotlin_version = "1.8.0" + project.ext.sentry_version = "6.12.1" dependencies { classpath 'com.android.tools.build:gradle:7.4.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"