mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-07 15:31:23 +08:00
refactor: move guia2 into uixt
This commit is contained in:
1
go.mod
1
go.mod
@@ -5,6 +5,7 @@ go 1.18
|
|||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.0.4
|
github.com/andybalholm/brotli v1.0.4
|
||||||
github.com/denisbrodbeck/machineid v1.0.1
|
github.com/denisbrodbeck/machineid v1.0.1
|
||||||
|
github.com/electricbubble/gadb v0.0.7
|
||||||
github.com/electricbubble/gidevice v0.6.2
|
github.com/electricbubble/gidevice v0.6.2
|
||||||
github.com/electricbubble/opencv-helper v0.0.3
|
github.com/electricbubble/opencv-helper v0.0.3
|
||||||
github.com/fatih/color v1.13.0
|
github.com/fatih/color v1.13.0
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -98,6 +98,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||||
|
github.com/electricbubble/gadb v0.0.7 h1:fxvVLVNs3IFKuYAEXDF2tDZUjT9jNCltoTSirjM5dgo=
|
||||||
|
github.com/electricbubble/gadb v0.0.7/go.mod h1:3293YJ6OWHv/Q6NA5dwSbK43MbmYm8+Vz2d7h5J3IA8=
|
||||||
github.com/electricbubble/gidevice v0.6.2 h1:eIeCHH7Xn5fTwnUv3qL8c7L4anKIHtjlTBkgr1LDVTc=
|
github.com/electricbubble/gidevice v0.6.2 h1:eIeCHH7Xn5fTwnUv3qL8c7L4anKIHtjlTBkgr1LDVTc=
|
||||||
github.com/electricbubble/gidevice v0.6.2/go.mod h1:bRHL2M9qgeEKju8KRvKMZUVEg7t5zMnTiG3SJ3QDH5o=
|
github.com/electricbubble/gidevice v0.6.2/go.mod h1:bRHL2M9qgeEKju8KRvKMZUVEg7t5zMnTiG3SJ3QDH5o=
|
||||||
github.com/electricbubble/opencv-helper v0.0.3 h1:p0sHTUPPPm8GqzVUtYH+wQbJoguzotUXVRAS7Ibk7nI=
|
github.com/electricbubble/opencv-helper v0.0.3 h1:p0sHTUPPPm8GqzVUtYH+wQbJoguzotUXVRAS7Ibk7nI=
|
||||||
|
|||||||
@@ -38,13 +38,13 @@ This uixt module is initially forked from the following repos and made a lot of
|
|||||||
|
|
||||||
- [electricbubble/gwda-ext-opencv]
|
- [electricbubble/gwda-ext-opencv]
|
||||||
- [electricbubble/gwda]
|
- [electricbubble/gwda]
|
||||||
- [electricbubble/guia]
|
- [electricbubble/guia2]
|
||||||
|
|
||||||
|
|
||||||
[electricbubble/gwda-ext-opencv]: https://github.com/electricbubble/gwda-ext-opencv
|
[electricbubble/gwda-ext-opencv]: https://github.com/electricbubble/gwda-ext-opencv
|
||||||
[appium/WebDriverAgent]: https://github.com/appium/WebDriverAgent
|
[appium/WebDriverAgent]: https://github.com/appium/WebDriverAgent
|
||||||
[electricbubble/gwda]: https://github.com/electricbubble/gwda
|
[electricbubble/gwda]: https://github.com/electricbubble/gwda
|
||||||
[electricbubble/guia]: https://github.com/electricbubble/guia2
|
[electricbubble/guia2]: https://github.com/electricbubble/guia2
|
||||||
[OpenCV 4]: https://opencv.org/
|
[OpenCV 4]: https://opencv.org/
|
||||||
[hybridgroup/gocv]: https://github.com/hybridgroup/gocv
|
[hybridgroup/gocv]: https://github.com/hybridgroup/gocv
|
||||||
[volcengine]: https://www.volcengine.com/product/text-recognition
|
[volcengine]: https://www.volcengine.com/product/text-recognition
|
||||||
|
|||||||
@@ -1 +1,158 @@
|
|||||||
package uixt
|
package uixt
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type touchGesture struct {
|
||||||
|
Touch PointF `json:"touch"`
|
||||||
|
Time float64 `json:"time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TouchAction []touchGesture
|
||||||
|
|
||||||
|
func NewTouchAction(cap ...int) *TouchAction {
|
||||||
|
if len(cap) == 0 || cap[0] <= 0 {
|
||||||
|
cap = []int{8}
|
||||||
|
}
|
||||||
|
tmp := make(TouchAction, 0, cap[0])
|
||||||
|
return &tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ta *TouchAction) Add(x, y int, startTime ...float64) *TouchAction {
|
||||||
|
return ta.AddFloat(float64(x), float64(y), startTime...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ta *TouchAction) AddFloat(x, y float64, startTime ...float64) *TouchAction {
|
||||||
|
if len(startTime) == 0 {
|
||||||
|
var tmp float64 = 0
|
||||||
|
if len(*ta) != 0 {
|
||||||
|
g := (*ta)[len(*ta)-1]
|
||||||
|
tmp = g.Time + 0.05
|
||||||
|
}
|
||||||
|
startTime = []float64{tmp}
|
||||||
|
}
|
||||||
|
*ta = append(*ta, touchGesture{Touch: PointF{x, y}, Time: startTime[0]})
|
||||||
|
return ta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ta *TouchAction) AddPoint(point Point, startTime ...float64) *TouchAction {
|
||||||
|
return ta.AddFloat(float64(point.X), float64(point.Y), startTime...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ta *TouchAction) AddPointF(point PointF, startTime ...float64) *TouchAction {
|
||||||
|
return ta.AddFloat(point.X, point.Y, startTime...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *uiaDriver) MultiPointerGesture(gesture1 *TouchAction, gesture2 *TouchAction, tas ...*TouchAction) (err error) {
|
||||||
|
// Must provide coordinates for at least 2 pointers
|
||||||
|
actions := make([]*TouchAction, 0)
|
||||||
|
actions = append(actions, gesture1, gesture2)
|
||||||
|
if len(tas) != 0 {
|
||||||
|
actions = append(actions, tas...)
|
||||||
|
}
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"actions": actions,
|
||||||
|
}
|
||||||
|
// register(postHandler, new MultiPointerGesture("/wd/hub/session/:sessionId/touch/multi/perform"))
|
||||||
|
_, err = d.httpPOST(data, "/session", d.sessionId, "/touch/multi/perform")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type w3cGesture map[string]interface{}
|
||||||
|
|
||||||
|
func _newW3CGesture() w3cGesture {
|
||||||
|
return make(w3cGesture)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g w3cGesture) _set(key string, value interface{}) w3cGesture {
|
||||||
|
g[key] = value
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g w3cGesture) pause(duration float64) w3cGesture {
|
||||||
|
return g._set("type", "pause").
|
||||||
|
_set("duration", duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g w3cGesture) keyDown(value string) w3cGesture {
|
||||||
|
return g._set("type", "keyDown").
|
||||||
|
_set("value", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g w3cGesture) keyUp(value string) w3cGesture {
|
||||||
|
return g._set("type", "keyUp").
|
||||||
|
_set("value", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g w3cGesture) pointerDown(button int) w3cGesture {
|
||||||
|
return g._set("type", "pointerDown")._set("button", button)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g w3cGesture) pointerUp(button int) w3cGesture {
|
||||||
|
return g._set("type", "pointerUp")._set("button", button)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g w3cGesture) pointerMove(x, y float64, origin string, duration float64, pressureAndSize ...float64) w3cGesture {
|
||||||
|
switch len(pressureAndSize) {
|
||||||
|
case 1:
|
||||||
|
g._set("pressure", pressureAndSize[0])
|
||||||
|
case 2:
|
||||||
|
g._set("pressure", pressureAndSize[0])
|
||||||
|
g._set("size", pressureAndSize[1])
|
||||||
|
}
|
||||||
|
return g._set("type", "pointerMove").
|
||||||
|
_set("duration", duration).
|
||||||
|
_set("origin", origin).
|
||||||
|
_set("x", x).
|
||||||
|
_set("y", y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g w3cGesture) size(size ...float64) w3cGesture {
|
||||||
|
if len(size) == 0 {
|
||||||
|
size = []float64{1.0}
|
||||||
|
}
|
||||||
|
return g._set("size", size[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g w3cGesture) pressure(pressure ...float64) w3cGesture {
|
||||||
|
if len(pressure) == 0 {
|
||||||
|
pressure = []float64{1.0}
|
||||||
|
}
|
||||||
|
return g._set("pressure", pressure[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
type W3CGestures []w3cGesture
|
||||||
|
|
||||||
|
func NewW3CGestures(cap ...int) *W3CGestures {
|
||||||
|
if len(cap) == 0 || cap[0] <= 0 {
|
||||||
|
cap = []int{8}
|
||||||
|
}
|
||||||
|
tmp := make(W3CGestures, 0, cap[0])
|
||||||
|
return &tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *W3CGestures) Pause(duration ...float64) *W3CGestures {
|
||||||
|
if len(duration) == 0 || duration[0] < 0 {
|
||||||
|
duration = []float64{0.5}
|
||||||
|
}
|
||||||
|
*g = append(*g, _newW3CGesture().pause(duration[0]*1000))
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *W3CGestures) KeyDown(value string) *W3CGestures {
|
||||||
|
*g = append(*g, _newW3CGesture().keyDown(value))
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *W3CGestures) KeyUp(value string) *W3CGestures {
|
||||||
|
*g = append(*g, _newW3CGesture().keyUp(value))
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *W3CGestures) SendKeys(text string) *W3CGestures {
|
||||||
|
ss := strings.Split(text, "")
|
||||||
|
for i := range ss {
|
||||||
|
g.KeyDown(ss[i])
|
||||||
|
g.KeyUp(ss[i])
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,117 @@
|
|||||||
package uixt
|
package uixt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/electricbubble/gadb"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
AdbServerHost = "localhost"
|
||||||
|
AdbServerPort = gadb.AdbServerPort // 5037
|
||||||
|
UIA2ServerPort = 6790
|
||||||
|
DeviceTempPath = "/data/local/tmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const forwardToPrefix = "forward-to-"
|
||||||
|
|
||||||
|
func InitUIAClient(device *AndroidDevice) (*DriverExt, error) {
|
||||||
|
var deviceOptions []AndroidDeviceOption
|
||||||
|
if device.SerialNumber != "" {
|
||||||
|
deviceOptions = append(deviceOptions, WithSerialNumber(device.SerialNumber))
|
||||||
|
}
|
||||||
|
if device.IP != "" {
|
||||||
|
deviceOptions = append(deviceOptions, WithAdbIP(device.IP))
|
||||||
|
}
|
||||||
|
if device.Port != 0 {
|
||||||
|
deviceOptions = append(deviceOptions, WithAdbPort(device.Port))
|
||||||
|
}
|
||||||
|
|
||||||
|
// init uia device
|
||||||
|
androidDevice, err := NewAndroidDevice(deviceOptions...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
driver, err := androidDevice.NewUSBDriver(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to init UIA driver")
|
||||||
|
}
|
||||||
|
fmt.Println(driver)
|
||||||
|
|
||||||
|
var driverExt *DriverExt
|
||||||
|
// TODO
|
||||||
|
// driverExt, err = Extend(driver)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, errors.Wrap(err, "failed to extend UIA Driver")
|
||||||
|
// }
|
||||||
|
|
||||||
|
return driverExt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AndroidDeviceOption func(*AndroidDevice)
|
||||||
|
|
||||||
|
func WithSerialNumber(serial string) AndroidDeviceOption {
|
||||||
|
return func(device *AndroidDevice) {
|
||||||
|
device.SerialNumber = serial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithAdbIP(ip string) AndroidDeviceOption {
|
||||||
|
return func(device *AndroidDevice) {
|
||||||
|
device.IP = ip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithAdbPort(port int) AndroidDeviceOption {
|
||||||
|
return func(device *AndroidDevice) {
|
||||||
|
device.Port = port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithAdbLogOn(logOn bool) AndroidDeviceOption {
|
||||||
|
return func(device *AndroidDevice) {
|
||||||
|
device.LogOn = logOn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAndroidDevice(options ...AndroidDeviceOption) (device *AndroidDevice, err error) {
|
||||||
|
deviceList, err := DeviceList()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get attached devices failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
device = &AndroidDevice{
|
||||||
|
Port: UIA2ServerPort,
|
||||||
|
IP: AdbServerHost,
|
||||||
|
}
|
||||||
|
for _, option := range options {
|
||||||
|
option(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
serialNumber := device.SerialNumber
|
||||||
|
for _, dev := range deviceList {
|
||||||
|
// find device by serial number if specified
|
||||||
|
if serialNumber != "" && dev.Serial() != serialNumber {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
device.SerialNumber = dev.Serial()
|
||||||
|
device.Device = dev
|
||||||
|
return device, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("device %s not found", device.SerialNumber)
|
||||||
|
}
|
||||||
|
|
||||||
type AndroidDevice struct {
|
type AndroidDevice struct {
|
||||||
|
gadb.Device
|
||||||
SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"`
|
SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"`
|
||||||
|
IP string `json:"ip,omitempty" yaml:"ip,omitempty"`
|
||||||
Port int `json:"port,omitempty" yaml:"port,omitempty"`
|
Port int `json:"port,omitempty" yaml:"port,omitempty"`
|
||||||
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
|
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -10,6 +120,436 @@ func (o AndroidDevice) UUID() string {
|
|||||||
return o.SerialNumber
|
return o.SerialNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitUIAClient(device *AndroidDevice) (*DriverExt, error) {
|
func DeviceList() (devices []gadb.Device, err error) {
|
||||||
return nil, nil
|
var adbClient gadb.Client
|
||||||
|
if adbClient, err = gadb.NewClientWith(AdbServerHost, AdbServerPort); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return adbClient.DeviceList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUSBDriver creates new client via USB connected device, this will also start a new session.
|
||||||
|
// TODO: replace uiaDriver with WebDriver
|
||||||
|
func (dev *AndroidDevice) NewUSBDriver(capabilities Capabilities) (driver *uiaDriver, err error) {
|
||||||
|
var localPort int
|
||||||
|
if localPort, err = getFreePort(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = dev.Forward(localPort, UIA2ServerPort); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rawURL := fmt.Sprintf("http://%s%d:6790/wd/hub", forwardToPrefix, localPort)
|
||||||
|
driver, err = NewUIADriver(capabilities, rawURL)
|
||||||
|
if err != nil {
|
||||||
|
_ = dev.ForwardKill(localPort)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
driver.adbDevice = dev.Device
|
||||||
|
driver.localPort = localPort
|
||||||
|
|
||||||
|
conn, err := net.Dial("tcp", fmt.Sprintf(":%d", localPort))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("adb forward: %w", err)
|
||||||
|
}
|
||||||
|
driver.client = convertToHTTPClient(conn)
|
||||||
|
|
||||||
|
return driver, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTPDriver creates new remote HTTP client, this will also start a new session.
|
||||||
|
// TODO: replace uiaDriver with WebDriver
|
||||||
|
func (dev *AndroidDevice) NewHTTPDriver(capabilities Capabilities) (driver *uiaDriver, err error) {
|
||||||
|
rawURL := fmt.Sprintf("http://%s:%d/wd/hub", dev.IP, dev.Port)
|
||||||
|
if driver, err = NewUIADriver(capabilities, rawURL); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
driver.adbDevice = dev.Device
|
||||||
|
return driver, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFreePort() (int, error) {
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("free port: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := net.ListenTCP("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("free port: %w", err)
|
||||||
|
}
|
||||||
|
defer func() { _ = l.Close() }()
|
||||||
|
return l.Addr().(*net.TCPAddr).Port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type UiSelectorHelper struct {
|
||||||
|
value *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUiSelectorHelper() UiSelectorHelper {
|
||||||
|
return UiSelectorHelper{value: bytes.NewBufferString("new UiSelector()")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s UiSelectorHelper) String() string {
|
||||||
|
return s.value.String() + ";"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text Set the search criteria to match the visible text displayed
|
||||||
|
// in a widget (for example, the text label to launch an app).
|
||||||
|
//
|
||||||
|
// The text for the element must match exactly with the string in your input
|
||||||
|
// argument. Matching is case-sensitive.
|
||||||
|
func (s UiSelectorHelper) Text(text string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.text("%s")`, text))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextMatches Set the search criteria to match the visible text displayed in a layout
|
||||||
|
// element, using a regular expression.
|
||||||
|
//
|
||||||
|
// The text in the widget must match exactly with the string in your
|
||||||
|
// input argument.
|
||||||
|
func (s UiSelectorHelper) TextMatches(regex string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.textMatches("%s")`, regex))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextStartsWith Set the search criteria to match visible text in a widget that is
|
||||||
|
// prefixed by the text parameter.
|
||||||
|
//
|
||||||
|
// The matching is case-insensitive.
|
||||||
|
func (s UiSelectorHelper) TextStartsWith(text string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.textStartsWith("%s")`, text))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextContains Set the search criteria to match the visible text in a widget
|
||||||
|
// where the visible text must contain the string in your input argument.
|
||||||
|
//
|
||||||
|
// The matching is case-sensitive.
|
||||||
|
func (s UiSelectorHelper) TextContains(text string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.textContains("%s")`, text))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClassName Set the search criteria to match the class property
|
||||||
|
// for a widget (for example, "android.widget.Button").
|
||||||
|
func (s UiSelectorHelper) ClassName(className string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.className("%s")`, className))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClassNameMatches Set the search criteria to match the class property
|
||||||
|
// for a widget, using a regular expression.
|
||||||
|
func (s UiSelectorHelper) ClassNameMatches(regex string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.classNameMatches("%s")`, regex))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description Set the search criteria to match the content-description
|
||||||
|
// property for a widget.
|
||||||
|
//
|
||||||
|
// The content-description is typically used
|
||||||
|
// by the Android Accessibility framework to
|
||||||
|
// provide an audio prompt for the widget when
|
||||||
|
// the widget is selected. The content-description
|
||||||
|
// for the widget must match exactly
|
||||||
|
// with the string in your input argument.
|
||||||
|
//
|
||||||
|
// Matching is case-sensitive.
|
||||||
|
func (s UiSelectorHelper) Description(desc string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.description("%s")`, desc))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescriptionMatches Set the search criteria to match the content-description
|
||||||
|
// property for a widget.
|
||||||
|
//
|
||||||
|
// The content-description is typically used
|
||||||
|
// by the Android Accessibility framework to
|
||||||
|
// provide an audio prompt for the widget when
|
||||||
|
// the widget is selected. The content-description
|
||||||
|
// for the widget must match exactly
|
||||||
|
// with the string in your input argument.
|
||||||
|
func (s UiSelectorHelper) DescriptionMatches(regex string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.descriptionMatches("%s")`, regex))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescriptionStartsWith Set the search criteria to match the content-description
|
||||||
|
// property for a widget.
|
||||||
|
//
|
||||||
|
// The content-description is typically used
|
||||||
|
// by the Android Accessibility framework to
|
||||||
|
// provide an audio prompt for the widget when
|
||||||
|
// the widget is selected. The content-description
|
||||||
|
// for the widget must start
|
||||||
|
// with the string in your input argument.
|
||||||
|
//
|
||||||
|
// Matching is case-insensitive.
|
||||||
|
func (s UiSelectorHelper) DescriptionStartsWith(desc string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.descriptionStartsWith("%s")`, desc))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescriptionContains Set the search criteria to match the content-description
|
||||||
|
// property for a widget.
|
||||||
|
//
|
||||||
|
// The content-description is typically used
|
||||||
|
// by the Android Accessibility framework to
|
||||||
|
// provide an audio prompt for the widget when
|
||||||
|
// the widget is selected. The content-description
|
||||||
|
// for the widget must contain
|
||||||
|
// the string in your input argument.
|
||||||
|
//
|
||||||
|
// Matching is case-insensitive.
|
||||||
|
func (s UiSelectorHelper) DescriptionContains(desc string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.descriptionContains("%s")`, desc))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceId Set the search criteria to match the given resource ID.
|
||||||
|
func (s UiSelectorHelper) ResourceId(id string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.resourceId("%s")`, id))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceIdMatches Set the search criteria to match the resource ID
|
||||||
|
// of the widget, using a regular expression.
|
||||||
|
func (s UiSelectorHelper) ResourceIdMatches(regex string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.resourceIdMatches("%s")`, regex))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index Set the search criteria to match the widget by its node
|
||||||
|
// index in the layout hierarchy.
|
||||||
|
//
|
||||||
|
// The index value must be 0 or greater.
|
||||||
|
//
|
||||||
|
// Using the index can be unreliable and should only
|
||||||
|
// be used as a last resort for matching. Instead,
|
||||||
|
// consider using the `Instance(int)` method.
|
||||||
|
func (s UiSelectorHelper) Index(index int) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.index(%d)`, index))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instance Set the search criteria to match the
|
||||||
|
// widget by its instance number.
|
||||||
|
//
|
||||||
|
// The instance value must be 0 or greater, where
|
||||||
|
// the first instance is 0.
|
||||||
|
//
|
||||||
|
// For example, to simulate a user click on
|
||||||
|
// the third image that is enabled in a UI screen, you
|
||||||
|
// could specify a a search criteria where the instance is
|
||||||
|
// 2, the `className(String)` matches the image
|
||||||
|
// widget class, and `enabled(boolean)` is true.
|
||||||
|
// The code would look like this:
|
||||||
|
// `new UiSelector().className("android.widget.ImageView")
|
||||||
|
// .enabled(true).instance(2);`
|
||||||
|
func (s UiSelectorHelper) Instance(instance int) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.instance(%d)`, instance))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled Set the search criteria to match widgets that are enabled.
|
||||||
|
//
|
||||||
|
// Typically, using this search criteria alone is not useful.
|
||||||
|
// You should also include additional criteria, such as text,
|
||||||
|
// content-description, or the class name for a widget.
|
||||||
|
//
|
||||||
|
// If no other search criteria is specified, and there is more
|
||||||
|
// than one matching widget, the first widget in the tree
|
||||||
|
// is selected.
|
||||||
|
func (s UiSelectorHelper) Enabled(b bool) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.enabled(%t)`, b))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focused Set the search criteria to match widgets that have focus.
|
||||||
|
//
|
||||||
|
// Typically, using this search criteria alone is not useful.
|
||||||
|
// You should also include additional criteria, such as text,
|
||||||
|
// content-description, or the class name for a widget.
|
||||||
|
//
|
||||||
|
// If no other search criteria is specified, and there is more
|
||||||
|
// than one matching widget, the first widget in the tree
|
||||||
|
// is selected.
|
||||||
|
func (s UiSelectorHelper) Focused(b bool) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.focused(%t)`, b))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focusable Set the search criteria to match widgets that are focusable.
|
||||||
|
//
|
||||||
|
// Typically, using this search criteria alone is not useful.
|
||||||
|
// You should also include additional criteria, such as text,
|
||||||
|
// content-description, or the class name for a widget.
|
||||||
|
//
|
||||||
|
// If no other search criteria is specified, and there is more
|
||||||
|
// than one matching widget, the first widget in the tree
|
||||||
|
// is selected.
|
||||||
|
func (s UiSelectorHelper) Focusable(b bool) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.focusable(%t)`, b))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scrollable Set the search criteria to match widgets that are scrollable.
|
||||||
|
//
|
||||||
|
// Typically, using this search criteria alone is not useful.
|
||||||
|
// You should also include additional criteria, such as text,
|
||||||
|
// content-description, or the class name for a widget.
|
||||||
|
//
|
||||||
|
// If no other search criteria is specified, and there is more
|
||||||
|
// than one matching widget, the first widget in the tree
|
||||||
|
// is selected.
|
||||||
|
func (s UiSelectorHelper) Scrollable(b bool) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.scrollable(%t)`, b))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selected Set the search criteria to match widgets that
|
||||||
|
// are currently selected.
|
||||||
|
//
|
||||||
|
// Typically, using this search criteria alone is not useful.
|
||||||
|
// You should also include additional criteria, such as text,
|
||||||
|
// content-description, or the class name for a widget.
|
||||||
|
//
|
||||||
|
// If no other search criteria is specified, and there is more
|
||||||
|
// than one matching widget, the first widget in the tree
|
||||||
|
// is selected.
|
||||||
|
func (s UiSelectorHelper) Selected(b bool) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.selected(%t)`, b))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checked Set the search criteria to match widgets that
|
||||||
|
// are currently checked (usually for checkboxes).
|
||||||
|
//
|
||||||
|
// Typically, using this search criteria alone is not useful.
|
||||||
|
// You should also include additional criteria, such as text,
|
||||||
|
// content-description, or the class name for a widget.
|
||||||
|
//
|
||||||
|
// If no other search criteria is specified, and there is more
|
||||||
|
// than one matching widget, the first widget in the tree
|
||||||
|
// is selected.
|
||||||
|
func (s UiSelectorHelper) Checked(b bool) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.checked(%t)`, b))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checkable Set the search criteria to match widgets that are checkable.
|
||||||
|
//
|
||||||
|
// Typically, using this search criteria alone is not useful.
|
||||||
|
// You should also include additional criteria, such as text,
|
||||||
|
// content-description, or the class name for a widget.
|
||||||
|
//
|
||||||
|
// If no other search criteria is specified, and there is more
|
||||||
|
// than one matching widget, the first widget in the tree
|
||||||
|
// is selected.
|
||||||
|
func (s UiSelectorHelper) Checkable(b bool) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.checkable(%t)`, b))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clickable Set the search criteria to match widgets that are clickable.
|
||||||
|
//
|
||||||
|
// Typically, using this search criteria alone is not useful.
|
||||||
|
// You should also include additional criteria, such as text,
|
||||||
|
// content-description, or the class name for a widget.
|
||||||
|
//
|
||||||
|
// If no other search criteria is specified, and there is more
|
||||||
|
// than one matching widget, the first widget in the tree
|
||||||
|
// is selected.
|
||||||
|
func (s UiSelectorHelper) Clickable(b bool) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.clickable(%t)`, b))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// LongClickable Set the search criteria to match widgets that are long-clickable.
|
||||||
|
//
|
||||||
|
// Typically, using this search criteria alone is not useful.
|
||||||
|
// You should also include additional criteria, such as text,
|
||||||
|
// content-description, or the class name for a widget.
|
||||||
|
//
|
||||||
|
// If no other search criteria is specified, and there is more
|
||||||
|
// than one matching widget, the first widget in the tree
|
||||||
|
// is selected.
|
||||||
|
func (s UiSelectorHelper) LongClickable(b bool) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.longClickable(%t)`, b))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// packageName Set the search criteria to match the package name
|
||||||
|
// of the application that contains the widget.
|
||||||
|
func (s UiSelectorHelper) packageName(name string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.packageName(%s)`, name))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageNameMatches Set the search criteria to match the package name
|
||||||
|
// of the application that contains the widget.
|
||||||
|
func (s UiSelectorHelper) PackageNameMatches(regex string) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.packageNameMatches(%s)`, regex))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChildSelector Adds a child UiSelector criteria to this selector.
|
||||||
|
//
|
||||||
|
// Use this selector to narrow the search scope to
|
||||||
|
// child widgets under a specific parent widget.
|
||||||
|
func (s UiSelectorHelper) ChildSelector(selector UiSelectorHelper) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.childSelector(%s)`, selector.value.String()))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s UiSelectorHelper) PatternSelector(selector UiSelectorHelper) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.patternSelector(%s)`, selector.value.String()))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s UiSelectorHelper) ContainerSelector(selector UiSelectorHelper) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.containerSelector(%s)`, selector.value.String()))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromParent Adds a child UiSelector criteria to this selector which is used to
|
||||||
|
// start search from the parent widget.
|
||||||
|
//
|
||||||
|
// Use this selector to narrow the search scope to
|
||||||
|
// sibling widgets as well all child widgets under a parent.
|
||||||
|
func (s UiSelectorHelper) FromParent(selector UiSelectorHelper) UiSelectorHelper {
|
||||||
|
s.value.WriteString(fmt.Sprintf(`.fromParent(%s)`, selector.value.String()))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type AndroidBySelector struct {
|
||||||
|
// Set the search criteria to match the given resource ResourceIdID.
|
||||||
|
ResourceIdID string `json:"id"`
|
||||||
|
// Set the search criteria to match the content-description property for a widget.
|
||||||
|
ContentDescription string `json:"accessibility id"`
|
||||||
|
XPath string `json:"xpath"`
|
||||||
|
// Set the search criteria to match the class property for a widget (for example, "android.widget.Button").
|
||||||
|
ClassName string `json:"class name"`
|
||||||
|
UiAutomator string `json:"-android uiautomator"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (by AndroidBySelector) getMethodAndSelector() (method, selector string) {
|
||||||
|
vBy := reflect.ValueOf(by)
|
||||||
|
tBy := reflect.TypeOf(by)
|
||||||
|
for i := 0; i < vBy.NumField(); i++ {
|
||||||
|
vi := vBy.Field(i).Interface()
|
||||||
|
// switch vi := vi.(type) {
|
||||||
|
// case string:
|
||||||
|
// selector = vi
|
||||||
|
// }
|
||||||
|
selector = vi.(string)
|
||||||
|
if selector != "" && selector != "UNKNOWN" {
|
||||||
|
method = tBy.Field(i).Tag.Get("json")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
1203
hrp/internal/uixt/android_driver.go
Normal file
1203
hrp/internal/uixt/android_driver.go
Normal file
File diff suppressed because it is too large
Load Diff
238
hrp/internal/uixt/android_elment.go
Normal file
238
hrp/internal/uixt/android_elment.go
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
package uixt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type uiaElement struct {
|
||||||
|
parent *uiaDriver
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) Text() (text string, err error) {
|
||||||
|
// register(getHandler, new GetText("/wd/hub/session/:sessionId/element/:id/text"))
|
||||||
|
var rawResp rawResponse
|
||||||
|
if rawResp, err = e.parent.httpGET("/session", e.parent.sessionId, "/element", e.id, "/text"); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
reply := new(struct{ Value string })
|
||||||
|
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
text = reply.Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) GetAttribute(name string) (attribute string, err error) {
|
||||||
|
// register(getHandler, new GetElementAttribute("/wd/hub/session/:sessionId/element/:id/attribute/:name"))
|
||||||
|
var rawResp rawResponse
|
||||||
|
if rawResp, err = e.parent.httpGET("/session", e.parent.sessionId, "/element", e.id, "/attribute", name); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
reply := new(struct{ Value string })
|
||||||
|
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
attribute = reply.Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) ContentDescription() (name string, err error) {
|
||||||
|
// register(getHandler, new GetName("/wd/hub/session/:sessionId/element/:id/name"))
|
||||||
|
var rawResp rawResponse
|
||||||
|
if rawResp, err = e.parent.httpGET("/session", e.parent.sessionId, "/element", e.id, "/name"); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
reply := new(struct{ Value string })
|
||||||
|
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
name = reply.Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) Size() (size Size, err error) {
|
||||||
|
// register(getHandler, new GetSize("/wd/hub/session/:sessionId/element/:id/size"))
|
||||||
|
var rawResp rawResponse
|
||||||
|
if rawResp, err = e.parent.httpGET("/session", e.parent.sessionId, "/element", e.id, "/size"); err != nil {
|
||||||
|
return Size{-1, -1}, err
|
||||||
|
}
|
||||||
|
reply := new(struct{ Value Size })
|
||||||
|
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||||
|
return Size{-1, -1}, err
|
||||||
|
}
|
||||||
|
size = reply.Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) Rect() (rect Rect, err error) {
|
||||||
|
// register(getHandler, new GetRect("/wd/hub/session/:sessionId/element/:id/rect"))
|
||||||
|
var rawResp rawResponse
|
||||||
|
if rawResp, err = e.parent.httpGET("/session", e.parent.sessionId, "/element", e.id, "/rect"); err != nil {
|
||||||
|
return Rect{}, err
|
||||||
|
}
|
||||||
|
reply := new(struct{ Value Rect })
|
||||||
|
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||||
|
return Rect{}, err
|
||||||
|
}
|
||||||
|
rect = reply.Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) Screenshot() (raw *bytes.Buffer, err error) {
|
||||||
|
// W3C endpoint
|
||||||
|
// register(getHandler, new GetElementScreenshot("/wd/hub/session/:sessionId/element/:id/screenshot"))
|
||||||
|
// JSONWP endpoint
|
||||||
|
// register(getHandler, new GetElementScreenshot("/wd/hub/session/:sessionId/screenshot/:id"))
|
||||||
|
var rawResp rawResponse
|
||||||
|
if rawResp, err = e.parent.httpGET("/session", e.parent.sessionId, "/element", e.id, "/screenshot"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
reply := new(struct{ Value string })
|
||||||
|
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var decodeStr []byte
|
||||||
|
if decodeStr, err = base64.StdEncoding.DecodeString(reply.Value); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
raw = bytes.NewBuffer(decodeStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) Location() (point Point, err error) {
|
||||||
|
// register(getHandler, new Location("/wd/hub/session/:sessionId/element/:id/location"))
|
||||||
|
var rawResp rawResponse
|
||||||
|
if rawResp, err = e.parent.httpGET("/session", e.parent.sessionId, "/element", e.id, "/location"); err != nil {
|
||||||
|
return Point{-1, -1}, err
|
||||||
|
}
|
||||||
|
reply := new(struct{ Value Point })
|
||||||
|
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||||
|
return Point{-1, -1}, err
|
||||||
|
}
|
||||||
|
point = reply.Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) Click() (err error) {
|
||||||
|
// register(postHandler, new Click("/wd/hub/session/:sessionId/element/:id/click"))
|
||||||
|
_, err = e.parent.httpPOST(nil, "/session", e.parent.sessionId, "/element", e.id, "/click")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) Clear() (err error) {
|
||||||
|
// register(postHandler, new Clear("/wd/hub/session/:sessionId/element/:id/clear"))
|
||||||
|
_, err = e.parent.httpPOST(nil, "/session", e.parent.sessionId, "/element", e.id, "/clear")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) SendKeys(text string, isReplace ...bool) (err error) {
|
||||||
|
if len(isReplace) == 0 {
|
||||||
|
isReplace = []bool{true}
|
||||||
|
}
|
||||||
|
// register(postHandler, new SendKeysToElement("/wd/hub/session/:sessionId/element/:id/value"))
|
||||||
|
// https://github.com/appium/appium-uiautomator2-server/blob/master/app/src/main/java/io/appium/uiautomator2/handler/SendKeysToElement.java#L76-L85
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"text": text,
|
||||||
|
"replace": isReplace[0],
|
||||||
|
}
|
||||||
|
_, err = e.parent.httpPOST(data, "/session", e.parent.sessionId, "/element", e.id, "/value")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) FindElements(by AndroidBySelector) (elements []*uiaElement, err error) {
|
||||||
|
method, selector := by.getMethodAndSelector()
|
||||||
|
return e.parent._findElements(method, selector, e.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) FindElement(by AndroidBySelector) (elem *uiaElement, err error) {
|
||||||
|
method, selector := by.getMethodAndSelector()
|
||||||
|
return e.parent._findElement(method, selector, e.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) Swipe(startX, startY, endX, endY int, steps ...int) (err error) {
|
||||||
|
return e.SwipeFloat(float64(startX), float64(startY), float64(endX), float64(endY), steps...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) SwipeFloat(startX, startY, endX, endY float64, steps ...int) (err error) {
|
||||||
|
if len(steps) == 0 {
|
||||||
|
steps = []int{12}
|
||||||
|
}
|
||||||
|
return e.parent._swipe(startX, startY, endX, endY, steps[0], e.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) SwipePoint(startPoint, endPoint Point, steps ...int) (err error) {
|
||||||
|
return e.Swipe(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y, steps...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) SwipePointF(startPoint, endPoint PointF, steps ...int) (err error) {
|
||||||
|
return e.SwipeFloat(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y, steps...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) Drag(endX, endY int, steps ...int) (err error) {
|
||||||
|
return e.DragFloat(float64(endX), float64(endY), steps...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) DragFloat(endX, endY float64, steps ...int) error {
|
||||||
|
if len(steps) == 0 {
|
||||||
|
steps = []int{12 * 10}
|
||||||
|
} else {
|
||||||
|
steps[0] = 12 * 10
|
||||||
|
}
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"elementId": e.id,
|
||||||
|
"endX": endX,
|
||||||
|
"endY": endY,
|
||||||
|
"steps": steps[0],
|
||||||
|
}
|
||||||
|
return e.parent._drag(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) DragPoint(endPoint Point, steps ...int) error {
|
||||||
|
return e.Drag(endPoint.X, endPoint.Y, steps...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) DragPointF(endPoint PointF, steps ...int) (err error) {
|
||||||
|
return e.DragFloat(endPoint.X, endPoint.Y, steps...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) DragTo(destElem *uiaElement, steps ...int) error {
|
||||||
|
if len(steps) == 0 {
|
||||||
|
steps = []int{12}
|
||||||
|
}
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"elementId": e.id,
|
||||||
|
"destElId": destElem.id,
|
||||||
|
"steps": steps[0],
|
||||||
|
}
|
||||||
|
return e.parent._drag(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) Flick(xOffset, yOffset, speed int) (err error) {
|
||||||
|
data := map[string]interface{}{
|
||||||
|
legacyWebElementIdentifier: e.id,
|
||||||
|
webElementIdentifier: e.id,
|
||||||
|
"xoffset": xOffset,
|
||||||
|
"yoffset": yOffset,
|
||||||
|
"speed": speed,
|
||||||
|
}
|
||||||
|
return e.parent._flick(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) ScrollTo(by AndroidBySelector, maxSwipes ...int) (err error) {
|
||||||
|
if len(maxSwipes) == 0 {
|
||||||
|
maxSwipes = []int{0}
|
||||||
|
}
|
||||||
|
method, selector := by.getMethodAndSelector()
|
||||||
|
return e.parent._scrollTo(method, selector, maxSwipes[0], e.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *uiaElement) ScrollToElement(element *uiaElement) (err error) {
|
||||||
|
// register(postHandler, new ScrollToElement("/wd/hub/session/:sessionId/appium/element/:id/scroll_to/:id2"))
|
||||||
|
_, err = e.parent.httpPOST(nil, "/session", e.parent.sessionId, "/appium/element", e.id, "/scroll_to", element.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
879
hrp/internal/uixt/android_key.go
Normal file
879
hrp/internal/uixt/android_key.go
Normal file
@@ -0,0 +1,879 @@
|
|||||||
|
package uixt
|
||||||
|
|
||||||
|
type KeyMeta int
|
||||||
|
|
||||||
|
const (
|
||||||
|
KMEmpty KeyMeta = 0 // As a `null`
|
||||||
|
KMCapLocked KeyMeta = 0x100 // SHIFT key locked in CAPS mode.
|
||||||
|
KMAltLocked KeyMeta = 0x200 // ALT key locked.
|
||||||
|
KMSymLocked KeyMeta = 0x400 // SYM key locked.
|
||||||
|
KMSelecting KeyMeta = 0x800 // Text is in selection mode.
|
||||||
|
// KMAltOn KeyMeta = 0x02 // This mask is used to check whether one of the ALT meta keys is pressed.
|
||||||
|
// KMAltLeftOn KeyMeta = 0x10 // This mask is used to check whether the left ALT meta key is pressed.
|
||||||
|
// KMAltRightOn KeyMeta = 0x20 // This mask is used to check whether the right the ALT meta key is pressed.
|
||||||
|
// KMShiftOn KeyMeta = 0x1 // This mask is used to check whether one of the SHIFT meta keys is pressed.
|
||||||
|
// KMShiftLeftOn KeyMeta = 0x40 // This mask is used to check whether the left SHIFT meta key is pressed.
|
||||||
|
// KMShiftRightOn KeyMeta = 0x80 // This mask is used to check whether the right SHIFT meta key is pressed.
|
||||||
|
// KMSymOn KeyMeta = 0x4 // This mask is used to check whether the SYM meta key is pressed.
|
||||||
|
// KMFunctionOn KeyMeta = 0x8 // This mask is used to check whether the FUNCTION meta key is pressed.
|
||||||
|
// KMCtrlOn KeyMeta = 0x1000 // This mask is used to check whether one of the CTRL meta keys is pressed.
|
||||||
|
// KMCtrlLeftOn KeyMeta = 0x2000 // This mask is used to check whether the left CTRL meta key is pressed.
|
||||||
|
// KMCtrlRightOn KeyMeta = 0x4000 // This mask is used to check whether the right CTRL meta key is pressed.
|
||||||
|
// KMMetaOn KeyMeta = 0x10000 // This mask is used to check whether one of the META meta keys is pressed.
|
||||||
|
// KMMetaLeftOn KeyMeta = 0x20000 // This mask is used to check whether the left META meta key is pressed.
|
||||||
|
// KMMetaRightOn KeyMeta = 0x40000 // This mask is used to check whether the right META meta key is pressed.
|
||||||
|
// KMCapsLockOn KeyMeta = 0x100000 // This mask is used to check whether the CAPS LOCK meta key is on.
|
||||||
|
// KMNumLockOn KeyMeta = 0x200000 // This mask is used to check whether the NUM LOCK meta key is on.
|
||||||
|
// KMScrollLockOn KeyMeta = 0x400000 // This mask is used to check whether the SCROLL LOCK meta key is on.
|
||||||
|
// KMShiftMask = KMShiftOn | KMShiftLeftOn | KMShiftRightOn
|
||||||
|
// KMAltMask = KMAltOn | KMAltLeftOn | KMAltRightOn
|
||||||
|
// KMCtrlMask = KMCtrlOn | KMCtrlLeftOn | KMCtrlRightOn
|
||||||
|
// KMMetaMask = KMMetaOn | KMMetaLeftOn | KMMetaRightOn
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyFlag int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// KFWokeHere This mask is set if the device woke because of this key event.
|
||||||
|
// Deprecated
|
||||||
|
KFWokeHere KeyFlag = 0x1
|
||||||
|
|
||||||
|
// KFSoftKeyboard This mask is set if the key event was generated by a software keyboard.
|
||||||
|
KFSoftKeyboard KeyFlag = 0x2
|
||||||
|
|
||||||
|
// KFKeepTouchMode This mask is set if we don't want the key event to cause us to leave touch mode.
|
||||||
|
KFKeepTouchMode KeyFlag = 0x4
|
||||||
|
|
||||||
|
// KFFromSystem This mask is set if an event was known to come from a trusted part
|
||||||
|
// of the system. That is, the event is known to come from the user,
|
||||||
|
// and could not have been spoofed by a third party component.
|
||||||
|
KFFromSystem KeyFlag = 0x8
|
||||||
|
|
||||||
|
// KFEditorAction This mask is used for compatibility, to identify enter keys that are
|
||||||
|
// coming from an IME whose enter key has been auto-labelled "next" or
|
||||||
|
// "done". This allows TextView to dispatch these as normal enter keys
|
||||||
|
// for old applications, but still do the appropriate action when receiving them.
|
||||||
|
KFEditorAction KeyFlag = 0x10
|
||||||
|
|
||||||
|
// KFCanceled When associated with up key events, this indicates that the key press
|
||||||
|
// has been canceled. Typically this is used with virtual touch screen
|
||||||
|
// keys, where the user can slide from the virtual key area on to the
|
||||||
|
// display: in that case, the application will receive a canceled up
|
||||||
|
// event and should not perform the action normally associated with the
|
||||||
|
// key. Note that for this to work, the application can not perform an
|
||||||
|
// action for a key until it receives an up or the long press timeout has expired.
|
||||||
|
KFCanceled KeyFlag = 0x20
|
||||||
|
|
||||||
|
// KFVirtualHardKey This key event was generated by a virtual (on-screen) hard key area.
|
||||||
|
// Typically this is an area of the touchscreen, outside of the regular
|
||||||
|
// display, dedicated to "hardware" buttons.
|
||||||
|
KFVirtualHardKey KeyFlag = 0x40
|
||||||
|
|
||||||
|
// KFLongPress This flag is set for the first key repeat that occurs after the long press timeout.
|
||||||
|
KFLongPress KeyFlag = 0x80
|
||||||
|
|
||||||
|
// KFCanceledLongPress Set when a key event has `KFCanceled` set because a long
|
||||||
|
// press action was executed while it was down.
|
||||||
|
KFCanceledLongPress KeyFlag = 0x100
|
||||||
|
|
||||||
|
// KFTracking Set for `ACTION_UP` when this event's key code is still being
|
||||||
|
// tracked from its initial down. That is, somebody requested that tracking
|
||||||
|
// started on the key down and a long press has not caused
|
||||||
|
// the tracking to be canceled.
|
||||||
|
KFTracking KeyFlag = 0x200
|
||||||
|
|
||||||
|
// KFFallback Set when a key event has been synthesized to implement default behavior
|
||||||
|
// for an event that the application did not handle.
|
||||||
|
// Fallback key events are generated by unhandled trackball motions
|
||||||
|
// (to emulate a directional keypad) and by certain unhandled key presses
|
||||||
|
// that are declared in the key map (such as special function numeric keypad
|
||||||
|
// keys when numlock is off).
|
||||||
|
KFFallback KeyFlag = 0x400
|
||||||
|
|
||||||
|
// KFPredispatch Signifies that the key is being predispatched.
|
||||||
|
// KFPredispatch KeyFlag = 0x20000000
|
||||||
|
|
||||||
|
// KFStartTracking Private control to determine when an app is tracking a key sequence.
|
||||||
|
// KFStartTracking KeyFlag = 0x40000000
|
||||||
|
|
||||||
|
// KFTainted Private flag that indicates when the system has detected that this key event
|
||||||
|
// may be inconsistent with respect to the sequence of previously delivered key events,
|
||||||
|
// such as when a key up event is sent but the key was not down.
|
||||||
|
// KFTainted KeyFlag = 0x80000000
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyCode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ KeyCode = 0 // Unknown key code.
|
||||||
|
|
||||||
|
// KCSoftLeft Soft Left key
|
||||||
|
// Usually situated below the display on phones and used as a multi-function
|
||||||
|
// feature key for selecting a software defined function shown on the bottom left
|
||||||
|
// of the display.
|
||||||
|
KCSoftLeft KeyCode = 1
|
||||||
|
|
||||||
|
// KCSoftRight Soft Right key.
|
||||||
|
// Usually situated below the display on phones and used as a multi-function
|
||||||
|
// feature key for selecting a software defined function shown on the bottom right
|
||||||
|
// of the display.
|
||||||
|
KCSoftRight KeyCode = 2
|
||||||
|
|
||||||
|
// KCHome Home key.
|
||||||
|
// This key is handled by the framework and is never delivered to applications.
|
||||||
|
KCHome KeyCode = 3
|
||||||
|
|
||||||
|
KCBack KeyCode = 4 // Back key
|
||||||
|
KCCall KeyCode = 5 // Call key
|
||||||
|
KCEndCall KeyCode = 6 // End Call key
|
||||||
|
KC0 KeyCode = 7 // '0' key
|
||||||
|
KC1 KeyCode = 8 // '1' key
|
||||||
|
KC2 KeyCode = 9 // '2' key
|
||||||
|
KC3 KeyCode = 10 // '3' key
|
||||||
|
KC4 KeyCode = 11 // '4' key
|
||||||
|
KC5 KeyCode = 12 // '5' key
|
||||||
|
KC6 KeyCode = 13 // '6' key
|
||||||
|
KC7 KeyCode = 14 // '7' key
|
||||||
|
KC8 KeyCode = 15 // '8' key
|
||||||
|
KC9 KeyCode = 16 // '9' key
|
||||||
|
KCStar KeyCode = 17 // '*' key
|
||||||
|
KCPound KeyCode = 18 // '#' key
|
||||||
|
|
||||||
|
// KCDPadUp KeycodeDPadUp Directional Pad Up key.
|
||||||
|
// May also be synthesized from trackball motions.
|
||||||
|
KCDPadUp KeyCode = 19
|
||||||
|
|
||||||
|
// KCDPadDown Directional Pad Down key.
|
||||||
|
// May also be synthesized from trackball motions.
|
||||||
|
KCDPadDown KeyCode = 20
|
||||||
|
|
||||||
|
// KCDPadLeft Directional Pad Left key.
|
||||||
|
// May also be synthesized from trackball motions.
|
||||||
|
KCDPadLeft KeyCode = 21
|
||||||
|
|
||||||
|
// KCDPadRight Directional Pad Right key.
|
||||||
|
// May also be synthesized from trackball motions.
|
||||||
|
KCDPadRight KeyCode = 22
|
||||||
|
|
||||||
|
// KCDPadCenter Directional Pad Center key.
|
||||||
|
// May also be synthesized from trackball motions.
|
||||||
|
KCDPadCenter KeyCode = 23
|
||||||
|
|
||||||
|
// KCVolumeUp Volume Up key.
|
||||||
|
// Adjusts the speaker volume up.
|
||||||
|
KCVolumeUp KeyCode = 24
|
||||||
|
|
||||||
|
// KCVolumeDown Volume Down key.
|
||||||
|
// Adjusts the speaker volume down.
|
||||||
|
KCVolumeDown KeyCode = 25
|
||||||
|
|
||||||
|
// KCPower Power key.
|
||||||
|
KCPower KeyCode = 26
|
||||||
|
|
||||||
|
// KCCamera Camera key.
|
||||||
|
// Used to launch a camera application or take pictures.
|
||||||
|
KCCamera KeyCode = 27
|
||||||
|
|
||||||
|
KCClear KeyCode = 28 // Clear key
|
||||||
|
KCa KeyCode = 29 // 'a' key
|
||||||
|
KCb KeyCode = 30 // 'b' key
|
||||||
|
KCc KeyCode = 31 // 'c' key
|
||||||
|
KCd KeyCode = 32 // 'd' key
|
||||||
|
KCe KeyCode = 33 // 'e' key
|
||||||
|
KCf KeyCode = 34 // 'f' key
|
||||||
|
KCg KeyCode = 35 // 'g' key
|
||||||
|
KCh KeyCode = 36 // 'h' key
|
||||||
|
KCi KeyCode = 37 // 'i' key
|
||||||
|
KCj KeyCode = 38 // 'j' key
|
||||||
|
KCk KeyCode = 39 // 'k' key
|
||||||
|
KCl KeyCode = 40 // 'l' key
|
||||||
|
KCm KeyCode = 41 // 'm' key
|
||||||
|
KCn KeyCode = 42 // 'n' key
|
||||||
|
KCo KeyCode = 43 // 'o' key
|
||||||
|
KCp KeyCode = 44 // 'p' key
|
||||||
|
KCq KeyCode = 45 // 'q' key
|
||||||
|
KCr KeyCode = 46 // 'r' key
|
||||||
|
KCs KeyCode = 47 // 's' key
|
||||||
|
KCt KeyCode = 48 // 't' key
|
||||||
|
KCu KeyCode = 49 // 'u' key
|
||||||
|
KCv KeyCode = 50 // 'v' key
|
||||||
|
KCw KeyCode = 51 // 'w' key
|
||||||
|
KCx KeyCode = 52 // 'x' key
|
||||||
|
KCy KeyCode = 53 // 'y' key
|
||||||
|
KCz KeyCode = 54 // 'z' key
|
||||||
|
KCComma KeyCode = 55 // ',' key
|
||||||
|
KCPeriod KeyCode = 56 // '.' key
|
||||||
|
KCAltLeft KeyCode = 57 // Left Alt modifier key
|
||||||
|
KCAltRight KeyCode = 58 // Right Alt modifier key
|
||||||
|
KCShiftLeft KeyCode = 59 // Left Shift modifier key
|
||||||
|
KCShiftRight KeyCode = 60 // Right Shift modifier key
|
||||||
|
KCTab KeyCode = 61 // Tab key
|
||||||
|
KCSpace KeyCode = 62 // Space key
|
||||||
|
|
||||||
|
// KCSym Symbol modifier key.
|
||||||
|
// Used to enter alternate symbols.
|
||||||
|
KCSym KeyCode = 63
|
||||||
|
|
||||||
|
// KCExplorer Explorer special function key.
|
||||||
|
// Used to launch a browser application.
|
||||||
|
KCExplorer KeyCode = 64
|
||||||
|
|
||||||
|
// KCEnvelope Envelope special function key.
|
||||||
|
// Used to launch a mail application.
|
||||||
|
KCEnvelope KeyCode = 65
|
||||||
|
|
||||||
|
// KCEnter Enter key.
|
||||||
|
KCEnter KeyCode = 66
|
||||||
|
|
||||||
|
// KCDel Backspace key.
|
||||||
|
// Deletes characters before the insertion point, unlike `KCForwardDel`.
|
||||||
|
KCDel KeyCode = 67
|
||||||
|
|
||||||
|
KCGrave KeyCode = 68 // '`' (backtick) key
|
||||||
|
KCMinus KeyCode = 69 // '-'
|
||||||
|
KCEquals KeyCode = 70 // '=' key
|
||||||
|
KCLeftBracket KeyCode = 71 // '[' key
|
||||||
|
KCRightBracket KeyCode = 72 // ']' key
|
||||||
|
KCBackslash KeyCode = 73 // '\' key
|
||||||
|
KCSemicolon KeyCode = 74 // '' key
|
||||||
|
KCApostrophe KeyCode = 75 // ''' (apostrophe) key
|
||||||
|
KCSlash KeyCode = 76 // '/' key
|
||||||
|
KCAt KeyCode = 77 // '@' key
|
||||||
|
|
||||||
|
// KCNum Number modifier key.
|
||||||
|
// Used to enter numeric symbols.
|
||||||
|
// This key is not Num Lock; it is more like `KCAltLeft` and is
|
||||||
|
// interpreted as an ALT key by {@link android.text.method.MetaKeyKeyListener}.
|
||||||
|
KCNum KeyCode = 78
|
||||||
|
|
||||||
|
// KCHeadsetHook Headset Hook key.
|
||||||
|
// Used to hang up calls and stop media.
|
||||||
|
KCHeadsetHook KeyCode = 79
|
||||||
|
|
||||||
|
// KCFocus Camera Focus key.
|
||||||
|
// Used to focus the camera.
|
||||||
|
// *Camera* focus
|
||||||
|
KCFocus KeyCode = 80
|
||||||
|
|
||||||
|
KCPlus KeyCode = 81 // '+' key.
|
||||||
|
KCMenu KeyCode = 82 // Menu key.
|
||||||
|
KCNotification KeyCode = 83 // Notification key.
|
||||||
|
KCSearch KeyCode = 84 // Search key.
|
||||||
|
KCMediaPlayPause KeyCode = 85 // Play/Pause media key.
|
||||||
|
KCMediaStop KeyCode = 86 // Stop media key.
|
||||||
|
KCMediaNext KeyCode = 87 // Play Next media key.
|
||||||
|
KCMediaPrevious KeyCode = 88 // Play Previous media key.
|
||||||
|
KCMediaRewind KeyCode = 89 // Rewind media key.
|
||||||
|
KCMediaFastForward KeyCode = 90 // Fast Forward media key.
|
||||||
|
|
||||||
|
// KCMute Mute key.
|
||||||
|
// Mutes the microphone, unlike `KCVolumeMute`
|
||||||
|
KCMute KeyCode = 91
|
||||||
|
|
||||||
|
// KCPageUp Page Up key.
|
||||||
|
KCPageUp KeyCode = 92
|
||||||
|
|
||||||
|
// KCPageDown Page Down key.
|
||||||
|
KCPageDown KeyCode = 93
|
||||||
|
|
||||||
|
// KCPictSymbols Picture Symbols modifier key.
|
||||||
|
// Used to switch symbol sets (Emoji, Kao-moji).
|
||||||
|
// switch symbol-sets (Emoji,Kao-moji)
|
||||||
|
KCPictSymbols KeyCode = 94
|
||||||
|
|
||||||
|
// KCSwitchCharset Switch Charset modifier key.
|
||||||
|
// Used to switch character sets (Kanji, Katakana).
|
||||||
|
// switch char-sets (Kanji,Katakana)
|
||||||
|
KCSwitchCharset KeyCode = 95
|
||||||
|
|
||||||
|
// KCButtonA A Button key.
|
||||||
|
// On a game controller, the A button should be either the button labeled A
|
||||||
|
// or the first button on the bottom row of controller buttons.
|
||||||
|
KCButtonA KeyCode = 96
|
||||||
|
|
||||||
|
// KCButtonB B Button key.
|
||||||
|
// On a game controller, the B button should be either the button labeled B
|
||||||
|
// or the second button on the bottom row of controller buttons.
|
||||||
|
KCButtonB KeyCode = 97
|
||||||
|
|
||||||
|
// KCButtonC C Button key.
|
||||||
|
// On a game controller, the C button should be either the button labeled C
|
||||||
|
// or the third button on the bottom row of controller buttons.
|
||||||
|
KCButtonC KeyCode = 98
|
||||||
|
|
||||||
|
// KCButtonX X Button key.
|
||||||
|
// On a game controller, the X button should be either the button labeled X
|
||||||
|
// or the first button on the upper row of controller buttons.
|
||||||
|
KCButtonX KeyCode = 99
|
||||||
|
|
||||||
|
// KCButtonY Y Button key.
|
||||||
|
// On a game controller, the Y button should be either the button labeled Y
|
||||||
|
// or the second button on the upper row of controller buttons.
|
||||||
|
KCButtonY KeyCode = 100
|
||||||
|
|
||||||
|
// KCButtonZ Z Button key.
|
||||||
|
// On a game controller, the Z button should be either the button labeled Z
|
||||||
|
// or the third button on the upper row of controller buttons.
|
||||||
|
KCButtonZ KeyCode = 101
|
||||||
|
|
||||||
|
// KCButtonL1 L1 Button key.
|
||||||
|
// On a game controller, the L1 button should be either the button labeled L1 (or L)
|
||||||
|
// or the top left trigger button.
|
||||||
|
KCButtonL1 KeyCode = 102
|
||||||
|
|
||||||
|
// KCButtonR1 R1 Button key.
|
||||||
|
// On a game controller, the R1 button should be either the button labeled R1 (or R)
|
||||||
|
// or the top right trigger button.
|
||||||
|
KCButtonR1 KeyCode = 103
|
||||||
|
|
||||||
|
// KCButtonL2 L2 Button key.
|
||||||
|
// On a game controller, the L2 button should be either the button labeled L2
|
||||||
|
// or the bottom left trigger button.
|
||||||
|
KCButtonL2 KeyCode = 104
|
||||||
|
|
||||||
|
// KCButtonR2 R2 Button key.
|
||||||
|
// On a game controller, the R2 button should be either the button labeled R2
|
||||||
|
// or the bottom right trigger button.
|
||||||
|
KCButtonR2 KeyCode = 105
|
||||||
|
|
||||||
|
// KCButtonTHUMBL Left Thumb Button key.
|
||||||
|
// On a game controller, the left thumb button indicates that the left (or only)
|
||||||
|
// joystick is pressed.
|
||||||
|
KCButtonTHUMBL KeyCode = 106
|
||||||
|
|
||||||
|
// KCButtonTHUMBR Right Thumb Button key.
|
||||||
|
// On a game controller, the right thumb button indicates that the right
|
||||||
|
// joystick is pressed.
|
||||||
|
KCButtonTHUMBR KeyCode = 107
|
||||||
|
|
||||||
|
// KCButtonStart Start Button key.
|
||||||
|
// On a game controller, the button labeled Start.
|
||||||
|
KCButtonStart KeyCode = 108
|
||||||
|
|
||||||
|
// KCButtonSelect Select Button key.
|
||||||
|
// On a game controller, the button labeled Select.
|
||||||
|
KCButtonSelect KeyCode = 109
|
||||||
|
|
||||||
|
// KCButtonMode Mode Button key.
|
||||||
|
// On a game controller, the button labeled Mode.
|
||||||
|
KCButtonMode KeyCode = 110
|
||||||
|
|
||||||
|
// KCEscape Escape key.
|
||||||
|
KCEscape KeyCode = 111
|
||||||
|
|
||||||
|
// KCForwardDel Forward Delete key.
|
||||||
|
// Deletes characters ahead of the insertion point, unlike `KCDel`.
|
||||||
|
KCForwardDel KeyCode = 112
|
||||||
|
|
||||||
|
KCCtrlLeft KeyCode = 113 // Left Control modifier key
|
||||||
|
KCCtrlRight KeyCode = 114 // Right Control modifier key
|
||||||
|
KCCapsLock KeyCode = 115 // Caps Lock key
|
||||||
|
KCScrollLock KeyCode = 116 // Scroll Lock key
|
||||||
|
KCMetaLeft KeyCode = 117 // Left Meta modifier key
|
||||||
|
KCMetaRight KeyCode = 118 // Right Meta modifier key
|
||||||
|
KCFunction KeyCode = 119 // Function modifier key
|
||||||
|
KCSysRq KeyCode = 120 // System Request / Print Screen key
|
||||||
|
KCBreak KeyCode = 121 // Break / Pause key
|
||||||
|
|
||||||
|
// KCMoveHome Home Movement key.
|
||||||
|
// Used for scrolling or moving the cursor around to the start of a line
|
||||||
|
// or to the top of a list.
|
||||||
|
KCMoveHome KeyCode = 122
|
||||||
|
|
||||||
|
// KCMoveEnd End Movement key.
|
||||||
|
// Used for scrolling or moving the cursor around to the end of a line
|
||||||
|
// or to the bottom of a list.
|
||||||
|
KCMoveEnd KeyCode = 123
|
||||||
|
|
||||||
|
// KCInsert Insert key.
|
||||||
|
// Toggles insert / overwrite edit mode.
|
||||||
|
KCInsert KeyCode = 124
|
||||||
|
|
||||||
|
// KCForward Forward key.
|
||||||
|
// Navigates forward in the history stack. Complement of `KCBack`.
|
||||||
|
KCForward KeyCode = 125
|
||||||
|
|
||||||
|
// KCMediaPlay Play media key.
|
||||||
|
KCMediaPlay KeyCode = 126
|
||||||
|
|
||||||
|
// KCMediaPause Pause media key.
|
||||||
|
KCMediaPause KeyCode = 127
|
||||||
|
|
||||||
|
// KCMediaClose Close media key.
|
||||||
|
// May be used to close a CD tray, for example.
|
||||||
|
KCMediaClose KeyCode = 128
|
||||||
|
|
||||||
|
// KCMediaEject Eject media key.
|
||||||
|
// May be used to eject a CD tray, for example.
|
||||||
|
KCMediaEject KeyCode = 129
|
||||||
|
|
||||||
|
// KCMediaRecord Record media key.
|
||||||
|
KCMediaRecord KeyCode = 130
|
||||||
|
|
||||||
|
KCF1 KeyCode = 131 // F1 key.
|
||||||
|
KCF2 KeyCode = 132 // F2 key.
|
||||||
|
KCF3 KeyCode = 133 // F3 key.
|
||||||
|
KCF4 KeyCode = 134 // F4 key.
|
||||||
|
KCF5 KeyCode = 135 // F5 key.
|
||||||
|
KCF6 KeyCode = 136 // F6 key.
|
||||||
|
KCF7 KeyCode = 137 // F7 key.
|
||||||
|
KCF8 KeyCode = 138 // F8 key.
|
||||||
|
KCF9 KeyCode = 139 // F9 key.
|
||||||
|
KCF10 KeyCode = 140 // F10 key.
|
||||||
|
KCF11 KeyCode = 141 // F11 key.
|
||||||
|
KCF12 KeyCode = 142 // F12 key.
|
||||||
|
|
||||||
|
// KCNumLock Num Lock key.
|
||||||
|
// This is the Num Lock key; it is different from `KCNum`.
|
||||||
|
// This key alters the behavior of other keys on the numeric keypad.
|
||||||
|
KCNumLock KeyCode = 143
|
||||||
|
|
||||||
|
KCNumpad0 KeyCode = 144 // Numeric keypad '0' key
|
||||||
|
KCNumpad1 KeyCode = 145 // Numeric keypad '1' key
|
||||||
|
KCNumpad2 KeyCode = 146 // Numeric keypad '2' key
|
||||||
|
KCNumpad3 KeyCode = 147 // Numeric keypad '3' key
|
||||||
|
KCNumpad4 KeyCode = 148 // Numeric keypad '4' key
|
||||||
|
KCNumpad5 KeyCode = 149 // Numeric keypad '5' key
|
||||||
|
KCNumpad6 KeyCode = 150 // Numeric keypad '6' key
|
||||||
|
KCNumpad7 KeyCode = 151 // Numeric keypad '7' key
|
||||||
|
KCNumpad8 KeyCode = 152 // Numeric keypad '8' key
|
||||||
|
KCNumpad9 KeyCode = 153 // Numeric keypad '9' key
|
||||||
|
KCNumpadDivide KeyCode = 154 // Numeric keypad '/' key (for division)
|
||||||
|
KCNumpadMultiply KeyCode = 155 // Numeric keypad '*' key (for multiplication)
|
||||||
|
KCNumpadSubtract KeyCode = 156 // Numeric keypad '-' key (for subtraction)
|
||||||
|
KCNumpadAdd KeyCode = 157 // Numeric keypad '+' key (for addition)
|
||||||
|
KCNumpadDot KeyCode = 158 // Numeric keypad '.' key (for decimals or digit grouping)
|
||||||
|
KCNumpadComma KeyCode = 159 // Numeric keypad ',' key (for decimals or digit grouping)
|
||||||
|
KCNumpadEnter KeyCode = 160 // Numeric keypad Enter key
|
||||||
|
KCNumpadEquals KeyCode = 161 // Numeric keypad 'KeyCode =' key
|
||||||
|
KCNumpadLeftParen KeyCode = 162 // Numeric keypad '(' key
|
||||||
|
KCNumpadRightParen KeyCode = 163 // Numeric keypad ')' key
|
||||||
|
|
||||||
|
// KCVolumeMute Volume Mute key.
|
||||||
|
// Mutes the speaker, unlike `KCMute`.
|
||||||
|
// This key should normally be implemented as a toggle such that the first press
|
||||||
|
// mutes the speaker and the second press restores the original volume.
|
||||||
|
KCVolumeMute KeyCode = 164
|
||||||
|
|
||||||
|
// KCInfo Info key.
|
||||||
|
// Common on TV remotes to show additional information related to what is
|
||||||
|
// currently being viewed.
|
||||||
|
KCInfo KeyCode = 165
|
||||||
|
|
||||||
|
// KCChannelUp Channel up key.
|
||||||
|
// On TV remotes, increments the television channel.
|
||||||
|
KCChannelUp KeyCode = 166
|
||||||
|
|
||||||
|
// KCChannelDown Channel down key.
|
||||||
|
// On TV remotes, decrements the television channel.
|
||||||
|
KCChannelDown KeyCode = 167
|
||||||
|
|
||||||
|
// KCZoomIn Zoom in key.
|
||||||
|
KCZoomIn KeyCode = 168
|
||||||
|
|
||||||
|
// KCZoomOut Zoom out key.
|
||||||
|
KCZoomOut KeyCode = 169
|
||||||
|
|
||||||
|
// KCTv TV key.
|
||||||
|
// On TV remotes, switches to viewing live TV.
|
||||||
|
KCTv KeyCode = 170
|
||||||
|
|
||||||
|
// KCWindow Window key.
|
||||||
|
// On TV remotes, toggles picture-in-picture mode or other windowing functions.
|
||||||
|
// On Android Wear devices, triggers a display offset.
|
||||||
|
KCWindow KeyCode = 171
|
||||||
|
|
||||||
|
// KCGuide Guide key.
|
||||||
|
// On TV remotes, shows a programming guide.
|
||||||
|
KCGuide KeyCode = 172
|
||||||
|
|
||||||
|
// KCDvr DVR key.
|
||||||
|
// On some TV remotes, switches to a DVR mode for recorded shows.
|
||||||
|
KCDvr KeyCode = 173
|
||||||
|
|
||||||
|
// KCBookmark Bookmark key.
|
||||||
|
// On some TV remotes, bookmarks content or web pages.
|
||||||
|
KCBookmark KeyCode = 174
|
||||||
|
|
||||||
|
// KCCaptions Toggle captions key.
|
||||||
|
// Switches the mode for closed-captioning text, for example during television shows.
|
||||||
|
KCCaptions KeyCode = 175
|
||||||
|
|
||||||
|
// KCSettings Settings key.
|
||||||
|
// Starts the system settings activity.
|
||||||
|
KCSettings KeyCode = 176
|
||||||
|
|
||||||
|
// KCTvPower TV power key.
|
||||||
|
// On TV remotes, toggles the power on a television screen.
|
||||||
|
KCTvPower KeyCode = 177
|
||||||
|
|
||||||
|
// KCTvInput TV input key.
|
||||||
|
// On TV remotes, switches the input on a television screen.
|
||||||
|
KCTvInput KeyCode = 178
|
||||||
|
|
||||||
|
// KCStbPower Set-top-box power key.
|
||||||
|
// On TV remotes, toggles the power on an external Set-top-box.
|
||||||
|
KCStbPower KeyCode = 179
|
||||||
|
|
||||||
|
// KCStbInput Set-top-box input key.
|
||||||
|
// On TV remotes, switches the input mode on an external Set-top-box.
|
||||||
|
KCStbInput KeyCode = 180
|
||||||
|
|
||||||
|
// KCAvrPower A/V Receiver power key.
|
||||||
|
// On TV remotes, toggles the power on an external A/V Receiver.
|
||||||
|
KCAvrPower KeyCode = 181
|
||||||
|
|
||||||
|
// KCAvrInput A/V Receiver input key.
|
||||||
|
// On TV remotes, switches the input mode on an external A/V Receiver.
|
||||||
|
KCAvrInput KeyCode = 182
|
||||||
|
|
||||||
|
// KCProgRed Red "programmable" key.
|
||||||
|
// On TV remotes, acts as a contextual/programmable key.
|
||||||
|
KCProgRed KeyCode = 183
|
||||||
|
|
||||||
|
// KCProgGreen Green "programmable" key.
|
||||||
|
// On TV remotes, actsas a contextual/programmable key.
|
||||||
|
KCProgGreen KeyCode = 184
|
||||||
|
|
||||||
|
// KCProgYellow Yellow "programmable" key.
|
||||||
|
// On TV remotes, acts as a contextual/programmable key.
|
||||||
|
KCProgYellow KeyCode = 185
|
||||||
|
|
||||||
|
// KCProgBlue Blue "programmable" key.
|
||||||
|
// On TV remotes, acts as a contextual/programmable key.
|
||||||
|
KCProgBlue KeyCode = 186
|
||||||
|
|
||||||
|
// KCAppSwitch App switch key.
|
||||||
|
// Should bring up the application switcher dialog.
|
||||||
|
KCAppSwitch KeyCode = 187
|
||||||
|
|
||||||
|
KCButton1 KeyCode = 188 // Generic Game Pad Button #1
|
||||||
|
KCButton2 KeyCode = 189 // Generic Game Pad Button #2
|
||||||
|
KCButton3 KeyCode = 190 // Generic Game Pad Button #3
|
||||||
|
KCButton4 KeyCode = 191 // Generic Game Pad Button #4
|
||||||
|
KCButton5 KeyCode = 192 // Generic Game Pad Button #5
|
||||||
|
KCButton6 KeyCode = 193 // Generic Game Pad Button #6
|
||||||
|
KCButton7 KeyCode = 194 // Generic Game Pad Button #7
|
||||||
|
KCButton8 KeyCode = 195 // Generic Game Pad Button #8
|
||||||
|
KCButton9 KeyCode = 196 // Generic Game Pad Button #9
|
||||||
|
KCButton10 KeyCode = 197 // Generic Game Pad Button #10
|
||||||
|
KCButton11 KeyCode = 198 // Generic Game Pad Button #11
|
||||||
|
KCButton12 KeyCode = 199 // Generic Game Pad Button #12
|
||||||
|
KCButton13 KeyCode = 200 // Generic Game Pad Button #13
|
||||||
|
KCButton14 KeyCode = 201 // Generic Game Pad Button #14
|
||||||
|
KCButton15 KeyCode = 202 // Generic Game Pad Button #15
|
||||||
|
KCButton16 KeyCode = 203 // Generic Game Pad Button #16
|
||||||
|
|
||||||
|
// KCLanguageSwitch Language Switch key.
|
||||||
|
// Toggles the current input language such as switching between English and Japanese on
|
||||||
|
// a QWERTY keyboard. On some devices, the same function may be performed by
|
||||||
|
// pressing Shift+Spacebar.
|
||||||
|
KCLanguageSwitch KeyCode = 204
|
||||||
|
|
||||||
|
// Manner Mode key.
|
||||||
|
// Toggles silent or vibrate mode on and off to make the device behave more politely
|
||||||
|
// in certain settings such as on a crowded train. On some devices, the key may only
|
||||||
|
// operate when long-pressed.
|
||||||
|
KCMannerMode KeyCode = 205
|
||||||
|
|
||||||
|
// 3D Mode key.
|
||||||
|
// Toggles the display between 2D and 3D mode.
|
||||||
|
KC3dMode KeyCode = 206
|
||||||
|
|
||||||
|
// Contacts special function key.
|
||||||
|
// Used to launch an address book application.
|
||||||
|
KCContacts KeyCode = 207
|
||||||
|
|
||||||
|
// Calendar special function key.
|
||||||
|
// Used to launch a calendar application.
|
||||||
|
KCCalendar KeyCode = 208
|
||||||
|
|
||||||
|
// Music special function key.
|
||||||
|
// Used to launch a music player application.
|
||||||
|
KCMusic KeyCode = 209
|
||||||
|
|
||||||
|
// Calculator special function key.
|
||||||
|
// Used to launch a calculator application.
|
||||||
|
KCCalculator KeyCode = 210
|
||||||
|
|
||||||
|
// Japanese full-width / half-width key.
|
||||||
|
KCZenkakuHankaku KeyCode = 211
|
||||||
|
|
||||||
|
// Japanese alphanumeric key.
|
||||||
|
KCEisu KeyCode = 212
|
||||||
|
|
||||||
|
// Japanese non-conversion key.
|
||||||
|
KCMuhenkan KeyCode = 213
|
||||||
|
|
||||||
|
// Japanese conversion key.
|
||||||
|
KCHenkan KeyCode = 214
|
||||||
|
|
||||||
|
// Japanese katakana / hiragana key.
|
||||||
|
KCKatakanaHiragana KeyCode = 215
|
||||||
|
|
||||||
|
// Japanese Yen key.
|
||||||
|
KCYen KeyCode = 216
|
||||||
|
|
||||||
|
// Japanese Ro key.
|
||||||
|
KCRo KeyCode = 217
|
||||||
|
|
||||||
|
// Japanese kana key.
|
||||||
|
KCKana KeyCode = 218
|
||||||
|
|
||||||
|
// Assist key.
|
||||||
|
// Launches the global assist activity. Not delivered to applications.
|
||||||
|
KCAssist KeyCode = 219
|
||||||
|
|
||||||
|
// Brightness Down key.
|
||||||
|
// Adjusts the screen brightness down.
|
||||||
|
KCBrightnessDown KeyCode = 220
|
||||||
|
|
||||||
|
// Brightness Up key.
|
||||||
|
// Adjusts the screen brightness up.
|
||||||
|
KCBrightnessUp KeyCode = 221
|
||||||
|
|
||||||
|
// Audio Track key.
|
||||||
|
// Switches the audio tracks.
|
||||||
|
KCMediaAudioTrack KeyCode = 222
|
||||||
|
|
||||||
|
// Sleep key.
|
||||||
|
// Puts the device to sleep. Behaves somewhat like {@link #KEYCODE_POWER} but it
|
||||||
|
// has no effect if the device is already asleep.
|
||||||
|
KCSleep KeyCode = 223
|
||||||
|
|
||||||
|
// Wakeup key.
|
||||||
|
// Wakes up the device. Behaves somewhat like {@link #KEYCODE_POWER} but it
|
||||||
|
// has no effect if the device is already awake.
|
||||||
|
KCWakeup KeyCode = 224
|
||||||
|
|
||||||
|
// Pairing key.
|
||||||
|
// Initiates peripheral pairing mode. Useful for pairing remote control
|
||||||
|
// devices or game controllers, especially if no other input mode is
|
||||||
|
// available.
|
||||||
|
KCPairing KeyCode = 225
|
||||||
|
|
||||||
|
// Media Top Menu key.
|
||||||
|
// Goes to the top of media menu.
|
||||||
|
KCMediaTopMenu KeyCode = 226
|
||||||
|
|
||||||
|
// '11' key.
|
||||||
|
KC11 KeyCode = 227
|
||||||
|
|
||||||
|
// '12' key.
|
||||||
|
KC12 KeyCode = 228
|
||||||
|
|
||||||
|
// Last Channel key.
|
||||||
|
// Goes to the last viewed channel.
|
||||||
|
KCLastChannel KeyCode = 229
|
||||||
|
|
||||||
|
// TV data service key.
|
||||||
|
// Displays data services like weather, sports.
|
||||||
|
KCTvDataService KeyCode = 230
|
||||||
|
|
||||||
|
// Voice Assist key.
|
||||||
|
// Launches the global voice assist activity. Not delivered to applications.
|
||||||
|
KCVoiceAssist KeyCode = 231
|
||||||
|
|
||||||
|
// Radio key.
|
||||||
|
// Toggles TV service / Radio service.
|
||||||
|
KCTvRadioService KeyCode = 232
|
||||||
|
|
||||||
|
// Teletext key.
|
||||||
|
// Displays Teletext service.
|
||||||
|
KCTvTeletext KeyCode = 233
|
||||||
|
|
||||||
|
// Number entry key.
|
||||||
|
// Initiates to enter multi-digit channel nubmber when each digit key is assigned
|
||||||
|
// for selecting separate channel. Corresponds to Number Entry Mode (0x1D) of CEC
|
||||||
|
// User Control Code.
|
||||||
|
KCTvNumberEntry KeyCode = 234
|
||||||
|
|
||||||
|
// Analog Terrestrial key.
|
||||||
|
// Switches to analog terrestrial broadcast service.
|
||||||
|
KCTvTerrestrialAnalog KeyCode = 235
|
||||||
|
|
||||||
|
// Digital Terrestrial key.
|
||||||
|
// Switches to digital terrestrial broadcast service.
|
||||||
|
KCTvTerrestrialDigital KeyCode = 236
|
||||||
|
|
||||||
|
// Satellite key.
|
||||||
|
// Switches to digital satellite broadcast service.
|
||||||
|
KCTvSatellite KeyCode = 237
|
||||||
|
|
||||||
|
// BS key.
|
||||||
|
// Switches to BS digital satellite broadcasting service available in Japan.
|
||||||
|
KCTvSatelliteBs KeyCode = 238
|
||||||
|
|
||||||
|
// CS key.
|
||||||
|
// Switches to CS digital satellite broadcasting service available in Japan.
|
||||||
|
KCTvSatelliteCs KeyCode = 239
|
||||||
|
|
||||||
|
// BS/CS key.
|
||||||
|
// Toggles between BS and CS digital satellite services.
|
||||||
|
KCTvSatelliteService KeyCode = 240
|
||||||
|
|
||||||
|
// Toggle Network key.
|
||||||
|
// Toggles selecting broacast services.
|
||||||
|
KCTvNetwork KeyCode = 241
|
||||||
|
|
||||||
|
// Antenna/Cable key.
|
||||||
|
// Toggles broadcast input source between antenna and cable.
|
||||||
|
KCTvAntennaCable KeyCode = 242
|
||||||
|
|
||||||
|
// HDMI #1 key.
|
||||||
|
// Switches to HDMI input #1.
|
||||||
|
KCTvInputHdmi1 KeyCode = 243
|
||||||
|
|
||||||
|
// HDMI #2 key.
|
||||||
|
// Switches to HDMI input #2.
|
||||||
|
KCTvInputHdmi2 KeyCode = 244
|
||||||
|
|
||||||
|
// HDMI #3 key.
|
||||||
|
// Switches to HDMI input #3.
|
||||||
|
KCTvInputHdmi3 KeyCode = 245
|
||||||
|
|
||||||
|
// HDMI #4 key.
|
||||||
|
// Switches to HDMI input #4.
|
||||||
|
KCTvInputHdmi4 KeyCode = 246
|
||||||
|
|
||||||
|
// Composite #1 key.
|
||||||
|
// Switches to composite video input #1.
|
||||||
|
KCTvInputComposite1 KeyCode = 247
|
||||||
|
|
||||||
|
// Composite #2 key.
|
||||||
|
// Switches to composite video input #2.
|
||||||
|
KCTvInputComposite2 KeyCode = 248
|
||||||
|
|
||||||
|
// Component #1 key.
|
||||||
|
// Switches to component video input #1.
|
||||||
|
KCTvInputComponent1 KeyCode = 249
|
||||||
|
|
||||||
|
// Component #2 key.
|
||||||
|
// Switches to component video input #2.
|
||||||
|
KCTvInputComponent2 KeyCode = 250
|
||||||
|
|
||||||
|
// VGA #1 key.
|
||||||
|
// Switches to VGA (analog RGB) input #1.
|
||||||
|
KCTvInputVga1 KeyCode = 251
|
||||||
|
|
||||||
|
// Audio description key.
|
||||||
|
// Toggles audio description off / on.
|
||||||
|
KCTvAudioDescription KeyCode = 252
|
||||||
|
|
||||||
|
// Audio description mixing volume up key.
|
||||||
|
// Louden audio description volume as compared with normal audio volume.
|
||||||
|
KCTvAudioDescriptionMixUp KeyCode = 253
|
||||||
|
|
||||||
|
// Audio description mixing volume down key.
|
||||||
|
// Lessen audio description volume as compared with normal audio volume.
|
||||||
|
KCTvAudioDescriptionMixDown KeyCode = 254
|
||||||
|
|
||||||
|
// Zoom mode key.
|
||||||
|
// Changes Zoom mode (Normal, Full, Zoom, Wide-zoom, etc.)
|
||||||
|
KCTvZoomMode KeyCode = 255
|
||||||
|
|
||||||
|
// Contents menu key.
|
||||||
|
// Goes to the title list. Corresponds to Contents Menu (0x0B) of CEC User Control
|
||||||
|
// Code
|
||||||
|
KCTvContentsMenu KeyCode = 256
|
||||||
|
|
||||||
|
// Media context menu key.
|
||||||
|
// Goes to the context menu of media contents. Corresponds to Media Context-sensitive
|
||||||
|
// Menu (0x11) of CEC User Control Code.
|
||||||
|
KCTvMediaContextMenu KeyCode = 257
|
||||||
|
|
||||||
|
// Timer programming key.
|
||||||
|
// Goes to the timer recording menu. Corresponds to Timer Programming (0x54) of
|
||||||
|
// CEC User Control Code.
|
||||||
|
KCTvTimerProgramming KeyCode = 258
|
||||||
|
|
||||||
|
// Help key.
|
||||||
|
KCHelp KeyCode = 259
|
||||||
|
|
||||||
|
// Navigate to previous key.
|
||||||
|
// Goes backward by one item in an ordered collection of items.
|
||||||
|
KCNavigatePrevious KeyCode = 260
|
||||||
|
|
||||||
|
// Navigate to next key.
|
||||||
|
// Advances to the next item in an ordered collection of items.
|
||||||
|
KCNavigateNext KeyCode = 261
|
||||||
|
|
||||||
|
// Navigate in key.
|
||||||
|
// Activates the item that currently has focus or expands to the next level of a navigation
|
||||||
|
// hierarchy.
|
||||||
|
KCNavigateIn KeyCode = 262
|
||||||
|
|
||||||
|
// Navigate out key.
|
||||||
|
// Backs out one level of a navigation hierarchy or collapses the item that currently has
|
||||||
|
// focus.
|
||||||
|
KCNavigateOut KeyCode = 263
|
||||||
|
|
||||||
|
// Primary stem key for Wear
|
||||||
|
// Main power/reset button on watch.
|
||||||
|
KCStemPrimary KeyCode = 264
|
||||||
|
|
||||||
|
// Generic stem key 1 for Wear
|
||||||
|
KCStem1 KeyCode = 265
|
||||||
|
|
||||||
|
// Generic stem key 2 for Wear
|
||||||
|
KCStem2 KeyCode = 266
|
||||||
|
|
||||||
|
// Generic stem key 3 for Wear
|
||||||
|
KCStem3 KeyCode = 267
|
||||||
|
|
||||||
|
// Directional Pad Up-Left
|
||||||
|
KCDPadUpLeft KeyCode = 268
|
||||||
|
|
||||||
|
// Directional Pad Down-Left
|
||||||
|
KCDPadDownLeft KeyCode = 269
|
||||||
|
|
||||||
|
// Directional Pad Up-Right
|
||||||
|
KCDPadUpRight KeyCode = 270
|
||||||
|
|
||||||
|
// Directional Pad Down-Right
|
||||||
|
KCDPadDownRight KeyCode = 271
|
||||||
|
|
||||||
|
// Skip forward media key.
|
||||||
|
KCMediaSkipForward KeyCode = 272
|
||||||
|
|
||||||
|
// Skip backward media key.
|
||||||
|
KCMediaSkipBackward KeyCode = 273
|
||||||
|
|
||||||
|
// Step forward media key.
|
||||||
|
// Steps media forward, one frame at a time.
|
||||||
|
KCMediaStepForward KeyCode = 274
|
||||||
|
|
||||||
|
// Step backward media key.
|
||||||
|
// Steps media backward, one frame at a time.
|
||||||
|
KCMediaStepBackward KeyCode = 275
|
||||||
|
|
||||||
|
// put device to sleep unless a wakelock is held.
|
||||||
|
KCSoftSleep KeyCode = 276
|
||||||
|
|
||||||
|
// Cut key.
|
||||||
|
KCCut KeyCode = 277
|
||||||
|
|
||||||
|
// Copy key.
|
||||||
|
KCCopy KeyCode = 278
|
||||||
|
|
||||||
|
// Paste key.
|
||||||
|
KCPaste KeyCode = 279
|
||||||
|
|
||||||
|
// Consumed by the system for navigation up
|
||||||
|
KCSystemNavigationUp KeyCode = 280
|
||||||
|
|
||||||
|
// Consumed by the system for navigation down
|
||||||
|
KCSystemNavigationDown KeyCode = 281
|
||||||
|
|
||||||
|
// Consumed by the system for navigation left*/
|
||||||
|
KCSystemNavigationLeft KeyCode = 282
|
||||||
|
|
||||||
|
// Consumed by the system for navigation right
|
||||||
|
KCSystemNavigationRight KeyCode = 283
|
||||||
|
|
||||||
|
// Show all apps
|
||||||
|
KCAllApps KeyCode = 284
|
||||||
|
|
||||||
|
// Refresh key.
|
||||||
|
KCRefresh KeyCode = 285
|
||||||
|
)
|
||||||
1384
hrp/internal/uixt/android_test.go
Normal file
1384
hrp/internal/uixt/android_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
|||||||
package uixt
|
|
||||||
|
|
||||||
type uiaWebDriver struct{}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
package uixt
|
|
||||||
104
hrp/internal/uixt/client.go
Normal file
104
hrp/internal/uixt/client.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package uixt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Driver struct {
|
||||||
|
urlPrefix *url.URL
|
||||||
|
sessionId string
|
||||||
|
client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wd *Driver) concatURL(u *url.URL, elem ...string) string {
|
||||||
|
var tmp *url.URL
|
||||||
|
if u == nil {
|
||||||
|
u = wd.urlPrefix
|
||||||
|
}
|
||||||
|
tmp, _ = url.Parse(u.String())
|
||||||
|
tmp.Path = path.Join(append([]string{u.Path}, elem...)...)
|
||||||
|
return tmp.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wd *Driver) httpGET(pathElem ...string) (rawResp rawResponse, err error) {
|
||||||
|
return wd.httpRequest(http.MethodGet, wd.concatURL(nil, pathElem...), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wd *Driver) httpPOST(data interface{}, pathElem ...string) (rawResp rawResponse, err error) {
|
||||||
|
var bsJSON []byte = nil
|
||||||
|
if data != nil {
|
||||||
|
if bsJSON, err = json.Marshal(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wd.httpRequest(http.MethodPost, wd.concatURL(nil, pathElem...), bsJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wd *Driver) httpDELETE(pathElem ...string) (rawResp rawResponse, err error) {
|
||||||
|
return wd.httpRequest(http.MethodDelete, wd.concatURL(nil, pathElem...), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wd *Driver) httpRequest(method string, rawURL string, rawBody []byte) (rawResp rawResponse, err error) {
|
||||||
|
log.Debug().Str("method", method).Str("url", rawURL).Str("body", string(rawBody)).Msg("request WDA")
|
||||||
|
|
||||||
|
var req *http.Request
|
||||||
|
if req, err = http.NewRequest(method, rawURL, bytes.NewBuffer(rawBody)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
var resp *http.Response
|
||||||
|
if resp, err = wd.client.Do(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
// https://github.com/etcd-io/etcd/blob/v3.3.25/pkg/httputil/httputil.go#L16-L22
|
||||||
|
_, _ = io.Copy(ioutil.Discard, resp.Body)
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
rawResp, err = ioutil.ReadAll(resp.Body)
|
||||||
|
logger := log.Debug().Int("statusCode", resp.StatusCode).Str("duration", time.Since(start).String())
|
||||||
|
if !strings.HasSuffix(rawURL, "screenshot") {
|
||||||
|
// avoid printing screenshot data
|
||||||
|
logger.Str("response", string(rawResp))
|
||||||
|
}
|
||||||
|
logger.Msg("get WDA response")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = rawResp.checkErr(); err != nil {
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
return rawResp, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertToHTTPClient(conn net.Conn) *http.Client {
|
||||||
|
return &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
return conn, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Timeout: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ package uixt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (dExt *DriverExt) GesturePassword(pathname string, password ...int) (err error) {
|
func (dExt *DriverExt) GesturePassword(pathname string, password ...int) (err error) {
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ type BatteryInfo struct {
|
|||||||
|
|
||||||
// Battery state ( 1: on battery, discharging; 2: plugged in, less than 100%, 3: plugged in, at 100% )
|
// Battery state ( 1: on battery, discharging; 2: plugged in, less than 100%, 3: plugged in, at 100% )
|
||||||
State BatteryState `json:"state"`
|
State BatteryState `json:"state"`
|
||||||
|
|
||||||
|
Status BatteryStatus `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BatteryState int
|
type BatteryState int
|
||||||
@@ -683,6 +685,11 @@ type Point struct {
|
|||||||
Y int `json:"y"` // upper left Y coordinate of selected element
|
Y int `json:"y"` // upper left Y coordinate of selected element
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PointF struct {
|
||||||
|
X float64 `json:"x"`
|
||||||
|
Y float64 `json:"y"`
|
||||||
|
}
|
||||||
|
|
||||||
type Rect struct {
|
type Rect struct {
|
||||||
Point
|
Point
|
||||||
Size
|
Size
|
||||||
@@ -708,6 +715,11 @@ func WithFrequency(frequency int) DataOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// current implemeted device: IOSDevice, AndroidDevice
|
||||||
|
type Device interface {
|
||||||
|
UUID() string
|
||||||
|
}
|
||||||
|
|
||||||
// WebDriver defines methods supported by WebDriver drivers.
|
// WebDriver defines methods supported by WebDriver drivers.
|
||||||
type WebDriver interface {
|
type WebDriver interface {
|
||||||
// NewSession starts a new session and returns the SessionInfo.
|
// NewSession starts a new session and returns the SessionInfo.
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package uixt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
builtinJSON "encoding/json"
|
builtinJSON "encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -14,7 +13,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
giDevice "github.com/electricbubble/gidevice"
|
giDevice "github.com/electricbubble/gidevice"
|
||||||
@@ -40,7 +38,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultPort = 8100
|
defaultWDAPort = 8100
|
||||||
defaultMjpegPort = 9100
|
defaultMjpegPort = 9100
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -102,10 +100,6 @@ func InitWDAClient(device *IOSDevice) (*DriverExt, error) {
|
|||||||
return driverExt, nil
|
return driverExt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Device interface {
|
|
||||||
UUID() string
|
|
||||||
}
|
|
||||||
|
|
||||||
type IOSDeviceOption func(*IOSDevice)
|
type IOSDeviceOption func(*IOSDevice)
|
||||||
|
|
||||||
func WithUDID(udid string) IOSDeviceOption {
|
func WithUDID(udid string) IOSDeviceOption {
|
||||||
@@ -144,7 +138,7 @@ func NewIOSDevice(options ...IOSDeviceOption) (device *IOSDevice, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
device = &IOSDevice{
|
device = &IOSDevice{
|
||||||
Port: defaultPort,
|
Port: defaultWDAPort,
|
||||||
MjpegPort: defaultMjpegPort,
|
MjpegPort: defaultMjpegPort,
|
||||||
}
|
}
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
@@ -191,37 +185,33 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities Capabilities) (driver WebDriver
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
wd.sessionId = sessionInfo.SessionId
|
wd.sessionId = sessionInfo.SessionId
|
||||||
|
wd.client = http.DefaultClient
|
||||||
|
|
||||||
if wd.mjpegConn, err = net.Dial(
|
if wd.mjpegHTTPConn, err = net.Dial(
|
||||||
"tcp",
|
"tcp",
|
||||||
fmt.Sprintf("%s:%d", wd.urlPrefix.Hostname(),
|
fmt.Sprintf("%s:%d", wd.urlPrefix.Hostname(),
|
||||||
dev.MjpegPort),
|
dev.MjpegPort),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
wd.mjpegClient = convertToHTTPClient(wd.mjpegConn)
|
wd.mjpegClient = convertToHTTPClient(wd.mjpegHTTPConn)
|
||||||
|
|
||||||
return wd, nil
|
return wd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUSBDriver creates new client via USB connected device, this will also start a new session.
|
// NewUSBDriver creates new client via USB connected device, this will also start a new session.
|
||||||
func (dev *IOSDevice) NewUSBDriver(capabilities Capabilities) (driver WebDriver, err error) {
|
func (dev *IOSDevice) NewUSBDriver(capabilities Capabilities) (driver WebDriver, err error) {
|
||||||
wd := &wdaDriver{
|
wd := new(wdaDriver)
|
||||||
usbCli: &struct {
|
|
||||||
httpCli *http.Client
|
if wd.defaultConn, err = dev.NewConnect(dev.Port, 0); err != nil {
|
||||||
defaultConn, mjpegConn giDevice.InnerConn
|
|
||||||
sync.Mutex
|
|
||||||
}{},
|
|
||||||
}
|
|
||||||
if wd.usbCli.defaultConn, err = dev.NewConnect(dev.Port, 0); err != nil {
|
|
||||||
return nil, fmt.Errorf("create connection: %w", err)
|
return nil, fmt.Errorf("create connection: %w", err)
|
||||||
}
|
}
|
||||||
wd.usbCli.httpCli = convertToHTTPClient(wd.usbCli.defaultConn.RawConn())
|
wd.client = convertToHTTPClient(wd.defaultConn.RawConn())
|
||||||
|
|
||||||
if wd.usbCli.mjpegConn, err = dev.NewConnect(dev.MjpegPort, 0); err != nil {
|
if wd.mjpegUSBConn, err = dev.NewConnect(dev.MjpegPort, 0); err != nil {
|
||||||
return nil, fmt.Errorf("create connection MJPEG: %w", err)
|
return nil, fmt.Errorf("create connection MJPEG: %w", err)
|
||||||
}
|
}
|
||||||
wd.mjpegClient = convertToHTTPClient(wd.usbCli.mjpegConn.RawConn())
|
wd.mjpegClient = convertToHTTPClient(wd.mjpegUSBConn.RawConn())
|
||||||
|
|
||||||
if wd.urlPrefix, err = url.Parse("http://" + dev.UDID); err != nil {
|
if wd.urlPrefix, err = url.Parse("http://" + dev.UDID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -245,17 +235,6 @@ func (dev *IOSDevice) NewUSBDriver(capabilities Capabilities) (driver WebDriver,
|
|||||||
return wd, err
|
return wd, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertToHTTPClient(_conn net.Conn) *http.Client {
|
|
||||||
return &http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
|
||||||
return _conn, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Timeout: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type wdaResponse struct {
|
type wdaResponse struct {
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
SessionID string `json:"sessionId"`
|
SessionID string `json:"sessionId"`
|
||||||
@@ -374,9 +353,10 @@ type rawResponse []byte
|
|||||||
func (r rawResponse) checkErr() (err error) {
|
func (r rawResponse) checkErr() (err error) {
|
||||||
reply := new(struct {
|
reply := new(struct {
|
||||||
Value struct {
|
Value struct {
|
||||||
Err string `json:"error"`
|
Err string `json:"error"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Traceback string `json:"traceback"`
|
Traceback string `json:"traceback"` // wda
|
||||||
|
Stacktrace string `json:"stacktrace"` // uia
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if err = json.Unmarshal(r, reply); err != nil {
|
if err = json.Unmarshal(r, reply); err != nil {
|
||||||
|
|||||||
@@ -5,57 +5,46 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
builtinJSON "encoding/json"
|
builtinJSON "encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
giDevice "github.com/electricbubble/gidevice"
|
giDevice "github.com/electricbubble/gidevice"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v4/hrp/internal/json"
|
"github.com/httprunner/httprunner/v4/hrp/internal/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
type wdaDriver struct {
|
type wdaDriver struct {
|
||||||
urlPrefix *url.URL
|
Driver
|
||||||
sessionId string
|
|
||||||
|
|
||||||
usbCli *struct {
|
// default port
|
||||||
httpCli *http.Client
|
defaultConn giDevice.InnerConn
|
||||||
defaultConn, mjpegConn giDevice.InnerConn
|
|
||||||
sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
mjpegClient *http.Client
|
// mjpeg port
|
||||||
mjpegConn net.Conn
|
mjpegUSBConn giDevice.InnerConn // via USB
|
||||||
|
mjpegHTTPConn net.Conn // via HTTP
|
||||||
|
mjpegClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wd *wdaDriver) GetMjpegHTTPClient() *http.Client {
|
func (wd *wdaDriver) GetMjpegClient() *http.Client {
|
||||||
return wd.mjpegClient
|
return wd.mjpegClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wd *wdaDriver) Close() error {
|
func (wd *wdaDriver) Close() error {
|
||||||
if wd.usbCli == nil {
|
if wd.defaultConn != nil {
|
||||||
|
wd.defaultConn.Close()
|
||||||
|
}
|
||||||
|
if wd.mjpegUSBConn != nil {
|
||||||
|
wd.mjpegUSBConn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if wd.mjpegClient != nil {
|
||||||
wd.mjpegClient.CloseIdleConnections()
|
wd.mjpegClient.CloseIdleConnections()
|
||||||
return wd.mjpegConn.Close()
|
|
||||||
}
|
}
|
||||||
|
return wd.mjpegHTTPConn.Close()
|
||||||
wd.usbCli.Lock()
|
|
||||||
defer wd.usbCli.Unlock()
|
|
||||||
|
|
||||||
if wd.usbCli.defaultConn != nil {
|
|
||||||
wd.usbCli.defaultConn.Close()
|
|
||||||
}
|
|
||||||
if wd.usbCli.mjpegConn != nil {
|
|
||||||
wd.usbCli.mjpegConn.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wd *wdaDriver) NewSession(capabilities Capabilities) (sessionInfo SessionInfo, err error) {
|
func (wd *wdaDriver) NewSession(capabilities Capabilities) (sessionInfo SessionInfo, err error) {
|
||||||
@@ -844,83 +833,3 @@ func (wd *wdaDriver) WaitWithTimeout(condition Condition, timeout time.Duration)
|
|||||||
func (wd *wdaDriver) Wait(condition Condition) error {
|
func (wd *wdaDriver) Wait(condition Condition) error {
|
||||||
return wd.WaitWithTimeoutAndInterval(condition, DefaultWaitTimeout, DefaultWaitInterval)
|
return wd.WaitWithTimeoutAndInterval(condition, DefaultWaitTimeout, DefaultWaitInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wd *wdaDriver) concatURL(u *url.URL, elem ...string) string {
|
|
||||||
var tmp *url.URL
|
|
||||||
if u == nil {
|
|
||||||
u = wd.urlPrefix
|
|
||||||
}
|
|
||||||
tmp, _ = url.Parse(u.String())
|
|
||||||
tmp.Path = path.Join(append([]string{u.Path}, elem...)...)
|
|
||||||
return tmp.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wd *wdaDriver) httpGET(pathElem ...string) (rawResp rawResponse, err error) {
|
|
||||||
return wd.httpRequest(http.MethodGet, wd.concatURL(nil, pathElem...), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wd *wdaDriver) httpPOST(data interface{}, pathElem ...string) (rawResp rawResponse, err error) {
|
|
||||||
var bsJSON []byte = nil
|
|
||||||
if data != nil {
|
|
||||||
if bsJSON, err = json.Marshal(data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return wd.httpRequest(http.MethodPost, wd.concatURL(nil, pathElem...), bsJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wd *wdaDriver) httpDELETE(pathElem ...string) (rawResp rawResponse, err error) {
|
|
||||||
return wd.httpRequest(http.MethodDelete, wd.concatURL(nil, pathElem...), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wd *wdaDriver) httpRequest(method string, rawURL string, rawBody []byte) (rawResp rawResponse, err error) {
|
|
||||||
log.Debug().Str("method", method).Str("url", rawURL).Str("body", string(rawBody)).Msg("request WDA")
|
|
||||||
|
|
||||||
var req *http.Request
|
|
||||||
if req, err = http.NewRequest(method, rawURL, bytes.NewBuffer(rawBody)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
|
|
||||||
req.Header.Set("Accept", "application/json")
|
|
||||||
|
|
||||||
var httpCli *http.Client
|
|
||||||
if wd.usbCli != nil {
|
|
||||||
wd.usbCli.Lock()
|
|
||||||
defer wd.usbCli.Unlock()
|
|
||||||
httpCli = wd.usbCli.httpCli
|
|
||||||
} else {
|
|
||||||
httpCli = http.DefaultClient
|
|
||||||
}
|
|
||||||
httpCli.Timeout = 0
|
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
var resp *http.Response
|
|
||||||
if resp, err = httpCli.Do(req); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
// https://github.com/etcd-io/etcd/blob/v3.3.25/pkg/httputil/httputil.go#L16-L22
|
|
||||||
_, _ = io.Copy(ioutil.Discard, resp.Body)
|
|
||||||
_ = resp.Body.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
rawResp, err = ioutil.ReadAll(resp.Body)
|
|
||||||
logger := log.Debug().Int("statusCode", resp.StatusCode).Str("duration", time.Since(start).String())
|
|
||||||
if !strings.HasSuffix(rawURL, "screenshot") {
|
|
||||||
// avoid printing screenshot data
|
|
||||||
logger.Bytes("response", rawResp)
|
|
||||||
}
|
|
||||||
logger.Msg("get WDA response")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = rawResp.checkErr(); err != nil {
|
|
||||||
if resp.StatusCode == http.StatusOK {
|
|
||||||
return rawResp, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -351,8 +351,6 @@ func Test_remoteWD_Homescreen(t *testing.T) {
|
|||||||
func Test_remoteWD_AppLaunch(t *testing.T) {
|
func Test_remoteWD_AppLaunch(t *testing.T) {
|
||||||
setup(t)
|
setup(t)
|
||||||
|
|
||||||
// SetDebug(true)
|
|
||||||
|
|
||||||
// bundleId = "com.hustlzp.xcz"
|
// bundleId = "com.hustlzp.xcz"
|
||||||
// bundleId = "com.github.stormbreaker.prod"
|
// bundleId = "com.github.stormbreaker.prod"
|
||||||
// bundleId = "com.360buy.jdmobile"
|
// bundleId = "com.360buy.jdmobile"
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ func TestDriverExt_TapWithNumber(t *testing.T) {
|
|||||||
|
|
||||||
pathSearch := "/Users/hero/Documents/temp/2020-05/opencv/flag7.png"
|
pathSearch := "/Users/hero/Documents/temp/2020-05/opencv/flag7.png"
|
||||||
|
|
||||||
// SetDebug(true)
|
|
||||||
|
|
||||||
err = driverExt.TapWithNumber(pathSearch, 3)
|
err = driverExt.TapWithNumber(pathSearch, 3)
|
||||||
checkErr(t, err)
|
checkErr(t, err)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user