Add NoodleDebug for user-debugging and update FoxCompat to fix a bug.

pull/206/head
Fox2Code 2 years ago
parent 95b1c80abb
commit 3289775875

@ -117,7 +117,7 @@ dependencies {
implementation "dev.rikka.rikkax.insets:insets:1.3.0"
implementation 'com.github.Dimezis:BlurView:version-1.6.6'
implementation 'com.github.KieronQuinn:MonetCompat:0.4.1'
implementation 'com.github.Fox2Code:FoxCompat:0.1.3'
implementation 'com.github.Fox2Code:FoxCompat:0.1.4b'
// Utils
implementation 'androidx.work:work-runtime:2.7.1'

@ -34,6 +34,7 @@ import com.fox2code.mmm.settings.SettingsActivity;
import com.fox2code.mmm.utils.ExternalHelper;
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.IntentHelper;
import com.fox2code.mmm.utils.NoodleDebug;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import eightbitlab.com.blurview.BlurView;
@ -44,6 +45,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
OverScrollManager.OverScrollHelper {
private static final String TAG = "MainActivity";
private static final int PRECISION = 10000;
public static boolean noodleDebugState = BuildConfig.DEBUG;
public final ModuleViewListBuilder moduleViewListBuilder;
public LinearProgressIndicator progressIndicator;
private ModuleViewAdapter moduleViewAdapter;
@ -59,6 +61,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
private RecyclerView moduleList;
private CardView searchCard;
private SearchView searchView;
private NoodleDebug noodleDebug;
private boolean initMode;
public MainActivity() {
@ -106,6 +109,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
this.moduleList = findViewById(R.id.module_list);
this.searchCard = findViewById(R.id.search_card);
this.searchView = findViewById(R.id.search_bar);
this.noodleDebug = new NoodleDebug(this, R.id.noodle_debug);
this.moduleViewAdapter = new ModuleViewAdapter();
this.moduleList.setAdapter(this.moduleViewAdapter);
this.moduleList.setLayoutManager(new LinearLayoutManager(this));
@ -152,6 +156,8 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
moduleViewListBuilder.addNotification(NotificationType.MAGISK_OUTDATED);
if (!MainApplication.isShowcaseMode())
moduleViewListBuilder.addNotification(NotificationType.INSTALL_FROM_STORAGE);
noodleDebug.setEnabled(noodleDebugState);
noodleDebug.bind();
ModuleManager.getINSTANCE().scan();
ModuleManager.getINSTANCE().runAfterScan(
moduleViewListBuilder::appendInstalledModules);
@ -161,18 +167,22 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
@Override
public void onFailure(int error) {
Log.i(TAG, "Failed to get magisk path!");
noodleDebug.setEnabled(noodleDebugState);
noodleDebug.bind();
moduleViewListBuilder.addNotification(
InstallerInitializer.getErrorNotification());
this.commonNext();
}
public void commonNext() {
NoodleDebug noodleDebug = NoodleDebug.getNoodleDebug();
swipeRefreshBlocker = System.currentTimeMillis() + 5_000L;
updateScreenInsets(); // Fix an edge case
if (MainApplication.isShowcaseMode())
moduleViewListBuilder.addNotification(NotificationType.SHOWCASE_MODE);
if (!Http.hasWebView()) // Check Http for WebView availability
moduleViewListBuilder.addNotification(NotificationType.NO_WEB_VIEW);
noodleDebug.push("Apply");
moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter);
runOnUiThread(() -> {
progressIndicator.setIndeterminate(false);
@ -181,11 +191,14 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
updateScreenInsets(getResources().getConfiguration());
});
Log.i(TAG, "Scanning for modules!");
noodleDebug.replace("Initialize Update");
final int max = ModuleManager.getINSTANCE().getUpdatableModuleCount();
if (RepoManager.getINSTANCE().getCustomRepoManager().needUpdate()) {
Log.w(TAG, "Need update on create?");
}
noodleDebug.replace("Check Update Compat");
AppUpdateManager.getAppUpdateManager().checkUpdateCompat();
noodleDebug.replace("Check Update");
RepoManager.getINSTANCE().update(value -> runOnUiThread(max == 0 ? () ->
progressIndicator.setProgressCompat(
(int) (value * PRECISION), true) :() ->
@ -196,13 +209,17 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
} else {
// Compatibility data still needs to be updated
AppUpdateManager appUpdateManager = AppUpdateManager.getAppUpdateManager();
noodleDebug.replace("Check App Update");
if (BuildConfig.ENABLE_AUTO_UPDATER && appUpdateManager.checkUpdate(true))
moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE);
noodleDebug.replace("Check Json Update");
if (max != 0) {
int current = 0;
noodleDebug.push("");
for (LocalModuleInfo localModuleInfo :
ModuleManager.getINSTANCE().getModules().values()) {
if (localModuleInfo.updateJson != null) {
noodleDebug.replace(localModuleInfo.id);
try {
localModuleInfo.checkModuleUpdate();
} catch (Exception e) {
@ -216,6 +233,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
+ (PRECISION * 0.75F)), true));
}
}
noodleDebug.pop();
}
}
runOnUiThread(() -> {
@ -225,10 +243,13 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
setActionBarBackground(null);
updateScreenInsets(getResources().getConfiguration());
});
noodleDebug.push("Apply");
RepoManager.getINSTANCE().runAfterUpdate(
moduleViewListBuilder::appendRemoteModules);
moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter);
noodleDebug.pop();
Log.i(TAG, "Finished app opening state!");
noodleDebug.unbind();
}
}, true);
ExternalHelper.INSTANCE.refreshHelper(this);
@ -321,6 +342,8 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
moduleViewListBuilder.addNotification(NotificationType.MAGISK_OUTDATED);
if (!MainApplication.isShowcaseMode())
moduleViewListBuilder.addNotification(NotificationType.INSTALL_FROM_STORAGE);
noodleDebug.setEnabled(noodleDebugState);
noodleDebug.bind();
ModuleManager.getINSTANCE().scan();
ModuleManager.getINSTANCE().runAfterScan(
moduleViewListBuilder::appendInstalledModules);
@ -331,11 +354,14 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
public void onFailure(int error) {
moduleViewListBuilder.addNotification(
InstallerInitializer.getErrorNotification());
noodleDebug.setEnabled(noodleDebugState);
noodleDebug.bind();
this.commonNext();
}
public void commonNext() {
Log.i(TAG, "Common Before");
NoodleDebug noodleDebug = NoodleDebug.getNoodleDebug();
if (MainApplication.isShowcaseMode())
moduleViewListBuilder.addNotification(NotificationType.SHOWCASE_MODE);
if (!NotificationType.NO_INTERNET.shouldRemove())
@ -343,11 +369,13 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
else if (AppUpdateManager.getAppUpdateManager().checkUpdate(false))
moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE);
RepoManager.getINSTANCE().updateEnabledStates();
noodleDebug.push("");
if (RepoManager.getINSTANCE().getCustomRepoManager().needUpdate()) {
runOnUiThread(() -> {
progressIndicator.setIndeterminate(false);
progressIndicator.setMax(PRECISION);
});
noodleDebug.replace("Check Update");
RepoManager.getINSTANCE().update(value -> runOnUiThread(() ->
progressIndicator.setProgressCompat(
(int) (value * PRECISION), true)));
@ -356,11 +384,14 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
progressIndicator.setVisibility(View.GONE);
});
}
noodleDebug.replace("Apply");
RepoManager.getINSTANCE().runAfterUpdate(
moduleViewListBuilder::appendRemoteModules);
Log.i(TAG, "Common Before applyTo");
moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter);
noodleDebug.pop();
Log.i(TAG, "Common After");
noodleDebug.unbind();
}
});
this.initMode = false;
@ -384,14 +415,49 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
this.swipeRefreshBlocker = System.currentTimeMillis() + 5_000L;
// this.swipeRefreshLayout.setRefreshing(true); ??
new Thread(() -> {
noodleDebug.setEnabled(noodleDebugState);
NoodleDebug noodleDebug = this.noodleDebug.bind();
Http.cleanDnsCache(); // Allow DNS reload from network
RepoManager.getINSTANCE().update(value -> runOnUiThread(() ->
this.progressIndicator.setProgressCompat(
(int) (value * PRECISION), true)));
if (!NotificationType.NO_INTERNET.shouldRemove())
noodleDebug.push("Check Update");
final int max = ModuleManager.getINSTANCE().getUpdatableModuleCount();
RepoManager.getINSTANCE().update(value -> runOnUiThread(max == 0 ? () ->
progressIndicator.setProgressCompat(
(int) (value * PRECISION), true) :() ->
progressIndicator.setProgressCompat(
(int) (value * PRECISION * 0.75F), true)));
if (!NotificationType.NO_INTERNET.shouldRemove()) {
moduleViewListBuilder.addNotification(NotificationType.NO_INTERNET);
else if (AppUpdateManager.getAppUpdateManager().checkUpdate(true))
moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE);
} else {
// Compatibility data still needs to be updated
AppUpdateManager appUpdateManager = AppUpdateManager.getAppUpdateManager();
noodleDebug.replace("Check App Update");
if (BuildConfig.ENABLE_AUTO_UPDATER && appUpdateManager.checkUpdate(true))
moduleViewListBuilder.addNotification(NotificationType.UPDATE_AVAILABLE);
noodleDebug.replace("Check Json Update");
if (max != 0) {
int current = 0;
noodleDebug.push("");
for (LocalModuleInfo localModuleInfo :
ModuleManager.getINSTANCE().getModules().values()) {
if (localModuleInfo.updateJson != null) {
noodleDebug.replace(localModuleInfo.id);
try {
localModuleInfo.checkModuleUpdate();
} catch (Exception e) {
Log.e("MainActivity", "Failed to fetch update of: "
+ localModuleInfo.id, e);
}
current++;
final int currentTmp = current;
runOnUiThread(() -> progressIndicator.setProgressCompat(
(int) ((1F * currentTmp / max) * PRECISION * 0.25F
+ (PRECISION * 0.75F)), true));
}
}
noodleDebug.pop();
}
}
noodleDebug.replace("Apply");
runOnUiThread(() -> {
this.progressIndicator.setVisibility(View.GONE);
this.swipeRefreshLayout.setRefreshing(false);
@ -403,6 +469,8 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
RepoManager.getINSTANCE().runAfterUpdate(
moduleViewListBuilder::appendRemoteModules);
this.moduleViewListBuilder.applyTo(moduleList, moduleViewAdapter);
noodleDebug.pop();
noodleDebug.unbind();
},"Repo update thread").start();
}

@ -8,6 +8,7 @@ import androidx.annotation.NonNull;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.installer.InstallerInitializer;
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.NoodleDebug;
import com.fox2code.mmm.utils.PropUtils;
import com.fox2code.mmm.utils.SyncManager;
import com.topjohnwu.superuser.Shell;
@ -47,6 +48,8 @@ public final class ModuleManager extends SyncManager {
}
protected void scanInternal(@NonNull UpdateListener updateListener) {
NoodleDebug noodleDebug = NoodleDebug.getNoodleDebug();
noodleDebug.push("Initialize scan");
boolean firstScan = this.bootPrefs.getBoolean("mm_first_scan", true);
SharedPreferences.Editor editor = firstScan ? this.bootPrefs.edit() : null;
for (ModuleInfo v : this.moduleInfos.values()) {
@ -67,10 +70,13 @@ public final class ModuleManager extends SyncManager {
if (!FORCE_NEED_FALLBACK && needFallback) {
Log.e(TAG, "Failed to detect modules folder, using fallback instead.");
}
noodleDebug.replace("Scan");
if (modules != null) {
noodleDebug.push("");
for (String module : modules) {
if (!new SuFile("/data/adb/modules/" + module).isDirectory())
continue; // Ignore non directory files inside modules folder
noodleDebug.replace(module);
LocalModuleInfo moduleInfo = moduleInfos.get(module);
if (moduleInfo == null) {
moduleInfo = new LocalModuleInfo(module);
@ -112,10 +118,16 @@ public final class ModuleManager extends SyncManager {
moduleInfo.flags |= FLAG_MM_INVALID;
}
}
noodleDebug.pop();
}
noodleDebug.replace("Scan update");
String[] modules_update = new SuFile("/data/adb/modules_update").list();
if (modules_update != null) {
noodleDebug.push("");
for (String module : modules_update) {
if (!new SuFile("/data/adb/modules_update/" + module).isDirectory())
continue; // Ignore non directory files inside modules folder
noodleDebug.replace(module);
LocalModuleInfo moduleInfo = moduleInfos.get(module);
if (moduleInfo == null) {
moduleInfo = new LocalModuleInfo(module);
@ -131,12 +143,16 @@ public final class ModuleManager extends SyncManager {
moduleInfo.flags |= FLAG_MM_INVALID;
}
}
noodleDebug.pop();
}
noodleDebug.replace("Finalize scan");
this.updatableModuleCount = 0;
Iterator<LocalModuleInfo> moduleInfoIterator =
this.moduleInfos.values().iterator();
noodleDebug.push("");
while (moduleInfoIterator.hasNext()) {
LocalModuleInfo moduleInfo = moduleInfoIterator.next();
noodleDebug.replace(moduleInfo.id);
if ((moduleInfo.flags & FLAG_MM_UNPROCESSED) != 0) {
moduleInfoIterator.remove();
continue; // Don't process fallbacks if unreferenced
@ -158,10 +174,12 @@ public final class ModuleManager extends SyncManager {
}
moduleInfo.verify();
}
noodleDebug.pop();
if (firstScan) {
editor.putBoolean("mm_first_scan", false);
editor.apply();
}
noodleDebug.pop();
}
public HashMap<String, LocalModuleInfo> getModules() {

@ -13,6 +13,7 @@ import com.fox2code.mmm.manager.ModuleInfo;
import com.fox2code.mmm.utils.Files;
import com.fox2code.mmm.utils.Hashes;
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.NoodleDebug;
import com.fox2code.mmm.utils.PropUtils;
import com.fox2code.mmm.utils.SyncManager;
@ -185,6 +186,8 @@ public final class RepoManager extends SyncManager {
private static final double STEP3 = 0.1D;
protected void scanInternal(@NonNull UpdateListener updateListener) {
NoodleDebug noodleDebug = NoodleDebug.getNoodleDebug();
noodleDebug.push("Downloading indexes");
this.modules.clear();
updateListener.update(0D);
// Using LinkedHashSet to deduplicate Androidacy entry.
@ -192,18 +195,26 @@ public final class RepoManager extends SyncManager {
this.repoData.values()).toArray(new RepoData[0]);
RepoUpdater[] repoUpdaters = new RepoUpdater[repoDatas.length];
int moduleToUpdate = 0;
noodleDebug.push("");
for (int i = 0; i < repoDatas.length; i++) {
noodleDebug.replace(repoDatas[i].getName());
moduleToUpdate += (repoUpdaters[i] =
new RepoUpdater(repoDatas[i])).fetchIndex();
updateListener.update(STEP1 / repoDatas.length * (i + 1));
}
noodleDebug.pop();
noodleDebug.replace("Updating meta-data");
int updatedModules = 0;
boolean allowLowQualityModules = MainApplication.isDisableLowQualityModuleFilter();
noodleDebug.push("");
for (int i = 0; i < repoUpdaters.length; i++) {
List<RepoModule> repoModules = repoUpdaters[i].toUpdate();
RepoData repoData = repoDatas[i];
noodleDebug.replace(repoData.getName());
Log.d(TAG, "Registering " + repoData.getName());
noodleDebug.push("");
for (RepoModule repoModule:repoModules) {
noodleDebug.replace(repoModule.id);
try {
if (repoModule.propUrl != null &&
!repoModule.propUrl.isEmpty()) {
@ -229,6 +240,7 @@ public final class RepoManager extends SyncManager {
updatedModules++;
updateListener.update(STEP1 + (STEP2 / moduleToUpdate * updatedModules));
}
noodleDebug.pop();
for (RepoModule repoModule:repoUpdaters[i].toApply()) {
if ((repoModule.moduleInfo.flags & ModuleInfo.FLAG_METADATA_INVALID) == 0) {
RepoModule registeredRepoModule = this.modules.get(repoModule.id);
@ -241,14 +253,20 @@ public final class RepoManager extends SyncManager {
}
}
}
noodleDebug.pop();
noodleDebug.replace("Finishing update");
noodleDebug.push("");
boolean hasInternet = false;
for (int i = 0; i < repoDatas.length; i++) {
noodleDebug.replace(repoUpdaters[i].repoData.getName());
hasInternet |= repoUpdaters[i].finish();
updateListener.update(STEP1 + STEP2 + (STEP3 / repoDatas.length * (i + 1)));
}
noodleDebug.pop();
Log.i(TAG, "Got " + this.modules.size() + " modules!");
updateListener.update(1D);
this.repoLastResult = hasInternet;
noodleDebug.pop(); // pop "Finishing update"
}
public void updateEnabledStates() {

@ -0,0 +1,163 @@
package com.fox2code.mmm.utils;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.util.Log;
import android.widget.TextView;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import java.lang.ref.WeakReference;
import java.util.LinkedList;
import java.util.Objects;
public class NoodleDebug {
private static final String TAG = "NoodleDebug";
private static final WeakReference<Thread> NULL_THREAD_REF = new WeakReference<>(null);
private static final ThreadLocal<NoodleDebug> THREAD_NOODLE = new ThreadLocal<>();
@SuppressLint("StaticFieldLeak") // <- Null initialized
private static final NoodleDebug NULL = new NoodleDebug() {
@Override
public void setEnabled(boolean enabled) {}
@Override
protected void markDirty() {}
};
private final Activity activity;
private final TextView textView;
private final LinkedList<String> tokens;
private final StringBuilder debug;
private WeakReference<Thread> thread;
private boolean enabled, updating;
private NoodleDebug() {
this.activity = null;
this.textView = null;
this.tokens = new LinkedList<>();
this.debug = new StringBuilder(0);
this.thread = NULL_THREAD_REF;
}
public NoodleDebug(Activity activity,@IdRes int textViewId) {
this(activity, activity.findViewById(textViewId));
}
public NoodleDebug(Activity activity, TextView textView) {
this.activity = Objects.requireNonNull(activity);
this.textView = Objects.requireNonNull(textView);
this.tokens = new LinkedList<>();
this.debug = new StringBuilder(64);
this.thread = NULL_THREAD_REF;
}
public NoodleDebug bind() {
synchronized (this.tokens) {
Thread thread;
if ((thread = this.thread.get()) != null) {
Log.e(TAG, "Trying to bind to thread \"" + Thread.currentThread().getName() +
"\" while already bound to \"" + thread.getName() + "\"");
return NULL;
}
this.tokens.clear();
}
if (this.enabled) {
this.thread = new WeakReference<>(Thread.currentThread());
THREAD_NOODLE.set(this);
} else {
this.thread = null;
THREAD_NOODLE.remove();
}
return this;
}
public void unbind() {
this.thread = NULL_THREAD_REF;
boolean markDirty;
synchronized (this.tokens) {
markDirty = !this.tokens.isEmpty();
this.tokens.clear();
}
if (markDirty) this.markDirty();
}
public boolean isBound() {
return this.thread.get() != null;
}
public void push(String token) {
if (!this.enabled) return;
synchronized (this.tokens) {
this.tokens.add(token);
}
if (!token.isEmpty())
this.markDirty();
}
public void pop() {
if (!this.enabled) return;
String last;
synchronized (this.tokens) {
last = this.tokens.removeLast();
}
if (!last.isEmpty())
this.markDirty();
}
public void replace(String token) {
if (!this.enabled) return;
String last;
synchronized (this.tokens) {
last = this.tokens.removeLast();
this.tokens.add(token);
}
if (!last.equals(token))
this.markDirty();
}
public void setEnabled(boolean enabled) {
if (this.enabled && !enabled) {
this.thread = NULL_THREAD_REF;
synchronized (this.tokens) {
this.tokens.clear();
}
this.markDirty();
}
this.enabled = enabled;
}
protected void markDirty() {
assert this.activity != null;
assert this.textView != null;
if (this.updating) return;
this.updating = true;
this.activity.runOnUiThread(() -> {
String debugText;
synchronized (this.tokens) {
StringBuilder debug = this.debug;
debug.setLength(0);
boolean first = true;
for (String text : this.tokens) {
if (text.isEmpty()) continue;
if (first) first = false;
else debug.append(" > ");
debug.append(text);
}
debugText = debug.toString();
}
this.updating = false;
this.textView.setText(debugText);
});
}
@NonNull
public static NoodleDebug getNoodleDebug() {
NoodleDebug noodleDebug = THREAD_NOODLE.get();
if (noodleDebug == null) return NULL;
if (noodleDebug.thread.get() != Thread.currentThread()) {
THREAD_NOODLE.remove();
return NULL;
}
return noodleDebug;
}
}

@ -51,6 +51,13 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/action_bar_blur" />
<TextView
android:id="@+id/noodle_debug"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/action_bar_blur" />
<LinearLayout
android:id="@+id/search_container"
android:layout_width="match_parent"

Loading…
Cancel
Save