frida-float-menu 1.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 (44) hide show
  1. package/README.md +404 -0
  2. package/lib/api.d.ts +50 -0
  3. package/lib/api.js +149 -0
  4. package/lib/component/button.d.ts +11 -0
  5. package/lib/component/button.js +57 -0
  6. package/lib/component/category.d.ts +8 -0
  7. package/lib/component/category.js +37 -0
  8. package/lib/component/checkBox.d.ts +28 -0
  9. package/lib/component/checkBox.js +199 -0
  10. package/lib/component/collapsible.d.ts +19 -0
  11. package/lib/component/collapsible.js +177 -0
  12. package/lib/component/image.d.ts +18 -0
  13. package/lib/component/image.js +87 -0
  14. package/lib/component/input.d.ts +33 -0
  15. package/lib/component/input.js +346 -0
  16. package/lib/component/selector.d.ts +22 -0
  17. package/lib/component/selector.js +95 -0
  18. package/lib/component/slider.d.ts +18 -0
  19. package/lib/component/slider.js +155 -0
  20. package/lib/component/style/style.d.ts +5 -0
  21. package/lib/component/style/style.js +261 -0
  22. package/lib/component/style/theme.d.ts +29 -0
  23. package/lib/component/style/theme.js +23 -0
  24. package/lib/component/switch.d.ts +12 -0
  25. package/lib/component/switch.js +77 -0
  26. package/lib/component/text.d.ts +9 -0
  27. package/lib/component/text.js +36 -0
  28. package/lib/component/ui-components.d.ts +24 -0
  29. package/lib/component/ui-components.js +49 -0
  30. package/lib/component/views/log-view.d.ts +25 -0
  31. package/lib/component/views/log-view.js +231 -0
  32. package/lib/component/views/tabs-view.d.ts +35 -0
  33. package/lib/component/views/tabs-view.js +296 -0
  34. package/lib/event-emitter.d.ts +10 -0
  35. package/lib/event-emitter.js +52 -0
  36. package/lib/float-menu.d.ts +63 -0
  37. package/lib/float-menu.js +568 -0
  38. package/lib/index.d.ts +12 -0
  39. package/lib/index.js +28 -0
  40. package/lib/logger.d.ts +43 -0
  41. package/lib/logger.js +183 -0
  42. package/lib/utils.d.ts +8 -0
  43. package/lib/utils.js +16 -0
  44. package/package.json +36 -0
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LogView = void 0;
4
+ const api_1 = require("../../api");
5
+ const logger_1 = require("../../logger");
6
+ const style_1 = require("../style/style");
7
+ class LogView {
8
+ constructor(context, height, theme, logMaxLines = 100) {
9
+ this.isLogDrawerOpen = false;
10
+ this._logMaxLinesCache = 0;
11
+ this._logRing = null;
12
+ this._logHead = 0;
13
+ this._logSize = 0;
14
+ this._logPending = [];
15
+ this._logFlushScheduled = false;
16
+ this.context = context;
17
+ this.height = height;
18
+ this.logMaxLines = logMaxLines;
19
+ this.theme = theme;
20
+ }
21
+ bindLoggerToLogViewOnce() {
22
+ if (this._loggerUnsub)
23
+ return;
24
+ const self = this;
25
+ this._loggerUnsub = logger_1.Logger.instance.onLog((items) => {
26
+ for (let i = 0; i < items.length; i++) {
27
+ const it = items[i];
28
+ self.addLogToView(it.level, it.message, it.ts);
29
+ }
30
+ }, true);
31
+ }
32
+ addLogToView(level, message, ts) {
33
+ if (!this.logView)
34
+ return;
35
+ const maxLines = this.logMaxLines | 0;
36
+ if (maxLines <= 0)
37
+ return;
38
+ if (!this._logRing || this._logMaxLinesCache !== maxLines) {
39
+ this._logMaxLinesCache = maxLines;
40
+ this._logRing = new Array(maxLines);
41
+ this._logHead = 0;
42
+ this._logSize = 0;
43
+ this._logPending.length = 0;
44
+ this._logFlushScheduled = false;
45
+ this.logView.setText(api_1.API.JString.$new(""));
46
+ }
47
+ this._logPending.push(`[${level.toUpperCase()} ${new Date(ts).toTimeString().substring(0, 8)}] ${message}`);
48
+ if (this._logFlushScheduled)
49
+ return;
50
+ this._logFlushScheduled = true;
51
+ Java.scheduleOnMainThread(() => {
52
+ this._logFlushScheduled = false;
53
+ if (!this.logView || !this._logRing)
54
+ return;
55
+ while (this._logPending.length > 0) {
56
+ const line = this._logPending.shift();
57
+ this._logRing[this._logHead] = line;
58
+ this._logHead = (this._logHead + 1) % this._logMaxLinesCache;
59
+ if (this._logSize < this._logMaxLinesCache)
60
+ this._logSize++;
61
+ }
62
+ let out = "";
63
+ const start = (this._logHead - this._logSize + this._logMaxLinesCache) %
64
+ this._logMaxLinesCache;
65
+ for (let i = 0; i < this._logSize; i++) {
66
+ const idx = (start + i) % this._logMaxLinesCache;
67
+ const s = this._logRing[idx];
68
+ if (s == null)
69
+ continue;
70
+ out += s;
71
+ if (i !== this._logSize - 1)
72
+ out += "\n";
73
+ }
74
+ this.logView.setText(api_1.API.JString.$new(out));
75
+ });
76
+ }
77
+ createViewOnce(parentView) {
78
+ if (!parentView) {
79
+ console.error("LogView: parentView is null");
80
+ return;
81
+ }
82
+ this.parentView = parentView;
83
+ if (this.logDrawerPanel)
84
+ return;
85
+ const LinearLayout = api_1.API.LinearLayout;
86
+ const FrameLayoutParams = api_1.API.FrameLayoutParams;
87
+ const ViewGroupLayoutParams = api_1.API.ViewGroupLayoutParams;
88
+ const Gravity = api_1.API.Gravity;
89
+ const GradientDrawable = api_1.API.GradientDrawable;
90
+ const ctx = this.context;
91
+ try {
92
+ this.parentView.setClipChildren(false);
93
+ }
94
+ catch { }
95
+ try {
96
+ this.parentView.setClipToPadding(false);
97
+ }
98
+ catch { }
99
+ const panel = LinearLayout.$new(this.context);
100
+ panel.setOrientation(LinearLayout.VERTICAL.value);
101
+ const panelLp = FrameLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, this.height);
102
+ panelLp.gravity.value = Gravity.BOTTOM.value;
103
+ panel.setLayoutParams(panelLp);
104
+ panel.setAlpha(0.9);
105
+ const bg = GradientDrawable.$new();
106
+ bg.setCornerRadius((0, style_1.dp)(ctx, 14));
107
+ bg.setColor(this.theme.colors.cardBg);
108
+ bg.setStroke((0, style_1.dp)(ctx, 1), this.theme.colors.divider);
109
+ panel.setBackgroundDrawable(bg);
110
+ panel.setPadding((0, style_1.dp)(ctx, 8), (0, style_1.dp)(ctx, 8), (0, style_1.dp)(ctx, 8), (0, style_1.dp)(ctx, 8));
111
+ try {
112
+ panel.setTranslationY(this.height);
113
+ }
114
+ catch { }
115
+ try {
116
+ panel.setElevation(100001);
117
+ }
118
+ catch { }
119
+ try {
120
+ panel.setTranslationZ(100001);
121
+ }
122
+ catch { }
123
+ const logRoot = this.createLogView();
124
+ logRoot.setLayoutParams(ViewGroupLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.MATCH_PARENT.value));
125
+ this.logScrollView = logRoot;
126
+ panel.addView(logRoot);
127
+ this.bindLoggerToLogViewOnce();
128
+ Java.scheduleOnMainThread(() => {
129
+ try {
130
+ this.parentView.addView(panel);
131
+ try {
132
+ panel.bringToFront();
133
+ }
134
+ catch { }
135
+ }
136
+ catch (e) {
137
+ console.error("ensureLogDrawer failed: " + e);
138
+ }
139
+ });
140
+ this.logDrawerPanel = panel;
141
+ }
142
+ openLogDrawer() {
143
+ if (!this.logDrawerPanel)
144
+ return;
145
+ const View = api_1.API.View;
146
+ this.isLogDrawerOpen = true;
147
+ Java.scheduleOnMainThread(() => {
148
+ try {
149
+ this.logDrawerPanel.setVisibility(View.VISIBLE.value);
150
+ try {
151
+ this.logDrawerPanel.bringToFront();
152
+ }
153
+ catch { }
154
+ try {
155
+ this.logDrawerPanel.animate()
156
+ .translationY(0)
157
+ .setDuration(180)
158
+ .start();
159
+ }
160
+ catch {
161
+ this.logDrawerPanel.setTranslationY(0);
162
+ }
163
+ }
164
+ catch { }
165
+ });
166
+ }
167
+ closeLogDrawer() {
168
+ const View = api_1.API.View;
169
+ this.isLogDrawerOpen = false;
170
+ const endAction = Java.registerClass({
171
+ name: "LogBottomCloseEnd" +
172
+ Date.now() +
173
+ Math.random().toString(36).substring(4),
174
+ implements: [Java.use("java.lang.Runnable")],
175
+ methods: {
176
+ run: () => {
177
+ try {
178
+ this.logDrawerPanel.setVisibility(View.GONE.value);
179
+ }
180
+ catch { }
181
+ },
182
+ },
183
+ }).$new();
184
+ Java.scheduleOnMainThread(() => {
185
+ try {
186
+ try {
187
+ this.logDrawerPanel.animate()
188
+ .translationY(this.height)
189
+ .setDuration(160)
190
+ .withEndAction(endAction)
191
+ .start();
192
+ }
193
+ catch {
194
+ this.logDrawerPanel.setTranslationY(this.height);
195
+ this.logDrawerPanel.setVisibility(View.GONE.value);
196
+ }
197
+ }
198
+ catch { }
199
+ });
200
+ }
201
+ createLogView() {
202
+ const ScrollView = api_1.API.ScrollView;
203
+ const TextView = api_1.API.TextView;
204
+ const ViewGroupLayoutParams = api_1.API.ViewGroupLayoutParams;
205
+ const LinearLayoutParams = api_1.API.LinearLayoutParams;
206
+ const JString = api_1.API.JString;
207
+ const Gravity = api_1.API.Gravity;
208
+ const sv = ScrollView.$new(this.context);
209
+ sv.setLayoutParams(LinearLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.MATCH_PARENT.value));
210
+ try {
211
+ sv.setFillViewport(true);
212
+ sv.setVerticalScrollBarEnabled(false);
213
+ sv.setBackgroundColor(0x00000000);
214
+ }
215
+ catch (e) {
216
+ console.error("createLogView setFillViewport failed: " + e);
217
+ }
218
+ const tv = TextView.$new(this.context);
219
+ tv.setLayoutParams(ViewGroupLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value));
220
+ tv.setTextColor(this.theme.colors.text);
221
+ tv.setTextSize(2, this.theme.textSp.body);
222
+ tv.setGravity(Gravity.START.value);
223
+ tv.setIncludeFontPadding(false);
224
+ tv.setPadding((0, style_1.dp)(this.context, 10), (0, style_1.dp)(this.context, 8), (0, style_1.dp)(this.context, 10), (0, style_1.dp)(this.context, 8));
225
+ tv.setText(JString.$new(""));
226
+ sv.addView(tv);
227
+ this.logView = tv;
228
+ return sv;
229
+ }
230
+ }
231
+ exports.LogView = LogView;
@@ -0,0 +1,35 @@
1
+ import { Logger } from "../../logger";
2
+ import { Theme } from "../style/theme";
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
+ constructor(context: any, theme: Theme, initTabs: TabDefinition[], activeTabId?: string);
28
+ createTabView(parentView: any): void;
29
+ private switchTab;
30
+ private updateTabStyle;
31
+ private refreshTabsUI;
32
+ initializeTabs(): void;
33
+ private createTabContainer;
34
+ }
35
+ export {};
@@ -0,0 +1,296 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TabsView = void 0;
4
+ const api_1 = require("../../api");
5
+ const event_emitter_1 = require("../../event-emitter");
6
+ const logger_1 = require("../../logger");
7
+ const style_1 = require("../style/style");
8
+ class TabsView {
9
+ constructor(context, theme, initTabs, activeTabId) {
10
+ this.tabs = new Map();
11
+ this.logger = logger_1.Logger.instance;
12
+ this.eventEmitter = new event_emitter_1.EventEmitter();
13
+ this.context = context;
14
+ this.theme = theme;
15
+ this.initTabs = initTabs;
16
+ this.activeTabId = activeTabId;
17
+ }
18
+ createTabView(parentView) {
19
+ this.parentView = parentView;
20
+ try {
21
+ const LinearLayout = api_1.API.LinearLayout;
22
+ const LinearLayoutParams = api_1.API.LinearLayoutParams;
23
+ const ViewGroupLayoutParams = api_1.API.ViewGroupLayoutParams;
24
+ const TextView = api_1.API.TextView;
25
+ const OnClickListener = api_1.API.OnClickListener;
26
+ const JString = api_1.API.JString;
27
+ const HorizontalScrollView = api_1.API.HorizontalScrollView;
28
+ const GradientDrawable = api_1.API.GradientDrawable;
29
+ const Gravity = api_1.API.Gravity;
30
+ const self = this;
31
+ const scrollView = HorizontalScrollView.$new(this.context);
32
+ scrollView.setHorizontalScrollBarEnabled(false);
33
+ scrollView.setScrollbarFadingEnabled(true);
34
+ scrollView.setFillViewport(true);
35
+ const scrollLp = LinearLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value);
36
+ scrollView.setLayoutParams(scrollLp);
37
+ const bg = GradientDrawable.$new();
38
+ bg.setCornerRadius((0, style_1.dp)(this.context, 14));
39
+ bg.setColor(this.theme.colors.cardBg);
40
+ bg.setStroke((0, style_1.dp)(this.context, 1), this.theme.colors.divider);
41
+ scrollView.setBackgroundDrawable(bg);
42
+ scrollView.setPadding((0, style_1.dp)(this.context, 8), (0, style_1.dp)(this.context, 6), (0, style_1.dp)(this.context, 8), (0, style_1.dp)(this.context, 6));
43
+ const tabContainer = LinearLayout.$new(this.context);
44
+ tabContainer.setOrientation(0);
45
+ tabContainer.setLayoutParams(LinearLayoutParams.$new(ViewGroupLayoutParams.WRAP_CONTENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value));
46
+ this.tabContainer = tabContainer;
47
+ const styleTab = (tv, active) => {
48
+ tv.setAllCaps(false);
49
+ tv.setSingleLine(true);
50
+ tv.setGravity(Gravity.CENTER.value);
51
+ tv.setTextSize(2, this.theme.textSp.body);
52
+ tv.setPadding((0, style_1.dp)(this.context, 12), (0, style_1.dp)(this.context, 8), (0, style_1.dp)(this.context, 12), (0, style_1.dp)(this.context, 8));
53
+ const d = GradientDrawable.$new();
54
+ d.setCornerRadius((0, style_1.dp)(this.context, 12));
55
+ if (active) {
56
+ d.setColor(this.theme.colors.accent);
57
+ tv.setTextColor(0xffffffff | 0);
58
+ }
59
+ else {
60
+ d.setColor(0x00000000);
61
+ tv.setTextColor(this.theme.colors.subText);
62
+ d.setStroke((0, style_1.dp)(this.context, 1), this.theme.colors.divider);
63
+ }
64
+ tv.setBackgroundDrawable(d);
65
+ try {
66
+ tv.setTypeface(null, active ? 1 : 0);
67
+ }
68
+ catch (e) { }
69
+ };
70
+ for (const [tabId, tabInfo] of this.tabs) {
71
+ const tabText = TextView.$new(this.context);
72
+ tabText.setText(JString.$new(tabInfo.label));
73
+ const btnLp = LinearLayoutParams.$new(ViewGroupLayoutParams.WRAP_CONTENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value);
74
+ btnLp.setMargins((0, style_1.dp)(this.context, 6), (0, style_1.dp)(this.context, 2), (0, style_1.dp)(this.context, 6), (0, style_1.dp)(this.context, 2));
75
+ tabText.setLayoutParams(btnLp);
76
+ styleTab(tabText, tabId === this.activeTabId);
77
+ const tabClickListener = Java.registerClass({
78
+ name: "com.example.TabClickListener" +
79
+ Date.now() +
80
+ Math.random().toString(36).substring(6) +
81
+ "_" +
82
+ tabId,
83
+ implements: [OnClickListener],
84
+ methods: {
85
+ onClick: function () {
86
+ self.switchTab(tabId);
87
+ },
88
+ },
89
+ });
90
+ tabText.setOnClickListener(tabClickListener.$new());
91
+ tabContainer.addView(tabText);
92
+ }
93
+ scrollView.addView(tabContainer);
94
+ this.tabView = scrollView;
95
+ parentView.addView(this.tabView);
96
+ this.createTabContainer();
97
+ }
98
+ catch (error) {
99
+ this.logger.error("Failed to create tab view: " + error);
100
+ }
101
+ }
102
+ switchTab(tabId) {
103
+ if (!this.tabs.has(tabId) || tabId === this.activeTabId)
104
+ return;
105
+ const oldTabId = this.activeTabId;
106
+ this.activeTabId = tabId;
107
+ this.refreshTabsUI();
108
+ Java.scheduleOnMainThread(() => {
109
+ try {
110
+ const View = api_1.API.View;
111
+ for (const [id, tabInfo] of this.tabs) {
112
+ const root = tabInfo.root;
113
+ if (!root)
114
+ continue;
115
+ if (id === tabId) {
116
+ root.setVisibility(View.VISIBLE.value);
117
+ this.currentContentContainer = tabInfo.container;
118
+ this.currentScrollView = tabInfo.scrollView;
119
+ }
120
+ else {
121
+ root.setVisibility(View.GONE.value);
122
+ }
123
+ }
124
+ if (this.tabContainer) {
125
+ const tabIds = Array.from(this.tabs.keys());
126
+ const childCount = this.tabContainer.getChildCount();
127
+ for (let i = 0; i < childCount && i < tabIds.length; i++) {
128
+ const tv = Java.cast(this.tabContainer.getChildAt(i), api_1.API.TextView);
129
+ this.updateTabStyle(tv, tabIds[i] === tabId);
130
+ }
131
+ }
132
+ this.eventEmitter.emit("tabChanged", tabId, oldTabId);
133
+ }
134
+ catch (error) {
135
+ console.error(`Failed to switch to tab ${tabId}:`, error);
136
+ }
137
+ });
138
+ }
139
+ updateTabStyle(button, isActive) {
140
+ const GradientDrawable = api_1.API.GradientDrawable;
141
+ const ctx = button.getContext();
142
+ const radius = (0, style_1.dp)(ctx, 12);
143
+ const strokeW = (0, style_1.dp)(ctx, 1);
144
+ const padH = (0, style_1.dp)(ctx, 12);
145
+ const padV = (0, style_1.dp)(ctx, 8);
146
+ try {
147
+ button.setAllCaps(false);
148
+ }
149
+ catch (e) { }
150
+ button.setSingleLine(true);
151
+ button.setTextSize(2, this.theme.textSp.body);
152
+ button.setPadding(padH, padV, padH, padV);
153
+ const drawable = GradientDrawable.$new();
154
+ drawable.setCornerRadius(radius);
155
+ if (isActive) {
156
+ drawable.setColor(this.theme.colors.accent);
157
+ button.setTextColor(0xffffffff | 0);
158
+ try {
159
+ button.setTypeface(null, 1);
160
+ }
161
+ catch (e) { }
162
+ }
163
+ else {
164
+ drawable.setColor(0x00000000);
165
+ drawable.setStroke(strokeW, this.theme.colors.divider);
166
+ button.setTextColor(this.theme.colors.subText);
167
+ try {
168
+ button.setTypeface(null, 0);
169
+ }
170
+ catch (e) { }
171
+ }
172
+ button.setBackgroundDrawable(drawable);
173
+ }
174
+ refreshTabsUI() {
175
+ try {
176
+ if (!this.tabContainer)
177
+ return;
178
+ const GradientDrawable = api_1.API.GradientDrawable;
179
+ const count = this.tabContainer.getChildCount();
180
+ const tabIds = Array.from(this.tabs.keys());
181
+ for (let i = 0; i < count; i++) {
182
+ const tv = this.tabContainer.getChildAt(i);
183
+ const tabId = tabIds[i];
184
+ const active = tabId === this.activeTabId;
185
+ const d = GradientDrawable.$new();
186
+ d.setCornerRadius((0, style_1.dp)(this.context, 12));
187
+ if (active) {
188
+ d.setColor(this.theme.colors.accent);
189
+ tv.setTextColor(0xffffffff | 0);
190
+ try {
191
+ tv.setTypeface(null, 1);
192
+ }
193
+ catch (e) { }
194
+ }
195
+ else {
196
+ d.setColor(0x00000000);
197
+ d.setStroke((0, style_1.dp)(this.context, 1), this.theme.colors.divider);
198
+ tv.setTextColor(this.theme.colors.subText);
199
+ try {
200
+ tv.setTypeface(null, 0);
201
+ }
202
+ catch (e) { }
203
+ }
204
+ tv.setBackgroundDrawable(d);
205
+ }
206
+ }
207
+ catch (e) { }
208
+ }
209
+ initializeTabs() {
210
+ this.tabs.clear();
211
+ if (this.initTabs && this.initTabs.length > 0) {
212
+ for (const tabDef of this.initTabs) {
213
+ this.tabs.set(tabDef.id, {
214
+ label: tabDef.label,
215
+ container: null,
216
+ components: new Set(),
217
+ });
218
+ }
219
+ if (this.activeTabId && this.tabs.has(this.activeTabId)) {
220
+ this.activeTabId = this.activeTabId;
221
+ }
222
+ else if (this.initTabs.length > 0) {
223
+ this.activeTabId = this.initTabs[0].id;
224
+ }
225
+ }
226
+ else {
227
+ this.tabs.set("default", {
228
+ label: "Default",
229
+ container: null,
230
+ components: new Set(),
231
+ });
232
+ this.activeTabId = "default";
233
+ }
234
+ }
235
+ createTabContainer() {
236
+ const ScrollView = api_1.API.ScrollView;
237
+ const LinearLayout = api_1.API.LinearLayout;
238
+ const ViewGroupLayoutParams = api_1.API.ViewGroupLayoutParams;
239
+ const LinearLayoutParams = api_1.API.LinearLayoutParams;
240
+ const View = api_1.API.View;
241
+ const tabRootsWrapper = LinearLayout.$new(this.context);
242
+ tabRootsWrapper.setOrientation(LinearLayout.VERTICAL.value);
243
+ tabRootsWrapper.setLayoutParams(ViewGroupLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value));
244
+ if (!this.tabs || this.tabs.size === 0) {
245
+ console.warn("[FloatMenu] tabs is empty, tab container will be blank.");
246
+ }
247
+ let firstTabId = null;
248
+ let firstTabInfo = null;
249
+ for (const [tabId, tabInfo] of this.tabs) {
250
+ if (!firstTabId) {
251
+ firstTabId = tabId;
252
+ firstTabInfo = tabInfo;
253
+ }
254
+ const sv = ScrollView.$new(this.context);
255
+ sv.setLayoutParams(LinearLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, 0, 1.0));
256
+ try {
257
+ sv.setBackgroundColor(0x00000000);
258
+ }
259
+ catch (e) { }
260
+ try {
261
+ sv.setFillViewport(true);
262
+ sv.setVerticalScrollBarEnabled(false);
263
+ }
264
+ catch (e) { }
265
+ const tabContainer = LinearLayout.$new(this.context);
266
+ tabContainer.setOrientation(LinearLayout.VERTICAL.value);
267
+ tabContainer.setLayoutParams(ViewGroupLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value));
268
+ tabContainer.setPadding((0, style_1.dp)(this.context, 10), (0, style_1.dp)(this.context, 10), (0, style_1.dp)(this.context, 10), (0, style_1.dp)(this.context, 10));
269
+ tabContainer.setPadding(0, 0, 0, (0, style_1.dp)(this.context, 4));
270
+ sv.addView(tabContainer);
271
+ if (tabId === this.activeTabId) {
272
+ sv.setVisibility(View.VISIBLE.value);
273
+ this.currentContentContainer = tabContainer;
274
+ this.currentScrollView = sv;
275
+ }
276
+ else {
277
+ sv.setVisibility(View.GONE.value);
278
+ }
279
+ tabInfo.container = tabContainer;
280
+ tabInfo.scrollView = sv;
281
+ tabInfo.root = sv;
282
+ tabRootsWrapper.addView(sv);
283
+ }
284
+ if ((!this.currentContentContainer || !this.currentScrollView) &&
285
+ firstTabId &&
286
+ firstTabInfo) {
287
+ this.activeTabId = firstTabId;
288
+ if (firstTabInfo.root)
289
+ firstTabInfo.root.setVisibility(View.VISIBLE.value);
290
+ this.currentContentContainer = firstTabInfo.container;
291
+ this.currentScrollView = firstTabInfo.scrollView;
292
+ }
293
+ this.parentView.addView(tabRootsWrapper);
294
+ }
295
+ }
296
+ exports.TabsView = TabsView;
@@ -0,0 +1,10 @@
1
+ type Listener = (...args: any[]) => void;
2
+ export declare class EventEmitter {
3
+ private events;
4
+ on(event: string, listener: Listener): void;
5
+ off(event: string, listener: Listener): void;
6
+ emit(event: string, ...args: any[]): void;
7
+ once(event: string, listener: Listener): void;
8
+ removeAllListeners(event?: string): void;
9
+ }
10
+ export {};
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventEmitter = void 0;
4
+ class EventEmitter {
5
+ constructor() {
6
+ this.events = new Map();
7
+ }
8
+ on(event, listener) {
9
+ if (!this.events.has(event)) {
10
+ this.events.set(event, []);
11
+ }
12
+ this.events.get(event).push(listener);
13
+ }
14
+ off(event, listener) {
15
+ const listeners = this.events.get(event);
16
+ if (!listeners)
17
+ return;
18
+ const index = listeners.indexOf(listener);
19
+ if (index !== -1) {
20
+ listeners.splice(index, 1);
21
+ }
22
+ }
23
+ emit(event, ...args) {
24
+ const listeners = this.events.get(event);
25
+ if (!listeners)
26
+ return;
27
+ listeners.forEach((listener) => {
28
+ try {
29
+ setImmediate(() => listener(...args));
30
+ }
31
+ catch (error) {
32
+ console.error(`Error in event listener for ${event}:`, error);
33
+ }
34
+ });
35
+ }
36
+ once(event, listener) {
37
+ const onceWrapper = (...args) => {
38
+ this.off(event, onceWrapper);
39
+ listener(...args);
40
+ };
41
+ this.on(event, onceWrapper);
42
+ }
43
+ removeAllListeners(event) {
44
+ if (event) {
45
+ this.events.delete(event);
46
+ }
47
+ else {
48
+ this.events.clear();
49
+ }
50
+ }
51
+ }
52
+ exports.EventEmitter = EventEmitter;
@@ -0,0 +1,63 @@
1
+ import { UIComponent } from "./component/ui-components";
2
+ import { Logger } from "./logger";
3
+ import { Theme } from "./component/style/theme";
4
+ export interface TabDefinition {
5
+ id: string;
6
+ label: string;
7
+ }
8
+ export interface FloatMenuOptions {
9
+ width?: number;
10
+ height?: number;
11
+ x?: number;
12
+ y?: number;
13
+ iconWidth?: number;
14
+ iconHeight?: number;
15
+ iconBase64?: string;
16
+ logMaxLines?: number;
17
+ theme?: Theme;
18
+ title?: string;
19
+ tabs?: TabDefinition[];
20
+ activeTab?: string;
21
+ }
22
+ export declare class FloatMenu {
23
+ options: FloatMenuOptions;
24
+ private headerView;
25
+ private iconView;
26
+ private uiComponents;
27
+ private pendingComponents;
28
+ private eventEmitter;
29
+ private isIconMode;
30
+ private _context;
31
+ private lastTouchX;
32
+ private lastTouchY;
33
+ private screenWidth;
34
+ private screenHeight;
35
+ private menuWindowParams;
36
+ private iconWindowParams;
37
+ private iconContainerWin;
38
+ private menuContainerWin;
39
+ private menuPanelView;
40
+ logger: Logger;
41
+ private logPanelView;
42
+ private tabsView;
43
+ get context(): any;
44
+ private _windowManager;
45
+ get windowManager(): any;
46
+ constructor(options?: FloatMenuOptions);
47
+ private addDragListener;
48
+ private createMenuContainerWindow;
49
+ private updatePosition;
50
+ private createIconWindow;
51
+ toggleView(): void;
52
+ show(): void;
53
+ private processPendingComponents;
54
+ hide(): void;
55
+ toast(msg: string, duration?: 0 | 1): void;
56
+ addComponent(component: UIComponent, tabId?: string): void;
57
+ removeComponent(id: string): void;
58
+ getComponent<T extends UIComponent>(id: string): T | undefined;
59
+ setComponentValue(id: string, value: any): void;
60
+ on(event: string, callback: (...args: any[]) => void): void;
61
+ off(event: string, callback: (...args: any[]) => void): void;
62
+ private createHeaderView;
63
+ }