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,511 @@
1
+ import Java from "../../java-runtime.js";
2
+ import { API } from "../../api.js";
3
+ import { ConstantConfig } from "../../constant-config.js";
4
+ import { Logger } from "../../logger.js";
5
+ import { logicalToWindow, windowToLogical } from "../../utils.js";
6
+ import { ensureWindowNotFocusable, getOverlayWindowType, getWindowManager, } from "../../android-runtime.js";
7
+ import { dp } from "../style/style.js";
8
+ import { createJavaListener } from "../../android-listener.js";
9
+ import { ListenerRegistry } from "../../runtime/listener-registry.js";
10
+ export class LogViewWindow {
11
+ enforceNonFocusableWindow() {
12
+ if (!this.windowParams)
13
+ return false;
14
+ const changed = ensureWindowNotFocusable(this.windowParams, API.LayoutParams);
15
+ try {
16
+ this.windowRoot?.clearFocus?.();
17
+ }
18
+ catch { }
19
+ return changed;
20
+ }
21
+ constructor(context, theme, menu, logMaxLines = 100, onCloseButtonClick) {
22
+ this.windowRoot = null;
23
+ this.windowParams = null;
24
+ this.titleDragHandle = null;
25
+ this.logView = null;
26
+ this.isCreated = false;
27
+ this.isCreating = false;
28
+ this.isAttached = false;
29
+ this.isLogWindowVisible = false;
30
+ this._loggerUnsub = null;
31
+ this._logMaxLinesCache = 0;
32
+ this._logRing = null;
33
+ this._logHead = 0;
34
+ this._logSize = 0;
35
+ this._logPending = [];
36
+ this._logFlushScheduled = false;
37
+ this._onCloseButtonClick = null;
38
+ this.listeners = new ListenerRegistry();
39
+ this.context = context;
40
+ this.theme = theme;
41
+ this.menu = menu;
42
+ this.logMaxLines = logMaxLines;
43
+ this._onCloseButtonClick = onCloseButtonClick ?? null;
44
+ this.windowManager = getWindowManager(Java, this.context);
45
+ }
46
+ setOnCloseButtonClick(callback) {
47
+ this._onCloseButtonClick = callback ?? null;
48
+ }
49
+ bindLoggerToLogViewOnce() {
50
+ if (this._loggerUnsub)
51
+ return;
52
+ this._loggerUnsub = Logger.instance.onLog((items) => {
53
+ for (let i = 0; i < items.length; i++) {
54
+ const it = items[i];
55
+ this.addLogToView(it.level, it.message, it.ts);
56
+ }
57
+ }, true);
58
+ }
59
+ addLogToView(level, message, ts) {
60
+ if (!this.logView)
61
+ return;
62
+ const maxLines = this.logMaxLines | 0;
63
+ if (maxLines <= 0)
64
+ return;
65
+ if (!this._logRing || this._logMaxLinesCache !== maxLines) {
66
+ this._logMaxLinesCache = maxLines;
67
+ this._logRing = new Array(maxLines);
68
+ this._logHead = 0;
69
+ this._logSize = 0;
70
+ this._logPending.length = 0;
71
+ this._logFlushScheduled = false;
72
+ try {
73
+ this.logView.setText.overload('java.lang.CharSequence').call(this.logView, API.JString.$new(""));
74
+ }
75
+ catch { }
76
+ }
77
+ const t = new Date(ts).toTimeString().substring(0, 8);
78
+ this._logPending.push(`[${t}:${level}] ${message}`);
79
+ if (this._logFlushScheduled)
80
+ return;
81
+ this._logFlushScheduled = true;
82
+ Java.scheduleOnMainThread(() => {
83
+ this._logFlushScheduled = false;
84
+ if (!this.logView || !this._logRing)
85
+ return;
86
+ const pending = this._logPending;
87
+ this._logPending = [];
88
+ for (const line of pending) {
89
+ this._logRing[this._logHead] = line;
90
+ this._logHead = (this._logHead + 1) % this._logMaxLinesCache;
91
+ if (this._logSize < this._logMaxLinesCache)
92
+ this._logSize++;
93
+ }
94
+ let out = "";
95
+ const start = (this._logHead - this._logSize + this._logMaxLinesCache) %
96
+ this._logMaxLinesCache;
97
+ for (let i = 0; i < this._logSize; i++) {
98
+ const idx = (start + i) % this._logMaxLinesCache;
99
+ const s = this._logRing[idx];
100
+ if (s == null)
101
+ continue;
102
+ out += s;
103
+ if (i !== this._logSize - 1)
104
+ out += "\n";
105
+ }
106
+ try {
107
+ this.logView.setText.overload('java.lang.CharSequence').call(this.logView, API.JString.$new(out));
108
+ }
109
+ catch (e) {
110
+ Logger.instance.error("flush log text failed: " + e);
111
+ }
112
+ });
113
+ }
114
+ buildWindowType() {
115
+ const LayoutParams = API.LayoutParams;
116
+ return getOverlayWindowType(LayoutParams);
117
+ }
118
+ createWindowOnce() {
119
+ if (this.isCreated || this.isCreating || this.windowRoot)
120
+ return;
121
+ this.isCreating = true;
122
+ const self = this;
123
+ const LinearLayout = API.LinearLayout;
124
+ const LinearLayoutParams = API.LinearLayoutParams;
125
+ const ViewGroupLayoutParams = API.ViewGroupLayoutParams;
126
+ const TextView = API.TextView;
127
+ const ScrollView = API.ScrollView;
128
+ const GradientDrawable = API.GradientDrawable;
129
+ const Gravity = API.Gravity;
130
+ const JString = API.JString;
131
+ const LayoutParams = API.LayoutParams;
132
+ const View = API.View;
133
+ const FrameLayout = API.FrameLayout;
134
+ const FrameLayoutParams = API.FrameLayoutParams;
135
+ const PixelFormat = Java.use("android.graphics.PixelFormat");
136
+ try {
137
+ const root = FrameLayout.$new(this.context);
138
+ const parentLp = FrameLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.MATCH_PARENT.value);
139
+ root.setLayoutParams(parentLp);
140
+ root.setBackgroundColor(0x00000000);
141
+ root.setClickable(false);
142
+ root.setFocusable(false);
143
+ const panel = LinearLayout.$new(this.context);
144
+ panel.setOrientation(LinearLayout.VERTICAL.value);
145
+ panel.setClickable(true);
146
+ panel.setFocusable(false);
147
+ const rootLp = FrameLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.MATCH_PARENT.value);
148
+ panel.setLayoutParams(rootLp);
149
+ const rootBg = GradientDrawable.$new();
150
+ rootBg.setCornerRadius(dp(this.context, 12));
151
+ rootBg.setColor(0xb80b1324 | 0);
152
+ rootBg.setStroke(dp(this.context, 1.2), 0xff1f63ff | 0);
153
+ panel.setBackgroundDrawable(rootBg);
154
+ try {
155
+ panel.setElevation(100010);
156
+ panel.setTranslationZ(100010);
157
+ }
158
+ catch { }
159
+ const header = LinearLayout.$new(this.context);
160
+ header.setOrientation(LinearLayout.HORIZONTAL.value);
161
+ header.setGravity(Gravity.CENTER_VERTICAL.value);
162
+ header.setPadding(dp(this.context, 14), dp(this.context, 11), dp(this.context, 14), dp(this.context, 11));
163
+ const headerBg = GradientDrawable.$new();
164
+ headerBg.setColor(0xcc102347 | 0);
165
+ headerBg.setCornerRadii([
166
+ dp(this.context, 12),
167
+ dp(this.context, 12),
168
+ dp(this.context, 12),
169
+ dp(this.context, 12),
170
+ 0,
171
+ 0,
172
+ 0,
173
+ 0,
174
+ ]);
175
+ header.setBackgroundDrawable(headerBg);
176
+ const title = TextView.$new(this.context);
177
+ title.setText.overload('java.lang.CharSequence').call(title, JString.$new("系统日志"));
178
+ title.setTextColor(0xff1f7bff | 0);
179
+ try {
180
+ title.setTypeface(null, 1);
181
+ }
182
+ catch { }
183
+ title.setTextSize(2, this.theme?.textSp?.title ?? 15);
184
+ title.setLayoutParams(LinearLayoutParams.$new(0, ViewGroupLayoutParams.WRAP_CONTENT.value, 1.0));
185
+ const dots = TextView.$new(this.context);
186
+ dots.setText.overload('java.lang.CharSequence').call(dots, JString.$new("● ● ●"));
187
+ dots.setTextColor(0xff4a5e80 | 0);
188
+ dots.setTextSize(2, this.theme?.textSp?.caption ?? 11);
189
+ header.addView(title);
190
+ header.addView(dots);
191
+ const scrollView = ScrollView.$new(this.context);
192
+ scrollView.setLayoutParams(LinearLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, 0, 1.0));
193
+ try {
194
+ scrollView.setFillViewport(true);
195
+ scrollView.setVerticalScrollBarEnabled(false);
196
+ scrollView.setBackgroundColor(0x00000000);
197
+ }
198
+ catch { }
199
+ const logText = TextView.$new(this.context);
200
+ logText.setLayoutParams(ViewGroupLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value));
201
+ logText.setTextColor(0xffeaf2ff | 0);
202
+ logText.setTextSize(2, this.theme?.textSp?.body ?? 13);
203
+ logText.setPadding(dp(this.context, 14), dp(this.context, 12), dp(this.context, 14), dp(this.context, 12));
204
+ logText.setLineSpacing(0, 1.08);
205
+ logText.setText.overload('java.lang.CharSequence').call(logText, JString.$new(""));
206
+ scrollView.addView(logText);
207
+ const footer = LinearLayout.$new(this.context);
208
+ footer.setOrientation(LinearLayout.HORIZONTAL.value);
209
+ footer.setGravity(Gravity.END.value | Gravity.CENTER_VERTICAL.value);
210
+ footer.setPadding(dp(this.context, 12), dp(this.context, 10), dp(this.context, 12), dp(this.context, 10));
211
+ const footerBg = GradientDrawable.$new();
212
+ footerBg.setColor(0xb8101d36 | 0);
213
+ footer.setBackgroundDrawable(footerBg);
214
+ const clearBtn = TextView.$new(this.context);
215
+ clearBtn.setText.overload('java.lang.CharSequence').call(clearBtn, JString.$new("清除"));
216
+ try {
217
+ clearBtn.setTypeface(null, 1);
218
+ }
219
+ catch { }
220
+ clearBtn.setTextColor(0xff9aaaca | 0);
221
+ clearBtn.setTextSize(2, this.theme?.textSp?.caption ?? 12);
222
+ clearBtn.setGravity(Gravity.CENTER.value);
223
+ clearBtn.setPadding(dp(this.context, 18), dp(this.context, 9), dp(this.context, 18), dp(this.context, 9));
224
+ const clearBg = GradientDrawable.$new();
225
+ clearBg.setCornerRadius(dp(this.context, 6));
226
+ clearBg.setColor(0x00112233 | 0);
227
+ clearBtn.setBackgroundDrawable(clearBg);
228
+ const closeBtn = TextView.$new(this.context);
229
+ closeBtn.setText.overload('java.lang.CharSequence').call(closeBtn, JString.$new("隐藏"));
230
+ try {
231
+ closeBtn.setTypeface(null, 1);
232
+ }
233
+ catch { }
234
+ closeBtn.setTextColor(0xffffffff | 0);
235
+ closeBtn.setTextSize(2, this.theme?.textSp?.caption ?? 12);
236
+ closeBtn.setGravity(Gravity.CENTER.value);
237
+ closeBtn.setPadding(dp(this.context, 20), dp(this.context, 10), dp(this.context, 20), dp(this.context, 10));
238
+ const closeBg = GradientDrawable.$new();
239
+ closeBg.setCornerRadius(dp(this.context, 6));
240
+ closeBg.setColor(0xff1b66ff | 0);
241
+ closeBg.setStroke(dp(this.context, 1), 0xff2f86ff | 0);
242
+ closeBtn.setBackgroundDrawable(closeBg);
243
+ const clearLp = LinearLayoutParams.$new(LinearLayoutParams.WRAP_CONTENT.value, LinearLayoutParams.WRAP_CONTENT.value);
244
+ clearLp.setMargins(0, 0, dp(this.context, 10), 0);
245
+ clearBtn.setLayoutParams(clearLp);
246
+ footer.addView(clearBtn);
247
+ footer.addView(closeBtn);
248
+ panel.addView(header);
249
+ panel.addView(scrollView);
250
+ panel.addView(footer);
251
+ root.addView(panel, rootLp);
252
+ const width = dp(this.context, 320);
253
+ const height = dp(this.context, 290);
254
+ this.width = width;
255
+ this.height = height;
256
+ const params = LayoutParams.$new(width, height, 0, 0, this.buildWindowType(), LayoutParams.FLAG_NOT_FOCUSABLE.value |
257
+ LayoutParams.FLAG_NOT_TOUCH_MODAL.value, PixelFormat.TRANSLUCENT.value);
258
+ this.logView = logText;
259
+ this.windowRoot = root;
260
+ this.windowParams = params;
261
+ this.titleDragHandle = header;
262
+ this.isCreated = true;
263
+ const clearClickListener = createJavaListener({
264
+ key: "view-click",
265
+ interfaceClass: API.OnClickListener,
266
+ callbacks: {
267
+ onClick: () => {
268
+ try {
269
+ self._logRing = new Array(self._logMaxLinesCache || self.logMaxLines);
270
+ self._logHead = 0;
271
+ self._logSize = 0;
272
+ self._logPending.length = 0;
273
+ if (self.logView) {
274
+ self.logView.setText.overload('java.lang.CharSequence').call(self.logView, JString.$new(""));
275
+ }
276
+ }
277
+ catch (e) {
278
+ Logger.instance.error("clear log failed: " + e);
279
+ }
280
+ },
281
+ },
282
+ });
283
+ this.listeners.add("log-window", () => clearClickListener.dispose());
284
+ clearBtn.setOnClickListener(clearClickListener.instance);
285
+ const closeClickListener = createJavaListener({
286
+ key: "view-click",
287
+ interfaceClass: API.OnClickListener,
288
+ callbacks: {
289
+ onClick: () => {
290
+ self.closeLogWindow();
291
+ try {
292
+ if (self._onCloseButtonClick) {
293
+ self._onCloseButtonClick();
294
+ }
295
+ }
296
+ catch (e) {
297
+ Logger.instance.error("close callback failed: " + e);
298
+ }
299
+ },
300
+ },
301
+ });
302
+ this.listeners.add("log-window", () => closeClickListener.dispose());
303
+ closeBtn.setOnClickListener(closeClickListener.instance);
304
+ try {
305
+ root.setVisibility(View.GONE.value);
306
+ }
307
+ catch { }
308
+ this.addDragListener(this.titleDragHandle, this.windowRoot, this.windowParams, () => this.isLogWindowVisible);
309
+ this.bindLoggerToLogViewOnce();
310
+ }
311
+ catch (e) {
312
+ Logger.instance.error("create log window ui failed: " + e);
313
+ this.isCreated = false;
314
+ this.windowRoot = null;
315
+ this.windowParams = null;
316
+ this.titleDragHandle = null;
317
+ this.logView = null;
318
+ }
319
+ finally {
320
+ this.isCreating = false;
321
+ }
322
+ }
323
+ attachWindowIfNeeded() {
324
+ const self = this;
325
+ const View = API.View;
326
+ if (!this.windowRoot || !this.windowParams)
327
+ return;
328
+ if (this.isAttached)
329
+ return;
330
+ Java.scheduleOnMainThread(() => {
331
+ try {
332
+ if (!self.windowRoot || !self.windowParams || self.isAttached)
333
+ return;
334
+ self.enforceNonFocusableWindow();
335
+ self.windowManager.addView(self.windowRoot, self.windowParams);
336
+ self.isAttached = true;
337
+ if (self.isLogWindowVisible) {
338
+ try {
339
+ self.windowRoot.setVisibility(View.VISIBLE.value);
340
+ }
341
+ catch { }
342
+ }
343
+ }
344
+ catch (e) {
345
+ Logger.instance.error("attach log window failed: " + e);
346
+ }
347
+ });
348
+ }
349
+ addDragListener(targetView, window, winParams, isShowing) {
350
+ const OnTouchListener = API.OnTouchListener;
351
+ const MotionEvent = API.MotionEvent;
352
+ targetView.setClickable(true);
353
+ const getBounds = () => {
354
+ const w = this.width;
355
+ const h = this.height;
356
+ return {
357
+ left: 0,
358
+ top: -40,
359
+ right: ConstantConfig.screenWidth - w,
360
+ bottom: ConstantConfig.screenHeight - h,
361
+ };
362
+ };
363
+ const self = this;
364
+ let isDragging = false;
365
+ const DRAG_THRESHOLD = 5;
366
+ let touchOffsetX = 0;
367
+ let touchOffsetY = 0;
368
+ const touchListener = createJavaListener({
369
+ key: "view-touch",
370
+ interfaceClass: OnTouchListener,
371
+ fallback: { onTouch: false },
372
+ callbacks: {
373
+ onTouch: (_v, event) => {
374
+ const action = event.getAction();
375
+ switch (action) {
376
+ case MotionEvent.ACTION_DOWN.value: {
377
+ isDragging = false;
378
+ const rawX = event.getRawX();
379
+ const rawY = event.getRawY();
380
+ const startWx = winParams.x.value;
381
+ const startWy = winParams.y.value;
382
+ touchOffsetX = rawX - startWx;
383
+ touchOffsetY = rawY - startWy;
384
+ self.lastTouchX = rawX;
385
+ self.lastTouchY = rawY;
386
+ return true;
387
+ }
388
+ case MotionEvent.ACTION_MOVE.value: {
389
+ const rawX = event.getRawX();
390
+ const rawY = event.getRawY();
391
+ const dx = rawX - self.lastTouchX;
392
+ const dy = rawY - self.lastTouchY;
393
+ if (!isDragging &&
394
+ (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD)) {
395
+ isDragging = true;
396
+ }
397
+ if (isDragging) {
398
+ let wx = rawX - touchOffsetX;
399
+ let wy = rawY - touchOffsetY;
400
+ const p = windowToLogical(wx, wy, self.width, self.height);
401
+ let newX = p.x;
402
+ let newY = p.y;
403
+ const bounds = getBounds();
404
+ newX = Math.max(bounds.left, Math.min(bounds.right, newX));
405
+ newY = Math.max(bounds.top, Math.min(bounds.bottom, newY));
406
+ if (isShowing()) {
407
+ self.updatePosition(window, winParams, { x: newX, y: newY });
408
+ }
409
+ }
410
+ return true;
411
+ }
412
+ case MotionEvent.ACTION_UP.value:
413
+ case MotionEvent.ACTION_CANCEL.value: {
414
+ return true;
415
+ }
416
+ }
417
+ return false;
418
+ },
419
+ },
420
+ });
421
+ this.listeners.add("log-window", () => touchListener.dispose());
422
+ targetView.setOnTouchListener(touchListener.instance);
423
+ }
424
+ updatePosition(window, winParams, newPos) {
425
+ const { x: wx, y: wy } = logicalToWindow(newPos.x, newPos.y, this.width, this.height);
426
+ winParams.x.value = wx | 0;
427
+ winParams.y.value = wy | 0;
428
+ Java.scheduleOnMainThread(() => {
429
+ this.enforceNonFocusableWindow();
430
+ this.windowManager.updateViewLayout(window, winParams);
431
+ });
432
+ }
433
+ openLogWindow() {
434
+ const self = this;
435
+ const View = API.View;
436
+ this.isLogWindowVisible = true;
437
+ this.createWindowOnce();
438
+ this.attachWindowIfNeeded();
439
+ Java.scheduleOnMainThread(() => {
440
+ try {
441
+ if (!self.windowRoot)
442
+ return;
443
+ const flagsChanged = self.enforceNonFocusableWindow();
444
+ self.windowRoot.setVisibility(View.VISIBLE.value);
445
+ if (self.isAttached) {
446
+ try {
447
+ if (flagsChanged) {
448
+ self.windowManager.updateViewLayout(self.windowRoot, self.windowParams);
449
+ }
450
+ }
451
+ catch { }
452
+ }
453
+ }
454
+ catch (e) {
455
+ Logger.instance.error("open log window failed: " + e);
456
+ }
457
+ });
458
+ }
459
+ closeLogWindow() {
460
+ this.isLogWindowVisible = false;
461
+ if (!this.windowRoot)
462
+ return;
463
+ const self = this;
464
+ const View = API.View;
465
+ Java.scheduleOnMainThread(() => {
466
+ try {
467
+ if (!self.windowRoot)
468
+ return;
469
+ const flagsChanged = self.enforceNonFocusableWindow();
470
+ if (flagsChanged && self.isAttached) {
471
+ self.windowManager.updateViewLayout(self.windowRoot, self.windowParams);
472
+ }
473
+ self.windowRoot.setVisibility(View.GONE.value);
474
+ }
475
+ catch (e) {
476
+ Logger.instance.error("close log window failed: " + e);
477
+ }
478
+ });
479
+ }
480
+ destroy() {
481
+ const self = this;
482
+ this.isLogWindowVisible = false;
483
+ this.listeners.clearAll();
484
+ if (this._loggerUnsub) {
485
+ try {
486
+ this._loggerUnsub();
487
+ }
488
+ catch { }
489
+ this._loggerUnsub = null;
490
+ }
491
+ Java.scheduleOnMainThread(() => {
492
+ try {
493
+ if (self.windowRoot && self.isAttached) {
494
+ self.windowManager.removeView(self.windowRoot);
495
+ }
496
+ }
497
+ catch (e) {
498
+ Logger.instance.error("destroy remove log window failed: " + e);
499
+ }
500
+ finally {
501
+ self.windowRoot = null;
502
+ self.windowParams = null;
503
+ self.titleDragHandle = null;
504
+ self.logView = null;
505
+ self.isCreated = false;
506
+ self.isCreating = false;
507
+ self.isAttached = false;
508
+ }
509
+ });
510
+ }
511
+ }
@@ -0,0 +1,41 @@
1
+ import { Logger } from "../../logger.js";
2
+ import { Theme } from "../style/theme.js";
3
+ interface TabDefinition {
4
+ id: string;
5
+ label: string;
6
+ }
7
+ export declare class TabsView {
8
+ private context;
9
+ private parentView;
10
+ private theme;
11
+ tabContainer: any;
12
+ tabs: Map<string, {
13
+ label: string;
14
+ container: any;
15
+ root?: any;
16
+ scrollView?: any;
17
+ components: Set<string>;
18
+ }>;
19
+ activeTabId: any;
20
+ tabView: any;
21
+ menuPanelView: any;
22
+ logger: Logger;
23
+ initTabs: TabDefinition[];
24
+ currentContentContainer: any;
25
+ currentScrollView: any;
26
+ private eventEmitter;
27
+ tabScrollView: any;
28
+ tabIndicatorView: any;
29
+ private tabItemMap;
30
+ private readonly listeners;
31
+ constructor(context: any, theme: Theme, initTabs: TabDefinition[], activeTabId?: string);
32
+ createTabView(parentView: any): void;
33
+ selectTab(tabId: string): void;
34
+ private updateTabStyle;
35
+ private updateTabIndicator;
36
+ private refreshTabsUI;
37
+ initializeTabs(): void;
38
+ createTabContainer(): void;
39
+ destroy(): void;
40
+ }
41
+ export {};