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:\n%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(`<node\s+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("\r\r\n"), []byte("\n"), -1)
    img, err = png.Decode(bytes.NewReader(x))
    return
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM