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,156 @@
1
+ import Java from "../java-runtime.js";
2
+ import { API } from "../api.js";
3
+ import { Logger } from "../logger.js";
4
+ import { UIComponent } from "./ui-components.js";
5
+ import { deferSafe } from "../runtime/safe-runtime.js";
6
+ import { createJavaListener } from "../android-listener.js";
7
+ export class Slider extends UIComponent {
8
+ constructor(id, label, min, max, initialValue = min, step = 1, handler) {
9
+ super(id);
10
+ this.label = label;
11
+ this.min = min;
12
+ this.max = max;
13
+ this.step = step;
14
+ this.handler = handler;
15
+ this.value = this.clampToStep(initialValue);
16
+ }
17
+ createView(context) {
18
+ const LinearLayout = API.LinearLayout;
19
+ const TextView = API.TextView;
20
+ const SeekBar = API.SeekBar;
21
+ const Color = API.Color;
22
+ const String = API.JString;
23
+ const ViewGroupLayoutParams = API.ViewGroupLayoutParams;
24
+ const LinearLayoutParams = API.LinearLayoutParams;
25
+ const container = LinearLayout.$new(context);
26
+ container.setOrientation(0);
27
+ container.setLayoutParams(LinearLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value));
28
+ container.setPadding(16, 8, 16, 8);
29
+ const labelView = TextView.$new(context);
30
+ labelView.setText.overload('java.lang.CharSequence').call(labelView, this.label);
31
+ labelView.setTextColor(Color.WHITE.value);
32
+ labelView.setTextSize(14);
33
+ labelView.setLayoutParams(LinearLayoutParams.$new(0, ViewGroupLayoutParams.WRAP_CONTENT.value, 1.0));
34
+ const valueView = TextView.$new(context);
35
+ valueView.setText.overload('java.lang.CharSequence').call(valueView, String.$new(this.value.toString()));
36
+ valueView.setTextColor(Color.WHITE.value);
37
+ valueView.setTextSize(14);
38
+ valueView.setLayoutParams(LinearLayoutParams.$new(ViewGroupLayoutParams.WRAP_CONTENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value));
39
+ container.addView(labelView);
40
+ container.addView(valueView);
41
+ const seekBar = SeekBar.$new(context);
42
+ seekBar.setMax(this.calculateSeekBarMax());
43
+ seekBar.setProgress(this.valueToProgress(this.value));
44
+ seekBar.setLayoutParams(LinearLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value));
45
+ const verticalLayout = LinearLayout.$new(context);
46
+ verticalLayout.setOrientation(1);
47
+ verticalLayout.setLayoutParams(LinearLayoutParams.$new(ViewGroupLayoutParams.MATCH_PARENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value));
48
+ verticalLayout.addView(container);
49
+ verticalLayout.addView(seekBar);
50
+ this.view = verticalLayout;
51
+ this.view.seekBar = seekBar;
52
+ this.view.valueView = valueView;
53
+ this.view.labelView = labelView;
54
+ this.view.container = container;
55
+ const SeekBarOnSeekBarChangeListener = API.SeekBarOnSeekBarChangeListener;
56
+ const self = this;
57
+ const changeListener = createJavaListener({
58
+ key: "seekbar-change",
59
+ interfaceClass: SeekBarOnSeekBarChangeListener,
60
+ callbacks: {
61
+ onProgressChanged: (seekBar, progress, fromUser) => {
62
+ if (fromUser) {
63
+ const newValue = self.progressToValue(progress);
64
+ self.value = newValue;
65
+ Java.scheduleOnMainThread(() => {
66
+ const valueView = self.view.valueView;
67
+ if (valueView) {
68
+ valueView.setText.overload('java.lang.CharSequence').call(valueView, String.$new(newValue.toString()));
69
+ }
70
+ });
71
+ self.emit("valueChanged", newValue);
72
+ if (self.handler) {
73
+ deferSafe(`Slider:${self.id}:valueChanged`, () => self.handler(newValue));
74
+ }
75
+ }
76
+ },
77
+ onStartTrackingTouch: (_seekBar) => {
78
+ },
79
+ onStopTrackingTouch: (_seekBar) => {
80
+ },
81
+ },
82
+ });
83
+ this.own(() => changeListener.dispose());
84
+ seekBar.setOnSeekBarChangeListener(changeListener.instance);
85
+ }
86
+ onValueChange(handler) {
87
+ this.handler = handler;
88
+ }
89
+ updateView() {
90
+ if (!this.view) {
91
+ Logger.instance.warn(`[Slider:${this.id}] Cannot update view - view not initialized`);
92
+ return;
93
+ }
94
+ Java.scheduleOnMainThread(() => {
95
+ const seekBar = this.view.seekBar;
96
+ const valueView = this.view.valueView;
97
+ if (seekBar) {
98
+ seekBar.setProgress(this.valueToProgress(this.value));
99
+ }
100
+ if (valueView) {
101
+ const String = API.JString;
102
+ valueView.setText.overload('java.lang.CharSequence').call(valueView, String.$new(this.value.toString()));
103
+ }
104
+ });
105
+ }
106
+ setLabel(label) {
107
+ this.label = label;
108
+ if (!this.view) {
109
+ Logger.instance.warn(`[Slider:${this.id}] Cannot set label - view not initialized`);
110
+ return;
111
+ }
112
+ Java.scheduleOnMainThread(() => {
113
+ const labelView = this.view.labelView;
114
+ if (labelView) {
115
+ const String = API.JString;
116
+ labelView.setText.overload('java.lang.CharSequence').call(labelView, String.$new(label));
117
+ }
118
+ });
119
+ }
120
+ setRange(min, max, step = 1) {
121
+ this.min = min;
122
+ this.max = max;
123
+ this.step = step;
124
+ this.value = this.clampToStep(this.value);
125
+ if (!this.view) {
126
+ Logger.instance.warn(`[Slider:${this.id}] Cannot set range - view not initialized`);
127
+ return;
128
+ }
129
+ Java.scheduleOnMainThread(() => {
130
+ const seekBar = this.view.seekBar;
131
+ if (seekBar) {
132
+ seekBar.setMax(this.calculateSeekBarMax());
133
+ seekBar.setProgress(this.valueToProgress(this.value));
134
+ }
135
+ });
136
+ this.updateView();
137
+ }
138
+ calculateSeekBarMax() {
139
+ return Math.round((this.max - this.min) / this.step);
140
+ }
141
+ valueToProgress(value) {
142
+ return Math.round((value - this.min) / this.step);
143
+ }
144
+ progressToValue(progress) {
145
+ const value = this.min + progress * this.step;
146
+ return this.clampToStep(value);
147
+ }
148
+ clampToStep(value) {
149
+ let clamped = Math.max(this.min, Math.min(this.max, value));
150
+ if (this.step > 0) {
151
+ const steps = Math.round((clamped - this.min) / this.step);
152
+ clamped = this.min + steps * this.step;
153
+ }
154
+ return clamped;
155
+ }
156
+ }
@@ -0,0 +1,4 @@
1
+ import { Theme } from "./theme.js";
2
+ export type StyleRole = "overlay" | "card" | "category" | "row" | "fieldLabel" | "selectValue" | "text" | "caption" | "noteText" | "infoCard" | "noteCard" | "inputButton" | "inputTrigger" | "primaryButton" | "dangerButton" | "headerBar" | "iconButton" | "textButton" | "dangerTextButton" | "divider" | "chip" | "dialog" | "inputField";
3
+ export declare function dp(ctx: any, v: number): number;
4
+ export declare function applyStyle(view: any, role: StyleRole, theme: Theme): void;
@@ -0,0 +1,325 @@
1
+ import Java from "../../java-runtime.js";
2
+ import { API } from "../../api.js";
3
+ import { readDisplayDensity } from "../../android-runtime.js";
4
+ export function dp(ctx, v) {
5
+ return Math.floor(v * readDisplayDensity(ctx) + 0.5);
6
+ }
7
+ export function applyStyle(view, role, theme) {
8
+ const ctx = view.getContext();
9
+ const dpx = (v) => dp(ctx, v);
10
+ const withAlpha = (color, alpha) => {
11
+ const a = Math.max(0, Math.min(255, alpha)) & 0xff;
12
+ return (a << 24) | (color & 0x00ffffff) | 0;
13
+ };
14
+ const GradientDrawable = Java.use("android.graphics.drawable.GradientDrawable");
15
+ const TextView = Java.use("android.widget.TextView");
16
+ const rounded = (bg, rDp, stroke) => {
17
+ const d = GradientDrawable.$new();
18
+ d.setColor(bg | 0);
19
+ d.setCornerRadius(dp(ctx, rDp));
20
+ if (stroke)
21
+ d.setStroke(dp(ctx, stroke.wDp), stroke.c);
22
+ view.setBackground(d);
23
+ };
24
+ const asTextView = () => {
25
+ try {
26
+ return Java.cast(view, TextView);
27
+ }
28
+ catch (_e) {
29
+ return null;
30
+ }
31
+ };
32
+ switch (role) {
33
+ case "overlay": {
34
+ const bg = GradientDrawable.$new();
35
+ bg.setShape(GradientDrawable.RECTANGLE.value);
36
+ bg.setCornerRadius(dpx(theme.radiusDp.overlay));
37
+ bg.setColor(theme.colors.overlayBg | 0);
38
+ bg.setStroke(dpx(1), (theme.colors.accentStroke ?? theme.colors.controlStroke) | 0);
39
+ view.setBackgroundDrawable(bg);
40
+ view.setPadding(dpx(12), dpx(12), dpx(12), dpx(12));
41
+ try {
42
+ view.setElevation(dpx(12));
43
+ }
44
+ catch (_e) { }
45
+ break;
46
+ }
47
+ case "card":
48
+ rounded(theme.colors.cardBg, theme.radiusDp.card, {
49
+ c: theme.colors.divider,
50
+ wDp: 1,
51
+ });
52
+ view.setPadding(dpx(12), dpx(12), dpx(12), dpx(12));
53
+ view.setElevation(dpx(6));
54
+ break;
55
+ case "infoCard":
56
+ rounded(theme.colors.infoCardBg ?? withAlpha(theme.colors.cardBg, 0xdd), 12, {
57
+ c: withAlpha(theme.colors.accentStroke ?? theme.colors.controlStroke, 0x55),
58
+ wDp: 1,
59
+ });
60
+ view.setPadding(dpx(14), dpx(12), dpx(14), dpx(12));
61
+ break;
62
+ case "inputButton": {
63
+ const bg = theme.colors.rowBg ?? withAlpha(theme.colors.controlBg, 0x26);
64
+ const stroke = theme.colors.accentStroke ?? theme.colors.controlStroke;
65
+ rounded(bg, 14, { c: withAlpha(stroke, 0xaa), wDp: 1 });
66
+ view.setPadding(dpx(14), dpx(12), dpx(14), dpx(12));
67
+ view.setMinimumHeight(dp(ctx, 48));
68
+ const tv = asTextView();
69
+ if (tv) {
70
+ tv.setTextColor(theme.colors.text);
71
+ tv.setTextSize(2, 14);
72
+ tv.setAllCaps(false);
73
+ tv.setGravity(API.Gravity.LEFT.value | API.Gravity.CENTER_VERTICAL.value);
74
+ try {
75
+ tv.setTypeface(null, 1);
76
+ }
77
+ catch (_e) { }
78
+ }
79
+ break;
80
+ }
81
+ case "noteCard":
82
+ rounded(theme.colors.noteCardBg ?? withAlpha(theme.colors.accent, 0x18), 12, {
83
+ c: theme.colors.accentStroke ?? theme.colors.controlStroke,
84
+ wDp: 1,
85
+ });
86
+ view.setPadding(dpx(14), dpx(12), dpx(14), dpx(12));
87
+ break;
88
+ case "fieldLabel": {
89
+ const tv = asTextView();
90
+ if (tv) {
91
+ tv.setTextColor(theme.colors.subText);
92
+ tv.setTextSize(2, 11);
93
+ tv.setAllCaps(true);
94
+ try {
95
+ tv.setTypeface(null, 1);
96
+ }
97
+ catch (_e) { }
98
+ try {
99
+ tv.setLetterSpacing(0.04);
100
+ }
101
+ catch (_e) { }
102
+ }
103
+ break;
104
+ }
105
+ case "selectValue": {
106
+ const tv = asTextView();
107
+ if (tv) {
108
+ tv.setTextColor(theme.colors.text);
109
+ tv.setTextSize(2, 14);
110
+ try {
111
+ tv.setTypeface(null, 1);
112
+ }
113
+ catch (_e) { }
114
+ tv.setSingleLine(true);
115
+ tv.setGravity(API.Gravity.LEFT.value | API.Gravity.CENTER_VERTICAL.value);
116
+ }
117
+ break;
118
+ }
119
+ case "category": {
120
+ const tv = asTextView();
121
+ if (tv) {
122
+ tv.setTextColor(theme.colors.sectionText ?? theme.colors.subText);
123
+ tv.setTextSize(2, 14);
124
+ tv.setAllCaps(true);
125
+ try {
126
+ tv.setLetterSpacing(0.06);
127
+ }
128
+ catch (_e) { }
129
+ try {
130
+ tv.setTypeface(null, 1);
131
+ }
132
+ catch (_e) { }
133
+ tv.setGravity(API.Gravity.LEFT.value | API.Gravity.CENTER_VERTICAL.value);
134
+ }
135
+ break;
136
+ }
137
+ case "row": {
138
+ const rowBg = theme.colors.rowBg ?? withAlpha(theme.colors.controlBg, 0x22);
139
+ const stroke = withAlpha(theme.colors.controlStroke, 0x33);
140
+ rounded(rowBg, theme.radiusDp.control, { c: stroke, wDp: 1 });
141
+ view.setPadding(dpx(12), dpx(10), dpx(12), dpx(10));
142
+ break;
143
+ }
144
+ case "text": {
145
+ const tv = asTextView();
146
+ if (tv) {
147
+ tv.setTextColor(0xffd8e1f0 | 0);
148
+ tv.setTextSize(2, 13);
149
+ tv.setGravity(API.Gravity.LEFT.value);
150
+ try {
151
+ tv.setLineSpacing(dpx(4), 1.0);
152
+ }
153
+ catch (_e) { }
154
+ }
155
+ break;
156
+ }
157
+ case "noteText": {
158
+ const tv = asTextView();
159
+ if (tv) {
160
+ tv.setTextColor(0xffc9d4e8 | 0);
161
+ tv.setTextSize(2, 13);
162
+ tv.setGravity(API.Gravity.LEFT.value);
163
+ try {
164
+ tv.setLineSpacing(dpx(4), 1.0);
165
+ }
166
+ catch (_e) { }
167
+ }
168
+ break;
169
+ }
170
+ case "caption": {
171
+ const tv = asTextView();
172
+ if (tv) {
173
+ tv.setTextColor(theme.colors.subText);
174
+ tv.setTextSize(2, theme.textSp.caption);
175
+ }
176
+ break;
177
+ }
178
+ case "inputTrigger": {
179
+ const bg = theme.colors.accentSoft ?? theme.colors.controlBg;
180
+ const stroke = theme.colors.accentStroke ?? theme.colors.controlStroke;
181
+ rounded(bg, theme.radiusDp.control, { c: stroke, wDp: 2 });
182
+ view.setPadding(dpx(12), dpx(10), dpx(12), dpx(10));
183
+ view.setMinimumHeight(dpx(42));
184
+ const tv = asTextView();
185
+ if (tv) {
186
+ tv.setTextColor(theme.colors.text);
187
+ tv.setTextSize(2, theme.textSp.body);
188
+ tv.setAllCaps(false);
189
+ }
190
+ break;
191
+ }
192
+ case "primaryButton": {
193
+ rounded(theme.colors.accent, 14);
194
+ view.setPadding(dpx(16), dpx(12), dpx(16), dpx(12));
195
+ view.setMinimumHeight(dpx(48));
196
+ const tv = asTextView();
197
+ if (tv) {
198
+ tv.setTextColor(theme.colors.buttonText ?? 0xffffffff | 0);
199
+ tv.setTextSize(2, 14);
200
+ tv.setAllCaps(true);
201
+ try {
202
+ tv.setTypeface(null, 1);
203
+ }
204
+ catch (_e) { }
205
+ tv.setGravity(API.Gravity.CENTER.value);
206
+ }
207
+ break;
208
+ }
209
+ case "dangerButton": {
210
+ rounded(theme.colors.danger, 14);
211
+ view.setPadding(dpx(16), dpx(12), dpx(16), dpx(12));
212
+ view.setMinimumHeight(dpx(48));
213
+ const tv = asTextView();
214
+ if (tv) {
215
+ tv.setTextColor(theme.colors.buttonText ?? 0xffffffff | 0);
216
+ tv.setTextSize(2, 14);
217
+ tv.setAllCaps(true);
218
+ try {
219
+ tv.setTypeface(null, 1);
220
+ }
221
+ catch (_e) { }
222
+ tv.setGravity(API.Gravity.CENTER.value);
223
+ }
224
+ break;
225
+ }
226
+ case "headerBar": {
227
+ const bg = GradientDrawable.$new();
228
+ bg.setCornerRadius(dpx(14));
229
+ bg.setColor(theme.colors.cardBg);
230
+ bg.setStroke(dpx(1), theme.colors.accentStroke ?? theme.colors.controlStroke);
231
+ view.setBackgroundDrawable(bg);
232
+ view.setPadding(dpx(10), dpx(8), dpx(10), dpx(8));
233
+ break;
234
+ }
235
+ case "iconButton": {
236
+ const bg = GradientDrawable.$new();
237
+ bg.setCornerRadius(dpx(10));
238
+ bg.setColor(0x00000000);
239
+ bg.setStroke(dpx(1), theme.colors.accentStroke ?? theme.colors.controlStroke);
240
+ view.setBackgroundDrawable(bg);
241
+ view.setPadding(dpx(6), dpx(6), dpx(6), dpx(6));
242
+ if (view.setAllCaps)
243
+ view.setAllCaps(false);
244
+ if (view.setTextColor)
245
+ view.setTextColor(theme.colors.text);
246
+ if (view.setTextSize)
247
+ view.setTextSize(2, theme.textSp.body);
248
+ break;
249
+ }
250
+ case "textButton": {
251
+ try {
252
+ view.setBackground(null);
253
+ }
254
+ catch (_e) { }
255
+ if (view.setAllCaps)
256
+ view.setAllCaps(false);
257
+ if (view.setTextColor)
258
+ view.setTextColor(theme.colors.accent);
259
+ if (view.setTextSize)
260
+ view.setTextSize(2, theme.textSp.body);
261
+ view.setPadding(dpx(8), dpx(6), dpx(8), dpx(6));
262
+ break;
263
+ }
264
+ case "dangerTextButton": {
265
+ try {
266
+ view.setBackground(null);
267
+ }
268
+ catch (_e) { }
269
+ if (view.setAllCaps)
270
+ view.setAllCaps(false);
271
+ if (view.setTextColor)
272
+ view.setTextColor(theme.colors.danger ?? theme.colors.accent);
273
+ if (view.setTextSize)
274
+ view.setTextSize(2, theme.textSp.body);
275
+ view.setPadding(dpx(8), dpx(6), dpx(8), dpx(6));
276
+ break;
277
+ }
278
+ case "divider": {
279
+ view.setBackgroundColor(theme.colors.divider);
280
+ const lp = view.getLayoutParams?.();
281
+ if (lp) {
282
+ lp.height = dpx(1);
283
+ view.setLayoutParams(lp);
284
+ }
285
+ break;
286
+ }
287
+ case "chip": {
288
+ const bg = GradientDrawable.$new();
289
+ bg.setCornerRadius(dpx(999));
290
+ bg.setColor(theme.colors.chipBg ?? theme.colors.rowBg);
291
+ bg.setStroke(dpx(1), theme.colors.accentStroke ?? theme.colors.controlStroke);
292
+ view.setBackgroundDrawable(bg);
293
+ view.setPadding(dpx(10), dpx(4), dpx(10), dpx(4));
294
+ if (view.setTextColor)
295
+ view.setTextColor(theme.colors.subText);
296
+ if (view.setTextSize)
297
+ view.setTextSize(2, theme.textSp.caption ?? theme.textSp.body);
298
+ break;
299
+ }
300
+ case "dialog": {
301
+ const bg = GradientDrawable.$new();
302
+ bg.setCornerRadius(dpx(14));
303
+ bg.setColor(theme.colors.cardBg);
304
+ bg.setStroke(dpx(1), theme.colors.accentStroke ?? theme.colors.controlStroke);
305
+ view.setBackgroundDrawable(bg);
306
+ view.setPadding(dpx(12), dpx(12), dpx(12), dpx(12));
307
+ break;
308
+ }
309
+ case "inputField": {
310
+ const bg = GradientDrawable.$new();
311
+ bg.setCornerRadius(dpx(theme.radiusDp.control));
312
+ bg.setColor(theme.colors.inputBg ?? theme.colors.controlBg);
313
+ bg.setStroke(dpx(1), theme.colors.accentStroke ?? theme.colors.controlStroke);
314
+ view.setBackgroundDrawable(bg);
315
+ view.setPadding(dpx(12), dpx(10), dpx(12), dpx(10));
316
+ if (view.setTextColor)
317
+ view.setTextColor(theme.colors.text);
318
+ if (view.setHintTextColor)
319
+ view.setHintTextColor(theme.colors.subText ?? 0xff888888);
320
+ if (view.setTextSize)
321
+ view.setTextSize(2, theme.textSp.body);
322
+ break;
323
+ }
324
+ }
325
+ }
@@ -0,0 +1,33 @@
1
+ export type Theme = {
2
+ colors: {
3
+ overlayBg: number;
4
+ cardBg: number;
5
+ text: number;
6
+ subText: number;
7
+ accent: number;
8
+ danger: number;
9
+ divider: number;
10
+ controlBg: number;
11
+ controlStroke: number;
12
+ accentSoft?: number;
13
+ accentStroke?: number;
14
+ rowBg?: number;
15
+ inputBg?: number;
16
+ chipBg?: number;
17
+ infoCardBg?: number;
18
+ noteCardBg?: number;
19
+ sectionText?: number;
20
+ buttonText?: number;
21
+ };
22
+ radiusDp: {
23
+ overlay: number;
24
+ card: number;
25
+ control: number;
26
+ };
27
+ textSp: {
28
+ title: number;
29
+ body: number;
30
+ caption: number;
31
+ };
32
+ };
33
+ export declare const DarkNeonTheme: Theme;
@@ -0,0 +1,24 @@
1
+ export const DarkNeonTheme = {
2
+ colors: {
3
+ overlayBg: 0xE60a1426 | 0,
4
+ cardBg: 0xee101b31 | 0,
5
+ text: 0xfff3f6ff | 0,
6
+ subText: 0xff8fa5c7 | 0,
7
+ accent: 0xff0f6cff | 0,
8
+ danger: 0xffef4444 | 0,
9
+ divider: 0x263b5f93 | 0,
10
+ controlBg: 0x44111d33 | 0,
11
+ controlStroke: 0x2e4d79b8 | 0,
12
+ accentSoft: 0x1a0f6cff | 0,
13
+ accentStroke: 0xaa1f63ff | 0,
14
+ rowBg: 0x70111a2d | 0,
15
+ inputBg: 0x66101a2d | 0,
16
+ chipBg: 0x40111827 | 0,
17
+ infoCardBg: 0xcc102347 | 0,
18
+ noteCardBg: 0xd0122344 | 0,
19
+ sectionText: 0xffb4c3dc | 0,
20
+ buttonText: 0xffffffff | 0,
21
+ },
22
+ radiusDp: { overlay: 18, card: 14, control: 12 },
23
+ textSp: { title: 15, body: 13, caption: 12 },
24
+ };
@@ -0,0 +1,12 @@
1
+ import { UIComponent } from "./ui-components.js";
2
+ export declare class Switch extends UIComponent {
3
+ private label;
4
+ private handler?;
5
+ private labelView;
6
+ private switchView;
7
+ onValueChange(handler: (value: boolean) => void): void;
8
+ constructor(id: string, label: string, initialValue?: boolean, handler?: (value: boolean) => void);
9
+ protected createView(context: any): void;
10
+ protected updateView(): void;
11
+ setLabel(label: string): void;
12
+ }
@@ -0,0 +1,117 @@
1
+ import Java from "../java-runtime.js";
2
+ import { API } from "../api.js";
3
+ import { applyStyle, dp } from "./style/style.js";
4
+ import { UIComponent } from "./ui-components.js";
5
+ import { deferSafe } from "../runtime/safe-runtime.js";
6
+ import { createJavaListener } from "../android-listener.js";
7
+ export class Switch extends UIComponent {
8
+ onValueChange(handler) {
9
+ this.handler = handler;
10
+ }
11
+ constructor(id, label, initialValue = false, handler) {
12
+ super(id);
13
+ this.label = label;
14
+ this.value = initialValue;
15
+ this.handler = handler;
16
+ }
17
+ createView(context) {
18
+ const LinearLayout = API.LinearLayout;
19
+ const LinearLayoutParams = API.LinearLayoutParams;
20
+ const ViewGroupLayoutParams = API.ViewGroupLayoutParams;
21
+ const Gravity = API.Gravity;
22
+ const TextView = API.TextView;
23
+ const Switch = API.Switch;
24
+ const String = API.JString;
25
+ const OnClickListener = API.OnClickListener;
26
+ const OnCheckedChangeListener = API.OnCheckedChangeListener;
27
+ const theme = this.menu.options.theme;
28
+ const row = LinearLayout.$new(context);
29
+ row.setOrientation(LinearLayout.HORIZONTAL.value);
30
+ row.setGravity(Gravity.CENTER_VERTICAL.value);
31
+ applyStyle(row, "row", theme);
32
+ row.setPadding(dp(context, 14), dp(context, 12), dp(context, 14), dp(context, 12));
33
+ const label = TextView.$new(context);
34
+ label.setText.overload('java.lang.CharSequence').call(label, this.label);
35
+ applyStyle(label, "text", theme);
36
+ label.setTextSize(2, 14);
37
+ try {
38
+ label.setTypeface(null, 1);
39
+ }
40
+ catch (_e) { }
41
+ label.setSingleLine(true);
42
+ label.setGravity(Gravity.LEFT.value | Gravity.CENTER_VERTICAL.value);
43
+ label.setLayoutParams(LinearLayoutParams.$new(0, ViewGroupLayoutParams.WRAP_CONTENT.value, 1.0));
44
+ const sw = Switch.$new(context);
45
+ sw.setText.overload('java.lang.CharSequence').call(sw, "");
46
+ sw.setChecked(!!this.value);
47
+ sw.setShowText(false);
48
+ try {
49
+ sw.setMinWidth(dp(context, 50));
50
+ sw.setMinimumWidth(dp(context, 50));
51
+ }
52
+ catch (_e) { }
53
+ try {
54
+ sw.setSwitchMinWidth(dp(context, 50));
55
+ }
56
+ catch (_e) { }
57
+ try {
58
+ sw.setScaleX(1.05);
59
+ sw.setScaleY(1.05);
60
+ }
61
+ catch (_e) { }
62
+ const switchLp = LinearLayoutParams.$new(ViewGroupLayoutParams.WRAP_CONTENT.value, ViewGroupLayoutParams.WRAP_CONTENT.value);
63
+ switchLp.leftMargin = dp(context, 12);
64
+ sw.setLayoutParams(switchLp);
65
+ const self = this;
66
+ const checkedListener = createJavaListener({
67
+ key: "compound-button-checked-change",
68
+ interfaceClass: OnCheckedChangeListener,
69
+ callbacks: {
70
+ onCheckedChanged: (_buttonView, isChecked) => {
71
+ self.value = !!isChecked;
72
+ self.emit("valueChanged", self.value);
73
+ if (self.handler) {
74
+ deferSafe(`Switch:${self.id}:valueChanged`, () => self.handler(self.value));
75
+ }
76
+ },
77
+ },
78
+ });
79
+ this.own(() => checkedListener.dispose());
80
+ sw.setOnCheckedChangeListener(checkedListener.instance);
81
+ const clickListener = createJavaListener({
82
+ key: "view-click",
83
+ interfaceClass: OnClickListener,
84
+ callbacks: {
85
+ onClick: (_v) => {
86
+ if (!self.switchView)
87
+ return;
88
+ self.switchView.setChecked(!self.switchView.isChecked());
89
+ },
90
+ },
91
+ });
92
+ this.own(() => clickListener.dispose());
93
+ row.setClickable(true);
94
+ row.setOnClickListener(clickListener.instance);
95
+ row.addView(label);
96
+ row.addView(sw);
97
+ this.labelView = label;
98
+ this.switchView = sw;
99
+ this.view = row;
100
+ }
101
+ updateView() {
102
+ if (!this.switchView)
103
+ return;
104
+ Java.scheduleOnMainThread(() => {
105
+ this.switchView.setChecked(!!this.value);
106
+ });
107
+ }
108
+ setLabel(label) {
109
+ this.label = label;
110
+ if (!this.labelView)
111
+ return;
112
+ Java.scheduleOnMainThread(() => {
113
+ const String = API.JString;
114
+ this.labelView.setText.overload('java.lang.CharSequence').call(this.labelView, String.$new(label));
115
+ });
116
+ }
117
+ }
@@ -0,0 +1,9 @@
1
+ import { UIComponent } from "./ui-components.js";
2
+ export declare class TextView extends UIComponent {
3
+ private size?;
4
+ private kind;
5
+ constructor(id: string, content: string, kind?: "normal" | "note", size?: number);
6
+ protected createView(context: any): void;
7
+ protected updateView(): void;
8
+ setText(content: string): void;
9
+ }