Categorygithub.com/shi-yunsheng/driver
modulepackage
1.0.1
Repository: https://github.com/shi-yunsheng/driver.git
Documentation: pkg.go.dev

# README

English | 简体中文

go-android-uiautomator

Android automation development using Go, so you need to learn some Go syntax before continuing.

This project references openatx/uiautomator2. The dump method actually uses u2.jar from the original project, and the input method is modified from senzhk/ADBKeyBoard. Here I list the libraries and projects used in this project. If there are any omissions, please contact me, and I will add them immediately with apologies.

  • GitHub tag (latest SemVer)
  • GitHub tag (latest SemVer)

Respect to the developers.

The project is still under development, so if you encounter any problems or bugs, please submit Issues. I will definitely see them as long as nothing unexpected happens ( •͈ᴗ⁃͈)ᓂ- - -♡

Also, the documentation demonstrates using Douyin v32.0.0 app. For features not covered in the documentation, please refer to the source code.

Preparation

  1. Prepare an Android phone

  2. Optional during development

    • Install Python, then install uiauto.dev via pip to view page structure

      pip install -U uiautodev
      
      uiauto.dev
      
    • Connect to computer and enable USB debugging

    • Use wireless debugging

[!NOTE]

For first-time use, star-ime input method will be automatically installed. If an installation prompt appears, please allow it.

Quick Start

go get github.com/shi-yunsheng/driver
import "github.com/shi-yunsheng/driver"

func main() {
    d := driver.New()
    
    d.Connect("192.168.10.128") // Check device serial number or IP via adb devices

	d.StartApp("com.ss.android.ugc.aweme") // Launch Douyin

	searchBtn, _ := d.WaitElement(driver.By{Selector: driver.ContentDesc, Value: "搜索"})
	if searchBtn != nil {
		searchBtn.Tap()
	}

	dy := "ismeSYS"
	msg := "Hello, Lao Shi"

	search, _ := d.WaitElement(driver.By{Selector: driver.ResourceID, Value: "com.ss.android.ugc.aweme:id/et_search_kw"})
	if search != nil {
		search.Input(dy)
		search.Search()
	}

	user, _ := d.WaitElement(driver.By{Selector: driver.EndsWithText, Value: fmt.Sprintf("抖音号:%s,按钮", dy)})
	if user != nil {
		user.Tap()
	}

	more, _ := d.WaitElement(driver.By{Selector: driver.ContentDesc, Value: "更多"})
	if more != nil {
		more.Tap()
	}

	sendBtn, _ := d.WaitElement(driver.By{Selector: driver.Text, Value: "发私信"})
	if sendBtn != nil {
		sendBtn.Tap()
	}

	message, _ := d.WaitElement(driver.By{Selector: driver.Text, Value: "发送消息"})
	if message != nil {
		message.Input(msg)
		message.Send()

		for i := 0; i < 5; i++ {
			d.Back()
		}
	}

	defer d.Cleanup()
}

API Document

New()

Create a driver to drive the entire automation work.

func main() {
   	d := driver.New()
}

Connect()

Used to connect to devices when developing and testing on PC. If using wired connection, the parameter is the corresponding serial number; if using wireless connection, the parameter can be the IP address.

[!NOTE]

If compiling to run on Android, this needs to be commented out or deleted~~~

d.Connect("192.168.10.128") // WLAN
// or USB d.Connect("22e3e987") 

Info()

Get device information.

deviceInfo := d.Info()
fmt.Println(deviceInfo)

StartApp()

Launch an application by passing the package name.

d.StartApp("com.ss.android.ugc.aweme") // Launch Douyin

StopApp()

Stop an application by passing the package name.

d.StopApp("com.ss.android.ugc.aweme")

RestartApp()

Restart an application by passing the package name.

d.RestartApp("com.ss.android.ugc.aweme")

InstallApp()

Install an application by passing the installation package path.

d.InstallApp("/data/local/tmp/douyin.apk")

UninstallApp()

Uninstall an application by passing the package name.

d.UninstallApp("com.ss.android.ugc.aweme")

Document()

Get the current page structure.

doc := d.Document()
fmt.Println(doc.RawXML)

FindElement()

Find a specific element node. If the node doesn't exist, returns nil. Currently only supports finding through xpath.

doc := d.Document()
search := doc.FindElement(`//node[@resource-id="com.ss.android.ugc.aweme:id/et_search_kw"]`)
if search != nil {
   	search.Input("老石话不多")
   	search.Search()
}

FindElements()

Similar to FindElement(), but FindElements() returns a group of element nodes.

doc := d.Document()
navbar := doc.FindElements(`//node[@resource-id="com.ss.android.ugc.aweme:id/0zg"]`)
for _, bar := range navbar {
   	fmt.Println(bar.Text())
}

WaitElement()

Wait for an element node to appear. Accepts a By type parameter containing selector type, value, and timeout duration (milliseconds). Returns nil and error if timeout occurs.

Supported selector types:

  • Text: Match by text content
  • ContentDesc: Match by content-desc attribute
  • Class: Match by class name
  • ResourceID: Match by resource ID
  • StartsWithText: Match by text prefix
  • EndsWithText: Match by text suffix
  • StartsWithContentDesc: Match by content-desc prefix
  • EndsWithContentDesc: Match by content-desc suffix
  • StartsWithClass: Match by class name prefix
  • EndsWithClass: Match by class name suffix
  • StartsWithResourceID: Match by resource ID prefix
  • EndsWithResourceID: Match by resource ID suffix
d := driver.New()

search, _ := d.WaitElement(driver.By{
   	Selector: driver.ResourceID,
   	Value: "com.ss.android.ugc.aweme:id/et_search_kw",
   })
   
if search != nil {
   	search.Input("老石话不多")
   	search.Search()
}

Text()

Get the text attribute value of an element node. Returns empty string "" if not found.

doc := d.Document()
likes := doc.FindElement(`//node[@resource-id="com.ss.android.ugc.aweme:id/ffl"]`)
if likes != nil {
   	fmt.Println(likes.Text())
}

ContentDesc()

Get the content-desc attribute value of an element node. Returns empty string "" if not found.

doc := d.Document()
likes := doc.FindElement(`//node[@resource-id="com.ss.android.ugc.aweme:id/ffl"]`)
if likes != nil {
   	fmt.Println(likes.ContentDesc())
}

GetBounds()

Get the bounds attribute value of an element node. Returns nil if not found.

doc := d.Document()
likes := doc.FindElement(`//node[@resource-id="com.ss.android.ugc.aweme:id/ffl"]`)
if likes != nil {
   	fmt.Println(likes.ContentDesc())
}

GetAttribute()

If there are other attributes not provided, you can use this method to get them.

doc := d.Document()
like := doc.FindElement(`//node[@resource-id="com.ss.android.ugc.aweme:id/ffl"]`)
if like != nil {
   	fmt.Println(likes.GetAttribute("resource-id"))
}

Tap()

  • driver Tap()

    Pass in coordinates (x,y) to tap at that location.

    d := driver.New()
    d.Tap(100, 200) // Tap at screen coordinates (100, 200)
    
  • element Tap()

    Directly tap the element.

    doc := d.Document()
    like := doc.FindElement(`//node[@resource-id="com.ss.android.ugc.aweme:id/ffl"]`)
    if likes != nil {
       	like.Tap() // Tap this element
    }
    

[!NOTE]

Coordinates are relative to the screen's top-left corner, and the coordinate range is x: [0, screen width], y: [0, screen height]

LongTap()

  • driver LongTap()

    Pass in coordinates (x,y) to long press at that location.

    d := driver.New()
    d.LongTap(400, 600) // Long press at screen coordinates (400, 600)
    
  • element LongTap()

    Directly long press the element.

    doc := d.Document()
    chat := doc.FindElement(`//node[@resource-id="com.ss.android.ugc.aweme:id/wvq"][@class="android.widget.Button"]`)
    if chat != nil {
       	chat.LongTap()
    }
    

Swipe()

  • driver Swipe()

    Swipe on the screen according to the input direction.

    d := driver.New()
    d.Swipe(driver.SWIPE_DOWN) // Swipe down
    
  • element Swipe()

    Swipe within the element's bounds according to the input direction.

    doc := d.Document()
    tabbar := doc.FindElement(`//node[@resource-id="com.ss.android.ugc.aweme:id/vf5"]`)
    if tabbar != nil {
    	tabbar.Swipe(driver.SWIPE_RIGHT) // Swipe right
    }
    

Input()

  • driver Input()

    Input text at the specified coordinates.

    d := driver.New()
    d.Input(400, 600, "老石")
    
  • element Input()

    If the element node is input-capable, directly input text into that element.

    d := driver.New()
    search, _ := d.WaitElement(driver.By{
       	Selector: driver.ResourceID,
       	Value: "com.ss.android.ugc.aweme:id/et_search_kw",
    })
    
    if search != nil {
       	search.Input("老石话不多")
       	search.Search()
    }
    

Screenshot()

  • driver Screenshot()

    Take a screenshot of the current screen.

    img := d.Screenshot()
    
  • element Screenshot()

    Take a screenshot of the node.

    d := driver.New()
    avatar, _ := d.WaitElement(driver.By{
       	Selector: driver.ResourceID,
       	Value: "com.ss.android.ugc.aweme:id/user_avatar",
    })
    
    if avatar != nil {
           avatar.Screenshot()
       }
    

[!CAUTION]

If the node is obscured, the screenshot will only show the unobscured parts.

Run()

Execute Android shell commands.

if ls, err := d.Run("ls"); err != nil {
    fmt.Println(ls)
}

Cleanup()

Cleanup operations, generally used when the program completes.

defer d.Cleanup()

# Functions

CreateDir creates a directory and all necessary parent directories Parameters: - path: Path of directory to create Returns: - bool: true if successful, false otherwise.
CropImage crops a portion of the source image based on the specified rectangle.
Daemon runs the provided function as a daemon process Parameters: - exec: The function to run in the daemon process.
DeleteAll recursively deletes a file or directory Parameters: - path: Path to delete Returns: - bool: true if successful, false otherwise.
DirExists checks if a directory exists at the specified path Parameters: - path: Path to check Returns: - bool: true if directory exists, false otherwise.
FileExists checks if a file exists at the specified path Parameters: - path: Path to check Returns: - bool: true if file exists, false otherwise.
TODO: Implement FindImage.
GetRandomFloatInRange returns a random floating-point number between the specified min and max values (inclusive).
GetRandomIntInRange returns a random integer between the specified min and max values (inclusive).
Image2Base64 converts an image to a base64 encoded string.
LoadImage loads an image from the specified file path.
New creates and initializes a new driver instance Returns: - *Driver: Configured driver object ready for automation.
Request sends an HTTP request based on the provided Requester options.

# Constants

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
KEYCODE_TV_NUMBER_ENTRY
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Variables

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Structs

Bounds represents the coordinates of a UI element's bounding box.
By represents a selector and its value.
Driver represents the core structure for Android UI automation.
Requester defines the structure for an HTTP request.

# Type aliases

No description provided by the author
No description provided by the author
No description provided by the author
Selector represents a selector type.