frida-ui-runtime 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +181 -0
  2. package/lib/android-listener.d.ts +20 -0
  3. package/lib/android-listener.js +92 -0
  4. package/lib/android-runtime.d.ts +25 -0
  5. package/lib/android-runtime.js +310 -0
  6. package/lib/api.d.ts +1 -0
  7. package/lib/api.js +156 -0
  8. package/lib/component/button.d.ts +11 -0
  9. package/lib/component/button.js +61 -0
  10. package/lib/component/category.d.ts +9 -0
  11. package/lib/component/category.js +57 -0
  12. package/lib/component/checkBox.d.ts +33 -0
  13. package/lib/component/checkBox.js +276 -0
  14. package/lib/component/collapsible.d.ts +19 -0
  15. package/lib/component/collapsible.js +197 -0
  16. package/lib/component/image.d.ts +18 -0
  17. package/lib/component/image.js +85 -0
  18. package/lib/component/infoCardBox.d.ts +13 -0
  19. package/lib/component/infoCardBox.js +68 -0
  20. package/lib/component/input.d.ts +41 -0
  21. package/lib/component/input.js +342 -0
  22. package/lib/component/selector.d.ts +31 -0
  23. package/lib/component/selector.js +246 -0
  24. package/lib/component/slider.d.ts +18 -0
  25. package/lib/component/slider.js +156 -0
  26. package/lib/component/style/style.d.ts +4 -0
  27. package/lib/component/style/style.js +325 -0
  28. package/lib/component/style/theme.d.ts +33 -0
  29. package/lib/component/style/theme.js +24 -0
  30. package/lib/component/switch.d.ts +12 -0
  31. package/lib/component/switch.js +117 -0
  32. package/lib/component/text.d.ts +9 -0
  33. package/lib/component/text.js +36 -0
  34. package/lib/component/ui-components.d.ts +34 -0
  35. package/lib/component/ui-components.js +108 -0
  36. package/lib/component/views/header-view.d.ts +20 -0
  37. package/lib/component/views/header-view.js +198 -0
  38. package/lib/component/views/log-view.d.ts +41 -0
  39. package/lib/component/views/log-view.js +511 -0
  40. package/lib/component/views/tabs-view.d.ts +41 -0
  41. package/lib/component/views/tabs-view.js +334 -0
  42. package/lib/constant-config.d.ts +5 -0
  43. package/lib/constant-config.js +3 -0
  44. package/lib/event-emitter.d.ts +10 -0
  45. package/lib/event-emitter.js +52 -0
  46. package/lib/float-menu.d.ts +103 -0
  47. package/lib/float-menu.js +648 -0
  48. package/lib/index.d.ts +17 -0
  49. package/lib/index.js +16 -0
  50. package/lib/java-runtime.d.ts +2 -0
  51. package/lib/java-runtime.js +2 -0
  52. package/lib/logger.d.ts +43 -0
  53. package/lib/logger.js +200 -0
  54. package/lib/runtime/component-registry.d.ts +24 -0
  55. package/lib/runtime/component-registry.js +48 -0
  56. package/lib/runtime/launch-sequence.d.ts +5 -0
  57. package/lib/runtime/launch-sequence.js +20 -0
  58. package/lib/runtime/lifecycle-controller.d.ts +9 -0
  59. package/lib/runtime/lifecycle-controller.js +31 -0
  60. package/lib/runtime/listener-registry.d.ts +7 -0
  61. package/lib/runtime/listener-registry.js +40 -0
  62. package/lib/runtime/overlay-options.d.ts +9 -0
  63. package/lib/runtime/overlay-options.js +42 -0
  64. package/lib/runtime/safe-runtime.d.ts +3 -0
  65. package/lib/runtime/safe-runtime.js +20 -0
  66. package/lib/utils.d.ts +8 -0
  67. package/lib/utils.js +13 -0
  68. package/package.json +43 -0
@@ -0,0 +1,36 @@
1
+ import Java from "../java-runtime.js";
2
+ import { API } from "../api.js";
3
+ import { applyStyle } from "./style/style.js";
4
+ import { UIComponent } from "./ui-components.js";
5
+ export class TextView extends UIComponent {
6
+ constructor(id, content, kind = "normal", size) {
7
+ super(id);
8
+ this.value = content;
9
+ this.kind = kind;
10
+ this.size = size;
11
+ }
12
+ createView(context) {
13
+ const TextView = API.TextView;
14
+ const Html = API.Html;
15
+ const Gravity = API.Gravity;
16
+ this.view = TextView.$new(context);
17
+ applyStyle(this.view, this.kind === "note" ? "noteText" : "text", this.menu.options.theme);
18
+ if (this.size != null) {
19
+ this.view.setTextSize(2, this.size);
20
+ }
21
+ this.view.setGravity(Gravity.LEFT.value);
22
+ this.view.setText(Html.fromHtml(this.value));
23
+ }
24
+ updateView() {
25
+ if (!this.view)
26
+ return;
27
+ Java.scheduleOnMainThread(() => {
28
+ const Html = API.Html;
29
+ this.view.setText(Html.fromHtml(this.value));
30
+ });
31
+ }
32
+ setText(content) {
33
+ this.value = content;
34
+ this.updateView();
35
+ }
36
+ }
@@ -0,0 +1,34 @@
1
+ import { EventEmitter } from "../event-emitter.js";
2
+ import { FloatMenu } from "../float-menu.js";
3
+ export declare abstract class UIComponent {
4
+ protected emitter: EventEmitter;
5
+ protected view: any;
6
+ protected value: any;
7
+ protected id: string;
8
+ private initialized;
9
+ private attached;
10
+ private disposed;
11
+ private disposers;
12
+ protected menu: FloatMenu;
13
+ constructor(id: string);
14
+ setMenu(menu: FloatMenu): void;
15
+ apply(role: any, theme: any): void;
16
+ getView(): any;
17
+ getValue(): any;
18
+ getId(): string;
19
+ setValue(value: any): void;
20
+ on(event: string, listener: (...args: any[]) => void): () => void;
21
+ off(event: string, listener: (...args: any[]) => void): void;
22
+ protected emit(event: string, ...args: any[]): void;
23
+ protected abstract createView(context: any): void;
24
+ initialize(context: any, menu?: FloatMenu): void;
25
+ protected abstract updateView(): void;
26
+ protected own(dispose: () => void): () => void;
27
+ attach(_container?: any): void;
28
+ detach(): void;
29
+ captureState(): any;
30
+ restoreState(state: any): void;
31
+ dispose(): void;
32
+ get isInitialized(): boolean;
33
+ get isAttached(): boolean;
34
+ }
@@ -0,0 +1,108 @@
1
+ import Java from "../java-runtime.js";
2
+ import { EventEmitter } from "../event-emitter.js";
3
+ import { applyStyle } from "./style/style.js";
4
+ export class UIComponent {
5
+ constructor(id) {
6
+ this.emitter = new EventEmitter();
7
+ this.initialized = false;
8
+ this.attached = false;
9
+ this.disposed = false;
10
+ this.disposers = new Set();
11
+ this.id = id;
12
+ }
13
+ setMenu(menu) {
14
+ this.menu = menu;
15
+ }
16
+ apply(role, theme) {
17
+ if (!this.view)
18
+ return;
19
+ Java.scheduleOnMainThread(() => applyStyle(this.view, role, theme));
20
+ }
21
+ getView() {
22
+ return this.view;
23
+ }
24
+ getValue() {
25
+ return this.value;
26
+ }
27
+ getId() {
28
+ return this.id;
29
+ }
30
+ setValue(value) {
31
+ if (this.disposed)
32
+ throw new Error(`Component ${this.id} is disposed`);
33
+ this.value = value;
34
+ if (this.initialized)
35
+ this.updateView();
36
+ }
37
+ on(event, listener) {
38
+ return this.emitter.on(event, listener);
39
+ }
40
+ off(event, listener) {
41
+ this.emitter.off(event, listener);
42
+ }
43
+ emit(event, ...args) {
44
+ this.emitter.emit(event, ...args);
45
+ }
46
+ initialize(context, menu) {
47
+ if (this.disposed)
48
+ throw new Error(`Component ${this.id} is disposed`);
49
+ if (this.initialized)
50
+ return;
51
+ if (menu)
52
+ this.menu = menu;
53
+ this.createView(context);
54
+ this.initialized = true;
55
+ }
56
+ own(dispose) {
57
+ if (this.disposed) {
58
+ dispose();
59
+ return () => { };
60
+ }
61
+ let active = true;
62
+ const release = () => {
63
+ if (!active)
64
+ return;
65
+ active = false;
66
+ this.disposers.delete(release);
67
+ dispose();
68
+ };
69
+ this.disposers.add(release);
70
+ return release;
71
+ }
72
+ attach(_container) {
73
+ if (this.disposed)
74
+ throw new Error(`Component ${this.id} is disposed`);
75
+ this.attached = true;
76
+ }
77
+ detach() {
78
+ this.attached = false;
79
+ }
80
+ captureState() {
81
+ return this.value;
82
+ }
83
+ restoreState(state) {
84
+ this.setValue(state);
85
+ }
86
+ dispose() {
87
+ if (this.disposed)
88
+ return;
89
+ this.detach();
90
+ for (const dispose of Array.from(this.disposers).reverse()) {
91
+ try {
92
+ dispose();
93
+ }
94
+ catch { }
95
+ }
96
+ this.disposers.clear();
97
+ this.emitter.removeAllListeners();
98
+ this.view = null;
99
+ this.initialized = false;
100
+ this.disposed = true;
101
+ }
102
+ get isInitialized() {
103
+ return this.initialized;
104
+ }
105
+ get isAttached() {
106
+ return this.attached;
107
+ }
108
+ }
@@ -0,0 +1,20 @@
1
+ import { Theme } from "../style/theme.js";
2
+ export interface HeaderViewOptions {
3
+ context: any;
4
+ parent: any;
5
+ logMaxLines?: number;
6
+ title: string;
7
+ version: string;
8
+ }
9
+ export declare class HeaderView {
10
+ private readonly theme;
11
+ private headerDragView;
12
+ private logViewWindow;
13
+ private readonly listeners;
14
+ constructor(theme: Theme);
15
+ createView(options: HeaderViewOptions, callbacks: {
16
+ onMinimize: () => void;
17
+ onHide: () => void;
18
+ }): any;
19
+ destroy(): void;
20
+ }
@@ -0,0 +1,198 @@
1
+ import { API } from "../../api.js";
2
+ import { Logger } from "../../logger.js";
3
+ import { dp } from "../style/style.js";
4
+ import { LogViewWindow } from "./log-view.js";
5
+ import { createJavaListener } from "../../android-listener.js";
6
+ import { ListenerRegistry } from "../../runtime/listener-registry.js";
7
+ export class HeaderView {
8
+ constructor(theme) {
9
+ this.logViewWindow = null;
10
+ this.listeners = new ListenerRegistry();
11
+ this.theme = theme;
12
+ }
13
+ createView(options, callbacks) {
14
+ const { context, parent, logMaxLines, title, version } = options;
15
+ try {
16
+ const LinearLayout = API.LinearLayout;
17
+ const LinearLayoutParams = API.LinearLayoutParams;
18
+ const TextView = API.TextView;
19
+ const JString = API.JString;
20
+ const GradientDrawable = API.GradientDrawable;
21
+ const Gravity = API.Gravity;
22
+ const PAD_H = dp(context, 10);
23
+ const PAD_V = dp(context, 8);
24
+ const BTN_SIZE = dp(context, 34);
25
+ const BTN_RADIUS = dp(context, 10);
26
+ const createIconCharBtn = (ch, isDanger = false) => {
27
+ const btn = TextView.$new(context);
28
+ btn.setText.overload('java.lang.CharSequence').call(btn, JString.$new(ch));
29
+ btn.setGravity(Gravity.CENTER.value);
30
+ btn.setSingleLine(true);
31
+ btn.setTextSize(2, this.theme.textSp.title);
32
+ btn.setTextColor(isDanger ? this.theme.colors.accent : this.theme.colors.text);
33
+ const lp = LinearLayoutParams.$new(BTN_SIZE, BTN_SIZE);
34
+ btn.setLayoutParams(lp);
35
+ const touchListener = createJavaListener({
36
+ key: "view-touch",
37
+ interfaceClass: API.OnTouchListener,
38
+ fallback: { onTouch: false },
39
+ callbacks: {
40
+ onTouch: (v, ev) => {
41
+ try {
42
+ const MotionEvent = API.MotionEvent;
43
+ const action = ev.getAction();
44
+ if (action === MotionEvent.ACTION_DOWN.value)
45
+ v.setAlpha(0.6);
46
+ else if (action === MotionEvent.ACTION_UP.value ||
47
+ action === MotionEvent.ACTION_CANCEL.value) {
48
+ v.setAlpha(1.0);
49
+ }
50
+ }
51
+ catch { }
52
+ return false;
53
+ },
54
+ },
55
+ });
56
+ this.listeners.add("header", () => touchListener.dispose());
57
+ btn.setOnTouchListener(touchListener.instance);
58
+ const d = GradientDrawable.$new();
59
+ d.setCornerRadius(BTN_RADIUS);
60
+ d.setColor(0x00000000);
61
+ btn.setBackgroundDrawable(d);
62
+ btn.setPadding(dp(context, 6), dp(context, 6), dp(context, 6), dp(context, 6));
63
+ return btn;
64
+ };
65
+ const headerLp = LinearLayoutParams.$new(LinearLayoutParams.MATCH_PARENT.value, LinearLayoutParams.WRAP_CONTENT.value);
66
+ const headerRoot = LinearLayout.$new(context);
67
+ headerRoot.setOrientation(1);
68
+ headerRoot.setLayoutParams(headerLp);
69
+ const headerRow = LinearLayout.$new(context);
70
+ headerRow.setOrientation(0);
71
+ headerRow.setGravity(Gravity.CENTER_VERTICAL.value);
72
+ headerRow.setPadding(PAD_H, PAD_V, PAD_H, PAD_V);
73
+ this.headerDragView = headerRow;
74
+ try {
75
+ this.headerDragView.setBackgroundColor(0x00000000);
76
+ }
77
+ catch { }
78
+ const divider = API.View.$new(context);
79
+ const divLp = LinearLayoutParams.$new(LinearLayoutParams.MATCH_PARENT.value, dp(context, 1));
80
+ divider.setLayoutParams(divLp);
81
+ divider.setBackgroundColor(this.theme.colors.divider);
82
+ const leftBox = LinearLayout.$new(context);
83
+ leftBox.setOrientation(0);
84
+ leftBox.setGravity(Gravity.CENTER_VERTICAL.value);
85
+ const leftLp = LinearLayoutParams.$new(0, LinearLayoutParams.WRAP_CONTENT.value, 1.0);
86
+ leftBox.setLayoutParams(leftLp);
87
+ const icon = TextView.$new(context);
88
+ icon.setText.overload('java.lang.CharSequence').call(icon, JString.$new("▸"));
89
+ icon.setTextColor(this.theme.colors.accent);
90
+ icon.setTextSize(2, this.theme.textSp.title);
91
+ icon.setPadding(0, 0, dp(context, 8), 0);
92
+ const titleView = TextView.$new(context);
93
+ titleView.setText.overload('java.lang.CharSequence').call(titleView, JString.$new(title));
94
+ titleView.setSingleLine(true);
95
+ titleView.setTypeface(null, 1);
96
+ titleView.setTextColor(this.theme.colors.text);
97
+ titleView.setTextSize(2, this.theme.textSp.title);
98
+ const ver = TextView.$new(context);
99
+ ver.setText.overload('java.lang.CharSequence').call(ver, JString.$new(version));
100
+ ver.setSingleLine(true);
101
+ ver.setTextSize(2, this.theme.textSp.caption);
102
+ ver.setTextColor(this.theme.colors.accent);
103
+ const badgeBg = GradientDrawable.$new();
104
+ badgeBg.setCornerRadius(dp(context, 8));
105
+ badgeBg.setColor(0x22000000);
106
+ badgeBg.setStroke(dp(context, 1), this.theme.colors.divider);
107
+ ver.setBackgroundDrawable(badgeBg);
108
+ ver.setPadding(dp(context, 8), dp(context, 4), dp(context, 8), dp(context, 4));
109
+ const verLp = LinearLayoutParams.$new(LinearLayoutParams.WRAP_CONTENT.value, LinearLayoutParams.WRAP_CONTENT.value);
110
+ verLp.setMargins(dp(context, 10), 0, 0, 0);
111
+ ver.setLayoutParams(verLp);
112
+ leftBox.addView(icon);
113
+ leftBox.addView(titleView);
114
+ leftBox.addView(ver);
115
+ const rightBox = LinearLayout.$new(context);
116
+ rightBox.setOrientation(0);
117
+ rightBox.setGravity(Gravity.CENTER_VERTICAL.value);
118
+ const rightLp = LinearLayoutParams.$new(LinearLayoutParams.WRAP_CONTENT.value, LinearLayoutParams.WRAP_CONTENT.value);
119
+ rightBox.setLayoutParams(rightLp);
120
+ const logView = new LogViewWindow(context, this.theme, logMaxLines);
121
+ this.logViewWindow = logView;
122
+ logView.setOnCloseButtonClick(() => {
123
+ logButton.setText.overload('java.lang.CharSequence').call(logButton, API.JString.$new("L"));
124
+ });
125
+ const logButton = createIconCharBtn("L", false);
126
+ const logClickListener = createJavaListener({
127
+ key: "view-click",
128
+ interfaceClass: API.OnClickListener,
129
+ callbacks: {
130
+ onClick: () => {
131
+ if (logView.isLogWindowVisible) {
132
+ logView.closeLogWindow();
133
+ logButton.setText.overload('java.lang.CharSequence').call(logButton, API.JString.$new("L"));
134
+ }
135
+ else {
136
+ logView.openLogWindow();
137
+ logButton.setText.overload('java.lang.CharSequence').call(logButton, API.JString.$new("←"));
138
+ }
139
+ },
140
+ },
141
+ });
142
+ this.listeners.add("header", () => logClickListener.dispose());
143
+ logButton.setOnClickListener(logClickListener.instance);
144
+ const minButton = createIconCharBtn("—", false);
145
+ const minimizeClickListener = createJavaListener({
146
+ key: "view-click",
147
+ interfaceClass: API.OnClickListener,
148
+ callbacks: {
149
+ onClick: () => {
150
+ callbacks.onMinimize();
151
+ },
152
+ },
153
+ });
154
+ this.listeners.add("header", () => minimizeClickListener.dispose());
155
+ minButton.setOnClickListener(minimizeClickListener.instance);
156
+ const hideButton = createIconCharBtn("X", true);
157
+ const hideClickListener = createJavaListener({
158
+ key: "view-click",
159
+ interfaceClass: API.OnClickListener,
160
+ callbacks: {
161
+ onClick: () => {
162
+ callbacks.onHide();
163
+ },
164
+ },
165
+ });
166
+ this.listeners.add("header", () => hideClickListener.dispose());
167
+ hideButton.setOnClickListener(hideClickListener.instance);
168
+ const lp1 = LinearLayoutParams.$new(BTN_SIZE, BTN_SIZE);
169
+ lp1.setMargins(0, 0, dp(context, 4), 0);
170
+ logButton.setLayoutParams(lp1);
171
+ const lp2 = LinearLayoutParams.$new(BTN_SIZE, BTN_SIZE);
172
+ lp2.setMargins(0, 0, dp(context, 4), 0);
173
+ minButton.setLayoutParams(lp2);
174
+ const lp3 = LinearLayoutParams.$new(BTN_SIZE, BTN_SIZE);
175
+ lp3.setMargins(0, 0, 0, 0);
176
+ hideButton.setLayoutParams(lp3);
177
+ rightBox.addView(logButton);
178
+ rightBox.addView(minButton);
179
+ rightBox.addView(hideButton);
180
+ headerRoot.addView(headerRow);
181
+ headerRoot.addView(divider);
182
+ headerRow.addView(leftBox);
183
+ headerRow.addView(rightBox);
184
+ parent.addView(headerRoot);
185
+ return this.headerDragView;
186
+ }
187
+ catch (error) {
188
+ Logger.instance.error("Failed to create header view: " + error);
189
+ return null;
190
+ }
191
+ }
192
+ destroy() {
193
+ this.listeners.clearAll();
194
+ this.logViewWindow?.destroy();
195
+ this.logViewWindow = null;
196
+ this.headerDragView = null;
197
+ }
198
+ }
@@ -0,0 +1,41 @@
1
+ export declare class LogViewWindow {
2
+ private menu;
3
+ private context;
4
+ private windowManager;
5
+ private theme;
6
+ private logMaxLines;
7
+ private windowRoot;
8
+ private windowParams;
9
+ private titleDragHandle;
10
+ private logView;
11
+ private isCreated;
12
+ private isCreating;
13
+ private isAttached;
14
+ isLogWindowVisible: boolean;
15
+ private _loggerUnsub;
16
+ private _logMaxLinesCache;
17
+ private _logRing;
18
+ private _logHead;
19
+ private _logSize;
20
+ private _logPending;
21
+ private _logFlushScheduled;
22
+ private _onCloseButtonClick;
23
+ private enforceNonFocusableWindow;
24
+ private readonly listeners;
25
+ private width?;
26
+ private height?;
27
+ lastTouchX: any;
28
+ lastTouchY: any;
29
+ constructor(context: any, theme: any, menu: any, logMaxLines?: number, onCloseButtonClick?: () => void);
30
+ setOnCloseButtonClick(callback: (() => void) | null): void;
31
+ private bindLoggerToLogViewOnce;
32
+ private addLogToView;
33
+ private buildWindowType;
34
+ private createWindowOnce;
35
+ private attachWindowIfNeeded;
36
+ private addDragListener;
37
+ private updatePosition;
38
+ openLogWindow(): void;
39
+ closeLogWindow(): void;
40
+ destroy(): void;
41
+ }