• adb常用命令(golang版)及输入中文


    package main
    
    import (
        "crypto/md5"
        "fmt"
        "image/png"
        "io/ioutil"
    
        "log"
        "os"
        "regexp"
        "strings"
    
        "bytes"
        "os/exec"
        "strconv"
        "time"
    )
    
    const (
        //可用下面的AdbShellDumpsysActivityF函数获取包名和activity名
        APPPackageName = "cn.XXX.android"
        APP            = "cn.XXX.android/com.XXX.XXXActivity"
    )
    
    func main() {
    
        //如果手机是休眠状态,则打开电源
        if AdbShellDumpsysPowerOff() {
            AdbShellInputKeyEvent("26") //power
        }
        //进入手机主屏
        AdbShellInputKeyEvent("4") //back
        AdbShellInputKeyEvent("3") //home
        /*如果APP未启动,则启动APP
        if !strings.Contains(AdbShellDumpsysActivityF(), APPPackageName) {
            AdbShellAmStartN(APP)
        }
        */
        Tap("设置", 0)
        TimeSleepDuration(5) 
        TapOnce(`d我的`, 0, 3, 573)  
        AdbShellInputKeyEvent("26") //power
    
    }
    
    //模拟按键,如按下home键,键值参考;https://blog.csdn.net/shililang/article/details/14449527
    //adb shell input keyevent 3
    func AdbShellInputKeyEvent(s string) {
        exec.Command("adb", "shell", "input", "keyevent", s).Run()
    }
    
    //模拟屏幕点击
    //有的控件死活抓不到,只能直接点击
    //adb shell input tap  900 800
    func AdbShellInputTap(x, y int) {
        x2 := strconv.Itoa(x)
        y2 := strconv.Itoa(y)
        exec.Command("adb", "shell", "input", "tap", x2, y2).Run()
    }
    
    //模拟滑动
    //adb shell input swipe  0 0  600 600
    func AdbShellInputSwipe(x1, y1, x2, y2 int) {
        xx1 := strconv.Itoa(x1)
        yy1 := strconv.Itoa(y1)
        xx2 := strconv.Itoa(x2)
        yy2 := strconv.Itoa(y2)
        exec.Command("adb", "shell", "input", "swipe", xx1, yy1, xx2, yy2).Run()
    }
    
    //模拟长按 最后一个参数1000表示1秒,可将下面某个参数由500改为501,即允许坐标点有很小的变化。
    //adb shell input swipe  500 500  500 500 1000
    func AdbShellInputSwipeL(x1, y1, x2, y2, t int) {
        xx1 := strconv.Itoa(x1)
        yy1 := strconv.Itoa(y1)
        xx2 := strconv.Itoa(x2)
        yy2 := strconv.Itoa(y2)
        exec.Command("adb", "shell", "swipe", "tap", xx1, yy1, xx2, yy2).Run()
    }
    
    //模拟输入“字符”
    //adb shell input text "abc"
    //若需输入中文,可参考:https://blog.csdn.net/slimboy123/article/details/54140029
    func AdbShellInputText(s string) {
        exec.Command("adb", "shell", "input", "text", s).Run()
    }
    
    //等待几秒
    func TimeSleepDuration(x int) {
        time.Sleep(time.Duration(x) * time.Second)
    }
    
    //截屏并保存到当前目录下。
    //由于需在手机和电脑上复制文件,必要时可增加延时或用下面的PathExists()判断文件是否存在,如:
    //time.Sleep(time.Duration(2) * time.Second)
    func AdbShellScreencapPullRm() {
        exec.Command("adb", "shell", "screencap", "-p", "/sdcard/screen.png").Run()
        exec.Command("adb", "pull", "/sdcard/screen.png", ".").Run()
        exec.Command("adb", "shell", "rm", "/sdcard/screen.png").Run()
    }
    
    //根据图像中某一片矩形区域的左上点和右下点,计算该部分图像点的MD5,以便比较图像
    //后来发现不必用这种原始的办法,可以用下面的AdbShellUiautomatorDump()下载手机页面可视控件的XML文件进行解析
    func ReadPngPart2MD5(x1, y1, x2, y2 int) string {
        //先截图
        AdbShellScreencapPullRm()
        file, _ := os.Open("screen.png")
        defer file.Close()
        im, _ := png.Decode(file)
        //x := im.Bounds().Max.X
        //y := im.Bounds().Max.Y
        //按行扫描
        mybuff := new(bytes.Buffer)
        for j := y1; j <= y2; j++ {
            for i := x1; i <= x2; i++ {
                r, g, b, a := im.At(i, j).RGBA()
                mybuff.Write([]byte(fmt.Sprintf("%d ", r)))
                mybuff.Write([]byte(fmt.Sprintf("%d ", g)))
                mybuff.Write([]byte(fmt.Sprintf("%d ", b)))
                mybuff.Write([]byte(fmt.Sprintf("%d ", a)))
            }
        }
        ss := fmt.Sprint(md5.Sum(mybuff.Bytes()))
        //fmt.Printf("MobileMainPage[%d,%d][%d,%d]sum:
    %s", x1, y1, x2, y2, ss)
        return ss
    }
    
    //判断设备是否休眠。重要补充:注意:这里有错误,需要将exec.Command中的命令用逗号分隔,不能直接findstr,应在代码中查找
    //adb shell dumpsys power | findstr "Display Power:state="
    func AdbShellDumpsysPowerOff() bool {
        flag := false
        MyCmd := exec.Command("cmd.exe /c adb shell dumpsys power | findstr "Display Power:state="")
        MyOut, _ := MyCmd.StdoutPipe()
        MyCmd.Start()
        MyBytes, _ := ioutil.ReadAll(MyOut)
        MyCmd.Wait()
        MyOut.Close()
        s := string(MyBytes)
        if strings.Contains(s, "Display Power: state=OFF") {
            flag = true
        }
        return flag
    }
    
    //查看手机上应用的packageName
    //adb shell pm list packages
    func AdbShellPmListPackages() string {
        MyCmd := exec.Command("adb", "shell", "pm", "list", "packages")
        MyOut, _ := MyCmd.StdoutPipe()
        MyCmd.Start()
        MyBytes, _ := ioutil.ReadAll(MyOut)
        MyCmd.Wait()
        MyOut.Close()
        s := string(MyBytes)
        return s
    }
    
    //通过adb 查看最上层activity名字:
    //adb shell dumpsys activity | findstr "mFocusedActivity"
    //代码中不能直接执行findstr过滤,改正则匹配
    func AdbShellDumpsysActivityF() string {
        MyCmd := exec.Command("cmd.exe", "/c", "adb", "shell", "dumpsys", "activity")
        MyOut, _ := MyCmd.StdoutPipe()
        MyCmd.Start()
        MyBytes, _ := ioutil.ReadAll(MyOut)
        MyCmd.Wait()
        MyOut.Close()
        s := string(MyBytes)
        //正则匹配mFocusedActivity
        r := regexp.MustCompile(`mFocusedActivity.+?}`)
        match := r.FindString(s)
        fmt.Println(match)
        return match
    }
    
    //启动activity,如计算器com.android.calculator2/com.android.calculator2.Calculator
    //adb shell am start -n 包名/包名+类名(-n 类名,-a action,-d date,-m MIME-TYPE,-c category,-e 扩展数据,等
    //如:adb shell am start -n com.android.camera/.Camera
    func AdbShellAmStartN(p string) {
        exec.Command("adb", "shell", "am", "start", "-n", p).Run()
    }
    
    //获取当前应用屏幕上所有控件的信息并保存在sdcard下window_dump.xml文件里面. sdk版本16以上
    //如:adb shell uiautomator dump --compressed /sdcard/window_dump.xml
    //adb pull /sdcard/window_dump.xml .
    //adb shell rm /sdcard/window_dump.xml
    //可参考:https://blog.csdn.net/henni_719/article/details/72953251
    //由于需在手机和电脑上复制文件,必要时可增加延时或用下面的PathExists()判断文件是否存在,如:
    //time.Sleep(time.Duration(2) * time.Second) 但是经实测无需延时等待。
    //特别提醒注意:对于可scroll的页面,只能dump出显示在屏幕上的可见的部分。即滑动页面后需重新dump。这个问题曾困扰我一天。
    func AdbShellUiautomatorDump() {
        //删除当前目录下的window_dump.xml
        exec.Command("cmd", "/c", "del", "-y", "window_dump.xml").Run()
        //重新dump
        exec.Command("adb", "shell", "uiautomator", "dump", "/sdcard/window_dump.xml").Run()
        exec.Command("adb", "pull", "/sdcard/window_dump.xml", ".").Run()
        exec.Command("adb", "shell", "rm", "/sdcard/window_dump.xml").Run()
    }
    
    //用正则找xml文件中bounds的坐标点
    //感觉用xml解析不如用正则查找直观,这里需要你自己写正则表达式,返回bounds的两个坐标点[x1,y1][x2,y2]
    //如:x1, y1, x2, y2 :=RegXmlPoint(`<nodes+index="d+"s+text="我的".+?[(d+),(d+)][(d+),(d+)]`)
    func RegXmlPoint(s string) (x1, y1, x2, y2 int) {
        r := regexp.MustCompile(s)
        file, _ := os.Open("window_dump.xml")
        defer file.Close()
        doc, _ := ioutil.ReadAll(file)
        doc1 := string(doc)
        match := r.FindStringSubmatch(doc1)
        x1, _ = strconv.Atoi(match[1])
        y1, _ = strconv.Atoi(match[2])
        x2, _ = strconv.Atoi(match[3])
        y2, _ = strconv.Atoi(match[4])
        return x1, y1, x2, y2
    }
    
    //用法如:Tap(`设置`,0)  将打开手机设置
    //用正则根据`关键词`(反引号,可包含正则)匹配xml文件中node区域,其中有bounds的坐标点,计算bounds中心点,并Tap之
    //第一个参数为匹配用的关键词,第二个参数ix表示点击匹配到的第几个,0表示第一个,-1表示最后一个
    //正则参考:ss := fmt.Sprintf("%s%s%s", `<node.[^>]+?`, s, `.[^>]+?[(d+),(d+)][(d+),(d+)].+?[^>]`)
    //        golang正则匹配任意汉字可用reg = regexp.MustCompile(`[p{Han}]+`)  这里写正则费了较大功夫。
    func Tap(s string, ix int) {
        //先执行AdbShellUiautomatorDump函数。
        AdbShellUiautomatorDump()
        file, _ := os.Open("window_dump.xml")
        defer file.Close()
        doc, _ := ioutil.ReadAll(file)
        doc1 := string(doc)
        ss := fmt.Sprintf("%s%s%s", `<node.[^>]+?`, s, `.[^>]+?[(d+),(d+)][(d+),(d+)].+?>`)
        r := regexp.MustCompile(ss)
        match := r.FindAllStringSubmatch(doc1, -1)
        le := len(match)
        //匹配到1个或多个,ixx表示匹配到的第几个
        ixx := ix
        if le == 0 {
            log.Println("未匹配到:", s)
            return
        }
        if ix < 0 {
            ixx = le + ix
        }
        if ixx < 0 {
            ixx = 0
        }
    
        x1, _ := strconv.Atoi(fmt.Sprint(match[ixx][1]))
        y1, _ := strconv.Atoi(fmt.Sprint(match[ixx][2]))
        x2, _ := strconv.Atoi(fmt.Sprint(match[ixx][3]))
        y2, _ := strconv.Atoi(fmt.Sprint(match[ixx][4]))
    
        xx := (x2-x1)/2 + x1
        yy := (y2-y1)/2 + y1
        log.Println(s)
        AdbShellInputTap(xx, yy)
    }
    
    //用法如:TapOnce(`我的`,0,10,105) 可改为递归调用自身
    ///意思是:点击含有`我的`关键词(反引号,可包含正则)的第一个node(0表示第1个);会打开新页面,10秒后返回后,再
    //向上滑动页面,使该node的y2位置向上滚动到105px(页面上可滚动部分最上端的y1值,也就是上面不可滚动部分的y2值),使该node不可见。不能再点击。
    //注意:此代码不通用,主要是向上滚动时从开始点[500,y2]滚动到结束点[500,pos],这里的开始和结束点要根据实际选择。
    func TapOnce(s string, ix, tm, pos int) {
        //先执行AdbShellUiautomatorDump函数。
        AdbShellUiautomatorDump()
        file, _ := os.Open("window_dump.xml")
        defer file.Close()
        doc, _ := ioutil.ReadAll(file)
        doc1 := string(doc)
        ss := fmt.Sprintf("%s%s%s", `<node.[^>]+?`, s, `.[^>]+?[(d+),(d+)][(d+),(d+)].+?>`)
        r := regexp.MustCompile(ss)
        match := r.FindAllStringSubmatch(doc1, -1)
        le := len(match)
        //匹配到1个或多个,ixx表示匹配到的第几个
        ixx := ix
        if le == 0 {
            log.Println("未匹配到:", s)
            return
        }
        if ix < 0 {
            ixx = le + ix
        }
        if ixx < 0 {
            ixx = 0
        }
    
        x1, _ := strconv.Atoi(fmt.Sprint(match[ixx][1]))
        y1, _ := strconv.Atoi(fmt.Sprint(match[ixx][2]))
        x2, _ := strconv.Atoi(fmt.Sprint(match[ixx][3]))
        y2, _ := strconv.Atoi(fmt.Sprint(match[ixx][4]))
    
        xx := (x2-x1)/2 + x1
        yy := (y2-y1)/2 + y1
        log.Println(s)
        AdbShellInputTap(xx, yy)
        //此时app打开了新的内容页
        TimeSleepDuration(tm)
        AdbShellInputKeyEvent("4") //back
        TimeSleepDuration(1)
        //向上滚动
        AdbShellInputSwipe(500, y2, 500, pos)
    
    }
    
    //判断文件或文件夹是否存在
    func PathExists(path string) (bool, error) {
        _, err := os.Stat(path)
        if err == nil {
            return true, nil
        }
        if os.IsNotExist(err) {
            return false, nil
        }
        return false, err
    }
    
    //未实现
    func notimp(s ...string) {
        /*
            MyCmd := exec.Command("adb", "devices")
            MyOut, _ := MyCmd.StdoutPipe()
            MyCmd.Start()
            MyBytes, _ := ioutil.ReadAll(MyOut)
            MyCmd.Wait()
            MyOut.Close()
            fmt.Println(s)
            return string(MyBytes)
        */
    }

    补充一: 

    输入中文:(参考:https://blog.csdn.net/slimboy123/article/details/54140029)

    https://github.com/senzhk/ADBKeyBoard 

    第一步:从上面的地址下载安装ADBKeyBoard.apk文件

    打开手机或模拟器,adb install ADBKeyBoard.apk安装该输入法

    或者直接安装即可

    第二步:设置默认输入法

    在手机上找:设置-通用-语言和输入法 -默认-选择键盘-ADB Keyboard   然后确定,默认输入法也选择ADB keyboard(好像模拟器选择默认输入法的时候,hardware physical keyboard得off,默认是on)

    第三步:用adb命令输入中文测试OK

    adb shell am broadcast -a ADB_INPUT_TEXT --es msg '不错,可以学着品红酒的好工具'

     然而,我这里还是乱码。用chcp 65001 改cmd字体为lucida console 也不行。而且产生了副作用,用chcp 936改不回来了(后面讲重置cmd)。

    最后-------------------------------

    参考:https://blog.csdn.net/u011068616/article/details/47945927  下面的mmc0531的评论

    输入:adb shell am broadcast -a ADB_INPUT_B64 --es msg "5aSn5rGf"   成功了。(还有评论说不能输入半角空格,但可输入全角空格)

     重置cmd,恢复初始状态:

    用regedit.exe

    HKEY_CURRENT_USER    Console    %SystemRoot%_system32_cmd.exe

    删除文件夹 %SystemRoot%_system32_cmd.exe 就好了

    (右键这个文件夹 有删除选项) 

    然后重启cmd  就恢复了默认设置

    补充二:  

    adb 直接读取屏幕数据,速度更快 ,用  adb shell screencap -p

    之前一直没搞定替换/r/n并保存为png

    后来参考:https://studygolang.com/topics/4527/comment/13217

    看了https://github.com/henson/Answer  的代码,原来是这样:

    //GetImage 直接读取adb截图数据,速度更快
    func (android *Android) GetImage() (img image.Image, err error) {
        cmd := exec.Command("adb", "shell", "screencap", "-p")
        var out bytes.Buffer
        cmd.Stdout = &out
    
        if err = cmd.Run(); err != nil {
            fmt.Println(err.Error())
            return nil, err
        }
        x := bytes.Replace(out.Bytes(), []byte("
    
    "), []byte("
    "), -1)
        img, err = png.Decode(bytes.NewReader(x))
        return
    }
  • 相关阅读:
    python基础11——seek应用&文件修改
    emmm......就当练习了系列08
    python基础10——文件操作x/b模式&控制指针移动
    emmm......就当练习了系列07
    emmm......就当练习了系列06
    python基础09——文件操作
    已经free的pycharm突然"抢钱"?粗暴的搞定它!
    由于系统掉电,导致磁盘扩容时,系统进入救援模式
    关于/etc/fstab的理解
    Linux磁盘
  • 原文地址:https://www.cnblogs.com/pu369/p/10490668.html
Copyright © 2020-2023  润新知