diff --git a/src/base/index.ts b/src/base/index.ts index 33ec494..a2bf519 100644 --- a/src/base/index.ts +++ b/src/base/index.ts @@ -1,4 +1,3 @@ -import { clickForce } from '../lib/utils'; import { DPEvent } from './event'; type DPStarterEvents = { @@ -15,16 +14,6 @@ export abstract class DPStarter extends DPEvent { init() { console.log('init'); - const texts = className('android.widget.TextView') - .find() - .map((item) => item.text()); - console.log(texts); - - if (texts.includes('启动应用')) { - console.log('启动应用'); - - clickForce(id('button1').findOne()); - } this.doInit(); } @@ -35,31 +24,32 @@ export abstract class DPStarter extends DPEvent { this.init(); this.on('over', (task: DPTask) => { const index = this._tasks.indexOf(task); + task.removeAllListeners(); + this.removeTask(task); if (index < this._tasks.length - 1) { + this.doInit(); // 回到初始界面状态 this._tasks[index + 1].start(); } if (index === this._tasks.length - 1) { this.emit('exit'); } - - task.removeAllListeners(); - this.removeTask(task); }); + this._tasks[0]?.start(); } addTask(task: DPTask) { this._tasks.push(task); } - removeTask(task: DPTask) { + private removeTask(task: DPTask) { const index = this._tasks.indexOf(task); if (index >= 0) { this._tasks.splice(index, 1); } } - clearTasks() { + private clearTasks() { this._tasks = []; } } @@ -73,6 +63,10 @@ export enum TaskStatus { } export class DPTask extends DPEvent { + private _fn: (() => void) | undefined; + run(fn: () => void) { + this._fn = fn; + } // 任务状态 private _status: TaskStatus = TaskStatus.Init; @@ -101,6 +95,7 @@ export class DPTask extends DPEvent { constructor(owner: DPStarter) { super(); this.owner = owner; + this.owner.addTask(this); } init() { @@ -111,6 +106,11 @@ export class DPTask extends DPEvent { start() { console.log('start'); this.status = TaskStatus.Running; + if (!this._fn) { + console.error('任务未设置执行函数'); + return; + } + this._fn(); } pause() { diff --git a/src/index.ts b/src/index.ts index 64e8b0f..953c4dc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,26 @@ import { DPStarter, DPTask } from './base/index'; -import { clickForce } from './lib/utils'; +import { clickForce, findNextElement, withMakeSure } from './lib/utils'; + +// 弹窗关闭按钮 +const modalCloseBtn = className('com.lynx.tasm.behavior.ui.LynxFlattenUI') + .boundsInside(100, device.height / 2, device.width - 100, device.height) + .clickable(true) + .text(''); + +// 可能出现的是否打开app弹窗确定按钮 +const openAppModal = id('android:id/button1'); + +// 福利界面底部栏入口按钮 +const welfareEntry = id('di1').text('福利').visibleToUser().selected(false); class Hongguo extends DPStarter { doInit() { console.log('初始化!'); - - makeSure(app.launch('com.phoenix.read'), { - closeBtns: ['android:id/button1'], - landing: [id('di1').text('福利').visibleToUser().selected(false).findOne(1000)] + withMakeSure(() => app.launch('com.phoenix.read'), { + hasModal: [() => openAppModal.findOne(500), () => modalCloseBtn.findOne(500)], // 有可能出现的弹窗 + landOver: [() => welfareEntry.findOne(500)] // 确认是否到达目的页 }); - const widget = id('di1').text('福利').visibleToUser().selected(false).findOne(1000); + const widget = welfareEntry.findOne(500); widget && clickForce(widget); } } @@ -17,17 +28,29 @@ class Hongguo extends DPStarter { const hongguo = new Hongguo(); const task1 = new DPTask(hongguo); +// 新人见面礼立即领取 +task1.run(() => { + const startBtn = className('com.lynx.tasm.behavior.ui.text.FlattenUIText').textContains('立即领取').visibleToUser(); + const getGoldNext = className('com.lynx.tasm.behavior.ui.text.FlattenUIText').text('金币奖励可在「福利」查看').visibleToUser(); + const overBtn = className('com.lynx.tasm.behavior.ui.text.FlattenUIText').textContains('明日再来').visibleToUser(); + console.log('task1 新人见面礼立即领取'); -hongguo.start(); -function makeSure(result: any, options: any) { - if (!result) { - console.log('result is null'); - return; - } - if (options.closeBtns) { - options.closeBtns.forEach((cid: string) => { - const btn = id(cid).findOne(1000); - btn && clickForce(btn); - }); + withMakeSure(() => clickForce(startBtn.findOne(500)), { + landOver: [() => modalCloseBtn.findOne(500), () => getGoldNext.findOne(500)] // 确认是否到达目的页 + }); + + if (overBtn.findOne(500)) { + return clickForce(overBtn.findOne(500)); } -} + + withMakeSure(() => clickForce(startBtn.findOne(500)), { + landOver: [ + () => modalCloseBtn.findOne(500), + () => findNextElement(overBtn, getGoldNext, 'com.lynx.tasm.behavior.ui.text.FlattenUIText') + ] // 确认是否到达目的页 + }); + + return clickForce(overBtn.findOne(500)); +}); + +hongguo.start(); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 7ce4720..1318c7d 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,5 +1,5 @@ // 找到控件区域点击 -export function clickForce(ele: UiObject) { +export function clickForce(ele: UiObject | null) { if (!ele) { console.log('控件不存在'); return; @@ -19,3 +19,90 @@ export function clickForceXY(x: number, y: number, width: number, height: number click(x - 1, y + 1); click(x + 1, y - 1); } + +export function findNextElement(ele: UiSelector, nextEle: UiSelector, classNameString: string, step: number = 1) { + const findit = ele.findOne(500); + const finditNext = nextEle.findOne(500); + if (!findit || !finditNext) { + return false; + } + const eles = className(classNameString).find(); + const index = eles.findIndex((ele) => { + if (ele.bounds().centerX() === findit.bounds().centerX() && ele.bounds().centerY() === findit.bounds().centerY()) { + return ele; + } + }); + if ( + eles[index + step].bounds().centerX() === finditNext.bounds().centerX() && + eles[index + step].bounds().width() === finditNext.bounds().width() && + eles[index + step].bounds().height() === finditNext.bounds().height() && + eles[index + step].bounds().centerY() === finditNext.bounds().centerY() + ) { + return true; + } + return false; +} + +export function findPrevElement(ele: UiSelector, prevEle: UiSelector, classNameString: string, step: number = 1) { + const findit = ele.findOne(500); + const finditPrev = prevEle.findOne(500); + if (!findit || !finditPrev) { + return false; + } + const eles = className(classNameString).find(); + const index = eles.findIndex((ele) => { + if (ele.bounds().centerX() === findit.bounds().centerX() && ele.bounds().centerY() === findit.bounds().centerY()) { + return ele; + } + }); + if ( + eles[index - step].bounds().centerX() === finditPrev.bounds().centerX() && + eles[index - step].bounds().width() === finditPrev.bounds().width() && + eles[index - step].bounds().height() === finditPrev.bounds().height() && + eles[index - step].bounds().centerY() === finditPrev.bounds().centerY() + ) { + return true; + } + return false; +} + +export interface MakeSureOptions { + hasModal?: (() => UiObject | null)[]; + retry?: number; + landOver?: (() => void)[]; +} + +export function withMakeSure( + fn: () => T | null, + options: { hasModal?: (() => UiObject | null)[]; retry?: number; landOver?: (() => void)[] } +) { + const { hasModal, landOver, retry = 5 } = options; + let result = fn() || true; + + let whileNum = 0; + let ok = false; + + while (!ok) { + whileNum++; + if (whileNum > retry) { + result = false; + break; + } + if (hasModal) { + const modals = hasModal.map((fn) => fn()); + if (modals.length) { + modals.forEach((modal) => { + modal && clickForce(modal); + }); + } + } + if (landOver) { + ok = landOver.every((fn) => fn()); + } else { + ok = true; + } + sleep(500); + } + console.log('fn:', fn, 'whileNum:', whileNum, 'result:', result); + return result as T; +}