|
10 | 10 | import java.lang.reflect.Method; |
11 | 11 | import java.lang.reflect.Field; |
12 | 12 |
|
| 13 | +/** |
| 14 | + * Schedule a safe install after initialization. |
| 15 | + * Call this from your smali injection. |
| 16 | + */ |
13 | 17 | public class TintFieldHook { |
14 | 18 | private static final String TAG = "CustomFilterHook"; |
| 19 | + private static boolean installed = false; |
15 | 20 |
|
16 | | - // Simple ping to check injection |
17 | | - public static void ping(Object toolTable) { |
18 | | - try { |
19 | | - Log.i(TAG, "ping() called. toolTable class: " + (toolTable != null ? toolTable.getClass().getName() : "null")); |
20 | | - } catch (Throwable t) { |
21 | | - Log.e(TAG, "ping() error", t); |
22 | | - } |
| 21 | + /** |
| 22 | + * Schedule a safe install after initialization. |
| 23 | + * Call this from your smali injection. |
| 24 | + */ |
| 25 | + public static void scheduleInstall(final Object toolTable) { |
| 26 | + if (installed) return; // Only run once |
| 27 | + installed = true; |
| 28 | + |
| 29 | + // Ensure this runs on the main thread after a short delay |
| 30 | + new Handler(Looper.getMainLooper()).postDelayed(() -> { |
| 31 | + try { |
| 32 | + installTintField(toolTable); |
| 33 | + } catch (Throwable t) { |
| 34 | + Log.e(TAG, "scheduled install failed", t); |
| 35 | + } |
| 36 | + }, 300); // 300ms delay gives initialize() time to finish |
23 | 37 | } |
24 | 38 |
|
25 | | - // schedule the real install a little later on the main thread |
26 | | - public static void scheduleInstall(final Object toolTable) { |
27 | | - try { |
28 | | - new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { |
29 | | - @Override |
30 | | - public void run() { |
31 | | - try { |
32 | | - installTintField(toolTable); |
33 | | - } catch (Throwable t) { |
34 | | - Log.e(TAG, "scheduled install failed", t); |
35 | | - } |
36 | | - } |
37 | | - }, 200); // 200 ms delay |
38 | | - } catch (Throwable t) { |
39 | | - Log.e(TAG, "scheduleInstall failed", t); |
| 39 | + /** |
| 40 | + * Main hook: safely introspect the toolTable. |
| 41 | + */ |
| 42 | + public static void installTintField(Object toolTable) { |
| 43 | + if (toolTable == null) { |
| 44 | + Log.w(TAG, "toolTable is null, aborting hook"); |
| 45 | + return; |
40 | 46 | } |
41 | | - } |
42 | 47 |
|
43 | | - // do minimal, defensive reflection/UI code here |
44 | | - public static void installTintField(Object table) { |
45 | 48 | try { |
46 | | - // defensive: attempt to find context safely |
47 | | - Object contextObj = null; |
48 | | - try { |
49 | | - // In a lot of these classes getModule().getContext() is the path; adapt as needed |
50 | | - java.lang.reflect.Method getModule = table.getClass().getMethod("getModule"); |
51 | | - Object module = getModule.invoke(table); |
52 | | - java.lang.reflect.Method getContext = module.getClass().getMethod("getContext"); |
53 | | - contextObj = getContext.invoke(module); |
54 | | - } catch (Throwable inner) { |
55 | | - Log.w(TAG, "couldn't get context via getModule/getContext", inner); |
56 | | - } |
| 49 | + Log.i(TAG, "installTintField() invoked; table class: " + toolTable.getClass().getName()); |
57 | 50 |
|
58 | | - // fallback: try table.getContext() |
59 | | - if (contextObj == null) { |
60 | | - try { |
61 | | - java.lang.reflect.Method getContext2 = table.getClass().getMethod("getContext"); |
62 | | - contextObj = getContext2.invoke(table); |
63 | | - } catch (Throwable ignored) {} |
| 51 | + // Example: safely list all fields |
| 52 | + Field[] fields = toolTable.getClass().getDeclaredFields(); |
| 53 | + for (Field f : fields) { |
| 54 | + f.setAccessible(true); |
| 55 | + Object value = null; |
| 56 | + try { value = f.get(toolTable); } catch (Throwable ignored) {} |
| 57 | + Log.i(TAG, "Field: " + f.getName() + " | Type: " + f.getType().getSimpleName() + " | Value: " + value); |
64 | 58 | } |
65 | 59 |
|
66 | | - Context context = (contextObj instanceof Context) ? (Context) contextObj : null; |
67 | | - |
68 | | - // Show a toast safely on main thread if we have a context |
69 | | - if (context != null) { |
70 | | - final Context ctx = context; |
71 | | - new Handler(Looper.getMainLooper()).post(() -> |
72 | | - Toast.makeText(ctx, "✅ Custom filter slot installed", Toast.LENGTH_SHORT).show() |
73 | | - ); |
| 60 | + // Example: safely call a method (if exists) |
| 61 | + try { |
| 62 | + Method getContext = toolTable.getClass().getMethod("getContext"); |
| 63 | + Object context = getContext.invoke(toolTable); |
| 64 | + Log.i(TAG, "Retrieved context: " + context); |
| 65 | + } catch (Throwable e) { |
| 66 | + Log.w(TAG, "getContext() method not available", e); |
74 | 67 | } |
75 | 68 |
|
76 | | - Log.i(TAG, "installTintField() finished; table class: " + (table != null ? table.getClass().getName() : "null")); |
77 | | - } catch (Throwable t) { |
78 | | - Log.e(TAG, "installTintField failed", t); |
| 69 | + // === INSERT CUSTOM FILTER SLOT LOGIC BELOW === |
| 70 | + // At this point you can add your UI components / custom slot safely |
| 71 | + Log.i(TAG, "Custom filter hook ready for adding filter slot!"); |
| 72 | + |
| 73 | + } catch (Throwable e) { |
| 74 | + Log.e(TAG, "installTintField failed", e); |
79 | 75 | } |
80 | 76 | } |
81 | 77 | } |
|
0 commit comments