@yume-chan/android-bin 0.0.17 → 0.0.19

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 (56) hide show
  1. package/CHANGELOG.json +21 -0
  2. package/CHANGELOG.md +14 -1
  3. package/README.md +15 -0
  4. package/esm/bu.d.ts +14 -14
  5. package/esm/bu.d.ts.map +1 -1
  6. package/esm/bu.js +13 -9
  7. package/esm/bu.js.map +1 -1
  8. package/esm/bug-report.d.ts +37 -37
  9. package/esm/bug-report.d.ts.map +1 -1
  10. package/esm/bug-report.js +136 -124
  11. package/esm/bug-report.js.map +1 -1
  12. package/esm/cmd.d.ts +15 -0
  13. package/esm/cmd.d.ts.map +1 -0
  14. package/esm/cmd.js +54 -0
  15. package/esm/cmd.js.map +1 -0
  16. package/esm/demo-mode.d.ts +43 -42
  17. package/esm/demo-mode.d.ts.map +1 -1
  18. package/esm/demo-mode.js +179 -146
  19. package/esm/demo-mode.js.map +1 -1
  20. package/esm/index.d.ts +6 -4
  21. package/esm/index.d.ts.map +1 -1
  22. package/esm/index.js +7 -5
  23. package/esm/index.js.map +1 -1
  24. package/esm/logcat.d.ts +91 -89
  25. package/esm/logcat.d.ts.map +1 -1
  26. package/esm/logcat.js +288 -192
  27. package/esm/logcat.js.map +1 -1
  28. package/esm/overlay-display.d.ts +21 -0
  29. package/esm/overlay-display.d.ts.map +1 -0
  30. package/esm/overlay-display.js +78 -0
  31. package/esm/overlay-display.js.map +1 -0
  32. package/esm/pm.d.ts +139 -0
  33. package/esm/pm.d.ts.map +1 -0
  34. package/esm/pm.js +125 -0
  35. package/esm/pm.js.map +1 -0
  36. package/esm/settings.d.ts +12 -12
  37. package/esm/settings.d.ts.map +1 -1
  38. package/esm/settings.js +29 -24
  39. package/esm/settings.js.map +1 -1
  40. package/package.json +15 -11
  41. package/src/bu.ts +7 -3
  42. package/src/bug-report.ts +74 -48
  43. package/src/cmd.ts +67 -0
  44. package/src/demo-mode.ts +111 -62
  45. package/src/index.ts +6 -4
  46. package/src/logcat.ts +359 -97
  47. package/src/overlay-display.ts +115 -0
  48. package/src/pm.ts +281 -0
  49. package/src/settings.ts +45 -16
  50. package/tsconfig.build.json +3 -0
  51. package/tsconfig.build.tsbuildinfo +1 -0
  52. package/.rush/temp/package-deps_build.json +0 -17
  53. package/.rush/temp/package-deps_build_watch.json +0 -17
  54. package/.rush/temp/shrinkwrap-deps.json +0 -5
  55. package/android-bin.build.log +0 -1
  56. package/tsconfig.json +0 -11
package/src/logcat.ts CHANGED
@@ -1,8 +1,17 @@
1
1
  // cspell: ignore logcat
2
-
3
- import { AdbCommandBase, AdbSubprocessNoneProtocol } from '@yume-chan/adb';
4
- import { BufferedTransformStream, DecodeUtf8Stream, SplitStringStream, WrapReadableStream, WritableStream, type ReadableStream } from '@yume-chan/stream-extra';
5
- import Struct, { decodeUtf8, StructAsyncDeserializeStream } from '@yume-chan/struct';
2
+ // cspell: ignore usec
3
+
4
+ import { AdbCommandBase, AdbSubprocessNoneProtocol } from "@yume-chan/adb";
5
+ import type { ReadableStream } from "@yume-chan/stream-extra";
6
+ import {
7
+ BufferedTransformStream,
8
+ DecodeUtf8Stream,
9
+ SplitStringStream,
10
+ WrapReadableStream,
11
+ WritableStream,
12
+ } from "@yume-chan/stream-extra";
13
+ import type { StructAsyncDeserializeStream } from "@yume-chan/struct";
14
+ import Struct, { decodeUtf8 } from "@yume-chan/struct";
6
15
 
7
16
  // `adb logcat` is an alias to `adb shell logcat`
8
17
  // so instead of adding to core library, it's implemented here
@@ -34,17 +43,18 @@ export enum AndroidLogPriority {
34
43
  }
35
44
 
36
45
  // https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;l=140;drc=8dbf3b2bb6b6d1652d9797e477b9abd03278bb79
37
- export const AndroidLogPriorityToCharacter: Record<AndroidLogPriority, string> = {
38
- [AndroidLogPriority.Unknown]: '?',
39
- [AndroidLogPriority.Default]: '?',
40
- [AndroidLogPriority.Verbose]: 'V',
41
- [AndroidLogPriority.Debug]: 'D',
42
- [AndroidLogPriority.Info]: 'I',
43
- [AndroidLogPriority.Warn]: 'W',
44
- [AndroidLogPriority.Error]: 'E',
45
- [AndroidLogPriority.Fatal]: 'F',
46
- [AndroidLogPriority.Silent]: 'S',
47
- };
46
+ export const AndroidLogPriorityToCharacter: Record<AndroidLogPriority, string> =
47
+ {
48
+ [AndroidLogPriority.Unknown]: "?",
49
+ [AndroidLogPriority.Default]: "?",
50
+ [AndroidLogPriority.Verbose]: "V",
51
+ [AndroidLogPriority.Debug]: "D",
52
+ [AndroidLogPriority.Info]: "I",
53
+ [AndroidLogPriority.Warn]: "W",
54
+ [AndroidLogPriority.Error]: "E",
55
+ [AndroidLogPriority.Fatal]: "F",
56
+ [AndroidLogPriority.Silent]: "S",
57
+ };
48
58
 
49
59
  export enum LogcatFormat {
50
60
  Brief,
@@ -54,14 +64,15 @@ export enum LogcatFormat {
54
64
  Raw,
55
65
  Time,
56
66
  ThreadTime,
57
- Long
67
+ Long,
58
68
  }
59
69
 
60
70
  export interface LogcatFormatModifiers {
61
- usec?: boolean;
71
+ microseconds?: boolean;
72
+ nanoseconds?: boolean;
62
73
  printable?: boolean;
63
74
  year?: boolean;
64
- zone?: boolean;
75
+ timezone?: boolean;
65
76
  epoch?: boolean;
66
77
  monotonic?: boolean;
67
78
  uid?: boolean;
@@ -76,55 +87,271 @@ export interface LogcatOptions {
76
87
  const NANOSECONDS_PER_SECOND = BigInt(1e9);
77
88
 
78
89
  // https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/log/log_read.h;l=39;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
79
- export const LoggerEntry =
80
- new Struct({ littleEndian: true })
81
- .uint16('payloadSize')
82
- .uint16('headerSize')
83
- .int32('pid')
84
- .uint32('tid')
85
- .uint32('second')
86
- .uint32('nanoseconds')
87
- .uint32('logId')
88
- .uint32('uid')
89
- .extra({
90
- get timestamp() {
91
- return BigInt(this.second) * NANOSECONDS_PER_SECOND + BigInt(this.nanoseconds);
92
- },
93
- });
94
-
95
- export type LoggerEntry = typeof LoggerEntry['TDeserializeResult'];
90
+ export const LoggerEntry = new Struct({ littleEndian: true })
91
+ .uint16("payloadSize")
92
+ .uint16("headerSize")
93
+ .int32("pid")
94
+ .uint32("tid")
95
+ .uint32("seconds")
96
+ .uint32("nanoseconds")
97
+ .uint32("logId")
98
+ .uint32("uid")
99
+ .extra({
100
+ get timestamp() {
101
+ return (
102
+ BigInt(this.seconds) * NANOSECONDS_PER_SECOND +
103
+ BigInt(this.nanoseconds)
104
+ );
105
+ },
106
+ });
107
+
108
+ export type LoggerEntry = (typeof LoggerEntry)["TDeserializeResult"];
96
109
 
97
110
  // https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;drc=bbe77d66e7bee8bd1f0bc7e5492b5376b0207ef6;bpv=0
98
111
  export interface AndroidLogEntry extends LoggerEntry {
99
112
  priority: AndroidLogPriority;
100
113
  tag: string;
101
114
  message: string;
115
+
116
+ toString(format?: LogcatFormat, modifiers?: LogcatFormatModifiers): string;
102
117
  }
103
118
 
104
- // https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;l=1415;drc=8dbf3b2bb6b6d1652d9797e477b9abd03278bb79
105
- export function formatAndroidLogEntry(
106
- entry: AndroidLogEntry,
107
- format: LogcatFormat = LogcatFormat.Brief,
108
- modifier?: LogcatFormatModifiers
119
+ function formatSeconds(seconds: number, modifiers: LogcatFormatModifiers) {
120
+ if (modifiers.monotonic) {
121
+ return seconds.toString().padStart(6);
122
+ }
123
+
124
+ if (modifiers.epoch) {
125
+ return seconds.toString().padStart(19);
126
+ }
127
+
128
+ const date = new Date(seconds * 1000);
129
+
130
+ if (modifiers.year) {
131
+ // prettier-ignore
132
+ return `${
133
+ date.getFullYear().toString().padStart(4, "0")
134
+ }-${
135
+ (date.getMonth() + 1).toString().padStart(2, "0")
136
+ }-${
137
+ date.getDate().toString().padStart(2, "0")
138
+ } ${
139
+ date.getHours().toString().padStart(2, "0")
140
+ }:${
141
+ date.getMinutes().toString().padStart(2, "0")
142
+ }:${
143
+ date.getSeconds().toString().padStart(2, "0")
144
+ }`;
145
+ }
146
+
147
+ // prettier-ignore
148
+ return `${
149
+ (date.getMonth() + 1).toString().padStart(2, "0")
150
+ }-${
151
+ date.getDate().toString().padStart(2, "0")
152
+ } ${
153
+ date.getHours().toString().padStart(2, "0")
154
+ }:${
155
+ date.getMinutes().toString().padStart(2, "0")
156
+ }:${
157
+ date.getSeconds().toString().padStart(2, "0")
158
+ }`;
159
+ }
160
+
161
+ function formatNanoseconds(
162
+ nanoseconds: number,
163
+ modifiers: LogcatFormatModifiers
109
164
  ) {
110
- const uid = modifier?.uid ? `${entry.uid.toString().padStart(5)}:` : '';
165
+ if (modifiers.nanoseconds) {
166
+ return nanoseconds.toString().padStart(9, "0");
167
+ }
168
+
169
+ if (modifiers.microseconds) {
170
+ return ((nanoseconds / 1000) | 0).toString().padStart(6, "0");
171
+ }
111
172
 
173
+ return ((nanoseconds / 1000000) | 0).toString().padStart(3, "0");
174
+ }
175
+
176
+ function formatTimezone(seconds: number, modifiers: LogcatFormatModifiers) {
177
+ if (!modifiers.timezone || modifiers.monotonic || modifiers.epoch) {
178
+ return "";
179
+ }
180
+
181
+ const date = new Date(seconds * 1000);
182
+ const offset = date.getTimezoneOffset();
183
+ const sign = offset <= 0 ? "+" : "-";
184
+ const absolute = Math.abs(offset);
185
+ const hours = (absolute / 60) | 0;
186
+ const minutes = absolute % 60;
187
+
188
+ // prettier-ignore
189
+ return ` ${
190
+ sign
191
+ }${
192
+ hours.toString().padStart(2, "0")
193
+ }:${
194
+ minutes.toString().padStart(2, "0")
195
+ }`;
196
+ }
197
+
198
+ function formatTime(
199
+ seconds: number,
200
+ nanoseconds: number,
201
+ modifiers: LogcatFormatModifiers
202
+ ) {
203
+ const secondsString = formatSeconds(seconds, modifiers);
204
+ const nanosecondsString = formatNanoseconds(nanoseconds, modifiers);
205
+ const zoneString = formatTimezone(seconds, modifiers);
206
+ return `${secondsString}.${nanosecondsString}${zoneString}`;
207
+ }
208
+
209
+ function formatUid(
210
+ uid: number,
211
+ modifiers: LogcatFormatModifiers,
212
+ suffix: string
213
+ ) {
214
+ return modifiers.uid ? `${uid.toString().padStart(5)}${suffix}` : "";
215
+ }
216
+
217
+ function getFormatPrefix(
218
+ entry: AndroidLogEntry,
219
+ format: LogcatFormat,
220
+ modifiers: LogcatFormatModifiers
221
+ ) {
222
+ // https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;l=1415;drc=8dbf3b2bb6b6d1652d9797e477b9abd03278bb79
112
223
  switch (format) {
113
224
  // TODO: implement other formats
225
+ case LogcatFormat.Tag:
226
+ // prettier-ignore
227
+ return `${
228
+ AndroidLogPriorityToCharacter[entry.priority]
229
+ }/${
230
+ entry.tag.padEnd(8)
231
+ }: `;
232
+ case LogcatFormat.Process:
233
+ // prettier-ignore
234
+ return `${
235
+ AndroidLogPriorityToCharacter[entry.priority]
236
+ }(${
237
+ formatUid(entry.uid, modifiers, ":")
238
+ }${
239
+ entry.pid.toString().padStart(5)
240
+ }) `;
241
+ case LogcatFormat.Thread:
242
+ // prettier-ignore
243
+ return `${
244
+ AndroidLogPriorityToCharacter[entry.priority]
245
+ }(${
246
+ formatUid(entry.uid, modifiers, ":")
247
+ }${
248
+ entry.pid.toString().padStart(5)
249
+ }:${
250
+ entry.tid.toString().padStart(5)
251
+ }) `;
252
+ case LogcatFormat.Raw:
253
+ return "";
254
+ case LogcatFormat.Time:
255
+ // prettier-ignore
256
+ return `${
257
+ formatTime(entry.seconds, entry.nanoseconds, modifiers)
258
+ } ${
259
+ AndroidLogPriorityToCharacter[entry.priority]
260
+ }/${
261
+ entry.tag.padEnd(8)
262
+ }(${
263
+ formatUid(entry.uid, modifiers, ":")
264
+ }${
265
+ entry.pid.toString().padStart(5)
266
+ }): `;
267
+ case LogcatFormat.ThreadTime:
268
+ // prettier-ignore
269
+ return `${
270
+ formatTime(entry.seconds, entry.nanoseconds, modifiers)
271
+ } ${
272
+ formatUid(entry.uid, modifiers, " ")
273
+ }${
274
+ entry.pid.toString().padStart(5)
275
+ } ${
276
+ entry.tid.toString().padStart(5)
277
+ } ${
278
+ AndroidLogPriorityToCharacter[entry.priority]
279
+ } ${
280
+ entry.tag.toString().padEnd(8)
281
+ }: `;
282
+ case LogcatFormat.Brief:
283
+ default:
284
+ // prettier-ignore
285
+ return `${
286
+ AndroidLogPriorityToCharacter[entry.priority]
287
+ }/${
288
+ entry.tag.padEnd(8)
289
+ }(${
290
+ formatUid(entry.uid, modifiers, ":")
291
+ }${
292
+ entry.pid.toString().padStart(5)
293
+ }): `;
294
+ }
295
+ }
296
+
297
+ function getFormatSuffix(entry: AndroidLogEntry, format: LogcatFormat) {
298
+ switch (format) {
299
+ case LogcatFormat.Process:
300
+ return ` (${entry.tag})`;
301
+ default:
302
+ return "";
303
+ }
304
+ }
305
+
306
+ function formatEntryWrapLine(
307
+ entry: AndroidLogEntry,
308
+ format: LogcatFormat,
309
+ modifiers: LogcatFormatModifiers
310
+ ) {
311
+ const prefix = getFormatPrefix(entry, format, modifiers);
312
+ const suffix = getFormatSuffix(entry, format);
313
+ return (
314
+ prefix + entry.message.replaceAll("\n", suffix + "\n" + prefix) + suffix
315
+ );
316
+ }
317
+
318
+ function AndroidLogEntryToString(
319
+ this: AndroidLogEntry,
320
+ format: LogcatFormat = LogcatFormat.ThreadTime,
321
+ modifiers: LogcatFormatModifiers = {}
322
+ ) {
323
+ switch (format) {
324
+ case LogcatFormat.Long:
325
+ // prettier-ignore
326
+ return `[ ${
327
+ formatTime(this.seconds, this.nanoseconds, modifiers)
328
+ } ${
329
+ formatUid(this.uid, modifiers, ":")
330
+ }${
331
+ this.pid.toString().padStart(5)
332
+ }:${
333
+ this.tid.toString().padStart(5)
334
+ } ${
335
+ AndroidLogPriorityToCharacter[this.priority]
336
+ }/${
337
+ this.tag.padEnd(8)
338
+ } ]\n${
339
+ this.message
340
+ }\n`;
114
341
  default:
115
- return `${AndroidLogPriorityToCharacter[entry.priority]}/${entry.tag.padEnd(8)}(${uid}${entry.pid.toString().padStart(5)}): ${entry.message}`;
342
+ return formatEntryWrapLine(this, format, modifiers);
116
343
  }
117
344
  }
118
345
 
119
346
  function findTagEnd(payload: Uint8Array) {
120
- for (const separator of [0, ' '.charCodeAt(0), ':'.charCodeAt(0)]) {
347
+ for (const separator of [0, " ".charCodeAt(0), ":".charCodeAt(0)]) {
121
348
  const index = payload.indexOf(separator);
122
349
  if (index !== -1) {
123
350
  return index;
124
351
  }
125
352
  }
126
353
 
127
- const index = payload.findIndex(x => x >= 0x7f);
354
+ const index = payload.findIndex((x) => x >= 0x7f);
128
355
  if (index !== -1) {
129
356
  return index;
130
357
  }
@@ -132,8 +359,12 @@ function findTagEnd(payload: Uint8Array) {
132
359
  return payload.length;
133
360
  }
134
361
 
135
- export async function deserializeAndroidLogEntry(stream: StructAsyncDeserializeStream): Promise<AndroidLogEntry> {
136
- const entry = await LoggerEntry.deserialize(stream) as unknown as AndroidLogEntry;
362
+ export async function deserializeAndroidLogEntry(
363
+ stream: StructAsyncDeserializeStream
364
+ ): Promise<AndroidLogEntry> {
365
+ const entry = (await LoggerEntry.deserialize(
366
+ stream
367
+ )) as unknown as AndroidLogEntry;
137
368
  if (entry.headerSize !== LoggerEntry.size) {
138
369
  await stream.read(entry.headerSize - LoggerEntry.size);
139
370
  }
@@ -146,7 +377,11 @@ export async function deserializeAndroidLogEntry(stream: StructAsyncDeserializeS
146
377
  payload = payload.subarray(1);
147
378
  const tagEnd = findTagEnd(payload);
148
379
  entry.tag = decodeUtf8(payload.subarray(0, tagEnd));
149
- entry.message = tagEnd < payload.length - 1 ? decodeUtf8(payload.subarray(tagEnd + 1)) : '';
380
+ entry.message =
381
+ tagEnd < payload.length - 1
382
+ ? decodeUtf8(payload.subarray(tagEnd + 1))
383
+ : "";
384
+ entry.toString = AndroidLogEntryToString;
150
385
  return entry;
151
386
  }
152
387
 
@@ -166,72 +401,92 @@ export class Logcat extends AdbCommandBase {
166
401
 
167
402
  public static logNameToId(name: string): LogId {
168
403
  const key = name[0]!.toUpperCase() + name.substring(1);
169
- return (LogId as any)[key];
404
+ return LogId[key as keyof typeof LogId];
170
405
  }
171
406
 
172
407
  public static joinLogId(ids: LogId[]): string {
173
- return ids.map(id => Logcat.logIdToName(id)).join(',');
408
+ return ids.map((id) => Logcat.logIdToName(id)).join(",");
174
409
  }
175
410
 
176
411
  public static parseSize(value: number, multiplier: string): number {
177
- const MULTIPLIERS = ['', 'Ki', 'Mi', 'Gi'];
412
+ const MULTIPLIERS = ["", "Ki", "Mi", "Gi"];
178
413
  return value * 1024 ** (MULTIPLIERS.indexOf(multiplier) || 0);
179
414
  }
180
415
 
181
416
  // TODO: logcat: Support output format before Android 10
182
417
  // ref https://android-review.googlesource.com/c/platform/system/core/+/748128
183
- public static readonly LOG_SIZE_REGEX_10 = /(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed\), max entry is (.*) B, max payload is (.*) B/;
418
+ public static readonly LOG_SIZE_REGEX_10 =
419
+ /(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed\), max entry is (.*) B, max payload is (.*) B/;
184
420
 
185
421
  // Android 11 added `readable` part
186
422
  // ref https://android-review.googlesource.com/c/platform/system/core/+/1390940
187
- public static readonly LOG_SIZE_REGEX_11 = /(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed, (.*) (.*)B readable\), max entry is (.*) B, max payload is (.*) B/;
423
+ public static readonly LOG_SIZE_REGEX_11 =
424
+ /(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed, (.*) (.*)B readable\), max entry is (.*) B, max payload is (.*) B/;
188
425
 
189
426
  public async getLogSize(ids?: LogId[]): Promise<LogSize[]> {
190
427
  const { stdout } = await this.adb.subprocess.spawn([
191
- 'logcat',
192
- '-g',
193
- ...(ids ? ['-b', Logcat.joinLogId(ids)] : [])
428
+ "logcat",
429
+ "-g",
430
+ ...(ids ? ["-b", Logcat.joinLogId(ids)] : []),
194
431
  ]);
195
432
 
196
433
  const result: LogSize[] = [];
197
434
  await stdout
198
435
  .pipeThrough(new DecodeUtf8Stream())
199
- .pipeThrough(new SplitStringStream('\n'))
200
- .pipeTo(new WritableStream({
201
- write(chunk) {
202
- let match = chunk.match(Logcat.LOG_SIZE_REGEX_11);
203
- if (match) {
204
- result.push({
205
- id: Logcat.logNameToId(match[1]!),
206
- size: Logcat.parseSize(Number.parseInt(match[2]!, 10), match[3]!),
207
- readable: Logcat.parseSize(Number.parseInt(match[6]!, 10), match[7]!),
208
- consumed: Logcat.parseSize(Number.parseInt(match[4]!, 10), match[5]!),
209
- maxEntrySize: parseInt(match[8]!, 10),
210
- maxPayloadSize: parseInt(match[9]!, 10),
211
- });
212
- }
213
-
214
- match = chunk.match(Logcat.LOG_SIZE_REGEX_10);
215
- if (match) {
216
- result.push({
217
- id: Logcat.logNameToId(match[1]!),
218
- size: Logcat.parseSize(Number.parseInt(match[2]!, 10), match[3]!),
219
- consumed: Logcat.parseSize(Number.parseInt(match[4]!, 10), match[5]!),
220
- maxEntrySize: parseInt(match[6]!, 10),
221
- maxPayloadSize: parseInt(match[7]!, 10),
222
- });
223
- }
224
- },
225
- }));
436
+ .pipeThrough(new SplitStringStream("\n"))
437
+ .pipeTo(
438
+ new WritableStream({
439
+ write(chunk) {
440
+ let match = chunk.match(Logcat.LOG_SIZE_REGEX_11);
441
+ if (match) {
442
+ result.push({
443
+ id: Logcat.logNameToId(match[1]!),
444
+ size: Logcat.parseSize(
445
+ Number.parseInt(match[2]!, 10),
446
+ match[3]!
447
+ ),
448
+ readable: Logcat.parseSize(
449
+ Number.parseInt(match[6]!, 10),
450
+ match[7]!
451
+ ),
452
+ consumed: Logcat.parseSize(
453
+ Number.parseInt(match[4]!, 10),
454
+ match[5]!
455
+ ),
456
+ maxEntrySize: parseInt(match[8]!, 10),
457
+ maxPayloadSize: parseInt(match[9]!, 10),
458
+ });
459
+ return;
460
+ }
461
+
462
+ match = chunk.match(Logcat.LOG_SIZE_REGEX_10);
463
+ if (match) {
464
+ result.push({
465
+ id: Logcat.logNameToId(match[1]!),
466
+ size: Logcat.parseSize(
467
+ Number.parseInt(match[2]!, 10),
468
+ match[3]!
469
+ ),
470
+ consumed: Logcat.parseSize(
471
+ Number.parseInt(match[4]!, 10),
472
+ match[5]!
473
+ ),
474
+ maxEntrySize: parseInt(match[6]!, 10),
475
+ maxPayloadSize: parseInt(match[7]!, 10),
476
+ });
477
+ }
478
+ },
479
+ })
480
+ );
226
481
 
227
482
  return result;
228
483
  }
229
484
 
230
485
  public async clear(ids?: LogId[]) {
231
486
  await this.adb.subprocess.spawnAndWait([
232
- 'logcat',
233
- '-c',
234
- ...(ids ? ['-b', Logcat.joinLogId(ids)] : []),
487
+ "logcat",
488
+ "-c",
489
+ ...(ids ? ["-b", Logcat.joinLogId(ids)] : []),
235
490
  ]);
236
491
  }
237
492
 
@@ -239,18 +494,25 @@ export class Logcat extends AdbCommandBase {
239
494
  return new WrapReadableStream(async () => {
240
495
  // TODO: make `spawn` return synchronously with streams pending
241
496
  // so it's easier to chain them.
242
- const { stdout } = await this.adb.subprocess.spawn([
243
- 'logcat',
244
- '-B',
245
- ...(options?.pid ? ['--pid', options.pid.toString()] : []),
246
- ...(options?.ids ? ['-b', Logcat.joinLogId(options.ids)] : [])
247
- ], {
248
- // PERF: None protocol is 150% faster then Shell protocol
249
- protocols: [AdbSubprocessNoneProtocol],
250
- });
497
+ const { stdout } = await this.adb.subprocess.spawn(
498
+ [
499
+ "logcat",
500
+ "-B",
501
+ ...(options?.pid ? ["--pid", options.pid.toString()] : []),
502
+ ...(options?.ids
503
+ ? ["-b", Logcat.joinLogId(options.ids)]
504
+ : []),
505
+ ],
506
+ {
507
+ // PERF: None protocol is 150% faster then Shell protocol
508
+ protocols: [AdbSubprocessNoneProtocol],
509
+ }
510
+ );
251
511
  return stdout;
252
- }).pipeThrough(new BufferedTransformStream(stream => {
253
- return deserializeAndroidLogEntry(stream);
254
- }));
512
+ }).pipeThrough(
513
+ new BufferedTransformStream((stream) => {
514
+ return deserializeAndroidLogEntry(stream);
515
+ })
516
+ );
255
517
  }
256
518
  }
@@ -0,0 +1,115 @@
1
+ import type { Adb } from "@yume-chan/adb";
2
+ import { AdbCommandBase } from "@yume-chan/adb";
3
+
4
+ import { Settings } from "./settings.js";
5
+
6
+ export interface OverlayDisplayDeviceMode {
7
+ width: number;
8
+ height: number;
9
+ density: number;
10
+ }
11
+
12
+ export interface OverlayDisplayDevice {
13
+ modes: OverlayDisplayDeviceMode[];
14
+ secure: boolean;
15
+ ownContentOnly: boolean;
16
+ showSystemDecorations: boolean;
17
+ }
18
+
19
+ export class OverlayDisplay extends AdbCommandBase {
20
+ private settings: Settings;
21
+
22
+ public static readonly OVERLAY_DISPLAY_DEVICES_KEY =
23
+ "overlay_display_devices";
24
+
25
+ constructor(adb: Adb) {
26
+ super(adb);
27
+ this.settings = new Settings(adb);
28
+ }
29
+
30
+ public async get() {
31
+ const devices: OverlayDisplayDevice[] = [];
32
+
33
+ const settingString = await this.settings.get(
34
+ "global",
35
+ OverlayDisplay.OVERLAY_DISPLAY_DEVICES_KEY
36
+ );
37
+
38
+ for (const displayString of settingString.split(";")) {
39
+ const [modesString, ...flagStrings] = displayString.split(",");
40
+
41
+ if (!modesString) {
42
+ continue;
43
+ }
44
+
45
+ const device: OverlayDisplayDevice = {
46
+ modes: [],
47
+ secure: false,
48
+ ownContentOnly: false,
49
+ showSystemDecorations: false,
50
+ };
51
+ for (const modeString of modesString.split("|")) {
52
+ const match = modeString.match(/(\d+)x(\d+)\/(\d+)/);
53
+ if (!match) {
54
+ continue;
55
+ }
56
+ device.modes.push({
57
+ width: parseInt(match[1]!, 10),
58
+ height: parseInt(match[2]!, 10),
59
+ density: parseInt(match[3]!, 10),
60
+ });
61
+ }
62
+ if (device.modes.length === 0) {
63
+ continue;
64
+ }
65
+
66
+ for (const flagString of flagStrings) {
67
+ switch (flagString) {
68
+ case "secure":
69
+ device.secure = true;
70
+ break;
71
+ case "own_content_only":
72
+ device.ownContentOnly = true;
73
+ break;
74
+ case "show_system_decorations":
75
+ device.showSystemDecorations = true;
76
+ break;
77
+ }
78
+ }
79
+ devices.push(device);
80
+ }
81
+
82
+ return devices;
83
+ }
84
+
85
+ public async set(devices: OverlayDisplayDevice[]) {
86
+ let settingString = "";
87
+ for (const device of devices) {
88
+ if (settingString) {
89
+ settingString += ";";
90
+ }
91
+
92
+ settingString += device.modes
93
+ .map((mode) => `${mode.width}x${mode.height}/${mode.density}`)
94
+ .join("|");
95
+
96
+ if (device.secure) {
97
+ settingString += ",secure";
98
+ }
99
+
100
+ if (device.ownContentOnly) {
101
+ settingString += ",own_content_only";
102
+ }
103
+
104
+ if (device.showSystemDecorations) {
105
+ settingString += ",show_system_decorations";
106
+ }
107
+ }
108
+
109
+ await this.settings.put(
110
+ "global",
111
+ OverlayDisplay.OVERLAY_DISPLAY_DEVICES_KEY,
112
+ settingString
113
+ );
114
+ }
115
+ }