@skrillex1224/android-toolkit 1.0.9 → 1.0.10
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.
- package/dist/index.cjs +62 -70
- package/dist/index.cjs.map +3 -3
- package/dist/index.js +62 -70
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -282,7 +282,7 @@ function safeJson(value) {
|
|
|
282
282
|
|
|
283
283
|
// src/device.js
|
|
284
284
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
285
|
-
var
|
|
285
|
+
var Device2 = {
|
|
286
286
|
adbExec,
|
|
287
287
|
adbShell,
|
|
288
288
|
forceStopApp,
|
|
@@ -663,7 +663,7 @@ function createApifyKit(options = {}) {
|
|
|
663
663
|
if (failActor) {
|
|
664
664
|
let base64 = "";
|
|
665
665
|
try {
|
|
666
|
-
base64 = await
|
|
666
|
+
base64 = await Device2.screenshotBase64(ctx);
|
|
667
667
|
} catch (error) {
|
|
668
668
|
base64 = `\u622A\u56FE\u5931\u8D25: ${error?.message || String(error)}`;
|
|
669
669
|
}
|
|
@@ -781,7 +781,7 @@ var DeviceView = {
|
|
|
781
781
|
normalizeSelector
|
|
782
782
|
};
|
|
783
783
|
async function snapshot(ctx, options = {}) {
|
|
784
|
-
const xml = options.xml || await
|
|
784
|
+
const xml = options.xml || await Device2.dumpUiXml(ctx);
|
|
785
785
|
const parsed = parser.parse(xml);
|
|
786
786
|
const roots = toArray(parsed?.hierarchy?.node).map((node) => normalizeNode(node, null, 0));
|
|
787
787
|
const flat = [];
|
|
@@ -965,6 +965,12 @@ function selectorLabel(selector) {
|
|
|
965
965
|
}
|
|
966
966
|
|
|
967
967
|
// src/device-input.js
|
|
968
|
+
var SCROLL_MAX_SWIPES = 24;
|
|
969
|
+
var SCROLL_STABLE_ROUNDS = 1;
|
|
970
|
+
var SCROLL_SWIPE_RATIO = 0.72;
|
|
971
|
+
var SCROLL_DURATION_MS = 360;
|
|
972
|
+
var SCROLL_SETTLE_MS = 500;
|
|
973
|
+
var SCROLL_HASH_SOURCE = "screenshot";
|
|
968
974
|
var DeviceInput = {
|
|
969
975
|
click,
|
|
970
976
|
tap: click,
|
|
@@ -978,12 +984,12 @@ var DeviceInput = {
|
|
|
978
984
|
};
|
|
979
985
|
async function click(ctx, selectorOrPoint, options = {}) {
|
|
980
986
|
if (isPoint(selectorOrPoint)) {
|
|
981
|
-
await
|
|
987
|
+
await Device2.tapAbsolute(ctx, selectorOrPoint.x, selectorOrPoint.y);
|
|
982
988
|
return { point: selectorOrPoint };
|
|
983
989
|
}
|
|
984
990
|
if (isBounds(selectorOrPoint)) {
|
|
985
991
|
const point2 = centerOf(selectorOrPoint);
|
|
986
|
-
await
|
|
992
|
+
await Device2.tapAbsolute(ctx, point2.x, point2.y);
|
|
987
993
|
return { point: point2 };
|
|
988
994
|
}
|
|
989
995
|
const target = await DeviceView.find(ctx, selectorOrPoint, options);
|
|
@@ -1002,7 +1008,7 @@ async function click(ctx, selectorOrPoint, options = {}) {
|
|
|
1002
1008
|
actual: simplifyNode(actual),
|
|
1003
1009
|
point
|
|
1004
1010
|
});
|
|
1005
|
-
await
|
|
1011
|
+
await Device2.tapAbsolute(ctx, point.x, point.y);
|
|
1006
1012
|
await sleep(Number(options.settleMs || 250));
|
|
1007
1013
|
return { target, actual, point };
|
|
1008
1014
|
}
|
|
@@ -1013,7 +1019,7 @@ async function fill(ctx, selector, text2, options = {}) {
|
|
|
1013
1019
|
settleMs: Number(options.focusSettleMs || 350)
|
|
1014
1020
|
});
|
|
1015
1021
|
try {
|
|
1016
|
-
await
|
|
1022
|
+
await Device2.typeText(ctx, value);
|
|
1017
1023
|
} catch (error) {
|
|
1018
1024
|
throw new CrawlerError({
|
|
1019
1025
|
message: `automation_failed: View \u5199\u5165\u6587\u672C\u5931\u8D25 ${JSON.stringify(selector)}`,
|
|
@@ -1030,10 +1036,10 @@ async function fill(ctx, selector, text2, options = {}) {
|
|
|
1030
1036
|
return { target: clicked.actual || clicked.target, chars: value.length };
|
|
1031
1037
|
}
|
|
1032
1038
|
async function press(ctx, key) {
|
|
1033
|
-
await
|
|
1039
|
+
await Device2.pressKey(ctx, key);
|
|
1034
1040
|
}
|
|
1035
1041
|
async function pressEnter2(ctx) {
|
|
1036
|
-
await
|
|
1042
|
+
await Device2.pressEnter(ctx);
|
|
1037
1043
|
}
|
|
1038
1044
|
async function scroll(ctx, selector, direction, options = {}) {
|
|
1039
1045
|
const node = await DeviceView.find(ctx, selector, options);
|
|
@@ -1041,8 +1047,8 @@ async function scroll(ctx, selector, direction, options = {}) {
|
|
|
1041
1047
|
}
|
|
1042
1048
|
async function scrollBounds(ctx, bounds2, direction, options = {}) {
|
|
1043
1049
|
const box = bounds2;
|
|
1044
|
-
const ratio = Number(options.swipeRatio ||
|
|
1045
|
-
const durationMs = Number(options.durationMs ||
|
|
1050
|
+
const ratio = Number(options.swipeRatio || SCROLL_SWIPE_RATIO);
|
|
1051
|
+
const durationMs = Number(options.durationMs || SCROLL_DURATION_MS);
|
|
1046
1052
|
const x = box.centerX;
|
|
1047
1053
|
const distance = Math.max(40, box.height * ratio);
|
|
1048
1054
|
const down = String(direction || "").toLowerCase() === "down";
|
|
@@ -1054,8 +1060,8 @@ async function scrollBounds(ctx, bounds2, direction, options = {}) {
|
|
|
1054
1060
|
x,
|
|
1055
1061
|
y: down ? Math.min(box.bottom - 10, from.y + distance) : Math.max(box.top + 10, from.y - distance)
|
|
1056
1062
|
};
|
|
1057
|
-
await
|
|
1058
|
-
await sleep(Number(options.settleMs ||
|
|
1063
|
+
await Device2.swipe(ctx, from, to, durationMs);
|
|
1064
|
+
await sleep(Number(options.settleMs || SCROLL_SETTLE_MS));
|
|
1059
1065
|
}
|
|
1060
1066
|
async function scrollToEnd(ctx, selector, options = {}) {
|
|
1061
1067
|
return scrollUntilStable(ctx, selector, "up", options);
|
|
@@ -1064,56 +1070,30 @@ async function scrollToTop(ctx, selector, options = {}) {
|
|
|
1064
1070
|
return scrollUntilStable(ctx, selector, "down", options);
|
|
1065
1071
|
}
|
|
1066
1072
|
async function scrollUntilStable(ctx, selector, direction, options = {}) {
|
|
1067
|
-
const maxSwipes = Math.max(1, Number(options.maxSwipes ||
|
|
1068
|
-
const stableRoundsTarget = Math.max(1, Number(options.stableRounds ||
|
|
1069
|
-
const hashSource = String(options.stability || options.hashSource || "view");
|
|
1070
|
-
if (hashSource === "screenshot") {
|
|
1071
|
-
return scrollUntilScreenshotStable(ctx, selector, direction, options, maxSwipes, stableRoundsTarget, hashSource);
|
|
1072
|
-
}
|
|
1073
|
-
let lastHash = "";
|
|
1074
|
-
let stableRounds = 0;
|
|
1075
|
-
for (let index = 0; index < maxSwipes; index += 1) {
|
|
1076
|
-
const currentHash = await scrollStateHash(ctx, selector, options, hashSource);
|
|
1077
|
-
if (currentHash === lastHash) {
|
|
1078
|
-
stableRounds += 1;
|
|
1079
|
-
if (stableRounds >= stableRoundsTarget) {
|
|
1080
|
-
return { swipes: index, stableRounds, hash: currentHash, hashSource, capped: false };
|
|
1081
|
-
}
|
|
1082
|
-
} else {
|
|
1083
|
-
stableRounds = 0;
|
|
1084
|
-
lastHash = currentHash;
|
|
1085
|
-
}
|
|
1086
|
-
await scroll(ctx, selector, direction, options);
|
|
1087
|
-
}
|
|
1088
|
-
return { swipes: maxSwipes, stableRounds, hash: lastHash, hashSource, capped: true };
|
|
1089
|
-
}
|
|
1090
|
-
async function scrollUntilScreenshotStable(ctx, selector, direction, options, maxSwipes, stableRoundsTarget, hashSource) {
|
|
1073
|
+
const maxSwipes = Math.max(1, Number(options.maxSwipes || SCROLL_MAX_SWIPES));
|
|
1074
|
+
const stableRoundsTarget = Math.max(1, Number(options.stableRounds || SCROLL_STABLE_ROUNDS));
|
|
1091
1075
|
const node = await DeviceView.find(ctx, selector, options);
|
|
1092
1076
|
const bounds2 = node.bounds;
|
|
1093
|
-
let lastHash = await scrollStateHash(ctx
|
|
1077
|
+
let lastHash = await scrollStateHash(ctx);
|
|
1094
1078
|
let stableRounds = 0;
|
|
1095
1079
|
for (let index = 0; index < maxSwipes; index += 1) {
|
|
1096
1080
|
await scrollBounds(ctx, bounds2, direction, options);
|
|
1097
|
-
const currentHash = await scrollStateHash(ctx
|
|
1081
|
+
const currentHash = await scrollStateHash(ctx);
|
|
1098
1082
|
if (currentHash === lastHash) {
|
|
1099
1083
|
stableRounds += 1;
|
|
1100
1084
|
if (stableRounds >= stableRoundsTarget) {
|
|
1101
|
-
return { swipes: index + 1, stableRounds, hash: currentHash, hashSource, capped: false };
|
|
1085
|
+
return { swipes: index + 1, stableRounds, hash: currentHash, hashSource: SCROLL_HASH_SOURCE, capped: false };
|
|
1102
1086
|
}
|
|
1103
1087
|
} else {
|
|
1104
1088
|
stableRounds = 0;
|
|
1105
1089
|
lastHash = currentHash;
|
|
1106
1090
|
}
|
|
1107
1091
|
}
|
|
1108
|
-
return { swipes: maxSwipes, stableRounds, hash: lastHash, hashSource, capped: true };
|
|
1092
|
+
return { swipes: maxSwipes, stableRounds, hash: lastHash, hashSource: SCROLL_HASH_SOURCE, capped: true };
|
|
1109
1093
|
}
|
|
1110
|
-
async function scrollStateHash(ctx
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
return (0, import_node_crypto2.createHash)("sha256").update(png).digest("hex");
|
|
1114
|
-
}
|
|
1115
|
-
const node = await DeviceView.find(ctx, selector, options);
|
|
1116
|
-
return DeviceView.hashNode(node);
|
|
1094
|
+
async function scrollStateHash(ctx) {
|
|
1095
|
+
const png = await Device2.screenshotPng(ctx);
|
|
1096
|
+
return (0, import_node_crypto2.createHash)("sha256").update(png).digest("hex");
|
|
1117
1097
|
}
|
|
1118
1098
|
function nearestClickable(node) {
|
|
1119
1099
|
let current = node;
|
|
@@ -1280,7 +1260,7 @@ async function pullDatabaseSnapshot(ctx, dbPath, localDir) {
|
|
|
1280
1260
|
}
|
|
1281
1261
|
}
|
|
1282
1262
|
async function adbSuShell(ctx, command, options = {}) {
|
|
1283
|
-
return
|
|
1263
|
+
return Device2.adbShell(ctx, [`su -c ${shellQuote(command)}`], options);
|
|
1284
1264
|
}
|
|
1285
1265
|
async function queryLocalSQLite(dbPath, config) {
|
|
1286
1266
|
const payloadPath = `${dbPath}.query.json`;
|
|
@@ -1717,6 +1697,11 @@ var DEFAULT_COLUMNS = 3;
|
|
|
1717
1697
|
var DEFAULT_SETTLE_MS = 550;
|
|
1718
1698
|
var DEFAULT_TIMEOUT_MS = 5e4;
|
|
1719
1699
|
var DEFAULT_POLL_INTERVAL_MS = 800;
|
|
1700
|
+
var RESET_TO_TOP_MAX_SWIPES = 16;
|
|
1701
|
+
var RESET_TO_TOP_STABLE_ROUNDS = 1;
|
|
1702
|
+
var CAPTURE_SCROLL_STEP_RATIO = 0.56;
|
|
1703
|
+
var CAPTURE_SCROLL_DURATION_MS = 360;
|
|
1704
|
+
var SPRITE_GAP = 16;
|
|
1720
1705
|
var Share = {
|
|
1721
1706
|
captureScreen,
|
|
1722
1707
|
captureLink
|
|
@@ -1726,15 +1711,13 @@ async function captureScreen(ctx, options = {}) {
|
|
|
1726
1711
|
const frameBuffers = [];
|
|
1727
1712
|
const seen = /* @__PURE__ */ new Set();
|
|
1728
1713
|
const targetSelector = options.selector || { id: "message_list" };
|
|
1729
|
-
const
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
settleMs: 400,
|
|
1737
|
-
stability: options.scrollStability || options.stability || "view"
|
|
1714
|
+
const resetToTop = options.resetToTop !== false;
|
|
1715
|
+
let topResult = null;
|
|
1716
|
+
if (resetToTop) {
|
|
1717
|
+
topResult = await DeviceInput.scrollToTop(ctx, targetSelector, {
|
|
1718
|
+
maxSwipes: RESET_TO_TOP_MAX_SWIPES,
|
|
1719
|
+
stableRounds: RESET_TO_TOP_STABLE_ROUNDS,
|
|
1720
|
+
settleMs: 400
|
|
1738
1721
|
});
|
|
1739
1722
|
Logger.info("captureScreen scrollToTop", {
|
|
1740
1723
|
swipes: topResult?.swipes,
|
|
@@ -1742,13 +1725,20 @@ async function captureScreen(ctx, options = {}) {
|
|
|
1742
1725
|
hashSource: topResult?.hashSource,
|
|
1743
1726
|
capped: topResult?.capped === true
|
|
1744
1727
|
});
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1728
|
+
if (topResult?.capped === true) {
|
|
1729
|
+
throw new CrawlerError({
|
|
1730
|
+
message: `source_extraction_failed: \u622A\u56FE\u524D\u672A\u80FD\u786E\u8BA4\u56DE\u5230\u9876\u90E8 swipes=${topResult.swipes} stableRounds=${topResult.stableRounds}`,
|
|
1731
|
+
code: Code.SourceExtractionFailed,
|
|
1732
|
+
context: {
|
|
1733
|
+
selector: targetSelector,
|
|
1734
|
+
swipes: topResult.swipes,
|
|
1735
|
+
stableRounds: topResult.stableRounds,
|
|
1736
|
+
hashSource: topResult.hashSource
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1748
1740
|
}
|
|
1749
1741
|
for (let index = 0; index < DEFAULT_CAPTURE_COUNT; index += 1) {
|
|
1750
|
-
if (shouldHideKeyboard) await Device.hideKeyboard(ctx, { attempts: 1, settleMs: 250 }).catch(() => {
|
|
1751
|
-
});
|
|
1752
1742
|
await sleep(DEFAULT_SETTLE_MS);
|
|
1753
1743
|
const png = await Device.screenshotPng(ctx);
|
|
1754
1744
|
const hash = (0, import_node_crypto3.createHash)("sha256").update(png).digest("hex");
|
|
@@ -1757,18 +1747,21 @@ async function captureScreen(ctx, options = {}) {
|
|
|
1757
1747
|
frameBuffers.push(png);
|
|
1758
1748
|
if (index >= DEFAULT_CAPTURE_COUNT - 1) break;
|
|
1759
1749
|
await DeviceInput.scroll(ctx, targetSelector, "up", {
|
|
1760
|
-
swipeRatio:
|
|
1761
|
-
durationMs:
|
|
1750
|
+
swipeRatio: CAPTURE_SCROLL_STEP_RATIO,
|
|
1751
|
+
durationMs: CAPTURE_SCROLL_DURATION_MS,
|
|
1762
1752
|
settleMs: DEFAULT_SETTLE_MS
|
|
1763
1753
|
}).catch(() => {
|
|
1764
1754
|
});
|
|
1765
1755
|
}
|
|
1766
|
-
const sprite = await composeSprite(frameBuffers, { columns: DEFAULT_COLUMNS, gap:
|
|
1756
|
+
const sprite = await composeSprite(frameBuffers, { columns: DEFAULT_COLUMNS, gap: SPRITE_GAP });
|
|
1767
1757
|
const compression = resolveImageCompression(options);
|
|
1768
1758
|
const base64 = await compressImageBufferToBase64(sprite, compression);
|
|
1769
1759
|
Logger.success("Share.captureScreen", {
|
|
1770
1760
|
duration: Logger.duration(startedAt),
|
|
1771
1761
|
frameCount: frameBuffers.length,
|
|
1762
|
+
scrollStepRatio: CAPTURE_SCROLL_STEP_RATIO,
|
|
1763
|
+
resetToTop,
|
|
1764
|
+
topCapped: topResult?.capped === true,
|
|
1772
1765
|
base64Bytes: Math.ceil(base64.length)
|
|
1773
1766
|
});
|
|
1774
1767
|
return `data:image/jpeg;base64,${base64}`;
|
|
@@ -1783,7 +1776,6 @@ async function captureLink(ctx, options = {}) {
|
|
|
1783
1776
|
});
|
|
1784
1777
|
}
|
|
1785
1778
|
const timeoutMs = Number(options.timeoutMs || DEFAULT_TIMEOUT_MS);
|
|
1786
|
-
const pollIntervalMs = Number(options.pollIntervalMs || DEFAULT_POLL_INTERVAL_MS);
|
|
1787
1779
|
const deadline = Date.now() + timeoutMs;
|
|
1788
1780
|
const prefix = String(share.prefix || "").trim();
|
|
1789
1781
|
Logger.start("Share.captureLink", { actor: actorInfo.key, prefix, timeoutMs });
|
|
@@ -1805,7 +1797,7 @@ async function captureLink(ctx, options = {}) {
|
|
|
1805
1797
|
if (link && link === beforeLink) {
|
|
1806
1798
|
lastEvent = { ...event, rejected: "same_as_baseline", beforeLink };
|
|
1807
1799
|
}
|
|
1808
|
-
await sleep(
|
|
1800
|
+
await sleep(DEFAULT_POLL_INTERVAL_MS);
|
|
1809
1801
|
}
|
|
1810
1802
|
throw new CrawlerError({
|
|
1811
1803
|
message: "source_extraction_failed: \u672A\u6355\u83B7\u5206\u4EAB\u94FE\u63A5",
|
|
@@ -1901,7 +1893,7 @@ async function run(handler, options = {}) {
|
|
|
1901
1893
|
const kit = {
|
|
1902
1894
|
Launch,
|
|
1903
1895
|
ApifyKit: apifyKit,
|
|
1904
|
-
Device,
|
|
1896
|
+
Device: Device2,
|
|
1905
1897
|
DeviceInput,
|
|
1906
1898
|
DeviceView,
|
|
1907
1899
|
DeviceSQLite,
|
|
@@ -1952,7 +1944,7 @@ var useAndroidToolKit = () => {
|
|
|
1952
1944
|
DeviceInput,
|
|
1953
1945
|
DeviceView,
|
|
1954
1946
|
DeviceSQLite,
|
|
1955
|
-
Device,
|
|
1947
|
+
Device: Device2,
|
|
1956
1948
|
Mutation,
|
|
1957
1949
|
Share,
|
|
1958
1950
|
Constants: constants_exports,
|