Android USB调试模式下自动上下滑动(Go实现)

简介

有的时候要对手机UI界面进行滑动测试, 手动或许太消耗时间, 理由Android USB调试模式对UI进行上下滑动测试。

adb指令

使用adb --help 可以查看所有的adb支持指令, 但这里我们只需要上下, 使用到的指令:

  1. adb devices #列举所有设备
    在这里插入图片描述

  2. adb -s 序列号 shell wm size # 获取指定设备的屏幕分辨率
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c650c29d6be44e9d95fae456ee4ffd29.png

  3. adb -s 序列号 shell input swipe startX startY endX endY
    通过startX,startY 起点 到 endX,endY 终点, 实现左/右/上/下滑动

代码

action.go

package mainimport ("encoding/json""fmt""io""os""os/exec""path/filepath""strconv""strings""sync""time"
)type Direction intconst (Move_LEFT  Direction = iotaMove_RIGHT           = 1Move_UP              = 2Move_DOWN            = 3
)type IJsonUnmarshal interface {FromJsonBytes(src []byte) error
}type ActionConfig struct {BaseConfig  CommandConfig `json:"base_config,omitempty"`ActionItems []ActionItem  `json:"action_items,omitempty"`
}func (a *ActionConfig) FromJsonBytes(src []byte) (err error) {err = json.Unmarshal(src, a)return
}type ActionItem struct {Direction   Direction `json:"direction"`BeforeSleep int64     `json:"before_sleep,omitempty"`AfterSleep  int64     `json:"after_sleep,omitempty"`StartX      int       `json:"start_x"`StartY      int       `json:"start_y"`EndX        int       `json:"end_x"`EndY        int       `json:"end_y"`
}type DeviceInfoItem struct {Width        intHeight       intSerialNumber string
}func getActionConfig() (actCfgs ActionConfig, err error) {var f *os.Filef, err = os.Open("./cfg.json")if nil != err {return}defer f.Close()var src []bytesrc, err = io.ReadAll(f)if nil != err {return}err = actCfgs.FromJsonBytes(src)if nil != err {return}if actCfgs.BaseConfig.WorkPath == "." {var exePath stringexePath, err = os.Executable()fmt.Println("1111: ", exePath)if err != nil {return}actCfgs.BaseConfig.WorkPath = filepath.Dir(exePath)}return//actCfgs = ActionConfig{//	ActionItems: []ActionItem{//		{Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},//		{Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},//		{Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},//		{Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},//		{Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},//		{Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},//		{Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},//		{Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},//		{Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},//		{Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},//		{Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},//		{Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},//	},//}//actCfgs.BaseConfig, err = GetBaseCommandConfig()
}func doCommandAction(cmdCfg CommandConfig, callback func(output []byte) error) error {fmt.Println(cmdCfg)// 创建Cmd结构体cmd := exec.Command(cmdCfg.ExePath, cmdCfg.Arguments...)cmd.Dir = cmdCfg.WorkPath// 运行命令并获取输出output, err := cmd.CombinedOutput()if err != nil {fmt.Println("1-Error:", err)return err}err = callback(output)return err
}func getSerialNumberAction(cmdCfg CommandConfig) (serials []string, err error) {cmdCfg.Arguments = []string{"devices"}err = doCommandAction(cmdCfg, func(output []byte) error {lines := strings.Split(string(output), "\n")serials = make([]string, 0)for i, line := range lines {if 0 == i {continue}fields := strings.Fields(line)if len(fields) > 0 {serials = append(serials, fields[0])//fmt.Println(fields[0])}}return nil})return
}func getDeviceInfo(cmdCfg CommandConfig, serialNumber string) (item DeviceInfoItem, err error) {item.SerialNumber = serialNumbercmdCfg.Arguments = []string{"-s", serialNumber, "shell", "wm", "size"}err = doCommandAction(cmdCfg, func(output []byte) error {fields := strings.Fields(string(output))if 3 > len(fields) {return fmt.Errorf("fail to get device info")}strs := strings.Split(fields[2], "x")var temp int64temp, _ = strconv.ParseInt(strs[0], 10, 64)item.Width = int(temp)temp, _ = strconv.ParseInt(strs[1], 10, 64)item.Height = int(temp)return nil})return
}func getActionItem(item DeviceInfoItem, actionItem *ActionItem) error {actionItem.StartX = item.Width / 2actionItem.EndX = item.Width / 2actionItem.StartY = item.Height / 2switch actionItem.Direction {case Move_UP:actionItem.EndY = actionItem.StartY / 2case Move_DOWN:actionItem.EndY = actionItem.StartY + actionItem.StartY/2case Move_LEFT:case Move_RIGHT:default:break}return nil
}func deviceAction(waiter *sync.WaitGroup, actCfgs ActionConfig, serialNumber string) error {defer waiter.Done()item, err := getDeviceInfo(actCfgs.BaseConfig, serialNumber)if nil != err {return err}for i := range actCfgs.ActionItems {err = getActionItem(item, &actCfgs.ActionItems[i])if nil != err {return err}}cfg := actCfgs.BaseConfigcfg.Arguments = []string{"-s", serialNumber, "shell", "input", "swipe", "startX", "startY", "endX", "endY"}for {for i := range actCfgs.ActionItems {fmt.Println(actCfgs.ActionItems[i])time.Sleep(time.Duration(actCfgs.ActionItems[i].BeforeSleep) * time.Millisecond)cfg.Arguments[len(cfg.Arguments)-4] = strconv.Itoa(actCfgs.ActionItems[i].StartX)cfg.Arguments[len(cfg.Arguments)-3] = strconv.Itoa(actCfgs.ActionItems[i].StartY)cfg.Arguments[len(cfg.Arguments)-2] = strconv.Itoa(actCfgs.ActionItems[i].EndX)cfg.Arguments[len(cfg.Arguments)-1] = strconv.Itoa(actCfgs.ActionItems[i].EndY)if err = doCommandAction(cfg, func(output []byte) error {return nil}); nil != err {return err}time.Sleep(time.Duration(actCfgs.ActionItems[i].AfterSleep) * time.Millisecond)}}
}func StartAction() error {var actCfgs, err = getActionConfig()var waiter sync.WaitGroupvar serials []stringserials, err = getSerialNumberAction(actCfgs.BaseConfig)if nil != err {return err}if 0 >= len(serials) {fmt.Println("not devices!!!")return nil}for i := range serials {waiter.Add(1)go deviceAction(&waiter, actCfgs, serials[i])}waiter.Wait()return nil
}

config.go

package mainimport ("fmt""os""path/filepath"
)const (adbDefaultPath = "D:\\Softwares\\Paths\\Android\\android-sdk\\platform-tools"
)type CommandConfig struct {WorkPath  string   `json:"work_path"`ExePath   string   `json:"exe_path"`Arguments []string `json:"arguments,omitempty"`
}func GetBaseCommandConfig() (cfg CommandConfig, err error) {var (exePath string)exePath, err = os.Executable()fmt.Println("1111: ", exePath)if err != nil {return}cfg.WorkPath = filepath.Dir(exePath)cfg.ExePath = fmt.Sprintf("%s\\%s", adbDefaultPath, "adb.exe")fmt.Println("11112: ", cfg)return
}

main.go

package mainimport ("fmt"
)func main() {err := StartAction()if nil != err {fmt.Println(err)return}}

配置文件, 放到程序同级目录
cfg.json

{"base_config": {"work_path": ".","exe_path": "D:/Softwares/Paths/Android/android-sdk/platform-tools/adb.exe"},"action_items": [{"direction": 2,"before_sleep": 0,"after_sleep": 2000}]
}

最后

将手机调试模式打开, 连上电脑,执行程序,需要什么动作在配置文件:

  1. 支持多台设备;
  2. 支持多个动作, 都可以在配置文件中添加;
  3. 支持动作前后延时;

其他

ADB指令基本用法&shell指令
ADB指令基本用法(官网)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/18503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不同类型的区块链钱包有什么特点和适用场景?

区块链钱包是用于存储和管理加密货币的重要工具,市面上有许多不同类型的区块链钱包可供选择。以下是几种主要类型的区块链钱包及其特点和适用场景。 1.软件钱包: 特点:软件钱包是最常见的一种区块链钱包,通常作为软件应用程序提供…

后量子加密算法的数学原理

后量子加密算法是一类专为抵御量子计算机攻击而设计的加密算法。随着量子计算技术的迅速发展,传统的加密算法如RSA和椭圆曲线密码学在量子计算机面前变得脆弱,因此,开发能够在量子计算时代保持安全性的加密算法变得尤为重要。下面将详细介绍后…

软件架构设计之质量属性浅析

引言 在数字化浪潮席卷而来的今天,软件已经渗透到我们生活的方方面面,从手机APP到大型企业级系统,无一不彰显着软件技术的魅力。然而,在这背后,软件架构设计作为软件开发的基石,其质量属性的重要性不言而喻…

Blazor入门-svg绘制-碰撞检测和图形坐标调整

上一篇: Blazor入门-简单svg绘制导出图像_blazor 画图-CSDN博客 https://blog.csdn.net/pxy7896/article/details/139003443 注意:本文只给出思路和框架,对于具体的计算细节,考虑到日后会写入软件著作权和专利文书,因…

STM32-11-电容触摸按键

STM32-01-认识单片机 STM32-02-基础知识 STM32-03-HAL库 STM32-04-时钟树 STM32-05-SYSTEM文件夹 STM32-06-GPIO STM32-07-外部中断 STM32-08-串口 STM32-09-IWDG和WWDG STM32-10-定时器 STM32电容触摸按键 电容触摸按键原理: 无手指触摸:上电时&…

【Java EE】网络原理——HTTP响应

目录 1.认识“状态码”(status code) 1.1 200 OK 1.2 404 Not Found 1.3 403 Forbodden 1.4 Method Not Allowed 1.5 Internal Sever Error 1.6 504 Gsteway Timeout 1.7 Move temporarily 1.8 Moved Permanently 1.9状态码小结 2.认识响应“报…

树莓派部署harbor_arm64

文章目录 树莓派4b部署Harbor-arm64版本docker-compose维护命令访问harbor 192.168.1.111认用户名密码admin/Harbor12345 树莓派4b部署Harbor-arm64版本 harbor-arm版本 部署:参考 wget https://github.com/hzliangbin/harbor-arm64/releases/download/v1.9.3/ha…

java项目之高校教师科研管理系统源码(springboot+vue+mysql)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的高校教师科研管理系统源码。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 高校教师科研管…

评测 香橙派OrangePi在智能交通上的应用

1、OrangePi应用场景 关于 Orange Pi AI Pro 开发板是香橙派联合华为精心打造的高性能 AI 开发板,其搭载了昇腾 AI 处理器,可提供 8TOPS INT8 的计算能力,内存提供了 8GB 和 16GB两种版本。可以实现图像、视频等多种数据分析与推理计算&#…

OpenMV的VisionBoard视觉识别开发板学习记录

此篇博客仅用于对VisionBoard的开发板的学习研究记录,没有教学内容。 一、资料来源 开发板资料链接 开发板环境搭建手册 开发板视频教程 板子的资料网站 openmv官方的网站 目录 一、资料来源二、针对 VisionBoard的目标识别和定位总结1. 目标识别功能1.1 物体检测…

芯片原厂驱动开发工程师:初学到精通,如何快速成长?

01 前言 大家好,我是XX,来自湖南XX学院,电子信息18级,也曾在创新基地控制组学习过两三年,毕业后就职于一家芯片原厂的解决方案部,担任驱动工程师的职位,算上实习期,我的工作时长已有…

htb-Mailing

因为做windows服务器渗透较少,不妥的地方还请师傅们指出 可先看思路,实在不行再看writeup 任意文件下载拿pop3登录邮箱——》利用邮件服务器漏洞拿下NTLM——》利用组件版本漏洞拿下 拿shell 端口扫描开放服务 Host is up (0.91s latency). Not shown:…

CSS学习笔记:rem实现移动端适配的原理——媒体查询

移动端适配 移动端即手机端,也称M端 移动端适配:同一套移动端页面在不同屏幕尺寸的手机上可以实现宽度和高度的自适应,也就是页面中元素的宽度和高度可以根据屏幕尺寸的变化等比缩放 rem配合媒体查询可实现移动端适配 rem单位 媒体查询 …

SpringAdminClient如何将Httpbasic账号密码告知SpringAdminServer

场景,因为Config Service开了权限校验,注册到eureka之后,SpringAdmin查看信息会报错401,如果想在SpringAdmin中正确的看到Config Service的actuator信息则需要将账号密码告知给SpringAdmin,磁力用的是Eureka作为发现服…

javaIO流知识点概况

一、前言: 1.1.流的概念: java将输入与输出比喻为"流",英文:Stream. 就像生活中的"电流","水流"一样,它是以同一个方向顺序移动的过程.只不过这里流动的是字节(2进制数据).所以在IO中有输入流和输出流之分,我们理解他们…

单点11.2.0.3备份恢复到单点11.2.0.4

保命法则:先备份再操作,磁盘空间紧张无法备份就让满足,给自己留退路。 场景说明: 1.本文档的环境为同平台、不同版本(操作系统版本可以不同,数据库小版本不同),源机器和目标机器部…

swiftui基础组件Image加载图片,以及记载gif动图示例

想要在swiftui中展示图片,可以使用Image这个组件,这个组件可以加载本地图片和网络图片,也可以调整图片大小等设置。先大概看一下Image的方法有哪些可以用。 常用的Image属性 1.调整图像尺寸: 使用 resizable() 方法使图像可调整…

量子密钥分发系统基础器件(一):光纤干涉仪

干涉仪的基本原理是利用波的叠加来获得波的相位信息,从而获取实验中所关心的物理量。光纤干涉仪是由光学干涉仪发展而来的,利用光纤实现光的干涉,由于光纤取代透镜系统构成的光路具有柔软、形状可随意变化、传输距离远等特点,当前…

【Linux】23. 线程封装

如何理解C11中的多线程(了解) #include <iostream> #include <unistd.h> #include <thread>void thread_run() {while (true){std::cout << "我是新线程..." << std::endl;sleep(1);} } int main() {// 任何语言需要在Linux上实现多线…

解决IDEA菜单栏找不到VCS的问题,且使用IDEA推送新项目到托管仓库

问题描述&#xff1a; 在idea软件中使用git推送项目&#xff0c;idea页面顶部菜单栏无VCS 解决方案&#xff1a; 一&#xff1a;File->Settings->Version Control-> 点击 ->选择项目->VCS:->点击ok&#xff1a; 二&#xff1a;托管平台创建一个Git仓库来保…