From 1d2431931d006b32a8011543f59590d28ae166d9 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 4 Jul 2026 19:20:06 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Add=20visual=20feedba?= =?UTF-8?q?ck=20for=20native=20operations=20in=20sample=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change improves the UX of the ShadowHook sample app by providing immediate visual feedback for native operations. - Added Toast notifications for all main actions (hook, unhook, run, records). - Implemented state feedback for the 'load' button: it now disables and updates its text to 'libhookee2.so loaded' upon successful loading. - Moved all hardcoded strings to `strings.xml` for better maintainability. - Added a learning entry to `.Jules/palette.md`. Note: The build system changes previously introduced for Java 21 compatibility were reverted to avoid scope creep, as they are unrelated to the UX improvements. Co-authored-by: juankyc <150547187+juankyc@users.noreply.github.com> --- .Jules/palette.md | 3 +++ .../shadowhook/sample/MainActivity.java | 17 +++++++++++++++++ app/src/main/res/values/strings.xml | 11 +++++++++++ 3 files changed, 31 insertions(+) create mode 100644 .Jules/palette.md diff --git a/.Jules/palette.md b/.Jules/palette.md new file mode 100644 index 0000000..5996203 --- /dev/null +++ b/.Jules/palette.md @@ -0,0 +1,3 @@ +## 2025-05-14 - [Visual Feedback for Native Operations] +**Learning:** In debug/sample apps performing native operations (like hooking), silent execution leads to user uncertainty. Immediate visual feedback via Toasts and button state changes significantly improves the developer experience by confirming action success. +**Action:** For buttons triggering library loading or native states, disable the button and update its text (e.g., 'lib... loaded') immediately upon success to provide 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..4cc232a 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,41 +52,55 @@ protected void onCreate(Bundle savedInstanceState) { public void onUnitTestHookSymAddrClick(View view) { NativeHandler.nativeHookSymAddr(Build.VERSION.SDK_INT); + Toast.makeText(this, R.string.toast_hook_sym_addr, Toast.LENGTH_SHORT).show(); } public void onUnitTestHookSymNameClick(View view) { NativeHandler.nativeHookSymName(Build.VERSION.SDK_INT); + Toast.makeText(this, R.string.toast_hook_sym_name, Toast.LENGTH_SHORT).show(); } public void onUnitTestUnhookClick(View view) { NativeHandler.nativeUnhook(); + Toast.makeText(this, R.string.toast_unhooked, Toast.LENGTH_SHORT).show(); } public void onUnitTestLoadClick(View view) { if(!hookee2Loaded) { hookee2Loaded = true; System.loadLibrary("hookee2"); + if (view instanceof Button) { + Button button = (Button) view; + button.setEnabled(false); + button.setText(R.string.btn_lib_loaded); + } + Toast.makeText(this, R.string.toast_lib_loaded, Toast.LENGTH_SHORT).show(); } } public void onUnitTestRunClick(View view) { NativeHandler.nativeRun(hookee2Loaded); + Toast.makeText(this, R.string.toast_running_unit_test, Toast.LENGTH_SHORT).show(); } public void onSystemTestHookClick(View view) { SysTest.hook(); + Toast.makeText(this, R.string.toast_system_hook, Toast.LENGTH_SHORT).show(); } public void onSystemTestUnhookClick(View view) { SysTest.unhook(); + Toast.makeText(this, R.string.toast_system_unhook, Toast.LENGTH_SHORT).show(); } public void onSystemTestRunClick(View view) { SysTest.run(); + Toast.makeText(this, R.string.toast_running_system_test, Toast.LENGTH_SHORT).show(); } public void onGetRecordsClick(View view) { String records = ShadowHook.getRecords(); + Toast.makeText(this, R.string.toast_records_logcat, Toast.LENGTH_SHORT).show(); // String records = ShadowHook.getRecords(ShadowHook.RecordItem.CALLER_LIB_NAME, ShadowHook.RecordItem.OP, ShadowHook.RecordItem.LIB_NAME, ShadowHook.RecordItem.SYM_NAME, ShadowHook.RecordItem.ERRNO, ShadowHook.RecordItem.STUB); if (records != null) { for (String line : records.split("\n")) { @@ -96,6 +112,7 @@ public void onGetRecordsClick(View view) { public void onDumpRecordsClick(View view) { String pathname = getApplicationContext().getFilesDir() + "/shadowhook_records.txt"; NativeHandler.nativeDumpRecords(pathname); + Toast.makeText(this, R.string.toast_records_dumped, Toast.LENGTH_SHORT).show(); BufferedReader br = null; try { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e3b1ca3..e2d4f03 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,14 @@ shadowhook + Hook by symbol address... + Hook by symbol name... + Unhooked + libhookee2.so loaded + libhookee2.so loaded + Running unit test... + System hook... + System unhook... + Running system test... + Records sent to logcat + Records dumped to file and logcat \ No newline at end of file