[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"maven\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/maven.yml",
    "content": "name: maven compile test\n\non: [pull_request]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up JDK 17\n      uses: actions/setup-java@v2\n      with:\n        java-version: '17'\n        distribution: 'temurin'\n        cache: maven\n    - name: Validate and Compile with Maven\n      run: mvn validate compile\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "# name: release to github\n\n# on:\n#   push:\n#     tags:\n#       - \"*.*.*\"\n\n# jobs:\n#   doc:\n#     runs-on: ubuntu-latest\n#     steps:\n#       - uses: actions/checkout@v2\n#         with:\n#           fetch-depth: 0\n#       - name: 'Get Previous tag'\n#         id: previoustag\n#         uses: \"WyriHaximus/github-action-get-previous-tag@v1\"\n#       - name: 'Get previous release tag'\n#         id: tag\n#         uses: \"sammcoe/get-previous-release-action@v1\"\n#       - name: replace version\n#         run: sed -i \"s/${{steps.tag.outputs.tag}}/${{steps.previoustag.outputs.tag}}/g\" README*.md\n#       - name: Create Pull Request\n#         id: cpr\n#         uses: peter-evans/create-pull-request@v4\n#         with:\n#           commit-message: Update README file.\n#           author: GitHub <noreply@github.com>\n#           signoff: false\n#           branch: doc/${{steps.previoustag.outputs.tag}}\n#           labels: document\n#           base: main\n#           delete-branch: true\n#           title: 'doc: update README files version to ${{steps.previoustag.outputs.tag}}'\n#           body: |\n#             **在提出此拉取请求时，我确认了以下几点（保存后请点击复选框）：**\n\n#             - [x] 标题为fix、feat或doc开头\n#             - [x] 我已检查没有与此请求重复的拉取请求。\n#             - [x] 我已经考虑过，并确认这份呈件对其他人很有价值。\n#             - [x] 我接受此提交可能不会被使用，并根据维护人员的意愿关闭拉取请求。\n\n#             **填写PR内容：**\n\n#             - Update README files version to latest tag by bot 🚀.\n\n#           draft: false\n#       - name: Auto approve\n#         if: steps.cpr.outputs.pull-request-operation == 'created'\n#         uses: juliangruber/approve-pull-request-action@v1\n#         with:\n#           github-token: ${{ secrets.PAT }}\n#           number: ${{ steps.cpr.outputs.pull-request-number }}\n#       - id: automerge\n#         name: automerge\n#         uses: \"pascalgn/automerge-action@v0.15.3\"\n#         env:\n#           MERGE_LABELS: \"document\"\n#           MERGE_DELETE_BRANCH: true\n#           GITHUB_TOKEN: \"${{ secrets.GITHUB_TOKEN }}\"\n#           PULL_REQUEST: ${{ steps.cpr.outputs.pull-request-number }}\n#           MERGE_RETRIES: 18\n#           MERGE_RETRY_SLEEP: 10000\n\n#   release:\n#     needs: doc\n#     runs-on: ubuntu-latest\n#     steps:\n#       - uses: actions/checkout@v2\n#         with:\n#           fetch-depth: 0\n#       - name: 'Get Previous tag'\n#         id: previoustag\n#         uses: \"WyriHaximus/github-action-get-previous-tag@v1\"\n#       - name: replace version\n#         run: sed -i \"s/SONIC_VERSION/${{ steps.previoustag.outputs.tag }}/g\" pom.xml\n#       - name: Set up Maven Central Repo\n#         uses: actions/setup-java@v1\n#         with:\n#           java-version: 1.8\n#           server-id: sonatype-nexus-staging\n#           server-username: ${{ secrets.OSSRH_USER }}\n#           server-password: ${{ secrets.OSSRH_PASSWORD }}\n#           gpg-passphrase: ${{ secrets.GPG_PASSWORD }}\n#       - name: Release Maven package\n#         uses: WasiqB/maven-publish-action@v1\n#         with:\n#           maven_args: -Dmaven.test.skip=true\n#           gpg_private_key: ${{ secrets.GPG_SECRET }}\n#           gpg_passphrase: ${{ secrets.GPG_PASSWORD }}\n#           nexus_username: ${{ secrets.OSSRH_USER }}\n#           nexus_password: ${{ secrets.OSSRH_PASSWORD }}\n#       - uses: softprops/action-gh-release@v1\n#         with:\n#           draft: false\n#           generate_release_notes: true\n# # mvn clean deploy -Pdeploy -Dmaven.test.skip=true"
  },
  {
    "path": ".gitignore",
    "content": "/.idea/\n/target/\n/logs/\n*.iml\n*/.DS_Store\n.DS_Store"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright © [SonicCloudOrg] Sonic Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img width=\"80px\" src=\"https://raw.githubusercontent.com/SonicCloudOrg/sonic-server/main/logo.png\">\n</p>\n<p align=\"center\">🎉The Sonic UIAutomation Driver Core</p>\n<p align=\"center\">\n  <span>English |</span>\n  <a href=\"https://github.com/SonicCloudOrg/sonic-driver-core/blob/main/README_CN.md\">  \n     简体中文\n  </a>\n</p>\n<p align=\"center\">\n  <a href=\"#\">  \n    <img src=\"https://img.shields.io/maven-central/v/io.github.soniccloudorg/sonic-driver-core\">\n  </a>\n  <a href=\"#\">  \n    <img src=\"https://img.shields.io/github/commit-activity/m/SonicCloudOrg/sonic-driver-core\">\n  </a>\n<a href=\"https://app.fossa.com/projects/git%2Bgithub.com%2FSonicCloudOrg%2Fsonic-driver-core?ref=badge_shield\" alt=\"FOSSA Status\"><img src=\"https://app.fossa.com/api/projects/git%2Bgithub.com%2FSonicCloudOrg%2Fsonic-driver-core.svg?type=shield\"/></a>\n  <a href=\"https://codecov.io/gh/SonicCloudOrg/sonic-driver-core\">  \n    <img src=\"https://codecov.io/gh/SonicCloudOrg/sonic-driver-core/branch/main/graph/badge.svg?token=PZ5295WQP1\">\n  </a>\n</p>\n<p align=\"center\">\n  <a href=\"https://github.com/SonicCloudOrg/sonic-driver-core\">  \n    <img src=\"https://www.oscs1024.com/platform/badge/SonicCloudOrg/sonic-driver-core.svg?size=large\">\n  </a>\n</p>\n\n## What is sonic-driver-core?\n\nsonic-driver-core can be separated from appium and interact directly with webdriveragent or uiautomator2, which reduces the communication layer of appium and makes the test faster and more stable.\n\n## Use in Java code\n\n### Add dependency\n#### Maven Central\n\n```xml\n\n<dependency>\n    <groupId>io.github.soniccloudorg</groupId>\n    <artifactId>sonic-driver-core</artifactId>\n    <version>1.1.30</version>\n</dependency>\n```\n\n#### Gradle\n\n```\nimplementation 'io.github.soniccloudorg:sonic-driver-core:1.1.30'\n```\n\n### Code\n\n```java\npackage org.cloud.sonic.driver.ios;\n\nimport org.cloud.sonic.driver.ios.enums.IOSSelector;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\npublic class MyTest {\n\n    public void test() throws SonicRespException {\n        IOSDriver iosDriver = new IOSDriver(\"http://localhost:8100\");\n        iosDriver.showLog();\n\n        //touch\n        iosDriver.swipe(100, 256, 50, 256);\n        iosDriver.tap(150, 81);\n        iosDriver.longPress(150, 281, 1500);\n        iosDriver.performTouchAction(new TouchActions().press(50, 256).wait(50).move(100, 256).wait(10).release());\n\n        //element\n        iosDriver.findElement(IOSSelector.XPATH, \"//XCUIElementTypeTextField\").click();\n\n        //more...\n    }\n}\n```\n\n## More Example\n\nSee [Here](https://github.com/SonicCloudOrg/sonic-uiautomation-example/tree/main/java-example).\n\n## Document\n\nSee [Here](https://sonic-cloud.cn/sdc/re-sdc.html).\n\n## Sponsors\n\nThank you to all our sponsors!\n\n[<img src=\"https://ceshiren.com/uploads/default/original/3X/7/0/70299922296e93e2dcab223153a928c4bfb27df9.jpeg\" alt=\"霍格沃兹测试开发学社\" width=\"500\">](https://qrcode.testing-studio.com/f?from=sonic&url=https://ceshiren.com)\n\n> [霍格沃兹测试开发学社](https://qrcode.testing-studio.com/f?from=sonic&url=https://ceshiren.com)是业界领先的测试开发技术高端教育品牌，隶属于[测吧（北京）科技有限公司](http://qrcode.testing-studio.com/f?from=sonic&url=https://www.testing-studio.com) 。学院课程由一线大厂测试经理与资深测试开发专家参与研发，实战驱动。课程涵盖 web/app 自动化测试、接口测试、性能测试、安全测试、持续集成/持续交付/DevOps，测试左移&右移、精准测试、测试平台开发、测试管理等内容，帮助测试工程师实现测试开发技术转型。通过优秀的学社制度（奖学金、内推返学费、行业竞赛等多种方式）来实现学员、学社及用人企业的三方共赢。[进入测试开发技术能力测评!](https://qrcode.testing-studio.com/f?from=sonic&url=https://ceshiren.com/t/topic/14940)\n\n## LICENSE\n\n[License](LICENSE)\n\n\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FSonicCloudOrg%2Fsonic-driver-core.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FSonicCloudOrg%2Fsonic-driver-core?ref=badge_large)\n"
  },
  {
    "path": "README_CN.md",
    "content": "<p align=\"center\">\n  <img width=\"80px\" src=\"https://raw.githubusercontent.com/SonicCloudOrg/sonic-server/main/logo.png\">\n</p>\n<p align=\"center\">🎉Sonic UI自动化Driver核心</p>\n<p align=\"center\">\n  <a href=\"https://github.com/SonicCloudOrg/sonic-driver-core/blob/main/README.md\">  \n    English\n  </a>\n  <span>| 简体中文</span>\n</p>\n<p align=\"center\">\n  <a href=\"#\">  \n    <img src=\"https://img.shields.io/maven-central/v/io.github.soniccloudorg/sonic-driver-core\">\n  </a>\n  <a href=\"#\">  \n    <img src=\"https://img.shields.io/github/commit-activity/m/SonicCloudOrg/sonic-driver-core\">\n  </a>\n  <a href=\"https://codecov.io/gh/SonicCloudOrg/sonic-driver-core\">  \n    <img src=\"https://codecov.io/gh/SonicCloudOrg/sonic-driver-core/branch/main/graph/badge.svg?token=PZ5295WQP1\">\n  </a>\n</p>\n<p align=\"center\">\n  <a href=\"https://github.com/SonicCloudOrg/sonic-driver-core\">  \n    <img src=\"https://www.oscs1024.com/platform/badge/SonicCloudOrg/sonic-driver-core.svg?size=large\">\n  </a>\n</p>\n\n## sonic-driver-core是什么？\n\nsonic-driver-core可以脱离Appium，直接与WebDriverAgent或UIautomator2交互，减少了Appium的通信层，让测试更快更稳定。\n\n## 在你的Java代码中使用\n\n### 引用库\n#### Maven\n```xml\n<dependency>\n    <groupId>io.github.soniccloudorg</groupId>\n    <artifactId>sonic-driver-core</artifactId>\n    <version>1.1.30</version>\n</dependency>\n```\n#### Gradle\n```\nimplementation 'io.github.soniccloudorg:sonic-driver-core:1.1.30'\n```\n\n### 代码\n\n```java\npackage org.cloud.sonic.driver.ios;\n\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\npublic class MyTest {\n\n    public void test() throws SonicRespException {\n        IOSDriver iosDriver = new IOSDriver(\"http://localhost:8100\");\n        iosDriver.showLog();\n\n        //touch\n        iosDriver.swipe(100, 256, 50, 256);\n        iosDriver.tap(150, 81);\n        iosDriver.longPress(150, 281, 1500);\n        iosDriver.performTouchAction(new TouchActions().press(50, 256).wait(50).move(100, 256).wait(10).release());\n\n        //element\n        iosDriver.findElement(IOSSelector.XPATH, \"//XCUIElementTypeTextField\").click();\n\n        //更多...\n    }\n}\n```\n\n## 更多例子\n\n查看 [这里](https://github.com/SonicCloudOrg/sonic-uiautomation-example/tree/main/java-example).\n\n## 文档\n\n查看 [这里](https://sonic-cloud.cn/sdc/re-sdc.html).\n\n## 赞助商\n\n感谢所有赞助商！\n\n[<img src=\"https://ceshiren.com/uploads/default/original/3X/7/0/70299922296e93e2dcab223153a928c4bfb27df9.jpeg\" alt=\"霍格沃兹测试开发学社\" width=\"500\">](https://qrcode.testing-studio.com/f?from=sonic&url=https://ceshiren.com)\n\n> [霍格沃兹测试开发学社](https://qrcode.testing-studio.com/f?from=sonic&url=https://ceshiren.com)是业界领先的测试开发技术高端教育品牌，隶属于[测吧（北京）科技有限公司](http://qrcode.testing-studio.com/f?from=sonic&url=https://www.testing-studio.com) 。学院课程由一线大厂测试经理与资深测试开发专家参与研发，实战驱动。课程涵盖 web/app 自动化测试、接口测试、性能测试、安全测试、持续集成/持续交付/DevOps，测试左移&右移、精准测试、测试平台开发、测试管理等内容，帮助测试工程师实现测试开发技术转型。通过优秀的学社制度（奖学金、内推返学费、行业竞赛等多种方式）来实现学员、学社及用人企业的三方共赢。[进入测试开发技术能力测评!](https://qrcode.testing-studio.com/f?from=sonic&url=https://ceshiren.com/t/topic/14940)\n\n## 开源许可协议\n\n[License](LICENSE)\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>io.github.soniccloudorg</groupId>\n    <artifactId>sonic-driver-core</artifactId>\n    <version>1.1.33</version>\n\n    <name>sonic-driver-core</name>\n    <description>The Sonic Project UIAutomation Driver Core for Android, iOS, Windows, Mac and so on.</description>\n    <url>https://github.com/SonicCloudOrg/sonic-driver-core</url>\n\n    <properties>\n        <maven.compiler.source>8</maven.compiler.source>\n        <maven.compiler.target>8</maven.compiler.target>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n    </properties>\n\n\n    <scm>\n        <tag>main</tag>\n        <url>https://github.com/SonicCloudOrg/sonic-driver-core</url>\n        <connection>https://github.com/SonicCloudOrg/sonic-driver-core</connection>\n        <developerConnection>https://github.com/SonicCloudOrg/sonic-driver-core</developerConnection>\n    </scm>\n\n    <licenses>\n        <license>\n            <name>APACHE2</name>\n            <url>https://github.com/SonicCloudOrg/sonic-driver-core/blob/main/LICENSE</url>\n        </license>\n    </licenses>\n\n    <issueManagement>\n        <system>Github Issue</system>\n        <url>https://github.com/SonicCloudOrg/sonic-driver-core/issues</url>\n    </issueManagement>\n\n    <distributionManagement>\n        <snapshotRepository>\n            <id>ossrh</id>\n            <url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>\n        </snapshotRepository>\n        <repository>\n            <id>ossrh</id>\n            <url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n        </repository>\n    </distributionManagement>\n\n    <developers>\n        <developer>\n            <name>SonicCloudOrg</name>\n            <email>soniccloudorg@163.com</email>\n            <timezone>+8</timezone>\n            <roles>\n                <role>Developer</role>\n            </roles>\n        </developer>\n    </developers>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>1.18.36</version>\n            <scope>compile</scope>\n        </dependency>\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-http</artifactId>\n            <version>5.8.5</version>\n        </dependency>\n        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-lang3</artifactId>\n            <version>3.17.0</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>2.0.56</version>\n        </dependency>\n        <dependency>\n            <groupId>org.java-websocket</groupId>\n            <artifactId>Java-WebSocket</artifactId>\n            <version>1.5.4</version>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.13.2</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-core</artifactId>\n            <version>4.6.1</version>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->\n        <dependency>\n            <groupId>org.jsoup</groupId>\n            <artifactId>jsoup</artifactId>\n            <version>1.15.4</version>\n        </dependency>\n\n    </dependencies>\n\n    <profiles>\n        <profile>\n            <id>deploy</id>\n            <build>\n                <plugins>\n                    <!-- Source plugin -->\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-source-plugin</artifactId>\n                        <version>2.4</version>\n                        <executions>\n                            <execution>\n                                <id>attach-sources</id>\n                                <goals>\n                                    <goal>jar-no-fork</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                    </plugin>\n\n                    <!-- Javadoc plugin -->\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-javadoc-plugin</artifactId>\n                        <version>2.10.4</version>\n                        <configuration>\n                            <additionalparam>-Xdoclint:none</additionalparam>\n                        </configuration>\n                        <executions>\n                            <execution>\n                                <id>attach-javadocs</id>\n                                <goals>\n                                    <goal>jar</goal>\n                                </goals>\n                            </execution>\n                        </executions>\n                    </plugin>\n\n                    <!-- GPG plugin -->\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-gpg-plugin</artifactId>\n                        <version>1.6</version>\n                        <executions>\n                            <execution>\n                                <id>sign-artifacts</id>\n                                <phase>verify</phase>\n                                <goals>\n                                    <goal>sign</goal>\n                                </goals>\n                                <configuration>\n                                    <gpgArguments>\n                                        <arg>--pinentry-mode</arg>\n                                        <arg>loopback</arg>\n                                    </gpgArguments>\n                                </configuration>\n                            </execution>\n                        </executions>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n    </profiles>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>cobertura-maven-plugin</artifactId>\n                <version>2.7</version>\n                <configuration>\n                    <formats>\n                        <format>html</format>\n                        <format>xml</format>\n                    </formats>\n                    <check/>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.sonatype.plugins</groupId>\n                <artifactId>nexus-staging-maven-plugin</artifactId>\n                <version>1.6.8</version>\n                <extensions>true</extensions>\n                <configuration>\n                    <serverId>ossrh</serverId>\n                    <nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>\n                    <autoReleaseAfterClose>true</autoReleaseAfterClose>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/AndroidDriver.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.android;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.android.enmus.AndroidSelector;\nimport org.cloud.sonic.driver.android.service.AndroidElement;\nimport org.cloud.sonic.driver.android.service.UiaClient;\nimport org.cloud.sonic.driver.android.service.impl.AndroidElementImpl;\nimport org.cloud.sonic.driver.android.service.impl.UiaClientImpl;\nimport org.cloud.sonic.driver.common.enums.PasteboardType;\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.RespHandler;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\nimport java.util.List;\n\n/**\n * @author Eason\n * android driver\n */\npublic class AndroidDriver {\n    private UiaClient uiaClient;\n\n    /**\n     * Init android driver.\n     *\n     * @param url\n     * @throws SonicRespException\n     */\n    public AndroidDriver(String url) throws SonicRespException {\n        this(url, RespHandler.DEFAULT_REQUEST_TIMEOUT);\n    }\n\n    /**\n     * Init android driver.\n     *\n     * @param url\n     * @param timeOut\n     * @throws SonicRespException\n     */\n    public AndroidDriver(String url, int timeOut) throws SonicRespException {\n        this(url, timeOut, new JSONObject());\n    }\n\n    /**\n     * Init android driver.\n     *\n     * @param url\n     * @param cap\n     * @throws SonicRespException\n     */\n    public AndroidDriver(String url, JSONObject cap) throws SonicRespException {\n        this(url, RespHandler.DEFAULT_REQUEST_TIMEOUT, cap);\n    }\n\n    /**\n     * Init android driver.\n     *\n     * @param url\n     * @param timeOut\n     * @param cap\n     * @throws SonicRespException\n     */\n    public AndroidDriver(String url, int timeOut, JSONObject cap) throws SonicRespException {\n        uiaClient = new UiaClientImpl();\n        uiaClient.setRemoteUrl(url);\n        uiaClient.setGlobalTimeOut(timeOut);\n        uiaClient.newSession(cap);\n    }\n\n    /**\n     * Get uia2 client.\n     *\n     * @return\n     */\n    public UiaClient getUiaClient() {\n        return uiaClient;\n    }\n\n    /**\n     * get uia2 sessionId.\n     *\n     * @return\n     */\n    public String getSessionId() {\n        return uiaClient.getSessionId();\n    }\n\n    /**\n     * destroy sessionId.\n     *\n     * @throws SonicRespException\n     */\n    public void closeDriver() throws SonicRespException {\n        uiaClient.closeSession();\n    }\n\n    /**\n     * show log.\n     */\n    public void showLog() {\n        uiaClient.showLog();\n    }\n\n    /**\n     * disable log.\n     */\n    public void disableLog() {\n        uiaClient.disableLog();\n    }\n\n    /**\n     * get device window size.\n     *\n     * @return\n     * @throws SonicRespException\n     */\n    public WindowSize getWindowSize() throws SonicRespException {\n        return uiaClient.getWindowSize();\n    }\n\n    /**\n     * send key without element.\n     *\n     * @param text\n     * @throws SonicRespException\n     */\n    public void sendKeys(String text) throws SonicRespException {\n        sendKeys(text, false);\n    }\n\n    /**\n     * send key without element.\n     *\n     * @param text\n     * @param isCover\n     * @throws SonicRespException\n     */\n    public void sendKeys(String text, boolean isCover) throws SonicRespException {\n        uiaClient.sendKeys(text, isCover);\n    }\n\n    /**\n     * set pasteboard.\n     *\n     * @param contentType\n     * @param content\n     * @throws SonicRespException\n     */\n    public void setPasteboard(String contentType, String content) throws SonicRespException {\n        uiaClient.setPasteboard(contentType, content);\n    }\n\n    /**\n     * set pasteboard.\n     *\n     * @param pasteboardType\n     * @param content\n     * @throws SonicRespException\n     */\n    public void setPasteboard(PasteboardType pasteboardType, String content) throws SonicRespException {\n        setPasteboard(pasteboardType.getType(), content);\n    }\n\n    /**\n     * get pasteboard.\n     *\n     * @param contentType\n     * @return\n     * @throws SonicRespException\n     */\n    public byte[] getPasteboard(String contentType) throws SonicRespException {\n        return uiaClient.getPasteboard(contentType);\n    }\n\n    /**\n     * get pasteboard.\n     *\n     * @param pasteboardType\n     * @return\n     * @throws SonicRespException\n     */\n    public byte[] getPasteboard(PasteboardType pasteboardType) throws SonicRespException {\n        return getPasteboard(pasteboardType.getType());\n    }\n\n    /**\n     * get page source.\n     *\n     * @return\n     * @throws SonicRespException\n     */\n    public String getPageSource() throws SonicRespException {\n        return uiaClient.pageSource();\n    }\n\n    /**\n     * set default FindElement retry time and interval.\n     *\n     * @param retry\n     * @param interval\n     */\n    public void setDefaultFindElementInterval(Integer retry, Integer interval) {\n        uiaClient.setDefaultFindElementInterval(retry, interval);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param androidSelector\n     * @param value\n     * @return\n     * @throws SonicRespException\n     */\n    public AndroidElement findElement(AndroidSelector androidSelector, String value) throws SonicRespException {\n        return findElement(androidSelector, value, null);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param uiaElementID This ID is the id returned by uia after finding the control\n     * @return\n     * @throws SonicRespException\n     */\n    public AndroidElement findElement(String uiaElementID) throws SonicRespException {\n        return new AndroidElementImpl(uiaElementID, uiaClient);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param selector\n     * @param value\n     * @return\n     * @throws SonicRespException\n     */\n    public AndroidElement findElement(String selector, String value) throws SonicRespException {\n        return findElement(selector, value, null);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param androidSelector\n     * @param value\n     * @param retry\n     * @return\n     * @throws SonicRespException\n     */\n    public AndroidElement findElement(AndroidSelector androidSelector, String value, Integer retry) throws SonicRespException {\n        return findElement(androidSelector, value, retry, null);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param selector\n     * @param value\n     * @param retry\n     * @return\n     * @throws SonicRespException\n     */\n    public AndroidElement findElement(String selector, String value, Integer retry) throws SonicRespException {\n        return findElement(selector, value, retry, null);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param androidSelector\n     * @param value\n     * @param retry\n     * @param interval\n     * @return\n     * @throws SonicRespException\n     */\n    public AndroidElement findElement(AndroidSelector androidSelector, String value, Integer retry, Integer interval) throws SonicRespException {\n        return findElement(androidSelector.getSelector(), value, retry, interval);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param selector\n     * @param value\n     * @param retry\n     * @param interval\n     * @return\n     * @throws SonicRespException\n     */\n    public AndroidElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException {\n        return uiaClient.findElement(selector, value, retry, interval);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param androidSelector\n     * @param value\n     * @return\n     * @throws SonicRespException\n     */\n    public List<AndroidElement> findElementList(AndroidSelector androidSelector, String value) throws SonicRespException {\n        return findElementList(androidSelector, value, null);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param selector\n     * @param value\n     * @return\n     * @throws SonicRespException\n     */\n    public List<AndroidElement> findElementList(String selector, String value) throws SonicRespException {\n        return findElementList(selector, value, null);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param androidSelector\n     * @param value\n     * @param retry\n     * @return\n     * @throws SonicRespException\n     */\n    public List<AndroidElement> findElementList(AndroidSelector androidSelector, String value, Integer retry) throws SonicRespException {\n        return findElementList(androidSelector, value, retry, null);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param selector\n     * @param value\n     * @param retry\n     * @return\n     * @throws SonicRespException\n     */\n    public List<AndroidElement> findElementList(String selector, String value, Integer retry) throws SonicRespException {\n        return findElementList(selector, value, retry, null);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param androidSelector\n     * @param value\n     * @param retry\n     * @param interval\n     * @return\n     * @throws SonicRespException\n     */\n    public List<AndroidElement> findElementList(AndroidSelector androidSelector, String value, Integer retry, Integer interval) throws SonicRespException {\n        return findElementList(androidSelector.getSelector(), value, retry, interval);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param selector\n     * @param value\n     * @param retry\n     * @param interval\n     * @return\n     * @throws SonicRespException\n     */\n    public List<AndroidElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException {\n        return uiaClient.findElementList(selector, value, retry, interval);\n    }\n\n    /**\n     * get screenshot.\n     *\n     * @return\n     * @throws SonicRespException\n     */\n    public byte[] screenshot() throws SonicRespException {\n        return uiaClient.screenshot();\n    }\n\n    /**\n     * set appium settings.\n     *\n     * @param settings\n     * @throws SonicRespException\n     */\n    public void setAppiumSettings(JSONObject settings) throws SonicRespException {\n        uiaClient.setAppiumSettings(settings);\n    }\n\n    /**\n     * tap position on screen.\n     *\n     * @param x\n     * @param y\n     * @throws SonicRespException\n     */\n    public void tap(int x, int y) throws SonicRespException {\n        uiaClient.tap(x, y);\n    }\n\n    /**\n     * long press position on screen.\n     *\n     * @param x\n     * @param y\n     * @param ms\n     * @throws SonicRespException\n     */\n    public void longPress(double x, double y, double ms) throws SonicRespException {\n        uiaClient.longPress(x, y, ms);\n    }\n\n    /**\n     * swipe position on screen.\n     *\n     * @param fromX\n     * @param fromY\n     * @param toX\n     * @param toY\n     * @throws SonicRespException\n     */\n    public void swipe(int fromX, int fromY, int toX, int toY) throws SonicRespException {\n        this.swipe(fromX, fromY, toX, toY, null);\n    }\n\n    /**\n     * swipe position on screen with target time\n     *\n     * @param fromX\n     * @param fromY\n     * @param toX\n     * @param toY\n     * @param duration\n     * @throws SonicRespException\n     */\n    public void swipe(int fromX, int fromY, int toX, int toY, Integer duration) throws SonicRespException {\n        uiaClient.swipe(fromX, fromY, toX, toY, duration);\n    }\n\n    /**\n     * Performs a long press followed by an immediate drag to a location and releases.\n     * fromX(Y) are required if elementId is not provided, so do toX(Y) if destElId is not provided.\n     *\n     * @param fromX     Starting X coordinate\n     * @param fromY     Starting Y coordinate\n     * @param toX       Ending X coordinate\n     * @param toY       Ending Y coordinate\n     * @param duration  Duration of the action in milliseconds\n     * @param elementId ID of the original element (optional), for specific interaction scenarios\n     * @param destElId  ID of the target element (optional), for specific interaction scenarios\n     * @throws SonicRespException Throws when the operation fails\n     */\n    public void drag(int fromX, int fromY, int toX, int toY, Integer duration, String elementId, String destElId) throws SonicRespException {\n        uiaClient.drag(fromX, fromY, toX, toY, duration, elementId, destElId);\n    }\n\n    /**\n     * Performs a touch action.\n     * This method delegates to the UIA client's touchAction method to simulate\n     * a touch event at a specified position on the screen with a given action type.\n     *\n     * @param methodType The type of touch action, enumerate in (down, up, move).\n     *                   Specific supported types depend on the UIA client's implementation.\n     * @param x          The X coordinate of the touch point.\n     * @param y          The Y coordinate of the touch point.\n     * @throws SonicRespException If an error occurs while performing the touch action,\n     *                            this exception is thrown.\n     */\n    public void touchAction(String methodType, int x, int y) throws SonicRespException {\n        uiaClient.touchAction(methodType, x, y);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/enmus/AndroidSelector.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.android.enmus;\n\npublic enum AndroidSelector {\n    CLASS_NAME(\"class name\"),\n    Id(\"id\"),\n    ACCESSIBILITY_ID(\"accessibility id\"),\n\n    XPATH(\"xpath\"),\n\n    UIAUTOMATOR(\"-android uiautomator\");\n\n    private final String selector;\n\n    AndroidSelector(String selector) {\n        this.selector = selector;\n    }\n\n    public String getSelector() {\n        return selector;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/service/AndroidElement.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.android.service;\n\nimport org.cloud.sonic.driver.common.models.BaseElement;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\n/**\n * @author Eason\n * web element interface\n */\npublic interface AndroidElement extends BaseElement {\n\n    void click() throws SonicRespException;\n\n    void sendKeys(String text) throws SonicRespException;\n\n    void sendKeys(String text, boolean isCover) throws SonicRespException;\n\n    void clear() throws SonicRespException;\n\n    String getText() throws SonicRespException;\n\n    byte[] screenshot() throws SonicRespException;\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/service/UiaClient.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.android.service;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.Logger;\nimport org.cloud.sonic.driver.common.tool.RespHandler;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\nimport java.util.List;\n\n/**\n * @author Eason\n * uia client interface\n */\npublic interface UiaClient {\n    //Client Setting\n    void setGlobalTimeOut(int timeOut);\n\n    RespHandler getRespHandler();\n\n    void setRespHandler(RespHandler respHandler);\n\n    Logger getLogger();\n\n    void showLog();\n\n    void disableLog();\n\n    //Session handler.\n    String getRemoteUrl();\n\n    void setRemoteUrl(String remoteUrl);\n\n    String getSessionId();\n\n    void setSessionId(String sessionId);\n\n    void newSession(JSONObject capabilities) throws SonicRespException;\n\n    void closeSession() throws SonicRespException;\n\n    void checkSessionId() throws SonicRespException;\n\n    //window handler.\n    WindowSize getWindowSize() throws SonicRespException;\n\n    //keyboard handler.\n    void sendKeys(String text, boolean isCover) throws SonicRespException;\n\n    void setPasteboard(String contentType, String content) throws SonicRespException;\n\n    byte[] getPasteboard(String contentType) throws SonicRespException;\n\n    //source handler.\n    String pageSource() throws SonicRespException;\n\n    //element handler.\n    void setDefaultFindElementInterval(Integer retry, Integer interval);\n\n    AndroidElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException;\n\n    List<AndroidElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException;\n\n    //screen handler.\n    byte[] screenshot() throws SonicRespException;\n\n    //appium setting handler.\n    void setAppiumSettings(JSONObject settings) throws SonicRespException;\n\n    void tap(int x, int y) throws SonicRespException;\n\n    void longPress(double x, double y, double ms) throws SonicRespException;\n\n    void swipe(int fromX, int fromY, int toX, int toY, Integer duration) throws SonicRespException;\n\n    void drag(int fromX, int fromY, int toX, int toY, Integer duration, String elementId, String destElId) throws SonicRespException;\n\n    // touch handler.\n    void touchAction(String methodType, int x, int y) throws SonicRespException;\n\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/service/impl/AndroidElementImpl.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.android.service.impl;\n\nimport cn.hutool.http.HttpUtil;\nimport com.alibaba.fastjson.JSONObject;\nimport com.alibaba.fastjson2.JSON;\nimport org.cloud.sonic.driver.android.service.AndroidElement;\nimport org.cloud.sonic.driver.android.service.UiaClient;\nimport org.cloud.sonic.driver.common.models.BaseResp;\nimport org.cloud.sonic.driver.common.models.ElementRect;\nimport org.cloud.sonic.driver.common.tool.Logger;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\nimport java.util.Base64;\n\npublic class AndroidElementImpl implements AndroidElement {\n    private String id;\n    private UiaClient uiaClient;\n    private Logger logger;\n\n    public AndroidElementImpl(String id, UiaClient uiaClient) {\n        this.id = id;\n        this.uiaClient = uiaClient;\n        logger = uiaClient.getLogger();\n    }\n\n    @Override\n    public void click() throws SonicRespException {\n        uiaClient.checkSessionId();\n        BaseResp b = uiaClient.getRespHandler().getResp(\n                HttpUtil.createPost(uiaClient.getRemoteUrl() + \"/session/\"\n                        + uiaClient.getSessionId() + \"/element/\" + id + \"/click\"));\n        if (b.getErr() == null) {\n            logger.info(\"click element %s.\", id);\n        } else {\n            logger.error(\"click element %s failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void sendKeys(String text) throws SonicRespException {\n        sendKeys(text, false);\n    }\n\n    @Override\n    public void sendKeys(String text, boolean isCover) throws SonicRespException {\n        uiaClient.checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"text\", text);\n        data.put(\"replace\", isCover);\n        BaseResp b = uiaClient.getRespHandler().getResp(\n                HttpUtil.createPost(uiaClient.getRemoteUrl() + \"/session/\"\n                                + uiaClient.getSessionId() + \"/element/\" + id + \"/value\")\n                        .body(data.toJSONString()), 60000);\n        if (b.getErr() == null) {\n            logger.info(\"send key to %s.\", id);\n        } else {\n            logger.error(\"send key to %s failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void clear() throws SonicRespException {\n        uiaClient.checkSessionId();\n        BaseResp b = uiaClient.getRespHandler().getResp(\n                HttpUtil.createPost(uiaClient.getRemoteUrl() + \"/session/\"\n                        + uiaClient.getSessionId() + \"/element/\" + id + \"/clear\"), 60000);\n        if (b.getErr() == null) {\n            logger.info(\"clear %s.\", id);\n        } else {\n            logger.error(\"clear %s failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public String getText() throws SonicRespException {\n        uiaClient.checkSessionId();\n        BaseResp b = uiaClient.getRespHandler().getResp(\n                HttpUtil.createGet(uiaClient.getRemoteUrl() + \"/session/\"\n                        + uiaClient.getSessionId() + \"/element/\" + id + \"/text\"));\n        if (b.getErr() == null) {\n            logger.info(\"get %s text %s.\", id, b.getValue().toString());\n            return b.getValue().toString();\n        } else {\n            logger.error(\"get %s text failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public String getAttribute(String name) throws SonicRespException {\n        uiaClient.checkSessionId();\n        BaseResp b = uiaClient.getRespHandler().getResp(\n                HttpUtil.createGet(uiaClient.getRemoteUrl() + \"/session/\"\n                        + uiaClient.getSessionId() + \"/element/\" + id + \"/attribute/\" + name));\n        if (b.getErr() == null) {\n            logger.info(\"get %s attribute %s result %s.\", id, name, b.getValue().toString());\n            return b.getValue().toString();\n        } else {\n            logger.error(\"get %s attribute failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public String getUniquelyIdentifies() throws SonicRespException {\n        return id;\n    }\n\n    @Override\n    public ElementRect getRect() throws SonicRespException {\n        uiaClient.checkSessionId();\n        BaseResp b = uiaClient.getRespHandler().getResp(\n                HttpUtil.createGet(uiaClient.getRemoteUrl() + \"/session/\"\n                        + uiaClient.getSessionId() + \"/element/\" + id + \"/rect\"));\n        if (b.getErr() == null) {\n            ElementRect elementRect = JSON.parseObject(b.getValue().toString(), ElementRect.class);\n            logger.info(\"get %s rect %s.\", id, elementRect.toString());\n            return elementRect;\n        } else {\n            logger.error(\"get %s rect failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public byte[] screenshot() throws SonicRespException {\n        uiaClient.checkSessionId();\n        BaseResp b = uiaClient.getRespHandler().getResp(\n                HttpUtil.createGet(uiaClient.getRemoteUrl() + \"/session/\"\n                        + uiaClient.getSessionId() + \"/element/\" + id + \"/screenshot\"), 60000);\n        if (b.getErr() == null) {\n            logger.info(\"get element %s screenshot.\", id);\n            return Base64.getMimeDecoder().decode(b.getValue().toString());\n        } else {\n            logger.error(\"get element %s screenshot failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public boolean isDisplayed() throws SonicRespException {\n        String result = getAttribute(\"displayed\");\n        return Boolean.parseBoolean(result);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/service/impl/UiaClientImpl.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.android.service.impl;\n\nimport cn.hutool.http.HttpUtil;\nimport cn.hutool.http.Method;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.android.service.AndroidElement;\nimport org.cloud.sonic.driver.android.service.UiaClient;\nimport org.cloud.sonic.driver.common.models.BaseResp;\nimport org.cloud.sonic.driver.common.models.SessionInfo;\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.Logger;\nimport org.cloud.sonic.driver.common.tool.RespHandler;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.*;\n\npublic class UiaClientImpl implements UiaClient {\n    private String remoteUrl;\n    private String sessionId;\n    private RespHandler respHandler;\n    private Logger logger;\n    private final String LEGACY_WEB_ELEMENT_IDENTIFIER = \"ELEMENT\";\n    private final String WEB_ELEMENT_IDENTIFIER = \"element-6066-11e4-a52e-4f735466cecf\";\n    private int FIND_ELEMENT_INTERVAL = 3000;\n    private int FIND_ELEMENT_RETRY = 5;\n    private WindowSize size;\n\n    public UiaClientImpl() {\n        respHandler = new RespHandler();\n        logger = new Logger();\n    }\n\n    private void checkBundleId(String bundleId) throws SonicRespException {\n        if (bundleId == null || bundleId.length() == 0) {\n            logger.error(\"bundleId not found.\");\n            throw new SonicRespException(\"bundleId not found.\");\n        }\n    }\n\n    private String parseElementId(Object o) {\n        JSONObject jsonObject = (JSONObject) o;\n        List<String> identifier = Arrays.asList(LEGACY_WEB_ELEMENT_IDENTIFIER, WEB_ELEMENT_IDENTIFIER);\n        for (String i : identifier) {\n            String result = jsonObject.getString(i);\n            if (result != null && result.length() > 0) {\n                return result;\n            }\n        }\n        return \"\";\n    }\n\n    @Override\n    public void setGlobalTimeOut(int timeOut) {\n        respHandler.setRequestTimeOut(timeOut);\n    }\n\n    @Override\n    public RespHandler getRespHandler() {\n        return respHandler;\n    }\n\n    @Override\n    public void setRespHandler(RespHandler respHandler) {\n        this.respHandler = respHandler;\n    }\n\n    @Override\n    public Logger getLogger() {\n        return logger;\n    }\n\n    @Override\n    public void showLog() {\n        logger.showLog();\n    }\n\n    @Override\n    public void disableLog() {\n        logger.disableLog();\n    }\n\n    @Override\n    public String getRemoteUrl() {\n        return remoteUrl;\n    }\n\n    @Override\n    public void setRemoteUrl(String remoteUrl) {\n        this.remoteUrl = remoteUrl;\n    }\n\n    @Override\n    public String getSessionId() {\n        return sessionId;\n    }\n\n    @Override\n    public void setSessionId(String sessionId) {\n        this.sessionId = sessionId;\n    }\n\n    @Override\n    public void newSession(JSONObject capabilities) throws SonicRespException {\n        JSONObject data = new JSONObject();\n        data.put(\"capabilities\", capabilities);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session\").body(data.toJSONString()));\n        if (b.getErr() == null) {\n            SessionInfo sessionInfo = JSON.parseObject(b.getValue().toString(), SessionInfo.class);\n            setSessionId(sessionInfo.getSessionId());\n            logger.info(\"start session successful!\");\n            logger.info(\"session : %s\", sessionInfo.getSessionId());\n        } else {\n            logger.error(\"start session failed.\");\n            logger.error(\"cause: %s\", b.getErr().toString());\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void closeSession() throws SonicRespException {\n        checkSessionId();\n        respHandler.getResp(HttpUtil.createRequest(Method.DELETE, remoteUrl + \"/session/\" + sessionId));\n        logger.info(\"close session successful!\");\n    }\n\n    @Override\n    public void checkSessionId() throws SonicRespException {\n        if (sessionId == null || sessionId.length() == 0) {\n            logger.error(\"sessionId not found.\");\n            throw new SonicRespException(\"sessionId not found.\");\n        }\n    }\n\n    @Override\n    public WindowSize getWindowSize() throws SonicRespException {\n        if (size == null) {\n            checkSessionId();\n            BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + \"/session/\" + sessionId + \"/window/:windowHandle/size\"));\n            if (b.getErr() == null) {\n                size = JSON.parseObject(b.getValue().toString(), WindowSize.class);\n                logger.info(\"get window size %s.\", size.toString());\n            } else {\n                logger.error(\"get window size failed.\");\n                throw new SonicRespException(b.getErr().getMessage());\n            }\n        }\n        return size;\n    }\n\n    @Override\n    public void sendKeys(String text, boolean isCover) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"text\", text);\n        data.put(\"replace\", isCover);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/keys\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"send key %s.\", text);\n        } else {\n            logger.error(\"send key failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void setPasteboard(String contentType, String content) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"contentType\", contentType.toUpperCase(Locale.ROOT));\n        data.put(\"content\", Base64.getEncoder().encodeToString(content.getBytes(StandardCharsets.UTF_8)));\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/appium/device/set_clipboard\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"set pasteboard %s.\", content);\n        } else {\n            logger.error(\"set pasteboard failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public byte[] getPasteboard(String contentType) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"contentType\", contentType.toUpperCase(Locale.ROOT));\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/appium/device/get_clipboard\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            byte[] result = Base64.getMimeDecoder().decode(b.getValue().toString());\n            logger.info(\"get pasteboard length: %d.\", result.length);\n            return result;\n        } else {\n            logger.error(\"get pasteboard failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public String pageSource() throws SonicRespException {\n        checkSessionId();\n        BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + \"/session/\" + sessionId + \"/source\"), 60000);\n        if (b.getErr() == null) {\n            logger.info(\"get page source.\");\n            return b.getValue().toString();\n        } else {\n            logger.error(\"get page source failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void setDefaultFindElementInterval(Integer retry, Integer interval) {\n        if (retry != null) {\n            FIND_ELEMENT_RETRY = retry;\n        }\n        if (interval != null) {\n            FIND_ELEMENT_INTERVAL = interval;\n        }\n    }\n\n    @Override\n    public AndroidElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException {\n        AndroidElement androidElement = null;\n        int wait = 0;\n        int intervalInit = (interval == null ? FIND_ELEMENT_INTERVAL : interval);\n        int retryInit = (retry == null ? FIND_ELEMENT_RETRY : retry);\n        String errMsg = \"\";\n        while (wait < retryInit) {\n            wait++;\n            checkSessionId();\n            JSONObject data = new JSONObject();\n            data.put(\"strategy\", selector);\n            data.put(\"selector\", value);\n            BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/element\")\n                    .body(data.toJSONString()));\n            if (b.getErr() == null) {\n                logger.info(\"find element successful.\");\n                String id = parseElementId(b.getValue());\n                if (id.length() > 0) {\n                    androidElement = new AndroidElementImpl(id, this);\n                    break;\n                } else {\n                    logger.error(\"parse element id %s failed. retried %d times, retry in %d ms.\", b.getValue().toString(), wait, intervalInit);\n                }\n            } else {\n                logger.error(\"element not found. retried %d times, retry in %d ms.\", wait, intervalInit);\n                errMsg = b.getErr().getMessage();\n            }\n            if (wait < retryInit) {\n                try {\n                    Thread.sleep(intervalInit);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        if (androidElement == null) {\n            throw new SonicRespException(errMsg);\n        }\n        return androidElement;\n    }\n\n    @Override\n    public List<AndroidElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException {\n        List<AndroidElement> androidElementList = new ArrayList<>();\n        int wait = 0;\n        int intervalInit = (interval == null ? FIND_ELEMENT_INTERVAL : interval);\n        int retryInit = (retry == null ? FIND_ELEMENT_RETRY : retry);\n        String errMsg = \"\";\n        while (wait < retryInit) {\n            wait++;\n            checkSessionId();\n            JSONObject data = new JSONObject();\n            data.put(\"strategy\", selector);\n            data.put(\"selector\", value);\n            BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/elements\")\n                    .body(data.toJSONString()));\n            if (b.getErr() == null) {\n                logger.info(\"find elements successful.\");\n                List<JSONObject> ids = JSON.parseObject(b.getValue().toString(), ArrayList.class);\n                for (JSONObject ele : ids) {\n                    String id = parseElementId(ele);\n                    if (id.length() > 0) {\n                        androidElementList.add(new AndroidElementImpl(id, this));\n                    } else {\n                        logger.error(\"parse element id %s failed.\", ele);\n                    }\n                }\n                if (androidElementList.size() > 0) {\n                    break;\n                }\n            } else {\n                logger.error(\"elements not found. retried %d times, retry in %d ms.\", wait, intervalInit);\n                errMsg = b.getErr().getMessage();\n            }\n            if (wait < retryInit) {\n                try {\n                    Thread.sleep(intervalInit);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        if (androidElementList.size() == 0) {\n            throw new SonicRespException(errMsg);\n        }\n        return androidElementList;\n    }\n\n    @Override\n    public byte[] screenshot() throws SonicRespException {\n        checkSessionId();\n        BaseResp b = respHandler.getResp(\n                HttpUtil.createGet(remoteUrl + \"/session/\" + sessionId + \"/screenshot\"), 60000);\n        if (b.getErr() == null) {\n            logger.info(\"get screenshot.\");\n            return Base64.getMimeDecoder().decode(b.getValue().toString());\n        } else {\n            logger.error(\"get screenshot failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void setAppiumSettings(JSONObject settings) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"settings\", settings);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/appium/settings\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"set appium settings %s.\", settings.toJSONString());\n        } else {\n            logger.error(\"set appium settings failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void tap(int x, int y) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"x\", x);\n        data.put(\"y\", y);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/appium/tap\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"perform tap action %s.\", data.toString());\n        } else {\n            logger.error(\"perform tap action failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void longPress(double x, double y, double ms) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        JSONObject touchEventParams = new JSONObject();\n        touchEventParams.put(\"x\", x);\n        touchEventParams.put(\"y\", y);\n        touchEventParams.put(\"duration\", ms);\n        data.put(\"params\", touchEventParams);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/touch/longclick\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"perform longPress action %s.\", data.toString());\n        } else {\n            logger.error(\"perform longPress action failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void swipe(int fromX, int fromY, int toX, int toY, Integer duration) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"startX\", fromX);\n        data.put(\"startY\", fromY);\n        data.put(\"endX\", toX);\n        data.put(\"endY\", toY);\n        // steps 参数为uiautomator层定义的单位\n        // example：So for a 100 steps, the swipe will take about 1/2 second to complete.\n        if (duration == null) {\n            data.put(\"steps\", 100);\n        } else {\n            data.put(\"steps\", duration / 5);\n        }\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/touch/perform\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"perform swipe action %s.\", data.toString());\n        } else {\n            logger.error(\"perform swipe action failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void drag(int fromX, int fromY, int toX, int toY, Integer duration, String elementId, String destElId) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"startX\", fromX);\n        data.put(\"startY\", fromY);\n        data.put(\"endX\", toX);\n        data.put(\"endY\", toY);\n        data.put(\"steps\", duration != null && duration > 0 ? duration / 5 : 100);\n        data.put(\"elementId\", elementId);\n        data.put(\"destElId\", destElId);\n        BaseResp move = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/touch/drag\").body(data.toJSONString()));\n        if (move.getErr() == null) {\n            logger.info(\"perform drag action %s.\", data.toString());\n        } else {\n            logger.error(\"perform drag action failed.\");\n            throw new SonicRespException(move.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void touchAction(String methodType, int x, int y) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        JSONObject touchEventParams = new JSONObject();\n        touchEventParams.put(\"x\", x);\n        touchEventParams.put(\"y\", y);\n        data.put(\"params\", touchEventParams);\n        String path = \"/touch/down\";\n        switch (methodType) {\n            case \"down\":\n                break;\n            case \"up\":\n                path = \"/touch/up\";\n                break;\n            case \"move\":\n                path = \"/touch/move\";\n                break;\n            default:\n                throw new RuntimeException(\"methodType error.\");\n        }\n\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + path)\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(String.format(\"perform touch %s action %s.\", methodType, data));\n        } else {\n            logger.error(\"perform touch %s action failed.\", methodType);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/enums/PasteboardType.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.common.enums;\n\npublic enum PasteboardType {\n    PLAIN_TEXT(\"plaintext\"),\n    IMAGE(\"image\"),\n    URL(\"url\");\n\n    private final String type;\n\n    PasteboardType(String pasteboardType) {\n        this.type = pasteboardType;\n    }\n\n    public String getType() {\n        return type;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/BaseElement.java",
    "content": "package org.cloud.sonic.driver.common.models;\n\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\npublic interface BaseElement {\n    ElementRect getRect() throws SonicRespException;\n\n    String getAttribute(String name) throws SonicRespException;\n\n    // the xpath or id or csspath...\n    String getUniquelyIdentifies() throws SonicRespException;\n\n//    List<BaseElement> getChildren() throws SonicRespException;\n    /**\n     * Is this element displayed or not?\n     * This method avoids the problem of having to parse an element's \"style\" attribute.\n     *\n     * @return whether the element is displayed\n     */\n    boolean isDisplayed() throws SonicRespException;\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/BaseResp.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.common.models;\n\nimport lombok.Getter;\nimport lombok.NoArgsConstructor;\n\n@Getter\n@NoArgsConstructor\npublic class BaseResp<T> {\n    private String sessionId;\n    private ErrorMsg err;\n    private T value;\n\n    public void setErr(ErrorMsg err) {\n        this.err = err;\n    }\n\n    public void setValue(T value) {\n        this.value = value;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/Capabilities.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.common.models;\n\nimport lombok.AllArgsConstructor;\nimport lombok.ToString;\n\n@ToString\n@AllArgsConstructor\npublic class Capabilities {\n    private String device;\n    private String browserName;\n    private String sdkVersion;\n    private String CFBundleIdentifier;\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/ElementRect.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.common.models;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.ToString;\n\n@Getter\n@ToString\n@AllArgsConstructor\npublic class ElementRect {\n    private int x;\n    private int y;\n    private int width;\n    private int height;\n\n    @Getter\n    @ToString\n    @AllArgsConstructor\n    public class IOSRectCenter {\n        private int x;\n        private int y;\n    }\n\n    public IOSRectCenter getCenter() {\n        return new IOSRectCenter(x / 2, y / 2);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/ErrorMsg.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.common.models;\n\nimport lombok.AllArgsConstructor;\nimport lombok.ToString;\n\n@ToString\n@AllArgsConstructor\npublic class ErrorMsg {\n    private String error;\n    private String message;\n    private String traceback;\n\n    public String getMessage() {\n        return message;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/SessionInfo.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.common.models;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.ToString;\n\n@Getter\n@ToString\n@AllArgsConstructor\npublic class SessionInfo {\n    private String sessionId;\n    private Capabilities capabilities;\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/WindowSize.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.common.models;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.ToString;\n\n@Getter\n@ToString\n@AllArgsConstructor\npublic class WindowSize {\n    private int width;\n    private int height;\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/tool/Logger.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.common.tool;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\npublic class Logger {\n    private SimpleDateFormat formatter;\n    private boolean isShowLog;\n\n    public Logger() {\n        formatter = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        isShowLog = true;\n    }\n\n    public void showLog() {\n        isShowLog = true;\n    }\n\n    public void disableLog() {\n        isShowLog = false;\n    }\n\n    private void print(String level, String msg, Object... args) {\n        if (isShowLog) {\n            System.out.println(String.format(\"[sonic-driver-core] %s [%s] %s\",\n                    formatter.format(new Date()), level, String.format(msg, args)));\n        }\n    }\n\n    public void info(String msg, Object... args) {\n        print(\"INFO\", msg, args);\n    }\n\n    public void error(String msg, Object... args) {\n        print(\"ERROR\", msg, args);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/tool/RespHandler.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.common.tool;\n\nimport cn.hutool.core.io.IORuntimeException;\nimport cn.hutool.http.HttpException;\nimport cn.hutool.http.HttpRequest;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.models.BaseResp;\nimport org.cloud.sonic.driver.common.models.ErrorMsg;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class RespHandler {\n    public static final int DEFAULT_REQUEST_TIMEOUT = 15000;\n    private int requestTimeout = 15000;\n\n    public void setRequestTimeOut(int timeOut) {\n        requestTimeout = timeOut;\n    }\n\n    public BaseResp getResp(HttpRequest httpRequest) throws SonicRespException {\n        return getResp(httpRequest, requestTimeout);\n    }\n\n    public BaseResp getResp(HttpRequest httpRequest, int timeout) throws SonicRespException {\n        synchronized (this) {\n            try {\n                return initResp(httpRequest.addHeaders(initHeader()).timeout(timeout).execute().body());\n            } catch (HttpException | IORuntimeException e) {\n                e.printStackTrace();\n                throw new SonicRespException(e.getMessage());\n            }\n        }\n    }\n\n    public BaseResp initResp(String response) {\n        if (response.contains(\"traceback\") || response.contains(\"stacktrace\")) {\n            return initErrorMsg(response.replace(\"stacktrace\", \"traceback\"));\n        } else {\n            return JSON.parseObject(response, BaseResp.class);\n        }\n    }\n\n    public Map<String, String> initHeader() {\n        Map<String, String> headers = new HashMap<>();\n        headers.put(\"Content-Type\", \"application/json; charset=utf-8\");\n        return headers;\n    }\n\n    public BaseResp initErrorMsg(String resp) {\n        BaseResp err = JSON.parseObject(resp, BaseResp.class);\n        ErrorMsg errorMsg = JSONObject.parseObject(err.getValue().toString(), ErrorMsg.class);\n        err.setErr(errorMsg);\n        err.setValue(null);\n        return err;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/tool/SonicRespException.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.common.tool;\n\npublic class SonicRespException extends Exception {\n    public SonicRespException(String message) {\n        super(message);\n    }\n\n    public SonicRespException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/IOSDriver.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.enums.PasteboardType;\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.RespHandler;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.ios.enums.*;\nimport org.cloud.sonic.driver.ios.models.TouchActions;\nimport org.cloud.sonic.driver.ios.service.IOSElement;\nimport org.cloud.sonic.driver.ios.service.WdaClient;\nimport org.cloud.sonic.driver.ios.service.impl.IOSElementImpl;\nimport org.cloud.sonic.driver.ios.service.impl.WdaClientImpl;\n\nimport java.util.List;\n\n/**\n * @author Eason\n * ios driver\n */\npublic class IOSDriver {\n    private WdaClient wdaClient;\n\n    /**\n     * Init ios driver.\n     *\n     * @param url\n     * @throws SonicRespException\n     */\n    public IOSDriver(String url) throws SonicRespException {\n        this(url, RespHandler.DEFAULT_REQUEST_TIMEOUT);\n    }\n\n    /**\n     * Init ios driver.\n     *\n     * @param url\n     * @param timeOut\n     * @throws SonicRespException\n     */\n    public IOSDriver(String url, int timeOut) throws SonicRespException {\n        this(url, timeOut, new JSONObject());\n    }\n\n    /**\n     * Init ios driver.\n     *\n     * @param url\n     * @param cap\n     * @throws SonicRespException\n     */\n    public IOSDriver(String url, JSONObject cap) throws SonicRespException {\n        this(url, RespHandler.DEFAULT_REQUEST_TIMEOUT, cap);\n    }\n\n    /**\n     * Init ios driver.\n     *\n     * @param url\n     * @param timeOut\n     * @param cap\n     * @throws SonicRespException\n     */\n    public IOSDriver(String url, int timeOut, JSONObject cap) throws SonicRespException {\n        wdaClient = new WdaClientImpl();\n        wdaClient.setRemoteUrl(url);\n        wdaClient.setGlobalTimeOut(timeOut);\n        wdaClient.newSession(cap);\n    }\n\n    /**\n     * Get wda client.\n     *\n     * @return\n     */\n    public WdaClient getWdaClient() {\n        return wdaClient;\n    }\n\n    /**\n     * get wda sessionId.\n     *\n     * @return\n     */\n    public String getSessionId() {\n        return wdaClient.getSessionId();\n    }\n\n    /**\n     * destroy sessionId.\n     *\n     * @throws SonicRespException\n     */\n    public void closeDriver() throws SonicRespException {\n        wdaClient.closeSession();\n    }\n\n    /**\n     * show log.\n     */\n    public void showLog() {\n        wdaClient.showLog();\n    }\n\n    /**\n     * disable log.\n     */\n    public void disableLog() {\n        wdaClient.disableLog();\n    }\n\n    /**\n     * get device window size.\n     *\n     * @return\n     * @throws SonicRespException\n     */\n    public WindowSize getWindowSize() throws SonicRespException {\n        return wdaClient.getWindowSize();\n    }\n\n    /**\n     * get device lock status.\n     *\n     * @return\n     * @throws SonicRespException\n     */\n    public boolean isLocked() throws SonicRespException {\n        return wdaClient.isLocked();\n    }\n\n    /**\n     * lock device.\n     *\n     * @throws SonicRespException\n     */\n    public void lock() throws SonicRespException {\n        wdaClient.lock();\n    }\n\n    /**\n     * unlock device.\n     *\n     * @throws SonicRespException\n     */\n    public void unlock() throws SonicRespException {\n        wdaClient.unlock();\n    }\n\n    /**\n     * tap position on screen.\n     *\n     * @param x\n     * @param y\n     * @throws SonicRespException\n     */\n    public void tap(int x, int y) throws SonicRespException {\n        performTouchAction(new TouchActions.FingerTouchAction().press(x, y).release());\n    }\n\n    /**\n     * double tap position on screen\n     * @param x\n     * @param y\n     * @throws SonicRespException\n     */\n    public void doubleTap(int x, int y) throws SonicRespException {\n        wdaClient.doubleTap(x,y);\n    }\n\n    /**\n     * long press position on screen.\n     *\n     * @param x\n     * @param y\n     * @param ms\n     * @throws SonicRespException\n     */\n    public void longPress(int x, int y, int ms) throws SonicRespException {\n        performTouchAction(new TouchActions.FingerTouchAction().press(x, y).wait(ms).release());\n    }\n\n    /**\n     * swipe position on screen.\n     *\n     * @param fromX\n     * @param fromY\n     * @param toX\n     * @param toY\n     * @throws SonicRespException\n     */\n    public void swipe(int fromX, int fromY, int toX, int toY) throws SonicRespException {\n        performTouchAction(new TouchActions.FingerTouchAction().press(fromX, fromY).wait(300).move(toX, toY).wait(10).release());\n    }\n\n    /**\n     * swipe position on screen with target time\n     *\n     * @param fromX\n     * @param fromY\n     * @param toX\n     * @param toY\n     * @param duration 滑动完成的时间，单位为毫秒\n     * @throws SonicRespException\n     */\n    public void swipe(double fromX, double fromY, double toX, double toY, double duration) throws SonicRespException {\n        wdaClient.swipe(fromX, fromY, toX, toY, duration);\n    }\n\n    /**\n     * perform touch action.\n     *\n     * @param touchActions\n     * @throws SonicRespException\n     */\n    public void performTouchAction(TouchActions touchActions) throws SonicRespException {\n        wdaClient.performTouchAction(touchActions);\n    }\n\n    public void performTouchAction(TouchActions.FingerTouchAction fingerTouchActions) throws SonicRespException {\n        performTouchAction(new TouchActions(fingerTouchActions));\n    }\n\n    /**\n     * press system button.\n     *\n     * @param systemButton\n     * @throws SonicRespException\n     */\n    public void pressButton(String systemButton) throws SonicRespException {\n        wdaClient.pressButton(systemButton);\n    }\n\n    /**\n     * press system button.\n     *\n     * @param systemButton\n     * @throws SonicRespException\n     */\n    public void pressButton(SystemButton systemButton) throws SonicRespException {\n        pressButton(systemButton.getButton());\n    }\n\n    /**\n     * send key without element.\n     *\n     * @param text\n     * @throws SonicRespException\n     */\n    public void sendKeys(String text) throws SonicRespException {\n        sendKeys(text, 3);\n    }\n\n    /**\n     * send key without element.\n     *\n     * @param text\n     * @param frequency\n     * @throws SonicRespException\n     */\n    public void sendKeys(String text, int frequency) throws SonicRespException {\n        wdaClient.sendKeys(text, frequency);\n    }\n\n    /**\n     * send key without element.\n     *\n     * @param text\n     * @throws SonicRespException\n     */\n    public void sendKeys(TextKey text) throws SonicRespException {\n        sendKeys(text, 3);\n    }\n\n    /**\n     * send key without element.\n     *\n     * @param text\n     * @param frequency\n     * @throws SonicRespException\n     */\n    public void sendKeys(TextKey text, int frequency) throws SonicRespException {\n        sendKeys(text.getKey(), frequency);\n    }\n\n    /**\n     * set pasteboard.\n     *\n     * @param contentType\n     * @param content\n     * @throws SonicRespException\n     */\n    public void setPasteboard(String contentType, String content) throws SonicRespException {\n        wdaClient.setPasteboard(contentType, content);\n    }\n\n    /**\n     * set pasteboard.\n     *\n     * @param pasteboardType\n     * @param content\n     * @throws SonicRespException\n     */\n    public void setPasteboard(PasteboardType pasteboardType, String content) throws SonicRespException {\n        setPasteboard(pasteboardType.getType(), content);\n    }\n\n    /**\n     * get pasteboard.\n     *\n     * @param contentType\n     * @return\n     * @throws SonicRespException\n     */\n    public byte[] getPasteboard(String contentType) throws SonicRespException {\n        return wdaClient.getPasteboard(contentType);\n    }\n\n    /**\n     * get pasteboard.\n     *\n     * @param pasteboardType\n     * @return\n     * @throws SonicRespException\n     */\n    public byte[] getPasteboard(PasteboardType pasteboardType) throws SonicRespException {\n        return getPasteboard(pasteboardType.getType());\n    }\n\n    /**\n     * get page source.\n     *\n     * @return\n     * @throws SonicRespException\n     */\n    public String getPageSource() throws SonicRespException {\n        return wdaClient.pageSource();\n    }\n\n    /**\n     * send siri command.\n     *\n     * @param command\n     * @throws SonicRespException\n     */\n    public void sendSiriCommand(String command) throws SonicRespException {\n        wdaClient.sendSiriCommand(command);\n    }\n\n    /**\n     * activate app.\n     *\n     * @param bundleId\n     * @throws SonicRespException\n     */\n    public void appActivate(String bundleId) throws SonicRespException {\n        wdaClient.appActivate(bundleId);\n    }\n\n    /**\n     * terminate app and get status.\n     *\n     * @param bundleId\n     * @return\n     * @throws SonicRespException\n     */\n    public boolean appTerminate(String bundleId) throws SonicRespException {\n        return wdaClient.appTerminate(bundleId);\n    }\n\n    /**\n     * run app background in seconds.\n     *\n     * @param duration\n     * @throws SonicRespException\n     */\n    public void appRunBackground(int duration) throws SonicRespException {\n        wdaClient.appRunBackground(duration);\n    }\n\n    /**\n     * reset app auth source.\n     *\n     * @param resource\n     * @throws SonicRespException\n     */\n    public void appAuthReset(int resource) throws SonicRespException {\n        wdaClient.appAuthReset(resource);\n    }\n\n    /**\n     * reset app auth source.\n     *\n     * @param authResource\n     * @throws SonicRespException\n     */\n    public void appAuthReset(AuthResource authResource) throws SonicRespException {\n        appAuthReset(authResource.getResource());\n    }\n\n    /**\n     * set default FindElement retry time and interval.\n     *\n     * @param retry\n     * @param interval\n     */\n    public void setDefaultFindElementInterval(Integer retry, Integer interval) {\n        wdaClient.setDefaultFindElementInterval(retry, interval);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param iosSelector\n     * @param value\n     * @return\n     * @throws SonicRespException\n     */\n    public IOSElement findElement(IOSSelector iosSelector, String value) throws SonicRespException {\n        return findElement(iosSelector, value, null);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param wdaElementID This id is the id returned by the wda lookup control\n     * @return\n     * @throws SonicRespException\n     */\n    public IOSElement findElement(String wdaElementID) throws SonicRespException {\n        return new IOSElementImpl(wdaElementID, wdaClient);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param xcuiElementType\n     * @return\n     * @throws SonicRespException\n     */\n    public IOSElement findElement(XCUIElementType xcuiElementType) throws SonicRespException {\n        return findElement(xcuiElementType, null);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param selector\n     * @param value\n     * @return\n     * @throws SonicRespException\n     */\n    public IOSElement findElement(String selector, String value) throws SonicRespException {\n        return findElement(selector, value, null);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param iosSelector\n     * @param value\n     * @param retry\n     * @return\n     * @throws SonicRespException\n     */\n    public IOSElement findElement(IOSSelector iosSelector, String value, Integer retry) throws SonicRespException {\n        return findElement(iosSelector, value, retry, null);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param xcuiElementType\n     * @param retry\n     * @return\n     * @throws SonicRespException\n     */\n    public IOSElement findElement(XCUIElementType xcuiElementType, Integer retry) throws SonicRespException {\n        return findElement(xcuiElementType, retry, null);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param selector\n     * @param value\n     * @param retry\n     * @return\n     * @throws SonicRespException\n     */\n    public IOSElement findElement(String selector, String value, Integer retry) throws SonicRespException {\n        return findElement(selector, value, retry, null);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param iosSelector\n     * @param value\n     * @param retry\n     * @param interval\n     * @return\n     * @throws SonicRespException\n     */\n    public IOSElement findElement(IOSSelector iosSelector, String value, Integer retry, Integer interval) throws SonicRespException {\n        return findElement(iosSelector.getSelector(), value, retry, interval);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param xcuiElementType\n     * @param retry\n     * @param interval\n     * @return\n     * @throws SonicRespException\n     */\n    public IOSElement findElement(XCUIElementType xcuiElementType, Integer retry, Integer interval) throws SonicRespException {\n        return findElement(IOSSelector.CLASS_NAME.getSelector(), xcuiElementType.getType(), retry, interval);\n    }\n\n    /**\n     * find element in device.\n     *\n     * @param selector\n     * @param value\n     * @param retry\n     * @param interval\n     * @return\n     * @throws SonicRespException\n     */\n    public IOSElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException {\n        return wdaClient.findElement(selector, value, retry, interval);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param iosSelector\n     * @param value\n     * @return\n     * @throws SonicRespException\n     */\n    public List<IOSElement> findElementList(IOSSelector iosSelector, String value) throws SonicRespException {\n        return findElementList(iosSelector, value, null);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param xcuiElementType\n     * @return\n     * @throws SonicRespException\n     */\n    public List<IOSElement> findElementList(XCUIElementType xcuiElementType) throws SonicRespException {\n        return findElementList(xcuiElementType, null);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param selector\n     * @param value\n     * @return\n     * @throws SonicRespException\n     */\n    public List<IOSElement> findElementList(String selector, String value) throws SonicRespException {\n        return findElementList(selector, value, null);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param iosSelector\n     * @param value\n     * @param retry\n     * @return\n     * @throws SonicRespException\n     */\n    public List<IOSElement> findElementList(IOSSelector iosSelector, String value, Integer retry) throws SonicRespException {\n        return findElementList(iosSelector, value, retry, null);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param xcuiElementType\n     * @param retry\n     * @return\n     * @throws SonicRespException\n     */\n    public List<IOSElement> findElementList(XCUIElementType xcuiElementType, Integer retry) throws SonicRespException {\n        return findElementList(xcuiElementType, retry, null);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param selector\n     * @param value\n     * @param retry\n     * @return\n     * @throws SonicRespException\n     */\n    public List<IOSElement> findElementList(String selector, String value, Integer retry) throws SonicRespException {\n        return findElementList(selector, value, retry, null);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param iosSelector\n     * @param value\n     * @param retry\n     * @param interval\n     * @return\n     * @throws SonicRespException\n     */\n    public List<IOSElement> findElementList(IOSSelector iosSelector, String value, Integer retry, Integer interval) throws SonicRespException {\n        return findElementList(iosSelector.getSelector(), value, retry, interval);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param xcuiElementType\n     * @param retry\n     * @param interval\n     * @return\n     * @throws SonicRespException\n     */\n    public List<IOSElement> findElementList(XCUIElementType xcuiElementType, Integer retry, Integer interval) throws SonicRespException {\n        return findElementList(IOSSelector.CLASS_NAME.getSelector(), xcuiElementType.getType(), retry, interval);\n    }\n\n    /**\n     * find element list in device.\n     *\n     * @param selector\n     * @param value\n     * @param retry\n     * @param interval\n     * @return\n     * @throws SonicRespException\n     */\n    public List<IOSElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException {\n        return wdaClient.findElementList(selector, value, retry, interval);\n    }\n\n    /**\n     * get current active element\n     * @return\n     * @throws SonicRespException\n     */\n    public IOSElement activeElement() throws SonicRespException{\n        return wdaClient.activeElement();\n    }\n\n    /**\n     * get screenshot.\n     *\n     * @return\n     * @throws SonicRespException\n     */\n    public byte[] screenshot() throws SonicRespException {\n        return wdaClient.screenshot();\n    }\n\n    /**\n     * set appium settings.\n     *\n     * @param settings\n     * @throws SonicRespException\n     */\n    public void setAppiumSettings(JSONObject settings) throws SonicRespException {\n        wdaClient.setAppiumSettings(settings);\n    }\n\n    /**\n     * rotate screen orientation\n     * @throws SonicRespException\n     */\n    public void rotate(Orientation orientation) throws SonicRespException {\n        wdaClient.rotate(orientation);\n    }\n\n    public Orientation getRotate() throws SonicRespException {\n        return wdaClient.getRotate();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/ActionType.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.enums;\n\npublic enum ActionType {\n    PRESS(\"pointerDown\"),\n    WAIT(\"pause\"),\n    MOVE(\"pointerMove\"),\n    RELEASE(\"pointerUp\");\n\n    private final String type;\n\n    ActionType(String type) {\n        this.type = type;\n    }\n\n    public String getType() {\n        return type;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/AuthResource.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.enums;\n\npublic enum AuthResource {\n    CONTACTS(1),\n    CALENDAR(2),\n    REMINDERS(3),\n    PHOTOS(4),\n    MICROPHONE(5),\n    CAMERA(6),\n    MEDIA_LIBRARY(7),\n    HOME_KIT(8),\n    SYSTEM_ROOT_DIRECTORY(0x40000000),\n    USER_DESKTOP_DIRECTORY(0x40000001),\n    USER_DOWNLOADS_DIRECTORY(0x40000002),\n    USER_DOCUMENTS_DIRECTORY(0x40000003),\n    BLUETOOTH(-0x40000000),\n    KEYBOARD_NETWORK(-0x40000001),\n    LOCATION(-0x40000002),\n    HEALTH(-0x40000003);\n\n    private final int resource;\n\n    AuthResource(int resource) {\n        this.resource = resource;\n    }\n\n    public int getResource() {\n        return resource;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/IOSSelector.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.enums;\n\npublic enum IOSSelector {\n    CLASS_NAME(\"class name\"),\n    NAME(\"name\"),\n    Id(\"id\"),\n    ACCESSIBILITY_ID(\"accessibility id\"),\n\n    XPATH(\"xpath\"),\n    PREDICATE(\"predicate string\"),\n\n    CLASS_CHAIN(\"class chain\"),\n    LINK_TEXT(\"link text\"),\n    PARTIAL_LINK_TEXT(\"partial link text\");\n\n    private final String selector;\n\n    IOSSelector(String selector) {\n        this.selector = selector;\n    }\n\n    public String getSelector() {\n        return selector;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/Orientation.java",
    "content": "package org.cloud.sonic.driver.ios.enums;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\n\nimport java.util.HashMap;\n\npublic enum Orientation {\n    UNKNOWN(0),\n    PORTRAIT(1),\n    PORTRAITUPSIDEDOWN(2),\n    LANDSCAPELEFT(3),\n    LANDSCAPERIGHT(4);\n\n    private final int orientation;\n\n    Orientation(int orientation) {\n        this.orientation = orientation;\n    }\n\n    public JSONObject getValue() {\n        JSONObject value = new JSONObject();\n        value.put(\"x\",0);\n        value.put(\"y\",0);\n        switch (this.orientation) {\n            case 1:\n                value.put(\"z\",0);\n                break;\n            case 2:\n                value.put(\"z\",180);\n                break;\n            case 3:\n                value.put(\"z\",90);\n                break;\n            case 4:\n                value.put(\"z\",270);\n                break;\n        }\n\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/SystemButton.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.enums;\n\npublic enum SystemButton {\n    HOME(\"home\"),\n    VOLUME_UP(\"volumeUp\"),\n    VOLUME_DOWN(\"volumeDown\");\n\n    private final String button;\n\n    SystemButton(String button) {\n        this.button = button;\n    }\n\n    public String getButton() {\n        return button;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/TextKey.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.enums;\n\npublic enum TextKey {\n    BACK_SPACE(\"\\u0008\"),\n    DELETE(\"\\u007F\");\n\n    private final String key;\n\n    TextKey(String key) {\n        this.key = key;\n    }\n\n    public String getKey() {\n        return key;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/XCUIElementType.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")),\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.enums;\n\npublic enum XCUIElementType {\n    ANY(\"XCUIElementTypeAny\"),\n    OTHER(\"XCUIElementTypeOther\"),\n    APPLICATION(\"XCUIElementTypeApplication\"),\n    GROUP(\"XCUIElementTypeGroup\"),\n    WINDOW(\"XCUIElementTypeWindow\"),\n    SHEET(\"XCUIElementTypeSheet\"),\n    DRAWER(\"XCUIElementTypeDrawer\"),\n    ALERT(\"XCUIElementTypeAlert\"),\n    DIALOG(\"XCUIElementTypeDialog\"),\n    BUTTON(\"XCUIElementTypeButton\"),\n    DISCLOSURE_TRIANGLE(\"XCUIElementTypeDisclosureTriangle\"),\n    POP_UP_BUTTON(\"XCUIElementTypePopUpButton\"),\n    COMBO_BOX(\"XCUIElementTypeComboBox\"),\n    MENU_BUTTON(\"XCUIElementTypeMenuButton\"),\n    TOOLBAR_BUTTON(\"XCUIElementTypeToolbarButton\"),\n    POPOVER(\"XCUIElementTypePopover\"),\n    KEYBOARD(\"XCUIElementTypeKeyboard\"),\n    NAVIGATION_BAR(\"XCUIElementTypeNavigationBar\"),\n    KEY(\"XCUIElementTypeKey\"),\n    STATUS_BAR(\"XCUIElementTypeStatusBar\"),\n    SWITCH(\"XCUIElementTypeSwitch\"),\n    TOGGLE(\"XCUIElementTypeToggle\"),\n    LINK(\"XCUIElementTypeLink\"),\n    IMAGE(\"XCUIElementTypeImage\"),\n    ICON(\"XCUIElementTypeIcon\"),\n    SEARCH_FIELD(\"XCUIElementTypeSearchField\"),\n    STATIC_TEXT(\"XCUIElementTypeStaticText\"),\n    CHECK_BOX(\"XCUIElementTypeCheckBox\"),\n    TEXT_VIEW(\"XCUIElementTypeTextView\"),\n    SEGMENTED_CONTROL(\"XCUIElementTypeSegmentedControl\"),\n    BROWSER(\"XCUIElementTypeBrowser\"),\n    COLLECTION_VIEW(\"XCUIElementTypeCollectionView\"),\n    SLIDER(\"XCUIElementTypeSlider\"),\n    MAP(\"XCUIElementTypeMap\"),\n    WEB_VIEW(\"XCUIElementTypeWebView\"),\n    TIME_LINE(\"XCUIElementTypeTimeline\"),\n    COLOR_WELL(\"XCUIElementTypeColorWell\"),\n    HELP_TAG(\"XCUIElementTypeHelpTag\"),\n    MATTE(\"XCUIElementTypeMatte\"),\n    DOCK_ITEM(\"XCUIElementTypeDockItem\"),\n    GRID(\"XCUIElementTypeGrid\"),\n    CELL(\"XCUIElementTypeCell\"),\n    HANDLE(\"XCUIElementTypeHandle\"),\n    STEPPER(\"XCUIElementTypeStepper\"),\n    TAB(\"XCUIElementTypeTab\"),\n    TOUCH_BAR(\"XCUIElementTypeTouchBar\"),\n    STATUS_ITEM(\"XCUIElementTypeStatusItem\"),\n\n    LAYOUT_AREA(\"XCUIElementTypeLayoutArea\"),\n    LAYOUT_ITEM(\"XCUIElementTypeLayoutItem\"),\n\n    RULER(\"XCUIElementTypeRuler\"),\n    RULER_MARKER(\"XCUIElementTypeRulerMarker\"),\n\n    RADIO_BUTTON(\"XCUIElementTypeRadioButton\"),\n    RADIO_GROUP(\"XCUIElementTypeRadioGroup\"),\n\n    TAB_BAR(\"XCUIElementTypeTabBar\"),\n    TAB_GROUP(\"XCUIElementTypeTabGroup\"),\n\n    TABLE(\"XCUIElementTypeTable\"),\n    TABLE_ROW(\"XCUIElementTypeTableRow\"),\n    TABLE_COLUMN(\"XCUIElementTypeTableColumn\"),\n\n    OUTLINE(\"XCUIElementTypeOutline\"),\n    OUTLINE_ROW(\"XCUIElementTypeOutlineRow\"),\n\n    PAGE_INDICATOR(\"XCUIElementTypePageIndicator\"),\n    PROGRESS_INDICATOR(\"XCUIElementTypeProgressIndicator\"),\n    ACTIVITY_INDICATOR(\"XCUIElementTypeActivityIndicator\"),\n    RATING_INDICATOR(\"XCUIElementTypeRatingIndicator\"),\n    VALUE_INDICATOR(\"XCUIElementTypeValueIndicator\"),\n    RELEVANCE_INDICATOR(\"XCUIElementTypeRelevanceIndicator\"),\n    LEVEL_INDICATOR(\"XCUIElementTypeLevelIndicator\"),\n\n    SPLIT_GROUP(\"XCUIElementTypeSplitGroup\"),\n    SPLITTER(\"XCUIElementTypeSplitter\"),\n\n    PICKER(\"XCUIElementTypePicker\"),\n    PICKER_WHEEL(\"XCUIElementTypePickerWheel\"),\n    DATE_PICKER(\"XCUIElementTypeDatePicker\"),\n\n    SCROLL_VIEW(\"XCUIElementTypeScrollView\"),\n    SCROLL_BAR(\"XCUIElementTypeScrollBar\"),\n\n    TEXT_FIELD(\"XCUIElementTypeTextField\"),\n    SECURE_TEXT_FIELD(\"XCUIElementTypeSecureTextField\"),\n\n    MENU(\"XCUIElementTypeMenu\"),\n    MENU_ITEM(\"XCUIElementTypeMenuItem\"),\n    MENU_BAR(\"XCUIElementTypeMenuBar\"),\n    MENU_BAR_ITEM(\"XCUIElementTypeMenuBarItem\"),\n\n    INCREMENT_ARROW(\"XCUIElementTypeIncrementArrow\"),\n    DECREMENT_ARROW(\"XCUIElementTypeDecrementArrow\"),\n    ;\n\n    private final String type;\n\n    XCUIElementType(String type) {\n        this.type = type;\n    }\n\n    public String getType() {\n        return type;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/models/TouchActions.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.models;\n\nimport cn.hutool.core.map.MapUtil;\nimport lombok.Getter;\nimport lombok.ToString;\nimport org.cloud.sonic.driver.ios.enums.ActionType;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n@Getter\n@ToString\npublic class TouchActions {\n\n    private List<FingerTouchAction> actions;\n\n    @Getter\n    @ToString\n    public static class FingerTouchAction {\n        private final String id;\n        private final String type = \"pointer\";\n        private final Map<String, String> parameters = MapUtil.of(\"pointerType\", \"touch\");\n        private final List<TouchAction> actions;\n\n        public FingerTouchAction(String fingerName) {\n            this.id = \"finger-\" + fingerName;\n            actions = new ArrayList<>();\n        }\n\n        public FingerTouchAction() {\n            this(\"0\");\n        }\n\n        public FingerTouchAction press(int x, int y) {\n            move(x, y);\n            TouchAction touchAction = new TouchAction(ActionType.PRESS);\n            actions.add(touchAction);\n            return this;\n        }\n\n        public FingerTouchAction wait(int ms) {\n            PauseAction touchAction = new PauseAction();\n            touchAction.duration = ms;\n            actions.add(touchAction);\n            return this;\n        }\n\n        public FingerTouchAction move(int x, int y) {\n            MoveAction touchAction = new MoveAction();\n            touchAction.x = x;\n            touchAction.y = y;\n            actions.add(touchAction);\n            return this;\n        }\n\n        public FingerTouchAction release() {\n            TouchAction touchAction = new TouchAction(ActionType.RELEASE);\n            actions.add(touchAction);\n            return this;\n        }\n    }\n\n    @Getter\n    @ToString\n    public static class TouchAction {\n        private final String type;\n\n        public TouchAction(ActionType actionType) {\n            this.type = actionType.getType();\n        }\n    }\n\n    @Getter\n    @ToString(callSuper = true)\n    public static class MoveAction extends TouchAction {\n        private int x;\n        private int y;\n\n        public MoveAction() {\n            super(ActionType.MOVE);\n        }\n    }\n\n    @Getter\n    @ToString(callSuper = true)\n    public static class PauseAction extends TouchAction {\n        private int duration;\n\n        public PauseAction() {\n            super(ActionType.WAIT);\n        }\n    }\n\n    public TouchActions() {\n        actions = new ArrayList<>();\n    }\n\n    public TouchActions(FingerTouchAction finger) {\n        this();\n        this.actions.add(finger);\n    }\n\n    public FingerTouchAction finger(String name) {\n        FingerTouchAction finger = new FingerTouchAction(name);\n        actions.add(finger);\n        return finger;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/service/IOSElement.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.service;\n\nimport org.cloud.sonic.driver.common.models.BaseElement;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\n/**\n * @author Eason\n * web element interface\n */\npublic interface IOSElement extends BaseElement {\n\n    void click() throws SonicRespException;\n\n    void sendKeys(String text) throws SonicRespException;\n\n    void sendKeys(String text, int frequency) throws SonicRespException;\n\n    void clear() throws SonicRespException;\n\n    String getText() throws SonicRespException;\n\n    byte[] screenshot() throws SonicRespException;\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/service/WdaClient.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.service;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.Logger;\nimport org.cloud.sonic.driver.common.tool.RespHandler;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.ios.enums.Orientation;\nimport org.cloud.sonic.driver.ios.models.TouchActions;\n\nimport java.util.List;\n\n/**\n * @author Eason\n * wda client interface\n */\npublic interface WdaClient {\n    //Client Setting\n    void setGlobalTimeOut(int timeOut);\n\n    RespHandler getRespHandler();\n\n    void setRespHandler(RespHandler respHandler);\n\n    Logger getLogger();\n\n    void showLog();\n\n    void disableLog();\n\n    //Session handler.\n    String getRemoteUrl();\n\n    void setRemoteUrl(String remoteUrl);\n\n    String getSessionId();\n\n    void setSessionId(String sessionId);\n\n    void newSession(JSONObject capabilities) throws SonicRespException;\n\n    void closeSession() throws SonicRespException;\n\n    void checkSessionId() throws SonicRespException;\n\n    //window handler.\n    WindowSize getWindowSize() throws SonicRespException;\n\n    //lock handler.\n    boolean isLocked() throws SonicRespException;\n\n    void lock() throws SonicRespException;\n\n    void unlock() throws SonicRespException;\n\n    //perform handler.\n    void performTouchAction(TouchActions touchActions) throws SonicRespException;\n    \n    //button handler.\n    void pressButton(String buttonName) throws SonicRespException;\n\n    void doubleTap(int x, int y) throws SonicRespException;\n\n    //keyboard handler.\n    void sendKeys(String text, Integer frequency) throws SonicRespException;\n\n    void setPasteboard(String contentType, String content) throws SonicRespException;\n\n    byte[] getPasteboard(String contentType) throws SonicRespException;\n\n    //source handler.\n    String pageSource() throws SonicRespException;\n\n    //siri handler.\n    void sendSiriCommand(String command) throws SonicRespException;\n\n    //app handler.\n    void appActivate(String bundleId) throws SonicRespException;\n\n    boolean appTerminate(String bundleId) throws SonicRespException;\n\n    void appRunBackground(int duration) throws SonicRespException;\n\n    void appAuthReset(int resource) throws SonicRespException;\n\n    //element handler.\n    void setDefaultFindElementInterval(Integer retry, Integer interval);\n\n    IOSElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException;\n\n    List<IOSElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException;\n\n    // get current active element\n    IOSElement activeElement() throws SonicRespException;\n\n    //screen handler.\n    byte[] screenshot() throws SonicRespException;\n\n    //appium setting handler.\n    void setAppiumSettings(JSONObject settings) throws SonicRespException;\n\n    void swipe(double fromX, double fromY, double toX, double toY, double duration) throws SonicRespException;\n    void rotate(Orientation orientation) throws SonicRespException;\n    Orientation getRotate() throws SonicRespException;\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/service/impl/IOSElementImpl.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.service.impl;\n\nimport cn.hutool.http.HttpUtil;\nimport com.alibaba.fastjson.JSONObject;\nimport com.alibaba.fastjson2.JSON;\nimport org.cloud.sonic.driver.common.models.BaseResp;\nimport org.cloud.sonic.driver.common.models.ElementRect;\nimport org.cloud.sonic.driver.common.tool.Logger;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.ios.service.IOSElement;\nimport org.cloud.sonic.driver.ios.service.WdaClient;\n\nimport java.util.Base64;\n\npublic class IOSElementImpl implements IOSElement {\n    private String id;\n    private WdaClient wdaClient;\n    private Logger logger;\n\n    public IOSElementImpl(String id, WdaClient wdaClient) {\n        this.id = id;\n        this.wdaClient = wdaClient;\n        logger = wdaClient.getLogger();\n    }\n\n    @Override\n    public void click() throws SonicRespException {\n        wdaClient.checkSessionId();\n        BaseResp b = wdaClient.getRespHandler().getResp(\n                HttpUtil.createPost(wdaClient.getRemoteUrl() + \"/session/\"\n                        + wdaClient.getSessionId() + \"/element/\" + id + \"/click\"));\n        if (b.getErr() == null) {\n            logger.info(\"click element %s.\", id);\n        } else {\n            logger.error(\"click element %s failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void sendKeys(String text) throws SonicRespException {\n        sendKeys(text, 3);\n    }\n\n    @Override\n    public void sendKeys(String text, int frequency) throws SonicRespException {\n        wdaClient.checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"value\", text.split(\"\"));\n        data.put(\"frequency\", frequency);\n        BaseResp b = wdaClient.getRespHandler().getResp(\n                HttpUtil.createPost(wdaClient.getRemoteUrl() + \"/session/\"\n                                + wdaClient.getSessionId() + \"/element/\" + id + \"/value\")\n                        .body(data.toJSONString()), 60000);\n        if (b.getErr() == null) {\n            logger.info(\"send key to %s.\", id);\n        } else {\n            logger.error(\"send key to %s failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void clear() throws SonicRespException {\n        wdaClient.checkSessionId();\n        BaseResp b = wdaClient.getRespHandler().getResp(\n                HttpUtil.createPost(wdaClient.getRemoteUrl() + \"/session/\"\n                        + wdaClient.getSessionId() + \"/element/\" + id + \"/clear\"), 60000);\n        if (b.getErr() == null) {\n            logger.info(\"clear %s.\", id);\n        } else {\n            logger.error(\"clear %s failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public String getText() throws SonicRespException {\n        wdaClient.checkSessionId();\n        BaseResp b = wdaClient.getRespHandler().getResp(\n                HttpUtil.createGet(wdaClient.getRemoteUrl() + \"/session/\"\n                        + wdaClient.getSessionId() + \"/element/\" + id + \"/text\"));\n        if (b.getErr() == null) {\n            logger.info(\"get %s text %s.\", id, b.getValue().toString());\n            return b.getValue().toString();\n        } else {\n            logger.error(\"get %s text failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public ElementRect getRect() throws SonicRespException {\n        wdaClient.checkSessionId();\n        BaseResp b = wdaClient.getRespHandler().getResp(\n                HttpUtil.createGet(wdaClient.getRemoteUrl() + \"/session/\"\n                        + wdaClient.getSessionId() + \"/element/\" + id + \"/rect\"));\n        if (b.getErr() == null) {\n            ElementRect elementRect = JSON.parseObject(b.getValue().toString(), ElementRect.class);\n            logger.info(\"get %s rect %s.\", id, elementRect.toString());\n            return elementRect;\n        } else {\n            logger.error(\"get %s rect failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public String getAttribute(String name) throws SonicRespException {\n        wdaClient.checkSessionId();\n        BaseResp b = wdaClient.getRespHandler().getResp(\n                HttpUtil.createGet(wdaClient.getRemoteUrl() + \"/session/\"\n                        + wdaClient.getSessionId() + \"/element/\" + id + \"/attribute/\" + name), 60000);\n        if (b.getErr() == null) {\n            logger.info(\"get %s attribute %s.\", id, name);\n            return b.getValue().toString();\n        } else {\n            logger.info(\"get %s attribute %s.\", id, name);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public String getUniquelyIdentifies() throws SonicRespException {\n        return id;\n    }\n\n    @Override\n    public byte[] screenshot() throws SonicRespException {\n        wdaClient.checkSessionId();\n        BaseResp b = wdaClient.getRespHandler().getResp(\n                HttpUtil.createGet(wdaClient.getRemoteUrl() + \"/session/\"\n                        + wdaClient.getSessionId() + \"/element/\" + id + \"/screenshot\"), 60000);\n        if (b.getErr() == null) {\n            logger.info(\"get element %s screenshot.\", id);\n            return Base64.getMimeDecoder().decode(b.getValue().toString());\n        } else {\n            logger.error(\"get element %s screenshot failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public boolean isDisplayed() throws SonicRespException {\n        wdaClient.checkSessionId();\n        BaseResp b = wdaClient.getRespHandler().getResp(\n                HttpUtil.createGet(wdaClient.getRemoteUrl() + \"/session/\"\n                        + wdaClient.getSessionId() + \"/element/\" + id + \"/displayed\"));\n        if (b.getErr() == null) {\n            logger.info(\"get %s displayed,result is %s.\", id, b.getValue().toString());\n            return Boolean.parseBoolean(b.getValue().toString());\n        } else {\n            logger.error(\"get %s displayed failed.\", id);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/service/impl/WdaClientImpl.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.service.impl;\n\nimport cn.hutool.http.HttpUtil;\nimport cn.hutool.http.Method;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.models.BaseResp;\nimport org.cloud.sonic.driver.common.models.SessionInfo;\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.Logger;\nimport org.cloud.sonic.driver.common.tool.RespHandler;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.ios.enums.Orientation;\nimport org.cloud.sonic.driver.ios.models.TouchActions;\nimport org.cloud.sonic.driver.ios.service.IOSElement;\nimport org.cloud.sonic.driver.ios.service.WdaClient;\nimport org.jsoup.select.CombiningEvaluator;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.*;\n\npublic class WdaClientImpl implements WdaClient {\n    private String remoteUrl;\n    private String sessionId;\n    private RespHandler respHandler;\n    private Logger logger;\n    private final String LEGACY_WEB_ELEMENT_IDENTIFIER = \"ELEMENT\";\n    private final String WEB_ELEMENT_IDENTIFIER = \"element-6066-11e4-a52e-4f735466cecf\";\n    private int FIND_ELEMENT_INTERVAL = 3000;\n    private int FIND_ELEMENT_RETRY = 5;\n\n    public WdaClientImpl() {\n        respHandler = new RespHandler();\n        logger = new Logger();\n    }\n\n    private void checkBundleId(String bundleId) throws SonicRespException {\n        if (bundleId == null || bundleId.length() == 0) {\n            logger.error(\"bundleId not found.\");\n            throw new SonicRespException(\"bundleId not found.\");\n        }\n    }\n\n    private String parseElementId(Object o) {\n        JSONObject jsonObject = (JSONObject) o;\n        List<String> identifier = Arrays.asList(LEGACY_WEB_ELEMENT_IDENTIFIER, WEB_ELEMENT_IDENTIFIER);\n        for (String i : identifier) {\n            String result = jsonObject.getString(i);\n            if (result != null && result.length() > 0) {\n                return result;\n            }\n        }\n        return \"\";\n    }\n\n    @Override\n    public void setGlobalTimeOut(int timeOut) {\n        respHandler.setRequestTimeOut(timeOut);\n    }\n\n    @Override\n    public RespHandler getRespHandler() {\n        return respHandler;\n    }\n\n    @Override\n    public void setRespHandler(RespHandler respHandler) {\n        this.respHandler = respHandler;\n    }\n\n    @Override\n    public Logger getLogger() {\n        return logger;\n    }\n\n    @Override\n    public void showLog() {\n        logger.showLog();\n    }\n\n    @Override\n    public void disableLog() {\n        logger.disableLog();\n    }\n\n    @Override\n    public String getRemoteUrl() {\n        return remoteUrl;\n    }\n\n    @Override\n    public void setRemoteUrl(String remoteUrl) {\n        this.remoteUrl = remoteUrl;\n    }\n\n    @Override\n    public String getSessionId() {\n        return sessionId;\n    }\n\n    @Override\n    public void setSessionId(String sessionId) {\n        this.sessionId = sessionId;\n    }\n\n    @Override\n    public void newSession(JSONObject capabilities) throws SonicRespException {\n        JSONObject data = new JSONObject();\n        data.put(\"capabilities\", capabilities);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session\").body(data.toJSONString()));\n        if (b.getErr() == null) {\n            SessionInfo sessionInfo = JSON.parseObject(b.getValue().toString(), SessionInfo.class);\n            setSessionId(sessionInfo.getSessionId());\n            logger.info(\"start session successful!\");\n            logger.info(\"session : %s\", sessionInfo.getSessionId());\n            logger.info(\"session capabilities : %s\", sessionInfo.getCapabilities().toString());\n        } else {\n            logger.error(\"start session failed.\");\n            logger.error(\"cause: %s\", b.getErr().toString());\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void closeSession() throws SonicRespException {\n        checkSessionId();\n        respHandler.getResp(HttpUtil.createRequest(Method.DELETE, remoteUrl + \"/session/\" + sessionId));\n        logger.info(\"close session successful!\");\n    }\n\n    @Override\n    public void checkSessionId() throws SonicRespException {\n        if (sessionId == null || sessionId.length() == 0) {\n            logger.error(\"sessionId not found.\");\n            throw new SonicRespException(\"sessionId not found.\");\n        }\n    }\n\n    @Override\n    public WindowSize getWindowSize() throws SonicRespException {\n        checkSessionId();\n        BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + \"/session/\" + sessionId + \"/window/size\"));\n        if (b.getErr() == null) {\n            logger.info(\"get window size %s.\", b.getValue().toString());\n            return JSON.parseObject(b.getValue().toString(), WindowSize.class);\n        } else {\n            logger.error(\"get window size failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public boolean isLocked() throws SonicRespException {\n        checkSessionId();\n        BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + \"/session/\" + sessionId + \"/wda/locked\"));\n        if (b.getErr() == null) {\n            logger.info(\"device lock status: %b.\", b.getValue());\n            return (boolean) b.getValue();\n        } else {\n            logger.error(\"get device lock status failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void lock() throws SonicRespException {\n        checkSessionId();\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/lock\"));\n        if (b.getErr() == null) {\n            logger.info(\"lock device.\");\n        } else {\n            logger.error(\"lock device failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void unlock() throws SonicRespException {\n        checkSessionId();\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/unlock\"));\n        if (b.getErr() == null) {\n            logger.info(\"unlock device.\");\n        } else {\n            logger.error(\"unlock device failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void performTouchAction(TouchActions touchActions) throws SonicRespException {\n        checkSessionId();\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/actions\")\n                .body(JSON.toJSONString(touchActions)));\n        if (b.getErr() == null) {\n            logger.info(\"perform action %s.\", touchActions.toString());\n        } else {\n            logger.error(\"perform failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void pressButton(String buttonName) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"name\", buttonName);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/pressButton\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"press button %s.\", buttonName);\n        } else {\n            logger.error(\"press button failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void doubleTap(int x, int y) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"x\",x);\n        data.put(\"y\",y);\n\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/doubleTap\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"double tap success. %s\", data.toJSONString());\n        } else {\n            logger.error(\"double tap failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void sendKeys(String text, Integer frequency) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"value\", text.split(\"\"));\n        data.put(\"frequency\", frequency);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/keys\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"send key %s.\", text);\n        } else {\n            logger.error(\"send key failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void setPasteboard(String contentType, String content) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"contentType\", contentType);\n        data.put(\"content\", Base64.getEncoder().encodeToString(content.getBytes(StandardCharsets.UTF_8)));\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/setPasteboard\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"set pasteboard %s.\", content);\n        } else {\n            logger.error(\"set pasteboard failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public byte[] getPasteboard(String contentType) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"contentType\", contentType);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/getPasteboard\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            byte[] result = Base64.getMimeDecoder().decode(b.getValue().toString());\n            logger.info(\"get pasteboard length: %d.\", result.length);\n            return result;\n        } else {\n            logger.error(\"get pasteboard failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public String pageSource() throws SonicRespException {\n        checkSessionId();\n        BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + \"/session/\" + sessionId + \"/source\"), 5 * 60 * 1000);\n        if (b.getErr() == null) {\n            logger.info(\"get page source.\");\n            return b.getValue().toString();\n        } else {\n            logger.error(\"get page source failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void sendSiriCommand(String command) throws SonicRespException {\n        if (command != null && command.length() != 0) {\n            checkSessionId();\n            JSONObject data = new JSONObject();\n            data.put(\"text\", command);\n            BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/siri/activate\")\n                    .body(data.toJSONString()));\n            if (b.getErr() == null) {\n                logger.info(\"send siri command: %s\", command);\n            } else {\n                logger.error(\"send siri command [%s] failed.\", command);\n                throw new SonicRespException(b.getErr().getMessage());\n            }\n        } else {\n            logger.error(\"siri command is null!\");\n            throw new SonicRespException(\"siri command is null!\");\n        }\n    }\n\n    @Override\n    public void appActivate(String bundleId) throws SonicRespException {\n        checkSessionId();\n        checkBundleId(bundleId);\n        JSONObject data = new JSONObject();\n        data.put(\"bundleId\", bundleId);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/apps/activate\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"activate app %s.\", bundleId);\n        } else {\n            logger.error(\"activate app %s failed.\", bundleId);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public boolean appTerminate(String bundleId) throws SonicRespException {\n        checkSessionId();\n        checkBundleId(bundleId);\n        JSONObject data = new JSONObject();\n        data.put(\"bundleId\", bundleId);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/apps/terminate\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"terminate app %s status: %b.\", bundleId, b.getValue());\n            return (boolean) b.getValue();\n        } else {\n            logger.error(\"terminate app failed.\", bundleId);\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void appRunBackground(int duration) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"duration\", duration);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/deactivateApp\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"run app background in %d seconds.\", duration);\n        } else {\n            logger.error(\"run app background failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void appAuthReset(int resource) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"resource\", resource);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/resetAppAuth\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"reset app auth %s.\", resource);\n        } else {\n            logger.error(\"reset app auth failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void setDefaultFindElementInterval(Integer retry, Integer interval) {\n        if (retry != null) {\n            FIND_ELEMENT_RETRY = retry;\n        }\n        if (interval != null) {\n            FIND_ELEMENT_INTERVAL = interval;\n        }\n    }\n\n    @Override\n    public IOSElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException {\n        IOSElement iosElement = null;\n        int wait = 0;\n        int intervalInit = (interval == null ? FIND_ELEMENT_INTERVAL : interval);\n        int retryInit = (retry == null ? FIND_ELEMENT_RETRY : retry);\n        String errMsg = \"\";\n        while (wait < retryInit) {\n            wait++;\n            checkSessionId();\n            JSONObject data = new JSONObject();\n            data.put(\"using\", selector);\n            data.put(\"value\", value);\n            BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/element\")\n                    .body(data.toJSONString()));\n            if (b.getErr() == null) {\n                logger.info(\"find element successful.\");\n                String id = parseElementId(b.getValue());\n                if (id.length() > 0) {\n                    iosElement = new IOSElementImpl(id, this);\n                    break;\n                } else {\n                    logger.error(\"parse element id %s failed. retried %d times, retry in %d ms.\", b.getValue().toString(), wait, intervalInit);\n                }\n            } else {\n                logger.error(\"element not found. retried %d times, retry in %d ms.\", wait, intervalInit);\n                errMsg = b.getErr().getMessage();\n            }\n            if (wait < retryInit) {\n                try {\n                    Thread.sleep(intervalInit);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        if (iosElement == null) {\n            throw new SonicRespException(errMsg);\n        }\n        return iosElement;\n    }\n\n    @Override\n    public List<IOSElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException {\n        List<IOSElement> iosElementList = new ArrayList<>();\n        int wait = 0;\n        int intervalInit = (interval == null ? FIND_ELEMENT_INTERVAL : interval);\n        int retryInit = (retry == null ? FIND_ELEMENT_RETRY : retry);\n        String errMsg = \"\";\n        while (wait < retryInit) {\n            wait++;\n            checkSessionId();\n            JSONObject data = new JSONObject();\n            data.put(\"using\", selector);\n            data.put(\"value\", value);\n            BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/elements\")\n                    .body(data.toJSONString()));\n            if (b.getErr() == null) {\n                logger.info(\"find elements successful.\");\n                List<JSONObject> ids = JSON.parseObject(b.getValue().toString(), ArrayList.class);\n                for (JSONObject ele : ids) {\n                    String id = parseElementId(ele);\n                    if (id.length() > 0) {\n                        iosElementList.add(new IOSElementImpl(id, this));\n                    } else {\n                        logger.error(\"parse element id %s failed.\", ele);\n                    }\n                }\n                if (iosElementList.size() > 0) {\n                    break;\n                }\n            } else {\n                logger.error(\"elements not found. retried %d times, retry in %d ms.\", wait, intervalInit);\n                errMsg = b.getErr().getMessage();\n            }\n            if (wait < retryInit) {\n                try {\n                    Thread.sleep(intervalInit);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        if (iosElementList.size() == 0) {\n            throw new SonicRespException(errMsg);\n        }\n        return iosElementList;\n    }\n\n    @Override\n    public IOSElement activeElement() throws SonicRespException {\n        checkSessionId();\n        BaseResp b = respHandler.getResp(\n                HttpUtil.createGet(remoteUrl + \"/session/\" + sessionId + \"/element/active\"));\n        if (b.getErr() == null) {\n            logger.info(\"find active elements successful.\");\n            JSONObject value = JSON.parseObject(b.getValue().toString(), JSONObject.class);\n            List<String> identifier = Arrays.asList(\"ELEMENT\", \"element-6066-11e4-a52e-4f735466cecf\");\n            Iterator var4 = identifier.iterator();\n            String eleId;\n            do {\n                if (!var4.hasNext()) {\n                    return null;\n                }\n                String i = (String)var4.next();\n                eleId = value.getString(i);\n            } while(eleId == null || eleId.length() <= 0);\n\n            return new IOSElementImpl(eleId,this);\n        } else {\n            logger.error(\"active element not found.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public byte[] screenshot() throws SonicRespException {\n        checkSessionId();\n        BaseResp b = respHandler.getResp(\n                HttpUtil.createGet(remoteUrl + \"/session/\" + sessionId + \"/screenshot\"), 60000);\n        if (b.getErr() == null) {\n            logger.info(\"get screenshot.\");\n            return Base64.getMimeDecoder().decode(b.getValue().toString());\n        } else {\n            logger.error(\"get screenshot failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void setAppiumSettings(JSONObject settings) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n        data.put(\"settings\", settings);\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/appium/settings\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"set appium settings %s.\", settings.toJSONString());\n        } else {\n            logger.error(\"set appium settings failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void swipe(double fromX, double fromY, double toX, double toY, double duration) throws SonicRespException {\n        checkSessionId();\n        JSONObject data = new JSONObject();\n\n        double maxSwipeDistance = Math.max(Math.abs(toX - fromX), Math.abs(toY - fromY));\n        double velocity = maxSwipeDistance / duration * 1000;\n        // velocity 单位为像素/秒，这里做个限制，如果超过1000则滑动的过快，很容易触发fling行为\n        velocity = Math.min(velocity, 1000);\n        data.put(\"fromX\", fromX);\n        data.put(\"fromY\", fromY);\n        data.put(\"toX\", toX);\n        data.put(\"toY\", toY);\n        data.put(\"velocity\", velocity);\n\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/session/\" + sessionId + \"/wda/pressAndDragWithVelocity\")\n                .body(data.toJSONString()));\n        if (b.getErr() == null) {\n            logger.info(\"perform swipe %s successful.\", data.toString());\n        } else {\n            logger.error(\"perform swipe %s failed.\", data.toString());\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public void rotate(Orientation orientation) throws SonicRespException {\n        JSONObject xyz = orientation.getValue();\n        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + \"/rotation\").body(String.valueOf(xyz.toJSONString())));\n        if (b.getErr() == null) {\n            logger.info(\"set orientation success. %s\", xyz.toJSONString());\n        } else {\n            logger.error(\"set orientation failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n\n    @Override\n    public Orientation getRotate() throws SonicRespException {\n        BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + \"/rotation\"));\n        if (b.getErr() == null) {\n            logger.info(\"get orientation %s.\",b.getValue());\n            JSONObject value = JSON.parseObject(b.getValue().toString(), JSONObject.class);\n            Integer zValue = Integer.valueOf(value.getString(\"z\"));\n            switch (zValue) {\n                case 0:\n                    return Orientation.PORTRAIT;\n                case 180:\n                    return Orientation.PORTRAITUPSIDEDOWN;\n                case 90:\n                    return Orientation.LANDSCAPELEFT;\n                case 270:\n                    return Orientation.LANDSCAPERIGHT;\n                default:\n                    return Orientation.UNKNOWN;\n            }\n        } else {\n            logger.error(\"get orientation failed.\");\n            throw new SonicRespException(b.getErr().getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/PocoDriver.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco;\n\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.poco.enums.PocoEngine;\nimport org.cloud.sonic.driver.poco.enums.PocoSelector;\nimport org.cloud.sonic.driver.poco.models.PocoElement;\nimport org.cloud.sonic.driver.poco.service.PocoClient;\nimport org.cloud.sonic.driver.poco.service.impl.PocoClientImpl;\nimport org.jsoup.nodes.Element;\n\nimport java.util.List;\n\n/**\n * @author Eason\n * poco driver\n */\npublic class PocoDriver {\n    private PocoClient pocoClient;\n\n    /**\n     * init poco driver\n     *\n     * @param pocoEngine\n     */\n    public PocoDriver(PocoEngine pocoEngine) {\n        this(pocoEngine, pocoEngine.getDefaultPort());\n    }\n\n    /**\n     * init poco driver\n     *\n     * @param pocoEngine\n     * @param port\n     */\n    public PocoDriver(PocoEngine pocoEngine, int port) {\n        pocoClient = new PocoClientImpl();\n        pocoClient.newClient(pocoEngine, port);\n    }\n\n    /**\n     * close driver\n     */\n    public void closeDriver() {\n        pocoClient.closeClient();\n    }\n\n    /**\n     * show log.\n     */\n    public void showLog() {\n        pocoClient.showLog();\n    }\n\n    /**\n     * disable log.\n     */\n    public void disableLog() {\n        pocoClient.disableLog();\n    }\n\n    /**\n     * get Poco element\n     *\n     * @return\n     * @throws SonicRespException\n     */\n    public PocoElement getPageSource() throws SonicRespException {\n        return pocoClient.pageSource();\n    }\n\n    /**\n     * get Poco element for Json\n     *\n     * @return\n     * @throws SonicRespException\n     */\n    public String getPageSourceForJsonString() throws SonicRespException {\n        return pocoClient.pageSourceForJsonString();\n    }\n\n    /**\n     * get Poco element for jsoup.Element\n     *\n     * @return\n     * @throws SonicRespException\n     */\n    public Element getPageSourceForXmlElement() throws SonicRespException {\n        return pocoClient.pageSourceForXmlElement();\n    }\n\n    /**\n     * find poco elements\n     *\n     * @param expression\n     * @return\n     */\n    public List<PocoElement> findElements(String selector, String expression) throws SonicRespException {\n        return pocoClient.findElements(selector, expression);\n    }\n\n    /**\n     * find poco elements\n     *\n     * @param expression\n     * @return\n     */\n    public List<PocoElement> findElements(PocoSelector selector, String expression) throws SonicRespException {\n        return findElements(selector.getSelector(), expression);\n    }\n\n    /**\n     * find poco element\n     *\n     * @param expression\n     * @return\n     */\n    public PocoElement findElement(String selector, String expression) throws SonicRespException {\n        return pocoClient.findElement(selector, expression);\n    }\n\n    /**\n     * find poco element\n     *\n     * @param expression\n     * @return\n     */\n    public PocoElement findElement(PocoSelector selector, String expression) throws SonicRespException {\n        return findElement(selector.getSelector(), expression);\n    }\n\n    /**\n     * Freeze page source\n     */\n    public void freezeSource() {\n        pocoClient.freezeSource();\n    }\n\n    /**\n     * Thaw page source\n     */\n    public void thawSource() {\n        pocoClient.thawSource();\n    }\n\n    /**\n     * get windows size\n     *\n     * @return\n     * @throws SonicRespException\n     */\n    public WindowSize getScreenSize() throws SonicRespException {\n        return pocoClient.getScreenSize();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/enums/PocoEngine.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.enums;\n\npublic enum PocoEngine {\n    UNITY_3D(\"Unity3d\", 5001),\n    UE4(\"UE4\", 5001),\n    COCOS_2DX_JS(\"Cocos2dx-js\", 5003),\n    COCOS_2DX_LUA(\"Cocos2dx-lua\", 15004),\n    COCOS_2DX_C_PLUS_1(\"Cocos2dx-c++\", 18888),\n    COCOS_CREATOR(\"cocos-creator\", 5003),\n    EGRET(\"Egret\", 5003);\n\n    private final String name;\n    private final int defaultPort;\n\n    PocoEngine(String name, int defaultPort) {\n        this.name = name;\n        this.defaultPort = defaultPort;\n    }\n\n    public int getDefaultPort() {\n        return defaultPort;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/enums/PocoSelector.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.enums;\n\npublic enum PocoSelector {\n    POCO(\"poco\"),\n    XPATH(\"xpath\"),\n    CSS_SELECTOR(\"cssSelector\");\n\n    private final String selector;\n\n    PocoSelector(String selector) {\n        this.selector = selector;\n    }\n\n    public String getSelector() {\n        return selector;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/models/PocoElement.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.models;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Getter;\nimport lombok.ToString;\nimport org.cloud.sonic.driver.common.models.BaseElement;\nimport org.cloud.sonic.driver.common.models.ElementRect;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.jsoup.nodes.Element;\nimport org.jsoup.select.Elements;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n@Getter\n@ToString\n@AllArgsConstructor\npublic class PocoElement implements BaseElement {\n    public String currentNodeSelector = \"Root\";\n    private Payload payload;\n    private List<PocoElement> children;\n    RootElement rootElement;\n    private Element currentNodeXmlElement;\n\n    private long version;\n\n    @Override\n    // the poco given pos is a point where the origin of the coordinates will change with\n    // the rotation of the screen. The function and others are not uniform, so the implementation\n    // is not considered, you can get the pos point through the payload, and then convert it at\n    // the application layer\n    public ElementRect getRect() throws SonicRespException {\n        throw new SonicRespException(\"poco element unrealized\");\n    }\n\n    @Override\n    public String getAttribute(String name) throws SonicRespException {\n        return currentNodeXmlElement.attr(name);\n    }\n\n    @Override\n    public String getUniquelyIdentifies() throws SonicRespException {\n        return currentNodeSelector;\n    }\n\n    @Override\n    public boolean isDisplayed() throws SonicRespException {\n        throw new SonicRespException(\"poco element unrealized\");\n    }\n\n    @Getter\n    @ToString\n    @AllArgsConstructor\n    public class Payload {\n        private String layer;\n        private String name;\n        private String tag;\n        private String text;\n        private String texture;\n        private Integer _instanceId;\n        private Integer _ilayer;\n        private String type;\n        private Boolean visible;\n        private ZOrders zOrders;\n        private List<String> components;\n        private List<Float> anchorPoint;\n        private List<Float> scale;\n        private List<Float> size;\n        private List<Float> pos;\n        private Boolean clickable;\n\n        @Getter\n        @ToString\n        @AllArgsConstructor\n        public class ZOrders {\n            private Float global;\n            private Float local;\n\n            public ZOrders() {\n            }\n        }\n\n        public Payload() {\n            zOrders = new ZOrders();\n        }\n    }\n\n    public PocoElement(RootElement root) {\n        this.rootElement = root;\n        this.currentNodeSelector = root.getXmlElement().cssSelector();\n        payload = new Payload();\n        children = new ArrayList<>();\n    }\n\n    public PocoElement(RootElement root, Element currentNodeXmlElement) {\n        this(root);\n        this.currentNodeXmlElement = currentNodeXmlElement;\n        this.currentNodeSelector = currentNodeXmlElement.cssSelector();\n        parseXmlNode(currentNodeXmlElement);\n    }\n\n    public Payload getPayload() {\n\n        if (rootElement.getVersion() != getVersion()) {\n            Element xmlPocoNode = rootElement.getXmlElement().select(currentNodeSelector).first();\n            if (xmlPocoNode == null) {\n                return null;\n            }\n            parseXmlNode(xmlPocoNode);\n            version = rootElement.getVersion();\n        }\n        return payload;\n    }\n\n    public List<PocoElement> getChildren() {\n\n        if (rootElement.getVersion() != getVersion()) {\n            Element xmlPocoNode = rootElement.getXmlElement().select(currentNodeSelector).first();\n\n            if (xmlPocoNode == null) {\n                return null;\n            }\n\n            children.clear();\n\n            currentNodeXmlElement = xmlPocoNode;\n\n            Elements childList = xmlPocoNode.children();\n\n            for (Element child : childList) {\n                children.add(new PocoElement(rootElement, child));\n            }\n\n            version = rootElement.getVersion();\n        }\n        return children;\n    }\n\n\n    public void parseXmlNode(Element xmlNode) {\n\n        payload.layer = xmlNode.attr(\"layer\");\n        payload.name = xmlNode.attr(\"name\");\n        payload.tag = xmlNode.attr(\"tag\");\n        payload.text = xmlNode.attr(\"text\");\n        payload.texture = xmlNode.attr(\"texture\");\n\n        String _instanceId = xmlNode.attr(\"_instanceId\");\n        payload._instanceId = _instanceId.isEmpty() ? 0 : Integer.parseInt(_instanceId);\n\n        String _ilayer = xmlNode.attr(\"_ilayer\");\n        payload._ilayer = _ilayer.isEmpty() ? 0 : Integer.parseInt(_ilayer);\n\n        payload.type = xmlNode.attr(\"type\");\n\n        String visible = xmlNode.attr(\"visible\");\n        payload.visible = Boolean.parseBoolean(visible);\n\n        String global = xmlNode.attr(\"global\");\n        payload.zOrders.global = global.isEmpty() ? 0 : Float.parseFloat(global);\n        String local = xmlNode.attr(\"local\");\n        payload.zOrders.local = local.isEmpty() ? 0 : Float.parseFloat(local);\n\n        String components = xmlNode.attr(\"components\");\n        if (components.length() > 2) {\n            components = components.substring(1, components.length() - 1);\n            payload.components = Arrays.asList(components.split(\",\"));\n        }\n        payload.anchorPoint = parseFloatAttrList(xmlNode.attr(\"anchorPoint\"));\n        payload.scale = parseFloatAttrList(xmlNode.attr(\"scale\"));\n        payload.size = parseFloatAttrList(xmlNode.attr(\"size\"));\n        payload.pos = parseFloatAttrList(xmlNode.attr(\"pos\"));\n        payload.clickable = Boolean.parseBoolean(xmlNode.attr(\"clickable\"));\n    }\n\n    private List<Float> parseFloatAttrList(String floatAttrStr) {\n        if (floatAttrStr.length() <= 2) return null;\n        List<Float> result = new ArrayList<Float>();\n        floatAttrStr = floatAttrStr.substring(1, floatAttrStr.length() - 1);\n        for (String numStr : floatAttrStr.split(\",\")) {\n            result.add(Float.parseFloat(numStr));\n        }\n        return result;\n    }\n\n    public Boolean currentTheNodeExists() {\n        return rootElement.getXmlElement().select(currentNodeSelector).first() != null;\n    }\n\n    public PocoElement getParentNode() {\n        Element xmlPocoNode = rootElement.getXmlElement().select(currentNodeSelector).first();\n\n        if (xmlPocoNode == null) {\n            return null;\n        }\n        Element parentNode = xmlPocoNode.parent();\n        if (parentNode == null) {\n            return null;\n        }\n        return new PocoElement(rootElement, parentNode);\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/models/RootElement.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.models;\n\nimport org.jsoup.nodes.Element;\n\npublic class RootElement {\n    private Element XmlElement;\n    private long version;\n\n    public RootElement() {\n    }\n\n    public RootElement(Element XmlElement) {\n        this.XmlElement = XmlElement;\n    }\n\n    public RootElement(Element XmlElement, long version) {\n        this(XmlElement);\n        this.version = version;\n    }\n\n\n    public Element getXmlElement() {\n        return XmlElement;\n    }\n\n    public void setXmlElement(Element xmlElement) {\n        this.XmlElement = xmlElement;\n    }\n\n    public long getVersion() {\n        return version;\n    }\n\n    public void setVersion(long version) {\n        this.version = version;\n    }\n\n    public synchronized void updateVersion(Element rootXmlElement) {\n        this.XmlElement = rootXmlElement;\n        this.version += 1;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/service/PocoClient.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.service;\n\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.Logger;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.poco.enums.PocoEngine;\nimport org.cloud.sonic.driver.poco.models.PocoElement;\nimport org.jsoup.nodes.Element;\n\nimport java.util.List;\n\n/**\n * @author Eason\n * poco client interface\n */\npublic interface PocoClient {\n    //Client Setting\n    Logger getLogger();\n\n    void showLog();\n\n    void disableLog();\n\n    //Client handler.\n    void newClient(PocoEngine engine, int port);\n\n    void closeClient();\n\n    //source handler.\n    PocoElement pageSource() throws SonicRespException;\n\n    String pageSourceForJsonString() throws SonicRespException;\n\n    Element pageSourceForXmlElement() throws SonicRespException;\n\n    PocoElement findElement(String selector, String expression) throws SonicRespException;\n\n    List<PocoElement> findElements(String selector, String expression) throws SonicRespException;\n\n    void freezeSource();\n\n    void thawSource();\n\n    //other\n    WindowSize getScreenSize() throws SonicRespException;\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/service/PocoConnection.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.service;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\npublic interface PocoConnection {\n    void connect();\n\n    void disconnect();\n\n    String sendAndReceive(JSONObject jsonObject) throws SonicRespException;\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/service/impl/PocoClientImpl.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.service.impl;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.Logger;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.poco.enums.PocoEngine;\nimport org.cloud.sonic.driver.poco.models.PocoElement;\nimport org.cloud.sonic.driver.poco.models.RootElement;\nimport org.cloud.sonic.driver.poco.service.PocoClient;\nimport org.cloud.sonic.driver.poco.service.PocoConnection;\nimport org.cloud.sonic.driver.poco.util.PocoJsonToXml;\nimport org.jsoup.Jsoup;\nimport org.jsoup.nodes.Element;\nimport org.jsoup.parser.Parser;\nimport org.jsoup.select.Elements;\n\nimport java.util.*;\n\nimport static org.cloud.sonic.driver.poco.enums.PocoEngine.*;\n\npublic class PocoClientImpl implements PocoClient {\n    private Logger logger;\n    private PocoConnection pocoConnection;\n    private PocoEngine engine;\n    private String source;\n\n    public RootElement rootNode;\n    private boolean isFrozen = false;\n\n    public PocoClientImpl() {\n        logger = new Logger();\n        rootNode = new RootElement();\n    }\n\n    @Override\n    public Logger getLogger() {\n        return logger;\n    }\n\n    @Override\n    public void showLog() {\n        logger.showLog();\n    }\n\n    @Override\n    public void disableLog() {\n        logger.disableLog();\n    }\n\n    @Override\n    public void newClient(PocoEngine engine, int port) {\n        this.engine = engine;\n        if (engine.equals(COCOS_2DX_JS) || engine.equals(COCOS_CREATOR) || engine.equals(EGRET)) {\n            pocoConnection = new WebSocketClientImpl(port, logger);\n        } else {\n            pocoConnection = new SocketClientImpl(port, logger);\n        }\n        pocoConnection.connect();\n    }\n\n    @Override\n    public void closeClient() {\n        pocoConnection.disconnect();\n    }\n\n    @Override\n    public PocoElement pageSource() throws SonicRespException {\n        pageSourceForXmlElement();\n        return new PocoElement(rootNode);\n    }\n\n    @Override\n    public String pageSourceForJsonString() throws SonicRespException {\n        if (isFrozen) {\n            return source;\n        }\n        JSONObject jsonObject = new JSONObject();\n        jsonObject.put(\"jsonrpc\", \"2.0\");\n        jsonObject.put(\"params\", Arrays.asList(true));\n        jsonObject.put(\"id\", UUID.randomUUID().toString());\n        jsonObject.put(\"method\", \"Dump\");\n        if (engine.equals(COCOS_CREATOR) || engine.equals(COCOS_2DX_JS)) {\n            jsonObject.put(\"method\", \"dump\");\n        }\n        source = pocoConnection.sendAndReceive(jsonObject);\n        return source;\n    }\n\n    @Override\n    public Element pageSourceForXmlElement() throws SonicRespException {\n        pageSourceForJsonString();\n//        String pocoJson = \"{\\\"Root\\\"\" + source.substring(\"{\\\"result\\\"\".length());\n        Element rootXmlElement = Jsoup.parse(PocoJsonToXml.jsonObjToXml(JSON.parseObject(source).getJSONObject(\"result\")), \"\", Parser.xmlParser());\n\n        rootNode.updateVersion(rootXmlElement);\n\n        return rootNode.getXmlElement();\n    }\n\n    @Override\n    public PocoElement findElement(String selector, String expression) throws SonicRespException {\n        List<PocoElement> pocoElements = findElements(selector, expression);\n        return pocoElements.get(0);\n    }\n\n    @Override\n    public List<PocoElement> findElements(String selector, String expression) throws SonicRespException {\n        if (rootNode.getXmlElement() == null) {\n            pageSourceForXmlElement();\n        }\n        Elements xmlNodes = null;\n        switch (selector) {\n            case \"poco\":\n                String newExpress = \"\";\n                String[] steps = expression.split(\"\\\\.\");\n                for (String step : steps) {\n                    if (step.startsWith(\"poco\")) {\n                        newExpress += (\"//*\" + parseAttr(step));\n                    } else if (step.startsWith(\"child\")) {\n                        newExpress += (\"/*\" + parseAttr(step));\n                    }\n                    if (step.endsWith(\"]\") && step.contains(\"[\")) {\n                        int index = Integer.parseInt(step.substring(step.indexOf(\"[\") + 1, step.indexOf(\"]\")));\n                        newExpress = String.format(\"(%s)[%d]\", newExpress, (index + 1));\n                    }\n                }\n                xmlNodes = rootNode.getXmlElement().selectXpath(newExpress);\n                break;\n            case \"xpath\":\n                xmlNodes = rootNode.getXmlElement().selectXpath(expression);\n                break;\n            case \"cssSelector\":\n                xmlNodes = rootNode.getXmlElement().select(expression);\n                break;\n        }\n        if (xmlNodes == null || xmlNodes.isEmpty()) {\n            throw new SonicRespException(String.format(\"poco element not found for selector:%s, value:%s\", selector, expression));\n        }\n        List<PocoElement> result = new ArrayList<>();\n        for (Element node : xmlNodes) {\n            PocoElement pocoElement = new PocoElement(rootNode, node);\n            result.add(pocoElement);\n        }\n        return result;\n    }\n\n    private String parseAttr(String express) {\n        String result = \"[\";\n        String attrExpression = express.substring(express.indexOf(\"(\") + 1, express.lastIndexOf(\")\"));\n        if (attrExpression.startsWith(\"\\\"\") && attrExpression.endsWith(\"\\\"\")) {\n            attrExpression = \"name=\" + attrExpression.replace(\"\\\"\", \"\");\n        }\n        String[] attrs = attrExpression.split(\",\");\n        for (String attr : attrs) {\n            String field = attr.substring(0, attr.indexOf(\"=\"));\n            String value = attr.substring(attr.indexOf(\"=\") + 1).replace(\"\\\"\", \"\");\n            if (\"visible\".equals(value) || \"clickable\".equals(value)) {\n                value = value.toLowerCase(Locale.ROOT);\n            }\n            result += (\"@\" + field + \"=\\\"\" + value + \"\\\" and \");\n        }\n        result += \"]\";\n        return result.replace(\" and ]\", \"]\");\n    }\n\n    @Override\n    public void freezeSource() {\n        isFrozen = true;\n    }\n\n    @Override\n    public void thawSource() {\n        isFrozen = false;\n    }\n\n    @Override\n    public WindowSize getScreenSize() throws SonicRespException {\n        if (engine.equals(UNITY_3D) || engine.equals(COCOS_2DX_LUA)) {\n            JSONObject jsonObject = new JSONObject();\n            jsonObject.put(\"jsonrpc\", \"2.0\");\n            jsonObject.put(\"params\", Arrays.asList(true));\n            jsonObject.put(\"id\", UUID.randomUUID().toString());\n            jsonObject.put(\"method\", \"GetScreenSize\");\n            List<Integer> result = ((JSONArray) JSONObject.parseObject(\n                            pocoConnection.sendAndReceive(jsonObject))\n                    .get(\"result\"))\n                    .toJavaList(Integer.class);\n            return new WindowSize(result.get(0), result.get(1));\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/service/impl/SocketClientImpl.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.service.impl;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.tool.Logger;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.poco.service.PocoConnection;\nimport org.cloud.sonic.driver.poco.util.PocoTool;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.Socket;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.StandardCharsets;\n\npublic class SocketClientImpl implements PocoConnection {\n\n    private Socket poco = null;\n    private InputStream inputStream = null;\n    private OutputStream outputStream = null;\n\n    private int port;\n    private Logger logger;\n\n    public SocketClientImpl(int port, Logger logger) {\n        this.port = port;\n        this.logger = logger;\n    }\n\n    @Override\n    public String sendAndReceive(JSONObject jsonObject) throws SonicRespException {\n        byte[] data = jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8);\n        byte[] header = intToByteArray(data.length);\n\n        synchronized (SocketClientImpl.class) {\n            try {\n                outputStream.write(header);\n                outputStream.write(data);\n                outputStream.flush();\n                byte[] head = new byte[4];\n                byte[] buffer = new byte[8192];\n                inputStream.read(head);\n                int headLen = toInt(head);\n                ByteBuffer rData = ByteBuffer.allocate(headLen);\n                while (poco.isConnected() && !Thread.interrupted()) {\n\n                    int realLen;\n                    realLen = inputStream.read(buffer);\n                    if (realLen > 0) {\n                        rData.put(buffer, 0, realLen);\n                    }\n\n                    if (rData.position() == headLen) {\n                        rData.flip();\n                        String pocoResult = new String(rData.array(), StandardCharsets.UTF_8);\n                        int subStartIndex = pocoResult.indexOf(\"\\\"result\\\"\");\n                        // when cocos is integrated there will be no id\n//                        String pocoPrefix = pocoResult.substring(0, subStartIndex) + \"}\";\n//                        if (PocoTool.checkPocoRpcResultID(pocoPrefix, jsonObject.getString(\"id\"))) {\n                            return \"{\" + pocoResult.substring(subStartIndex);\n//                        } else {\n//                            throw new SonicRespException(\"id not found!\");\n//                        }\n                    }\n                }\n            } catch (Exception e) {\n                throw new SonicRespException(e.getMessage());\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public void connect() {\n        int waitConnect = 0;\n        while (poco == null || !poco.isConnected()) {\n            try {\n                Thread.sleep(500);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n            waitConnect++;\n            if (waitConnect >= 20) {\n                break;\n            }\n            try {\n                poco = new Socket(\"localhost\", port);\n                inputStream = poco.getInputStream();\n                outputStream = poco.getOutputStream();\n            } catch (Exception e) {\n                logger.info(e.getMessage());\n            }\n        }\n        if (poco != null) {\n            logger.info(\"poco socket connected.\");\n        } else {\n            logger.info(\"poco socket disconnected.\");\n        }\n    }\n\n    @Override\n    public void disconnect() {\n        if (poco != null && poco.isConnected()) {\n            try {\n                poco.close();\n                logger.info(\"poco socket closed.\");\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        if (inputStream != null) {\n            try {\n                inputStream.close();\n                logger.info(\"poco input stream closed.\");\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        if (outputStream != null) {\n            try {\n                outputStream.close();\n                logger.info(\"poco output stream closed.\");\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    private byte[] intToByteArray(int i) {\n        byte[] result = new byte[4];\n        result[0] = (byte) (i & 0xff);\n        result[1] = (byte) (i >> 8 & 0xff);\n        result[2] = (byte) (i >> 16 & 0xff);\n        result[3] = (byte) (i >> 24 & 0xff);\n        return result;\n    }\n\n    private int toInt(byte[] b) {\n        int res = 0;\n        for (int i = 0; i < b.length; i++) {\n            res += (b[i] & 0xff) << (i * 8);\n        }\n        return res;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/service/impl/WebSocketClientImpl.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.service.impl;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.tool.Logger;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.poco.service.PocoConnection;\nimport org.cloud.sonic.driver.poco.util.PocoTool;\nimport org.java_websocket.handshake.ServerHandshake;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\n\npublic class WebSocketClientImpl implements PocoConnection {\n    private int port;\n    private Logger logger;\n\n    private org.java_websocket.client.WebSocketClient webSocketClient;\n    private String result = null;\n\n    public WebSocketClientImpl(int port, Logger logger) {\n        this.port = port;\n        this.logger = logger;\n    }\n\n    @Override\n    public String sendAndReceive(JSONObject jsonObject) throws SonicRespException {\n        synchronized (WebSocketClientImpl.class) {\n            webSocketClient.send(jsonObject.toString());\n            int wait = 0;\n            while (result == null) {\n                try {\n                    Thread.sleep(500);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n                wait++;\n                if (wait >= 20) {\n                    break;\n                }\n            }\n            if (result != null) {\n                int subStartIndex = result.indexOf(\"\\\"result\\\"\");\n\n                String pocoPrefix = result.substring(0, subStartIndex) + \"}\";\n\n                if (PocoTool.checkPocoRpcResultID(pocoPrefix, jsonObject.getString(\"id\"))) {\n                    String re = \"{\" + result.substring(subStartIndex);\n                    result = null;\n                    return re;\n                } else {\n                    throw new SonicRespException(\"id not found!\");\n                }\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public void connect() {\n        URI ws = null;\n        try {\n            ws = new URI(\"ws://localhost:\" + port);\n        } catch (URISyntaxException e) {\n            e.printStackTrace();\n        }\n        webSocketClient = new org.java_websocket.client.WebSocketClient(ws) {\n            @Override\n            public void onOpen(ServerHandshake serverHandshake) {\n                logger.info(\"poco ws connected.\");\n            }\n\n            @Override\n            public void onMessage(String s) {\n                logger.info(s);\n                result = s;\n            }\n\n            @Override\n            public void onClose(int i, String s, boolean b) {\n                logger.info(\"poco ws close.\");\n            }\n\n            @Override\n            public void onError(Exception e) {\n\n            }\n        };\n        webSocketClient.connect();\n        int waitConnect = 0;\n        while (!webSocketClient.isOpen()) {\n            try {\n                Thread.sleep(500);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n            waitConnect++;\n            if (waitConnect >= 20) {\n                break;\n            }\n        }\n    }\n\n    @Override\n    public void disconnect() {\n        webSocketClient.close();\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/util/PocoJsonToXml.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.util;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\nimport java.util.Map;\n\npublic class PocoJsonToXml {\n    /**\n     * convert json to xml\n     *\n     * @param jo JSONObject\n     * @return xml\n     */\n    public static String jsonObjToXml(JSONObject jo) throws SonicRespException {\n        String xml = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\" + jsonToXml(jo, \"\");\n        return xml;\n    }\n\n    /**\n     * json object to xml\n     *\n     * @param jo JSONObject\n     * @param gt \"\\n\" shifter\n     * @return XML\n     */\n    @SuppressWarnings(\"rawtypes\")\n    private static String jsonToXml(JSONObject jo, String gt) throws SonicRespException {\n        StringBuffer xmlStr = new StringBuffer();\n        try {\n            JSONObject payload = jo.getJSONObject(\"payload\");\n            JSONArray children = jo.getJSONArray(\"children\");\n\n            xmlStr.append(gt);\n            xmlStr.append(\"<\");\n            String name = String.join(\"__\", payload.get(\"name\").toString().split(\" \"));\n\n            if (name.equals(\"<Root>\")) name = \"Root\";\n            if (!checkName(name)) {\n                name = \"invalidName\";\n            }\n            xmlStr.append(name);\n            xmlStr.append(getAttrStr(payload));\n            xmlStr.append(\">\\n\");\n            if (children != null) {\n                for (int i = 0; i < children.size(); i++) {\n                    JSONObject child = children.getJSONObject(i);\n                    xmlStr.append(jsonToXml(child, gt + \"\\t\"));\n                }\n            }\n            addTag(xmlStr, gt, name, true);\n        } catch (Exception e) {\n            throw new SonicRespException(e.getMessage());\n        }\n        return xmlStr.toString();\n    }\n\n    public static void addTag(StringBuffer xmlStr, String gt, String tagName, boolean isItTheEnd) {\n        xmlStr.append(gt);\n        if (isItTheEnd) {\n            xmlStr.append(\"</\");\n        } else {\n            xmlStr.append(\"<\");\n        }\n        xmlStr.append(tagName);\n        xmlStr.append(\">\\n\");\n    }\n\n    public static StringBuilder getAttrStr(JSONObject payload) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(\" \");\n        for (Map.Entry<String, Object> stringObjectEntry : payload.entrySet()) {\n            Map.Entry entry = (Map.Entry) stringObjectEntry;\n            String key = entry.getKey().toString();\n            String val = entry.getValue().toString();\n            if (key.equals(\"zOrders\")) {\n                JSONObject zOrders = JSONObject.parseObject(val);\n                sb.append(String.format(\"global=\\\"%s\\\" \", zOrders.get(\"global\")));\n                sb.append(String.format(\"local=\\\"%s\\\" \", zOrders.get(\"local\")));\n            } else if (key.equals(\"components\")) {\n                val = val.replace(\"\\\"\", \"\");\n                sb.append(String.format(\"%s=\\\"%s\\\" \", key, val));\n            } else {\n                val = escapingSpecialCharacters(val);\n                sb.append(String.format(\"%s=\\\"%s\\\" \", key, val));\n            }\n        }\n        return sb;\n    }\n\n    private static boolean checkName(String name) {\n        String re = \"^[:A-Z_a-z\\\\u00C0\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02ff\\\\u0370-\\\\u037d\"\n                + \"\\\\u037f-\\\\u1fff\\\\u200c\\\\u200d\\\\u2070-\\\\u218f\\\\u2c00-\\\\u2fef\\\\u3001-\\\\ud7ff\"\n                + \"\\\\uf900-\\\\ufdcf\\\\ufdf0-\\\\ufffd\\\\x10000-\\\\xEFFFF]\"\n                + \"[:A-Z_a-z\\\\u00C0\\\\u00D6\\\\u00D8-\\\\u00F6\"\n                + \"\\\\u00F8-\\\\u02ff\\\\u0370-\\\\u037d\\\\u037f-\\\\u1fff\\\\u200c\\\\u200d\\\\u2070-\\\\u218f\"\n                + \"\\\\u2c00-\\\\u2fef\\\\u3001-\\\\udfff\\\\uf900-\\\\ufdcf\\\\ufdf0-\\\\ufffd\\\\-\\\\.0-9\"\n                + \"\\\\u00b7\\\\u0300-\\\\u036f\\\\u203f-\\\\u2040]*\\\\Z\";\n        return name.matches(re);\n    }\n\n    private static String escapingSpecialCharacters(String originStr) {\n        if (originStr == null) return null;\n        originStr = originStr.replace(\"&\", \"&amp;\");\n        originStr = originStr.replace(\"<\", \"&lt;\");\n        originStr = originStr.replace(\">\", \"&gt;\");\n        originStr = originStr.replace(\"\\\"\", \"&quot;\");\n        originStr = originStr.replace(\"'\", \"&apos;\");\n        return originStr;\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/util/PocoTool.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.util;\n\nimport com.alibaba.fastjson.JSONObject;\n\npublic class PocoTool {\n    public static boolean checkPocoRpcResultID(String pocoRpcResult, String sendID) {\n        JSONObject object = JSONObject.parseObject(pocoRpcResult);\n        return sendID.equals(object.getString(\"id\"));\n    }\n}\n"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/util/PocoXYTransformer.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.poco.util;\n\n/***\n * poco coordinate system conversion and vertical coordinate system converter\n */\npublic class PocoXYTransformer {\n    /**\n     * Convert adb vertical coordinates to poco coordinate system coordinates according to different directions\n     *\n     * @param x           x poco coordinate point x\n     * @param y           y poco coordinate point y\n     * @param w           w ScreenWidth\n     * @param h           h ScreenHeight\n     * @param orientation The device orientation, based on the vertical direction of the mobile device,\n     *                    rotate 90° counterclockwise, orientation=90, 180° counterclockwise, orientation=180,\n     *                    and so on\n     * @return {@link int[]}\n     */\n    public static double[] PocoTransformerVertical(double x, double y, double w, double h, Integer orientation) {\n        if (orientation == 90) {\n            double temp = x;\n            x = w - y;\n            y = temp;\n        } else if (orientation == 180) {\n            x = w - x;\n            y = h - y;\n        } else if (orientation == 270) {\n            double temp = x;\n            x = y;\n            y = h - temp;\n        }\n        return new double[]{x, y};\n    }\n\n    /**\n     * Convert the coordinate system of poco to the coordinates in the adb vertical coordinate system\n     *\n     * @param x           x vertical coordinate x\n     * @param y           y vertical coordinate y\n     * @param w           w ScreenWidth\n     * @param h           h ScreenHeight\n     * @param orientation The device orientation, based on the vertical direction of the mobile device,\n     *                    rotate 90° counterclockwise, orientation=90, 180° counterclockwise, orientation=180,\n     *                    and so on\n     * @return {@link int[]}\n     */\n    public static double[] VerticalTransformerPoco(double x, double y, double w, double h, Integer orientation) {\n        if (orientation == 90) {\n            double temp = x;\n            x = y;\n            y = w - temp;\n        } else if (orientation == 180) {\n            x = w - x;\n            y = h - y;\n        } else if (orientation == 270) {\n            double temp = x;\n            x = h - y;\n            y = temp;\n        }\n        return new double[]{x, y};\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/android/AndroidDriverTest.java",
    "content": "package org.cloud.sonic.driver.android;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.android.enmus.AndroidSelector;\nimport org.cloud.sonic.driver.android.service.AndroidElement;\nimport org.cloud.sonic.driver.common.enums.PasteboardType;\nimport org.cloud.sonic.driver.common.models.ElementRect;\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.junit.*;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\n\nimport javax.imageio.stream.FileImageOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.UUID;\n\n@RunWith(Parameterized.class)\npublic class AndroidDriverTest {\n    static AndroidDriver androidDriver;\n    static String url = \"http://localhost:6790\";\n\n    @Parameterized.Parameters\n    public static Object[][] data() {\n        return new Object[1][0];\n    }\n\n    @Before\n    public void before() throws InterruptedException {\n        Thread.sleep(2000);\n    }\n\n    @BeforeClass\n    public static void beforeClass() throws SonicRespException {\n        Boolean hasThrow = false;\n        try {\n            new AndroidDriver(url, null);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(\"io.appium.uiautomator2.common.exceptions.InvalidArgumentException: 'capabilities' are mandatory for session creation\", e.getMessage());\n        }\n        Assert.assertTrue(hasThrow);\n        androidDriver = new AndroidDriver(url, new JSONObject());\n        androidDriver.disableLog();\n        Assert.assertEquals(url, androidDriver.getUiaClient().getRemoteUrl());\n        Assert.assertTrue(androidDriver.getSessionId().length() > 0);\n        androidDriver.showLog();\n    }\n\n    @AfterClass\n    public static void afterClass() throws SonicRespException {\n        androidDriver.closeDriver();\n    }\n\n    @Test\n    public void testSession() {\n        String sessionId = androidDriver.getSessionId();\n        androidDriver.getUiaClient().setSessionId(null);\n        Boolean hasThrow = false;\n        try {\n            androidDriver.getUiaClient().pageSource();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(\"sessionId not found.\", e.getMessage());\n        }\n        Assert.assertTrue(hasThrow);\n        androidDriver.getUiaClient().setSessionId(\"\");\n        hasThrow = false;\n        try {\n            androidDriver.getUiaClient().pageSource();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(\"sessionId not found.\", e.getMessage());\n        }\n        Assert.assertTrue(hasThrow);\n        androidDriver.getUiaClient().setSessionId(sessionId);\n    }\n\n    @Test\n    public void testSource() throws SonicRespException {\n        Assert.assertTrue(androidDriver.getPageSource().contains(\"android.widget.FrameLayout\"));\n    }\n\n    @Test\n    public void testGetWindowSize() throws SonicRespException {\n        WindowSize size = androidDriver.getWindowSize();\n        Assert.assertNotNull(size);\n        Assert.assertTrue(size.getHeight() > 0);\n        Assert.assertTrue(size.getWidth() > 0);\n    }\n\n    @Test\n    public void testClipboard() throws SonicRespException {\n        androidDriver.setPasteboard(PasteboardType.PLAIN_TEXT, \"abc\");\n        androidDriver.getPasteboard(PasteboardType.PLAIN_TEXT);\n    }\n\n    @Test\n    public void testFindElement() throws SonicRespException, InterruptedException, IOException {\n        Boolean hasThrow = false;\n        try {\n            androidDriver.findElement(\"id\", \"android:id/content1\").click();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertTrue(e.getMessage().contains(\"An element could not be located on the page using the given search parameters\"));\n        }\n        Assert.assertTrue(hasThrow);\n        Thread.sleep(2000);\n        androidDriver.findElement(AndroidSelector.Id, \"android:id/content\").click();\n        Thread.sleep(2000);\n        AndroidElement w = androidDriver.findElement(AndroidSelector.XPATH, \"//*[@text='标题']\");\n        w.click();\n        String text = UUID.randomUUID().toString().substring(0, 6) + \"中文\";\n        w.sendKeys(text);\n        w.sendKeys(text, true);\n        Assert.assertEquals(text, w.getText());\n        w.clear();\n        androidDriver.setDefaultFindElementInterval(null, 3000);\n        androidDriver.setDefaultFindElementInterval(5, null);\n        androidDriver.setDefaultFindElementInterval(null, null);\n        ElementRect elementRect = w.getRect();\n        Assert.assertTrue(elementRect.getX() > 0);\n        Assert.assertTrue(elementRect.getY() > 0);\n        Assert.assertTrue(elementRect.getWidth() > 0);\n        Assert.assertTrue(elementRect.getHeight() > 0);\n        Assert.assertTrue(elementRect.getCenter().getX() > 0);\n        Assert.assertTrue(elementRect.getCenter().getY() > 0);\n        byte[] bt = w.screenshot();\n        File output = new File(\"./\" + UUID.randomUUID() + \".png\");\n        FileImageOutputStream imageOutput = new FileImageOutputStream(output);\n        imageOutput.write(bt, 0, bt.length);\n        imageOutput.close();\n        output.delete();\n        Assert.assertEquals(\"android.widget.EditText\", w.getAttribute(\"class\"));\n    }\n\n    @Test\n    public void testFindElementList() throws SonicRespException {\n        int eleSize = androidDriver.findElementList(\"id\", \"android:id/content\").size();\n        Assert.assertEquals(eleSize, androidDriver.findElementList(AndroidSelector.Id, \"android:id/content\").size());\n    }\n\n    @Test\n    public void testScreenshot() throws IOException, SonicRespException {\n        byte[] bt = androidDriver.screenshot();\n        File output = new File(\"./\" + UUID.randomUUID() + \".png\");\n        FileImageOutputStream imageOutput = new FileImageOutputStream(output);\n        imageOutput.write(bt, 0, bt.length);\n        imageOutput.close();\n        output.delete();\n    }\n\n    @Test\n    public void testSetAppiumSettings() throws SonicRespException {\n        androidDriver.setAppiumSettings(new JSONObject());\n    }\n\n    @Test\n    public void testSwipeAction() throws SonicRespException {\n        // 默认滑动操作的完成时间，500毫秒\n        androidDriver.swipe(540, 1710, 540, 200);\n        // 指定滑动操作在1000毫秒内完成\n        androidDriver.swipe(540, 1710, 540, 200, 1000);\n    }\n\n    @Test\n    public void testTapAction() throws SonicRespException {\n        // 替换为任意待测试的元素\n        AndroidElement androidElement = androidDriver.findElement(AndroidSelector.Id,\n                \"com.xueqiu.android:id/my_groups_new_title_bar_ding\");\n        ElementRect elementRect = androidElement.getRect();\n        androidDriver.tap(elementRect.getX() + elementRect.getWidth() / 2, elementRect.getY() + elementRect.getHeight() / 2);\n    }\n\n    @Test\n    public void testLongPressAction() throws SonicRespException {\n        // 替换为任意待测试的元素\n        AndroidElement androidElement = androidDriver.findElement(AndroidSelector.Id,\n                \"com.xueqiu.android:id/my_groups_list_item_stock_name_label\");\n        ElementRect elementRect = androidElement.getRect();\n        androidDriver.longPress((double) elementRect.getX() + (double) elementRect.getWidth() / 2,\n                (double) elementRect.getY() + (double) elementRect.getHeight() / 2,\n                100);\n    }\n\n    @Test\n    public void testDragAction() throws SonicRespException {\n        // 替换为任意待测试的元素\n        androidDriver.drag(182, 629, 565, 2259, 100, null, null);\n    }\n\n    @Test\n    public void testTouchActionDown() throws SonicRespException {\n        // 替换为任意待测试的元素\n        androidDriver.touchAction(\"down\", 182, 629);\n    }\n\n    @Test\n    public void testTouchActionMove() throws SonicRespException {\n        // 替换为任意待测试的元素\n        androidDriver.touchAction(\"move\", 565, 2259);\n    }\n\n    @Test\n    public void testTouchActionUp() throws SonicRespException {\n        // 替换为任意待测试的元素\n        androidDriver.touchAction(\"up\", 565, 2259);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/android/service/UiaClientTest.java",
    "content": "package org.cloud.sonic.driver.android.service;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.android.service.impl.UiaClientImpl;\nimport org.cloud.sonic.driver.common.enums.PasteboardType;\nimport org.cloud.sonic.driver.common.models.BaseResp;\nimport org.cloud.sonic.driver.common.models.ErrorMsg;\nimport org.cloud.sonic.driver.common.tool.RespHandler;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.ios.enums.IOSSelector;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.mockito.Mockito;\n\nimport java.lang.reflect.Field;\n\npublic class UiaClientTest {\n    static UiaClient uiaClient;\n    private static final String ERROR_MSG = \"err message\";\n    static String url = \"http://localhost:6790\";\n\n    @BeforeClass\n    public static void before() throws Exception {\n        uiaClient = new UiaClientImpl();\n        uiaClient.setSessionId(\"test\");\n        uiaClient.setRemoteUrl(url);\n        RespHandler respHandler = Mockito.mock(RespHandler.class);\n        BaseResp b = new BaseResp();\n        b.setErr(new ErrorMsg(\"testErr\", ERROR_MSG, \"traceback\"));\n        Assert.assertNull(b.getSessionId());\n        Mockito.when(respHandler.getResp(Mockito.any())).thenReturn(b);\n        Mockito.when(respHandler.getResp(Mockito.any(), Mockito.anyInt())).thenReturn(b);\n        Field respField = uiaClient.getClass().getDeclaredField(\"respHandler\");\n        respField.setAccessible(true);\n        respField.set(uiaClient, respHandler);\n    }\n\n    @Test\n    public void testGetWindowSize() {\n        Boolean hasThrow = false;\n        try {\n            uiaClient.getWindowSize();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n\n    @Test\n    public void testSendKeys() {\n        Boolean hasThrow = false;\n        try {\n            uiaClient.sendKeys(\"test\", false);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testSetPasteboard() {\n        Boolean hasThrow = false;\n        try {\n            uiaClient.setPasteboard(PasteboardType.PLAIN_TEXT.getType(), \"text\");\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testGetPasteboard() {\n        Boolean hasThrow = false;\n        try {\n            uiaClient.getPasteboard(PasteboardType.PLAIN_TEXT.getType());\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testPageSource() {\n        Boolean hasThrow = false;\n        try {\n            uiaClient.pageSource();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testFindElement() {\n        Boolean hasThrow = false;\n        try {\n            uiaClient.findElement(IOSSelector.ACCESSIBILITY_ID.getSelector(), \"abc\", 10, 10);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testFindElements() {\n        Boolean hasThrow = false;\n        try {\n            uiaClient.findElementList(IOSSelector.ACCESSIBILITY_ID.getSelector(), \"abc\", 10, 10);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testSetAppiumSettings() {\n        Boolean hasThrow = false;\n        try {\n            uiaClient.setAppiumSettings(new JSONObject());\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testScreenShot() {\n        Boolean hasThrow = false;\n        try {\n            uiaClient.screenshot();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/common/tool/RespHandlerTest.java",
    "content": "package org.cloud.sonic.driver.common.tool;\n\nimport cn.hutool.http.HttpUtil;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class RespHandlerTest {\n    @Test\n    public void testTimeOut() {\n        RespHandler respHandler = new RespHandler();\n        respHandler.setRequestTimeOut(0);\n        Boolean hasThrow = false;\n        try {\n            respHandler.getResp(HttpUtil.createGet(\"http://localhost:1234\"));\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertTrue(e.getMessage().length() > 0);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/common/tool/SonicRespExceptionTest.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.common.tool;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class SonicRespExceptionTest {\n\n    @Test\n    public void testSonicRespExceptionTest() {\n        SonicRespException e = new SonicRespException(\"test\");\n        Assert.assertEquals(\"test\", e.getMessage());\n    }\n\n    @Test\n    public void testSonicRespExceptionTestWithMsg() {\n        SonicRespException e = new SonicRespException(\"test\", new Exception(\"hello\"));\n        Assert.assertEquals(\"test\", e.getMessage());\n        Assert.assertEquals(\"hello\", e.getCause().getMessage());\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/ios/IOSDriverTest.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.enums.PasteboardType;\nimport org.cloud.sonic.driver.common.models.ElementRect;\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.ios.enums.*;\nimport org.cloud.sonic.driver.ios.models.TouchActions;\nimport org.cloud.sonic.driver.ios.service.IOSElement;\nimport org.junit.*;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\n\nimport javax.imageio.stream.FileImageOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.UUID;\n\n@RunWith(Parameterized.class)\npublic class IOSDriverTest {\n    static IOSDriver iosDriver;\n    static String url = \"http://localhost:8100\";\n\n    @Parameterized.Parameters\n    public static Object[][] data() {\n        return new Object[1][0];\n    }\n\n    @Before\n    public void before() throws InterruptedException {\n        Thread.sleep(2000);\n    }\n\n    @BeforeClass\n    public static void beforeClass() throws SonicRespException {\n        Boolean hasThrow = false;\n        try {\n            new IOSDriver(url, null);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(\"'capabilities' is mandatory to create a new session\", e.getMessage());\n        }\n        Assert.assertTrue(hasThrow);\n        iosDriver = new IOSDriver(url, new JSONObject());\n        iosDriver.disableLog();\n        iosDriver.showLog();\n        Assert.assertEquals(url, iosDriver.getWdaClient().getRemoteUrl());\n        Assert.assertTrue(iosDriver.getSessionId().length() > 0);\n        iosDriver.closeDriver();\n\n        iosDriver = new IOSDriver(url);\n        Assert.assertEquals(url, iosDriver.getWdaClient().getRemoteUrl());\n        Assert.assertTrue(iosDriver.getSessionId().length() > 0);\n    }\n\n    @Test\n    public void testApp() throws SonicRespException {\n        iosDriver.appActivate(\"developer.apple.wwdc-Release\");\n        Boolean hasThrow = false;\n        try {\n            iosDriver.appActivate(\"\");\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(\"bundleId not found.\", e.getMessage());\n        }\n        Assert.assertTrue(hasThrow);\n        hasThrow = false;\n        try {\n            iosDriver.appActivate(null);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(\"bundleId not found.\", e.getMessage());\n        }\n        Assert.assertTrue(hasThrow);\n        iosDriver.appRunBackground(5);\n        Assert.assertTrue(iosDriver.appTerminate(\"developer.apple.wwdc-Release\"));\n        Assert.assertFalse(iosDriver.appTerminate(\"developer.apple.wwdc-Release\"));\n        iosDriver.appAuthReset(AuthResource.CAMERA);\n    }\n\n    @Test\n    public void testSiriAndSendKeys() throws SonicRespException, InterruptedException {\n        iosDriver.sendSiriCommand(\"打开提醒事项\");\n        Boolean hasThrow = false;\n        try {\n            iosDriver.sendSiriCommand(\"\");\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(\"siri command is null!\", e.getMessage());\n        }\n        Assert.assertTrue(hasThrow);\n        hasThrow = false;\n        try {\n            iosDriver.sendSiriCommand(null);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(\"siri command is null!\", e.getMessage());\n        }\n        Assert.assertTrue(hasThrow);\n        Thread.sleep(4000);\n        iosDriver.tap(150, 181);\n        iosDriver.sendKeys(\"中文123\");\n        iosDriver.sendKeys(TextKey.DELETE);\n        iosDriver.sendKeys(TextKey.BACK_SPACE);\n        iosDriver.pressButton(SystemButton.HOME);\n    }\n\n    @Test\n    public void testPasteboard() throws SonicRespException, InterruptedException {\n        iosDriver.pressButton(SystemButton.HOME);\n        String text = UUID.randomUUID() + \"中文\";\n        iosDriver.appActivate(\"com.apple.springboard\");\n        iosDriver.findElement(IOSSelector.ACCESSIBILITY_ID, \"WebDriverAgentRunner-Runner\").click();\n        iosDriver.setPasteboard(PasteboardType.PLAIN_TEXT, text);\n        Thread.sleep(1000);\n        Assert.assertEquals(text, new String(iosDriver.getPasteboard(PasteboardType.PLAIN_TEXT), StandardCharsets.UTF_8));\n        iosDriver.pressButton(SystemButton.HOME);\n    }\n\n    @Test\n    public void testSwipe() throws SonicRespException, InterruptedException {\n        iosDriver.swipe(100, 256, 50, 256);\n        Thread.sleep(500);\n        iosDriver.swipe(100, 600, 100, 200);\n    }\n\n    @Test\n    public void testSwipeWithTime() throws SonicRespException, InterruptedException {\n        iosDriver.swipe(100, 600, 100, 200, 2000);\n    }\n\n    @Test\n    public void testTap() throws SonicRespException, InterruptedException {\n        iosDriver.tap(150, 81);\n        Thread.sleep(500);\n        iosDriver.pressButton(SystemButton.HOME);\n    }\n\n    @Test\n    public void testLongPress() throws SonicRespException {\n        iosDriver.longPress(150, 281, 1500);\n        iosDriver.pressButton(SystemButton.HOME);\n    }\n\n    @Test\n    public void testPerformTouchAction() throws SonicRespException, InterruptedException {\n        iosDriver.performTouchAction(new TouchActions.FingerTouchAction().press(100, 256).wait(50).move(50, 256).wait(10).release());\n        Thread.sleep(1500);\n        iosDriver.performTouchAction(new TouchActions.FingerTouchAction().press(50, 256).wait(50).move(100, 256).wait(10).release());\n    }\n\n    @Test\n    public void testMultiFingerTouchAction() throws SonicRespException, InterruptedException {\n        iosDriver.appActivate(\"com.apple.camera\");\n        Thread.sleep(1000);\n        TouchActions zoomIn = new TouchActions();\n        zoomIn.finger(\"0\").press(100, 256).wait(50).move(150, 256).wait(10).release();\n        zoomIn.finger(\"1\").press(100, 256).wait(50).move(50, 256).wait(10).release();\n        iosDriver.performTouchAction(zoomIn);\n        Thread.sleep(500);\n        TouchActions zoomOut = new TouchActions();\n        zoomOut.finger(\"0\").press(50, 256).wait(50).move(100, 256).wait(10).release();\n        zoomOut.finger(\"1\").press(150, 256).wait(50).move(100, 256).wait(10).release();\n        iosDriver.performTouchAction(zoomOut);\n        Thread.sleep(500);\n        iosDriver.pressButton(SystemButton.HOME);\n    }\n\n    @Test\n    public void testPressButton() throws SonicRespException, InterruptedException {\n        iosDriver.pressButton(SystemButton.HOME);\n        Thread.sleep(1000);\n        iosDriver.pressButton(SystemButton.VOLUME_DOWN);\n        Thread.sleep(1000);\n        iosDriver.pressButton(SystemButton.VOLUME_UP);\n        Thread.sleep(1000);\n        iosDriver.pressButton(\"home\");\n    }\n\n    @Test\n    public void testGetPageSource() throws SonicRespException {\n        Assert.assertTrue(iosDriver.getPageSource().contains(\"XCUIElementTypeApplication\"));\n    }\n\n    @Test\n    public void testLock() throws SonicRespException {\n        iosDriver.lock();\n        Assert.assertTrue(iosDriver.isLocked());\n        iosDriver.unlock();\n        Assert.assertFalse(iosDriver.isLocked());\n    }\n\n    @Test\n    public void testSession() {\n        String sessionId = iosDriver.getSessionId();\n        iosDriver.getWdaClient().setSessionId(null);\n        Boolean hasThrow = false;\n        try {\n            iosDriver.getWdaClient().lock();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(\"sessionId not found.\", e.getMessage());\n        }\n        Assert.assertTrue(hasThrow);\n        iosDriver.getWdaClient().setSessionId(\"\");\n        hasThrow = false;\n        try {\n            iosDriver.getWdaClient().lock();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(\"sessionId not found.\", e.getMessage());\n        }\n        Assert.assertTrue(hasThrow);\n        iosDriver.getWdaClient().setSessionId(sessionId);\n    }\n\n    @Test\n    public void testFindElement() throws SonicRespException, InterruptedException, IOException {\n        Boolean hasThrow = false;\n        try {\n            iosDriver.findElement(\"accessibility id\", \"地图1\").click();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertTrue(e.getMessage().contains(\"unable to find an element\"));\n        }\n        Assert.assertTrue(hasThrow);\n        iosDriver.pressButton(SystemButton.HOME);\n        Thread.sleep(2000);\n        iosDriver.findElement(\"accessibility id\", \"地图\").click();\n        iosDriver.findElement(XCUIElementType.ANY);\n        Thread.sleep(2000);\n        iosDriver.pressButton(SystemButton.HOME);\n        Thread.sleep(2000);\n        iosDriver.findElement(IOSSelector.ACCESSIBILITY_ID, \"地图\").click();\n        Thread.sleep(2000);\n        iosDriver.findElement(IOSSelector.ACCESSIBILITY_ID, \"搜索地点或地址\").click();\n        IOSElement w = iosDriver.findElement(IOSSelector.ACCESSIBILITY_ID, \"搜索地点或地址\");\n        String text = UUID.randomUUID().toString().substring(0, 6) + \"中文\";\n        w.sendKeys(text);\n        Assert.assertEquals(text, w.getText());\n        w.clear();\n        Assert.assertEquals(\"搜索地点或地址\", w.getAttribute(\"name\"));\n        Assert.assertEquals(\"搜索地点或地址\", w.getText());\n        ElementRect elementRect = w.getRect();\n        Assert.assertTrue(elementRect.getX() > 0);\n        Assert.assertTrue(elementRect.getY() > 0);\n        Assert.assertTrue(elementRect.getWidth() > 0);\n        Assert.assertTrue(elementRect.getHeight() > 0);\n        Assert.assertTrue(elementRect.getCenter().getX() > 0);\n        Assert.assertTrue(elementRect.getCenter().getY() > 0);\n        byte[] bt = w.screenshot();\n        File output = new File(\"./\" + UUID.randomUUID() + \".png\");\n        FileImageOutputStream imageOutput = new FileImageOutputStream(output);\n        imageOutput.write(bt, 0, bt.length);\n        imageOutput.close();\n        output.delete();\n        iosDriver.findElement(IOSSelector.ACCESSIBILITY_ID, \"取消\").click();\n        iosDriver.setDefaultFindElementInterval(null, 3000);\n        iosDriver.setDefaultFindElementInterval(5, null);\n        iosDriver.setDefaultFindElementInterval(null, null);\n        iosDriver.pressButton(SystemButton.HOME);\n    }\n\n    @Test\n    public void testFindElementList() throws SonicRespException {\n        int eleSize = iosDriver.findElementList(XCUIElementType.WINDOW).size();\n        Assert.assertEquals(eleSize, iosDriver.findElementList(\"class name\", \"XCUIElementTypeWindow\").size());\n        Assert.assertEquals(eleSize, iosDriver.findElementList(IOSSelector.CLASS_NAME, \"XCUIElementTypeWindow\").size());\n    }\n\n    @Test\n    public void testScreenshot() throws IOException, SonicRespException {\n        byte[] bt = iosDriver.screenshot();\n        File output = new File(\"./\" + UUID.randomUUID() + \".png\");\n        FileImageOutputStream imageOutput = new FileImageOutputStream(output);\n        imageOutput.write(bt, 0, bt.length);\n        imageOutput.close();\n        output.delete();\n    }\n\n    @Test\n    public void testGetWindowSize() throws SonicRespException {\n        WindowSize size = iosDriver.getWindowSize();\n        Assert.assertNotNull(size);\n        Assert.assertTrue(size.getHeight() > 0);\n        Assert.assertTrue(size.getWidth() > 0);\n    }\n\n    @Test\n    public void testSetAppiumSettings() throws SonicRespException {\n        iosDriver.setAppiumSettings(new JSONObject());\n    }\n\n    @Test\n    public void testIsDisplayed() throws SonicRespException {\n        String value = \"name CONTAINS 'QDII' AND label CONTAINS 'QDII' AND enabled == true AND visible == true\";\n        IOSElement element1 = iosDriver.findElement(IOSSelector.PREDICATE, value);\n        System.out.println(element1.getUniquelyIdentifies() + \",isDisplayed=\" + element1.isDisplayed());\n        System.out.println(element1.getUniquelyIdentifies() + \",rect=\" + element1.getRect());\n\n        IOSElement element2 = iosDriver.findElement(IOSSelector.ACCESSIBILITY_ID, \"QDII\");\n        System.out.println(element2.getUniquelyIdentifies() + \",isDisplayed=\" + element2.isDisplayed());\n        System.out.println(element2.getUniquelyIdentifies() + \",rect=\" + element2.getRect());\n    }\n\n    @Test\n    public void testDoubleTap() throws SonicRespException {\n        iosDriver.doubleTap(100, 100);\n        iosDriver.pressButton(\"HOME\");\n    }\n\n    @Test\n    public void testActiveElement() throws SonicRespException {\n        IOSElement element = iosDriver.activeElement();\n        Assert.assertNotNull(element);\n        element.sendKeys(\"find active element\");\n    }\n\n    @Test\n    public void testOrientation() throws SonicRespException, InterruptedException {\n        Orientation orientation = iosDriver.getRotate();\n        System.out.print(\"current orientation: \" + orientation);\n\n        iosDriver.rotate(Orientation.LANDSCAPELEFT);\n        Thread.sleep(2000);\n        iosDriver.rotate(Orientation.LANDSCAPERIGHT);\n        Thread.sleep(2000);\n        iosDriver.rotate(Orientation.PORTRAIT);\n    }\n\n    @AfterClass\n    public static void after() throws SonicRespException {\n        iosDriver.closeDriver();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/ios/service/WdaClientTest.java",
    "content": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *         http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n *\n */\npackage org.cloud.sonic.driver.ios.service;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.common.enums.PasteboardType;\nimport org.cloud.sonic.driver.common.models.BaseResp;\nimport org.cloud.sonic.driver.common.models.ErrorMsg;\nimport org.cloud.sonic.driver.common.tool.RespHandler;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.ios.enums.IOSSelector;\nimport org.cloud.sonic.driver.ios.models.TouchActions;\nimport org.cloud.sonic.driver.ios.service.impl.WdaClientImpl;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.mockito.Mockito;\n\nimport java.lang.reflect.Field;\n\npublic class WdaClientTest {\n    static WdaClient wdaClient;\n    private static final String ERROR_MSG = \"err message\";\n    static String url = \"http://localhost:8100\";\n\n    @BeforeClass\n    public static void before() throws Exception {\n        wdaClient = new WdaClientImpl();\n        wdaClient.setSessionId(\"test\");\n        wdaClient.setRemoteUrl(url);\n        RespHandler respHandler = Mockito.mock(RespHandler.class);\n        BaseResp b = new BaseResp();\n        b.setErr(new ErrorMsg(\"testErr\", ERROR_MSG, \"traceback\"));\n        Assert.assertNull(b.getSessionId());\n        Mockito.when(respHandler.getResp(Mockito.any())).thenReturn(b);\n        Mockito.when(respHandler.getResp(Mockito.any(), Mockito.anyInt())).thenReturn(b);\n        Field respField = wdaClient.getClass().getDeclaredField(\"respHandler\");\n        respField.setAccessible(true);\n        respField.set(wdaClient, respHandler);\n    }\n\n    @Test\n    public void testGetWindowSize() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.getWindowSize();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testLocked() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.isLocked();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testLock() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.lock();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testUnLock() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.unlock();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testPerformTouchAction() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.performTouchAction(new TouchActions());\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testSendKeys() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.sendKeys(\"test\", 1);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testSetPasteboard() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.setPasteboard(PasteboardType.PLAIN_TEXT.getType(), \"text\");\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testGetPasteboard() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.getPasteboard(PasteboardType.PLAIN_TEXT.getType());\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testPageSource() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.pageSource();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testPressButton() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.pressButton(\"home\");\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testSiriCommand() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.sendSiriCommand(\"home\");\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testAppActivate() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.appActivate(\"developer.apple.wwdc-Release\");\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testAppTerminate() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.appTerminate(\"developer.apple.wwdc-Release\");\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testAppRunBackground() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.appRunBackground(10);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testAppAuthReset() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.appAuthReset(6);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testFindElement() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.findElement(IOSSelector.ACCESSIBILITY_ID.getSelector(), \"abc\", 10, 10);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testFindElements() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.findElementList(IOSSelector.ACCESSIBILITY_ID.getSelector(), \"abc\", 10, 10);\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testSetAppiumSettings() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.setAppiumSettings(new JSONObject());\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n\n    @Test\n    public void testScreenShot() {\n        Boolean hasThrow = false;\n        try {\n            wdaClient.screenshot();\n        } catch (Throwable e) {\n            hasThrow = true;\n            Assert.assertEquals(SonicRespException.class, e.getClass());\n            Assert.assertEquals(e.getMessage(), ERROR_MSG);\n        }\n        Assert.assertTrue(hasThrow);\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/poco/PocoDriverTest.java",
    "content": "package org.cloud.sonic.driver.poco;\n\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.poco.enums.PocoEngine;\nimport org.cloud.sonic.driver.poco.enums.PocoSelector;\nimport org.cloud.sonic.driver.poco.models.PocoElement;\nimport org.junit.AfterClass;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\nimport java.util.List;\n\npublic class PocoDriverTest {\n    static PocoDriver pocoDriver;\n\n    @BeforeClass\n    public static void beforeClass() {\n        pocoDriver = new PocoDriver(PocoEngine.UNITY_3D);\n        pocoDriver.disableLog();\n        pocoDriver.showLog();\n    }\n\n    @Test\n    public void testPageSource() throws SonicRespException {\n//        Assert.assertEquals(\"Root\", pocoDriver.getPageSource().getPayload().getType());\n        Assert.assertTrue(pocoDriver.getPageSourceForJsonString().length() > 0);\n        Assert.assertNotNull(pocoDriver.getPageSourceForXmlElement().toString());\n        System.out.println(pocoDriver.getPageSourceForXmlElement().toString());\n    }\n\n    @Test\n    public void testFindElement() throws SonicRespException {\n        String expression = \"poco(\\\"star\\\")[3]\";\n        List<PocoElement> pocoElements = pocoDriver.findElements(PocoSelector.POCO, expression);\n        System.out.println(pocoElements.size());\n        for (PocoElement pocoElement : pocoElements) {\n            System.out.println(pocoElement.getPayload().getPos());\n            Assert.assertNotNull(pocoElement.getPayload().getName());\n            System.out.println(pocoElement.getPayload().getName());\n            Assert.assertNotNull(pocoElement.currentNodeSelector);\n            System.out.println(pocoElement.currentNodeSelector);\n        }\n    }\n\n    @Test\n    public void testFreeze() throws SonicRespException {\n        String r = pocoDriver.getPageSourceForJsonString();\n        pocoDriver.freezeSource();\n        try {\n            // change page tree operation\n            Thread.sleep(5 * 1000);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n        Assert.assertEquals(r, pocoDriver.getPageSourceForJsonString());\n        pocoDriver.thawSource();\n    }\n\n    @Test\n    public void testWindowsSize() throws SonicRespException {\n        WindowSize windowSize = pocoDriver.getScreenSize();\n        Assert.assertTrue(windowSize.getHeight() > 0);\n        Assert.assertTrue(windowSize.getWidth() > 0);\n    }\n\n    @Test\n    public void testNodeExist() throws SonicRespException {\n        String expression = \"Root > MEHolo > AnchorManager\";\n        PocoElement pocoElement = pocoDriver.findElement(PocoSelector.CSS_SELECTOR, expression);\n        Assert.assertTrue(pocoElement.currentTheNodeExists());\n        // mock node does not exist\n        pocoElement.currentNodeSelector = \"Root > children > MEHolo > children > AnchorManager222\";\n        Assert.assertTrue(pocoElement.currentTheNodeExists());\n    }\n\n    @Test\n    public void testGetParent() throws SonicRespException {\n        String expression = \"Root > MEHolo >  AnchorManager\";\n        PocoElement pocoElement = pocoDriver.findElement(PocoSelector.CSS_SELECTOR, expression);\n        PocoElement parentPocoElement = pocoElement.getParentNode();\n        Assert.assertNotNull(parentPocoElement.getPayload().getName());\n        System.out.println(parentPocoElement.getPayload().getName());\n        Assert.assertNotNull(parentPocoElement.currentNodeSelector);\n        System.out.println(parentPocoElement.currentNodeSelector);\n    }\n\n    @Test\n    public void testElementGetChild() throws SonicRespException {\n        String expression = \"Root > Canvas\";\n        PocoElement pocoElement = pocoDriver.findElement(PocoSelector.CSS_SELECTOR, expression);\n        Assert.assertTrue(!pocoElement.getChildren().isEmpty());\n        System.out.println(pocoElement.getChildren().size());\n    }\n\n    @Test\n    public void testUpdateRootCase() throws SonicRespException {\n        String expression = \"Root > MEHolo > AnchorManager\";\n        PocoElement pocoElement = pocoDriver.findElement(PocoSelector.CSS_SELECTOR, expression);\n        String lastRootXml = pocoElement.getRootElement().getXmlElement().toString();\n        try {\n            System.out.println(\"into blocking,please perform a page tree change operation\");\n            // change page tree operation\n            Thread.sleep(5 * 1000);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n        pocoDriver.getPageSource();\n        Assert.assertEquals(lastRootXml, pocoElement.getRootElement().getXmlElement().toString());\n    }\n\n    @Test\n    public void testGetAttribute() throws SonicRespException {\n        String expression = \"poco(\\\"star\\\")[3]\";\n        PocoElement pocoElement = pocoDriver.findElement(PocoSelector.POCO, expression);\n        System.out.println(pocoElement.getAttribute(\"_instanceId\"));\n        assert pocoElement.getAttribute(\"_instanceId\") != null;\n    }\n\n    @AfterClass\n    public static void afterClass() {\n        pocoDriver.closeDriver();\n    }\n}\n"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/poco/PocoJsonToXmlTest.java",
    "content": "package org.cloud.sonic.driver.poco;\n\nimport com.alibaba.fastjson.JSON;\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\nimport org.cloud.sonic.driver.poco.models.PocoElement;\nimport org.cloud.sonic.driver.poco.models.RootElement;\nimport org.cloud.sonic.driver.poco.service.impl.PocoClientImpl;\nimport org.cloud.sonic.driver.poco.util.PocoJsonToXml;\nimport org.jsoup.Jsoup;\nimport org.jsoup.nodes.Element;\nimport org.jsoup.parser.Parser;\nimport org.junit.Test;\n\nimport java.util.List;\n\n\npublic class PocoJsonToXmlTest {\n\n    @Test\n    public void testToXml() throws SonicRespException {\n        System.out.println(PocoJsonToXml.jsonObjToXml(JSON.parseObject(dump).getJSONObject(\"Root\")));\n    }\n\n    @Test\n    public void mockPocoResultTest() throws SonicRespException {\n        String expression = \"poco(\\\"<Root>\\\").child(\\\"Main&&Camera\\\")\";\n\n//        String expression = \"poco(\\\"<Root>\\\")\";\n\n        Element rootXmlElement = Jsoup.parse(PocoJsonToXml.jsonObjToXml(JSON.parseObject(dump).getJSONObject(\"result\")), \"\", Parser.xmlParser());\n\n        PocoClientImpl pocoClient = new PocoClientImpl();\n        RootElement rootElement = new RootElement(rootXmlElement);\n        pocoClient.rootNode = rootElement;\n        System.out.println(rootXmlElement);\n        List<PocoElement> result = pocoClient.findElements(\"poco\", expression);\n        System.out.println(result.size());\n\n//        pocoClient.findElements(\"cssSelector\", expression);\n    }\n\n    String dump = \"{\\\"result\\\":{\\\"name\\\":\\\"<Root>\\\",\\\"payload\\\":{\\\"name\\\":\\\"<Root>\\\",\\\"type\\\":\\\"Root\\\",\\\"visible\\\":true,\\\"pos\\\":[0.0,0.0],\\\"size\\\":[0.0,0.0],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"local\\\":0,\\\"global\\\":0}},\\\"children\\\":[{\\\"name\\\":\\\"Main Camera\\\",\\\"payload\\\":{\\\"name\\\":\\\"Main&&Camera\\\",\\\"type\\\":\\\"Camera\\\",\\\"visible\\\":true,\\\"pos\\\":[0.0,0.0],\\\"size\\\":[0.0,0.0],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":0.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"Transform\\\",\\\"Camera\\\",\\\"GUILayer\\\",\\\"FlareLayer\\\",\\\"AudioListener\\\"],\\\"tag\\\":\\\"MainCamera\\\",\\\"_instanceId\\\":10098}},{\\\"name\\\":\\\"Canvas\\\",\\\"payload\\\":{\\\"name\\\":\\\"Canvas\\\",\\\"type\\\":\\\"Node\\\",\\\"visible\\\":true,\\\"pos\\\":[0.5,0.5],\\\"size\\\":[1.0,1.0],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"Canvas\\\",\\\"CanvasScaler\\\",\\\"GraphicRaycaster\\\",\\\"PocoManager\\\",\\\"HunterInterface\\\"],\\\"_instanceId\\\":10158},\\\"children\\\":[{\\\"name\\\":\\\"plays\\\",\\\"payload\\\":{\\\"name\\\":\\\"plays\\\",\\\"type\\\":\\\"Node\\\",\\\"visible\\\":true,\\\"pos\\\":[0.5,0.5],\\\"size\\\":[1.0,1.0],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\"],\\\"_instanceId\\\":10074},\\\"children\\\":[{\\\"name\\\":\\\"playBasic\\\",\\\"payload\\\":{\\\"name\\\":\\\"playBasic\\\",\\\"type\\\":\\\"Image\\\",\\\"visible\\\":true,\\\"pos\\\":[0.501041651,0.495370358],\\\"size\\\":[1.94782829,1.90740752],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"CanvasGroup\\\"],\\\"texture\\\":\\\"Background\\\",\\\"_instanceId\\\":10188},\\\"children\\\":[{\\\"name\\\":\\\"title\\\",\\\"payload\\\":{\\\"name\\\":\\\"title\\\",\\\"type\\\":\\\"Text\\\",\\\"visible\\\":true,\\\"pos\\\":[0.226041645,0.140740708],\\\"size\\\":[0.322395861,0.06111111],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"text\\\":\\\"Basic test\\\",\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Text\\\"],\\\"_instanceId\\\":10226}},{\\\"name\\\":\\\"star_single\\\",\\\"payload\\\":{\\\"name\\\":\\\"star_single\\\",\\\"type\\\":\\\"Image\\\",\\\"visible\\\":true,\\\"pos\\\":[0.501041651,0.3],\\\"size\\\":[0.130208343,0.231481493],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"StrongFeedback\\\"],\\\"texture\\\":\\\"star\\\",\\\"_instanceId\\\":10078}},{\\\"name\\\":\\\"pos_input\\\",\\\"payload\\\":{\\\"name\\\":\\\"pos_input\\\",\\\"type\\\":\\\"InputField\\\",\\\"visible\\\":true,\\\"pos\\\":[0.501041651,0.5833333],\\\"size\\\":[0.3463542,0.06018519],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"InputField\\\"],\\\"texture\\\":\\\"InputFieldBackground\\\",\\\"_instanceId\\\":10184},\\\"children\\\":[{\\\"name\\\":\\\"pos_input Input Caret\\\",\\\"payload\\\":{\\\"name\\\":\\\"pos_input Input Caret\\\",\\\"type\\\":\\\"Node\\\",\\\"visible\\\":true,\\\"pos\\\":[0.501041651,0.5837963],\\\"size\\\":[0.3359375,0.04814815],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"LayoutElement\\\"],\\\"_instanceId\\\":-10060}},{\\\"name\\\":\\\"Placeholder\\\",\\\"payload\\\":{\\\"name\\\":\\\"Placeholder\\\",\\\"type\\\":\\\"Text\\\",\\\"visible\\\":true,\\\"pos\\\":[0.501041651,0.5837963],\\\"size\\\":[0.3359375,0.04814815],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"text\\\":\\\"Enter text...\\\",\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Text\\\"],\\\"_instanceId\\\":10152}},{\\\"name\\\":\\\"Text\\\",\\\"payload\\\":{\\\"name\\\":\\\"Text\\\",\\\"type\\\":\\\"Text\\\",\\\"visible\\\":true,\\\"pos\\\":[0.501041651,0.5837963],\\\"size\\\":[0.3359375,0.04814815],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"text\\\":\\\"xx&cc_ggg\\\",\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Text\\\"],\\\"_instanceId\\\":10148}}]}]}]},{\\\"name\\\":\\\"globalControl\\\",\\\"payload\\\":{\\\"name\\\":\\\"globalControl\\\",\\\"type\\\":\\\"Image\\\",\\\"visible\\\":true,\\\"pos\\\":[0.0770833045,0.862037063],\\\"size\\\":[0.08816848,0.0881684646],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"CanvasGroup\\\"],\\\"texture\\\":\\\"Background\\\",\\\"_instanceId\\\":10118},\\\"children\\\":[{\\\"name\\\":\\\"btn_back\\\",\\\"payload\\\":{\\\"name\\\":\\\"btn_back\\\",\\\"type\\\":\\\"Button\\\",\\\"visible\\\":true,\\\"pos\\\":[0.07740478,0.8608941],\\\"size\\\":[0.0551053,0.06530997],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":true,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"Button\\\",\\\"StrongFeedback\\\"],\\\"texture\\\":\\\"UISprite\\\",\\\"_instanceId\\\":10192},\\\"children\\\":[{\\\"name\\\":\\\"Text\\\",\\\"payload\\\":{\\\"name\\\":\\\"Text\\\",\\\"type\\\":\\\"Text\\\",\\\"visible\\\":true,\\\"pos\\\":[0.07740478,0.8608941],\\\"size\\\":[0.0551053,0.06530997],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"text\\\":\\\"Back\\\",\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Text\\\"],\\\"_instanceId\\\":10132}}]}]}]},{\\\"name\\\":\\\"EventSystem\\\",\\\"payload\\\":{\\\"name\\\":\\\"EventSystem\\\",\\\"type\\\":\\\"GameObject\\\",\\\"visible\\\":true,\\\"pos\\\":[0.0,0.0],\\\"size\\\":[0.0,0.0],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"Transform\\\",\\\"EventSystem\\\",\\\"StandaloneInputModule\\\",\\\"EventRegistration\\\"],\\\"_instanceId\\\":10160}}]}}\";\n//    String dump = \"{\\\"Root\\\":{\\\"name\\\":\\\"<Root>\\\",\\\"payload\\\":{\\\"name\\\":\\\"<Root>\\\",\\\"type\\\":\\\"Root\\\",\\\"visible\\\":true,\\\"pos\\\":[0.0,0.0],\\\"size\\\":[0.0,0.0],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"local\\\":0,\\\"global\\\":0}},\\\"children\\\":[{\\\"name\\\":\\\"Main&&Camera\\\",\\\"payload\\\":{\\\"name\\\":\\\"Main&&Camera\\\",\\\"type\\\":\\\"Camera\\\",\\\"visible\\\":true,\\\"pos\\\":[0.0,0.0],\\\"size\\\":[0.0,0.0],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":0.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"Transform\\\",\\\"Camera\\\",\\\"GUILayer\\\",\\\"FlareLayer\\\",\\\"AudioListener\\\",\\\"PocoManager\\\"],\\\"tag\\\":\\\"MainCamera\\\",\\\"_ilayer\\\":0,\\\"layer\\\":\\\"Default\\\",\\\"_instanceId\\\":838}},{\\\"name\\\":\\\"Canvas\\\",\\\"payload\\\":{\\\"name\\\":\\\"Canvas\\\",\\\"type\\\":\\\"Node\\\",\\\"visible\\\":true,\\\"pos\\\":[0.5,0.5],\\\"size\\\":[1.0,1.0],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"Canvas\\\",\\\"CanvasScaler\\\",\\\"GraphicRaycaster\\\",\\\"PocoManager\\\"],\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":898},\\\"children\\\":[{\\\"name\\\":\\\"plays\\\",\\\"payload\\\":{\\\"name\\\":\\\"plays\\\",\\\"type\\\":\\\"Node\\\",\\\"visible\\\":true,\\\"pos\\\":[0.5,0.5],\\\"size\\\":[1.0,1.0],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\"],\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":814},\\\"children\\\":[{\\\"name\\\":\\\"playDragAndDrop\\\",\\\"payload\\\":{\\\"name\\\":\\\"playDragAndDrop\\\",\\\"type\\\":\\\"Image\\\",\\\"visible\\\":true,\\\"pos\\\":[0.498958319,0.502777755],\\\"size\\\":[1.94782817,1.9074074],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"CanvasGroup\\\"],\\\"texture\\\":\\\"Background\\\",\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":934},\\\"children\\\":[{\\\"name\\\":\\\"collectionArea\\\",\\\"payload\\\":{\\\"name\\\":\\\"collectionArea\\\",\\\"type\\\":\\\"Node\\\",\\\"visible\\\":true,\\\"pos\\\":[0.516666651,0.220370367],\\\"size\\\":[0.0,0.0],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\"],\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":860},\\\"children\\\":[{\\\"name\\\":\\\"shell\\\",\\\"payload\\\":{\\\"name\\\":\\\"shell\\\",\\\"type\\\":\\\"Image\\\",\\\"visible\\\":true,\\\"pos\\\":[0.504166663,0.705555558],\\\"size\\\":[0.224570453,0.284520835],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"BoxCollider2D\\\"],\\\"texture\\\":\\\"clamshell_open\\\",\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":922}}]},{\\\"name\\\":\\\"star\\\",\\\"payload\\\":{\\\"name\\\":\\\"star\\\",\\\"type\\\":\\\"Image\\\",\\\"visible\\\":true,\\\"pos\\\":[0.156958327,0.317777783],\\\"size\\\":[0.130208328,0.231481478],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"TestDragAndDrop\\\"],\\\"texture\\\":\\\"star\\\",\\\"tag\\\":\\\"star\\\",\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":926}},{\\\"name\\\":\\\"star\\\",\\\"payload\\\":{\\\"name\\\":\\\"star\\\",\\\"type\\\":\\\"Image\\\",\\\"visible\\\":true,\\\"pos\\\":[0.335958362,0.317777783],\\\"size\\\":[0.130208328,0.231481478],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"TestDragAndDrop\\\"],\\\"texture\\\":\\\"star\\\",\\\"tag\\\":\\\"star\\\",\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":950}},{\\\"name\\\":\\\"star\\\",\\\"payload\\\":{\\\"name\\\":\\\"star\\\",\\\"type\\\":\\\"Image\\\",\\\"visible\\\":true,\\\"pos\\\":[0.5149583,0.317777783],\\\"size\\\":[0.130208328,0.231481478],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"TestDragAndDrop\\\"],\\\"texture\\\":\\\"star\\\",\\\"tag\\\":\\\"star\\\",\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":868}},{\\\"name\\\":\\\"star\\\",\\\"payload\\\":{\\\"name\\\":\\\"star\\\",\\\"type\\\":\\\"Image\\\",\\\"visible\\\":true,\\\"pos\\\":[0.693958342,0.317777783],\\\"size\\\":[0.130208328,0.231481478],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"TestDragAndDrop\\\"],\\\"texture\\\":\\\"star\\\",\\\"tag\\\":\\\"star\\\",\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":852}},{\\\"name\\\":\\\"star\\\",\\\"payload\\\":{\\\"name\\\":\\\"star\\\",\\\"type\\\":\\\"Image\\\",\\\"visible\\\":true,\\\"pos\\\":[0.872958362,0.317777783],\\\"size\\\":[0.130208328,0.231481478],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"TestDragAndDrop\\\"],\\\"texture\\\":\\\"star\\\",\\\"tag\\\":\\\"star\\\",\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":802}},{\\\"name\\\":\\\"Text\\\",\\\"payload\\\":{\\\"name\\\":\\\"Text\\\",\\\"type\\\":\\\"Text\\\",\\\"visible\\\":true,\\\"pos\\\":[0.15,0.143518522],\\\"size\\\":[0.11776042,0.153611109],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"text\\\":\\\"score\\\",\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Text\\\"],\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":844}},{\\\"name\\\":\\\"scoreVal\\\",\\\"payload\\\":{\\\"name\\\":\\\"scoreVal\\\",\\\"type\\\":\\\"Text\\\",\\\"visible\\\":true,\\\"pos\\\":[0.266145825,0.1435184],\\\"size\\\":[0.11776042,0.153611109],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"text\\\":\\\"0\\\",\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Text\\\"],\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":836}}]}]},{\\\"name\\\":\\\"globalControl\\\",\\\"payload\\\":{\\\"name\\\":\\\"globalControl\\\",\\\"type\\\":\\\"Image\\\",\\\"visible\\\":true,\\\"pos\\\":[0.0770833343,0.862037063],\\\"size\\\":[0.08816848,0.0881684646],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"CanvasGroup\\\"],\\\"texture\\\":\\\"Background\\\",\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":858},\\\"children\\\":[{\\\"name\\\":\\\"btn_back\\\",\\\"payload\\\":{\\\"name\\\":\\\"btn_back\\\",\\\"type\\\":\\\"Button\\\",\\\"visible\\\":true,\\\"pos\\\":[0.07740478,0.8608941],\\\"size\\\":[0.0551053025,0.06530998],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":true,\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Image\\\",\\\"Button\\\",\\\"StrongFeedback\\\"],\\\"texture\\\":\\\"UISprite\\\",\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":932},\\\"children\\\":[{\\\"name\\\":\\\"Text\\\",\\\"payload\\\":{\\\"name\\\":\\\"Text\\\",\\\"type\\\":\\\"Text\\\",\\\"visible\\\":true,\\\"pos\\\":[0.07740478,0.8608941],\\\"size\\\":[0.0551053025,0.06530998],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"text\\\":\\\"Back\\\",\\\"components\\\":[\\\"RectTransform\\\",\\\"CanvasRenderer\\\",\\\"Text\\\"],\\\"_ilayer\\\":5,\\\"layer\\\":\\\"UI\\\",\\\"_instanceId\\\":872}}]}]}]},{\\\"name\\\":\\\"EventSystem\\\",\\\"payload\\\":{\\\"name\\\":\\\"EventSystem\\\",\\\"type\\\":\\\"GameObject\\\",\\\"visible\\\":true,\\\"pos\\\":[0.0,0.0],\\\"size\\\":[0.0,0.0],\\\"scale\\\":[1.0,1.0],\\\"anchorPoint\\\":[0.5,0.5],\\\"zOrders\\\":{\\\"global\\\":0.0,\\\"local\\\":-10.0},\\\"clickable\\\":false,\\\"components\\\":[\\\"Transform\\\",\\\"EventSystem\\\",\\\"StandaloneInputModule\\\",\\\"EventRegistration\\\",\\\"BaseInput\\\"],\\\"_ilayer\\\":0,\\\"layer\\\":\\\"Default\\\",\\\"_instanceId\\\":900}}]}}\";\n}\n"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/poco/PocoXYTransformerTest.java",
    "content": "package org.cloud.sonic.driver.poco;\n\nimport org.cloud.sonic.driver.poco.util.PocoXYTransformer;\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class PocoXYTransformerTest {\n    double width = 100, height = 1000;\n\n    @Test\n    public void testOriToUP() {\n        double pocoX = 100, pocoY = 50;\n        double[] result = PocoXYTransformer.PocoTransformerVertical(pocoX, pocoY, width, height, 270);\n        Assert.assertEquals(result[0], pocoY, 0);\n        Assert.assertEquals(result[1], height - pocoX, 0);\n        System.out.printf(\"x:%s,y:%s\", result[0], result[1]);\n        System.out.println();\n\n        result = PocoXYTransformer.PocoTransformerVertical(pocoX, pocoY, width, height, 90);\n        Assert.assertEquals(result[0], pocoY, 0);\n        Assert.assertEquals(result[1], pocoX, 0);\n        System.out.printf(\"x:%s,y:%s\", result[0], result[1]);\n        System.out.println();\n    }\n\n    @Test\n    public void testUPToOri() {\n        double upx = 50, upy = 100;\n        double[] result = PocoXYTransformer.VerticalTransformerPoco(upx, upy, width, height, 270);\n        Assert.assertEquals(result[0], height - upy, 0);\n        Assert.assertEquals(result[1], upx, 0);\n        System.out.printf(\"x:%s,y:%s\", result[0], result[1]);\n        System.out.println();\n\n        result = PocoXYTransformer.VerticalTransformerPoco(upx, upy, width, height, 90);\n        Assert.assertEquals(result[0], upy, 0);\n        Assert.assertEquals(result[1], upx, 0);\n        System.out.printf(\"x:%s,y:%s\", result[0], result[1]);\n        System.out.println();\n    }\n}\n"
  }
]