diff --git a/.Jules/palette.md b/.Jules/palette.md new file mode 100644 index 0000000..75cf149 --- /dev/null +++ b/.Jules/palette.md @@ -0,0 +1,3 @@ +## 2025-05-15 - State Feedback for Native Library Loading +**Learning:** For actions that change the global state of the application (like loading a native library or initializing a hook), providing only a Toast is insufficient because the button remains interactive, which can lead to confusion or redundant calls. +**Action:** Disable the triggering button and update its text (e.g., from "load lib.so" to "lib.so loaded") immediately upon success to provide persistent, clear state feedback and prevent redundant interactions. diff --git a/app/src/main/java/com/bytedance/shadowhook/sample/MainActivity.java b/app/src/main/java/com/bytedance/shadowhook/sample/MainActivity.java index 90c3219..182fe98 100644 --- a/app/src/main/java/com/bytedance/shadowhook/sample/MainActivity.java +++ b/app/src/main/java/com/bytedance/shadowhook/sample/MainActivity.java @@ -29,6 +29,8 @@ import android.os.Bundle; import android.util.Log; import android.view.View; +import android.widget.Button; +import android.widget.Toast; import com.bytedance.shadowhook.ShadowHook; import com.bytedance.shadowhook.systest.SysTest; @@ -50,37 +52,49 @@ protected void onCreate(Bundle savedInstanceState) { public void onUnitTestHookSymAddrClick(View view) { NativeHandler.nativeHookSymAddr(Build.VERSION.SDK_INT); + Toast.makeText(this, "Hook by symbol address", Toast.LENGTH_SHORT).show(); } public void onUnitTestHookSymNameClick(View view) { NativeHandler.nativeHookSymName(Build.VERSION.SDK_INT); + Toast.makeText(this, "Hook by symbol name", Toast.LENGTH_SHORT).show(); } public void onUnitTestUnhookClick(View view) { NativeHandler.nativeUnhook(); + Toast.makeText(this, "Unhook", Toast.LENGTH_SHORT).show(); } public void onUnitTestLoadClick(View view) { if(!hookee2Loaded) { hookee2Loaded = true; System.loadLibrary("hookee2"); + if (view instanceof Button) { + ((Button) view).setEnabled(false); + ((Button) view).setText("libhookee2.so loaded"); + } + Toast.makeText(this, "libhookee2.so loaded", Toast.LENGTH_SHORT).show(); } } public void onUnitTestRunClick(View view) { NativeHandler.nativeRun(hookee2Loaded); + Toast.makeText(this, "Unit test run", Toast.LENGTH_SHORT).show(); } public void onSystemTestHookClick(View view) { SysTest.hook(); + Toast.makeText(this, "System test hook", Toast.LENGTH_SHORT).show(); } public void onSystemTestUnhookClick(View view) { SysTest.unhook(); + Toast.makeText(this, "System test unhook", Toast.LENGTH_SHORT).show(); } public void onSystemTestRunClick(View view) { SysTest.run(); + Toast.makeText(this, "System test run", Toast.LENGTH_SHORT).show(); } public void onGetRecordsClick(View view) { @@ -90,12 +104,14 @@ public void onGetRecordsClick(View view) { for (String line : records.split("\n")) { Log.i(tag, line); } + Toast.makeText(this, "Records printed to logcat", Toast.LENGTH_SHORT).show(); } } public void onDumpRecordsClick(View view) { String pathname = getApplicationContext().getFilesDir() + "/shadowhook_records.txt"; NativeHandler.nativeDumpRecords(pathname); + Toast.makeText(this, "Records dumped to " + pathname, Toast.LENGTH_LONG).show(); BufferedReader br = null; try { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c74fc8e..5bae0b8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -13,7 +13,8 @@ + android:orientation="vertical" + android:padding="16dp">