{"id":21311461,"url":"https://github.com/electricbubble/guia2","last_synced_at":"2025-08-27T15:08:16.388Z","repository":{"id":50665864,"uuid":"285430524","full_name":"electricbubble/guia2","owner":"electricbubble","description":"appium-uiautomator2-server ( Android ) Client Library in Golang","archived":false,"fork":false,"pushed_at":"2021-05-16T08:45:41.000Z","size":103,"stargazers_count":143,"open_issues_count":8,"forks_count":32,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-11T23:37:46.197Z","etag":null,"topics":["android","appium","appium-android","appium-uiautomator2-server","golang","uiautomator2"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/electricbubble.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-08-06T00:01:56.000Z","updated_at":"2025-06-10T08:28:54.000Z","dependencies_parsed_at":"2022-09-04T13:50:46.375Z","dependency_job_id":null,"html_url":"https://github.com/electricbubble/guia2","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/electricbubble/guia2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electricbubble%2Fguia2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electricbubble%2Fguia2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electricbubble%2Fguia2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electricbubble%2Fguia2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/electricbubble","download_url":"https://codeload.github.com/electricbubble/guia2/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/electricbubble%2Fguia2/sbom","scorecard":{"id":371854,"data":{"date":"2025-08-11","repo":{"name":"github.com/electricbubble/guia2","commit":"caeedcda69a3d750d887ea9b53607ce7477d6ccd"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/17 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T13:10:08.252Z","repository_id":50665864,"created_at":"2025-08-18T13:10:08.252Z","updated_at":"2025-08-18T13:10:08.252Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272342737,"owners_count":24917686,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-27T02:00:09.397Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","appium","appium-android","appium-uiautomator2-server","golang","uiautomator2"],"created_at":"2024-11-21T17:18:30.987Z","updated_at":"2025-08-27T15:08:16.356Z","avatar_url":"https://github.com/electricbubble.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Golang-UIAutomator2\n[![go doc](https://godoc.org/github.com/electricbubble/guia2?status.svg)](https://pkg.go.dev/github.com/electricbubble/guia2?tab=doc)\n[![license](https://img.shields.io/github/license/electricbubble/guia2)](https://github.com/electricbubble/guia2/blob/master/LICENSE)\n\n使用 Golang 实现 [appium/appium-uiautomator2-server](https://github.com/appium/appium-uiautomator2-server) 的客户端库\n\n## 扩展库\n\n- [electricbubble/guia2-ext-opencv](https://github.com/electricbubble/guia2-ext-opencv) 直接通过指定图片进行操作\n\n\u003e 如果使用 `IOS` 设备, 可查看 [electricbubble/gwda](https://github.com/electricbubble/gwda)\n\n## 安装\n```bash\ngo get github.com/electricbubble/guia2\n```\n\n## 使用\n\n\u003e 首次使用需要在 `Android` 设备中安装两个 `apk`  \n\u003e `appium-uiautomator2-server-debug-androidTest.apk`  \n\u003e `appium-uiautomator2-server-vXX.XX.XX.apk`\n\u003e\n\u003e\u003e `apk` 可以选择通过 [appium/appium-uiautomator2-server](https://github.com/appium/appium-uiautomator2-server#building-project) 进行构建  \n\u003e\u003e 也可以直接从这里下载 [electricbubble/appium-uiautomator2-server-apk](https://github.com/electricbubble/appium-uiautomator2-server-apk/releases)\n\u003e  \n\u003e\n\u003e 再通过 `adb` 启动 `appium-uiautomator2-server`  \n\u003e ```shell script\n\u003e adb shell am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner\n\u003e # ⬇️ 后台运行\n\u003e adb shell \"nohup am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner \u003e/sdcard/uia2server.log 2\u003e\u00261 \u0026\"\n\u003e # or\n\u003e adb -s $serial shell \"nohup am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner \u003e/sdcard/uia2server.log 2\u003e\u00261 \u0026\"\n\u003e ```\n\n### `guia2.NewUSBDriver()`\n该函数使用期间, `Android` 设备必须一直保持 `USB` 的连接 (`模拟器` 也使用该函数)\n\n### `guia2.NewWiFiDriver(\"192.168.1.28\")`\n1. 先通过 `USB` 连接 `Android` 设备\n2. 让设备在 5555 端口监听 TCP/IP 连接\n    ```shell script\n    adb tcpip 5555\n   # or\n    adb -s $serial tcpip 5555\n    ```\n3. 查询 `Android` 设备的 `IP` (这一步骤开始可选择断开 `USB` 连接)\n4. 通过 `IP` 连接 `Android` 设备\n    ```shell script\n    adb connect $deviceIP\n    ```\n5. 确认连接状态\n    ```shell script\n    adb devices\n    ```\n    看到以下格式的设备, 说明连接成功\n    ```shell script\n    $deviceIP:5555    device\n    ```\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"github.com/electricbubble/guia2\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n)\n\nfunc main() {\n\tdriver, err := guia2.NewUSBDriver()\n\t// driver, err := guia2.NewWiFiDriver(\"192.168.1.28\")\n\tcheckErr(err)\n\tdefer func() { _ = driver.Dispose() }()\n\n\t// err = driver.AppLaunch(\"tv.danmaku.bili\")\n\terr = driver.AppLaunch(\"tv.danmaku.bili\", guia2.BySelector{ResourceIdID: \"tv.danmaku.bili:id/action_bar_root\"})\n\tcheckErr(err, \"launch the app until the element appears\")\n\n\t// fmt.Println(driver.Source())\n\t// return\n\n\tdeviceSize, err := driver.DeviceSize()\n\tcheckErr(err)\n\n\tvar startX, startY, endX, endY int\n\tstartX = deviceSize.Width / 2\n\tstartY = deviceSize.Height / 2\n\tendX = startX\n\tendY = startY / 2\n\terr = driver.Swipe(startX, startY, endX, endY)\n\tcheckErr(err)\n\n\tvar startPoint, endPoint guia2.PointF\n\tstartPoint = guia2.PointF{X: float64(startX), Y: float64(startY)}\n\tendPoint = guia2.PointF{X: startPoint.X, Y: startPoint.Y * 1.6}\n\terr = driver.SwipePointF(startPoint, endPoint)\n\tcheckErr(err)\n\n\telement, err := driver.FindElement(guia2.BySelector{ResourceIdID: \"tv.danmaku.bili:id/expand_search\"})\n\tcheckErr(err)\n\n\terr = element.Click()\n\tcheckErr(err)\n\n\tbySelector := guia2.BySelector{UiAutomator: guia2.NewUiSelectorHelper().Focused(true).String()}\n\telement, err = waitForElement(driver, bySelector)\n\tcheckErr(err)\n\n\terr = element.SendKeys(\"雾山五行\")\n\tcheckErr(err)\n\n\terr = driver.PressKeyCode(guia2.KCEnter, guia2.KMEmpty)\n\tcheckErr(err)\n\n\tbySelector = guia2.BySelector{UiAutomator: guia2.NewUiSelectorHelper().TextStartsWith(\"番剧\").String()}\n\telement, err = waitForElement(driver, bySelector)\n\tcheckErr(err)\n\tcheckErr(element.Click())\n\n\tbySelector = guia2.BySelector{UiAutomator: guia2.NewUiSelectorHelper().Text(\"立即观看\").String()}\n\telement, err = waitForElement(driver, bySelector)\n\tcheckErr(err)\n\tcheckErr(element.Click())\n\n\tbySelector = guia2.BySelector{ResourceIdID: \"tv.danmaku.bili:id/videoview_container_space\"}\n\telement, err = waitForElement(driver, bySelector)\n\tcheckErr(err)\n\n\t// time.Sleep(time.Second * 5)\n\n\tscreenshot, err := element.Screenshot()\n\tcheckErr(err)\n\tuserHomeDir, _ := os.UserHomeDir()\n\tcheckErr(ioutil.WriteFile(userHomeDir+\"/Desktop/element.png\", screenshot.Bytes(), 0600))\n\n\terr = driver.PressKeyCode(guia2.KCMediaPause, guia2.KMEmpty)\n\tcheckErr(err)\n\n\terr = driver.PressBack()\n\tcheckErr(err)\n}\n\nfunc waitForElement(driver *guia2.Driver, bySelector guia2.BySelector) (element *guia2.Element, err error) {\n\tvar ce error\n\texists := func(d *guia2.Driver) (bool, error) {\n\t\telement, ce = d.FindElement(bySelector)\n\t\tif ce == nil {\n\t\t\treturn true, nil\n\t\t}\n\t\t// 如果直接返回 error 将直接终止 `driver.Wait`\n\t\treturn false, nil\n\t}\n\tif err = driver.Wait(exists); err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: %w\", err.Error(), ce)\n\t}\n\treturn\n}\n\nfunc checkErr(err error, msg ...string) {\n\tif err == nil {\n\t\treturn\n\t}\n\n\tvar output string\n\tif len(msg) != 0 {\n\t\toutput = msg[0] + \" \"\n\t}\n\toutput += err.Error()\n\tlog.Fatalln(output)\n}\n\n```\n\n\u003e 感谢小伙伴提供的 `红米 Note 5A`\n\n\n![example](https://github.com/electricbubble/ImageHosting/blob/master/img/202008192034_guia2.gif)\n\n\n## Thanks\n\nThank you [JetBrains](https://www.jetbrains.com/?from=gwda) for providing free open source licenses\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felectricbubble%2Fguia2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felectricbubble%2Fguia2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felectricbubble%2Fguia2/lists"}