main d8d310a20ccd cached
60 files
251.5 KB
61.9k tokens
568 symbols
1 requests
Download .txt
Showing preview only (275K chars total). Download the full file or copy to clipboard to get everything.
Repository: SonicCloudOrg/sonic-driver-core
Branch: main
Commit: d8d310a20ccd
Files: 60
Total size: 251.5 KB

Directory structure:
gitextract_j_5ezhz9/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── maven.yml
│       └── release.yml
├── .gitignore
├── LICENSE
├── README.md
├── README_CN.md
├── pom.xml
└── src/
    ├── main/
    │   └── java/
    │       └── org/
    │           └── cloud/
    │               └── sonic/
    │                   └── driver/
    │                       ├── android/
    │                       │   ├── AndroidDriver.java
    │                       │   ├── enmus/
    │                       │   │   └── AndroidSelector.java
    │                       │   └── service/
    │                       │       ├── AndroidElement.java
    │                       │       ├── UiaClient.java
    │                       │       └── impl/
    │                       │           ├── AndroidElementImpl.java
    │                       │           └── UiaClientImpl.java
    │                       ├── common/
    │                       │   ├── enums/
    │                       │   │   └── PasteboardType.java
    │                       │   ├── models/
    │                       │   │   ├── BaseElement.java
    │                       │   │   ├── BaseResp.java
    │                       │   │   ├── Capabilities.java
    │                       │   │   ├── ElementRect.java
    │                       │   │   ├── ErrorMsg.java
    │                       │   │   ├── SessionInfo.java
    │                       │   │   └── WindowSize.java
    │                       │   └── tool/
    │                       │       ├── Logger.java
    │                       │       ├── RespHandler.java
    │                       │       └── SonicRespException.java
    │                       ├── ios/
    │                       │   ├── IOSDriver.java
    │                       │   ├── enums/
    │                       │   │   ├── ActionType.java
    │                       │   │   ├── AuthResource.java
    │                       │   │   ├── IOSSelector.java
    │                       │   │   ├── Orientation.java
    │                       │   │   ├── SystemButton.java
    │                       │   │   ├── TextKey.java
    │                       │   │   └── XCUIElementType.java
    │                       │   ├── models/
    │                       │   │   └── TouchActions.java
    │                       │   └── service/
    │                       │       ├── IOSElement.java
    │                       │       ├── WdaClient.java
    │                       │       └── impl/
    │                       │           ├── IOSElementImpl.java
    │                       │           └── WdaClientImpl.java
    │                       └── poco/
    │                           ├── PocoDriver.java
    │                           ├── enums/
    │                           │   ├── PocoEngine.java
    │                           │   └── PocoSelector.java
    │                           ├── models/
    │                           │   ├── PocoElement.java
    │                           │   └── RootElement.java
    │                           ├── service/
    │                           │   ├── PocoClient.java
    │                           │   ├── PocoConnection.java
    │                           │   └── impl/
    │                           │       ├── PocoClientImpl.java
    │                           │       ├── SocketClientImpl.java
    │                           │       └── WebSocketClientImpl.java
    │                           └── util/
    │                               ├── PocoJsonToXml.java
    │                               ├── PocoTool.java
    │                               └── PocoXYTransformer.java
    └── test/
        └── java/
            └── org/
                └── cloud/
                    └── sonic/
                        └── driver/
                            ├── android/
                            │   ├── AndroidDriverTest.java
                            │   └── service/
                            │       └── UiaClientTest.java
                            ├── common/
                            │   └── tool/
                            │       ├── RespHandlerTest.java
                            │       └── SonicRespExceptionTest.java
                            ├── ios/
                            │   ├── IOSDriverTest.java
                            │   └── service/
                            │       └── WdaClientTest.java
                            └── poco/
                                ├── PocoDriverTest.java
                                ├── PocoJsonToXmlTest.java
                                └── PocoXYTransformerTest.java

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "maven"
    directory: "/"
    schedule:
      interval: "weekly"


================================================
FILE: .github/workflows/maven.yml
================================================
name: maven compile test

on: [pull_request]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up JDK 17
      uses: actions/setup-java@v2
      with:
        java-version: '17'
        distribution: 'temurin'
        cache: maven
    - name: Validate and Compile with Maven
      run: mvn validate compile


================================================
FILE: .github/workflows/release.yml
================================================
# name: release to github

# on:
#   push:
#     tags:
#       - "*.*.*"

# jobs:
#   doc:
#     runs-on: ubuntu-latest
#     steps:
#       - uses: actions/checkout@v2
#         with:
#           fetch-depth: 0
#       - name: 'Get Previous tag'
#         id: previoustag
#         uses: "WyriHaximus/github-action-get-previous-tag@v1"
#       - name: 'Get previous release tag'
#         id: tag
#         uses: "sammcoe/get-previous-release-action@v1"
#       - name: replace version
#         run: sed -i "s/${{steps.tag.outputs.tag}}/${{steps.previoustag.outputs.tag}}/g" README*.md
#       - name: Create Pull Request
#         id: cpr
#         uses: peter-evans/create-pull-request@v4
#         with:
#           commit-message: Update README file.
#           author: GitHub <noreply@github.com>
#           signoff: false
#           branch: doc/${{steps.previoustag.outputs.tag}}
#           labels: document
#           base: main
#           delete-branch: true
#           title: 'doc: update README files version to ${{steps.previoustag.outputs.tag}}'
#           body: |
#             **在提出此拉取请求时,我确认了以下几点(保存后请点击复选框):**

#             - [x] 标题为fix、feat或doc开头
#             - [x] 我已检查没有与此请求重复的拉取请求。
#             - [x] 我已经考虑过,并确认这份呈件对其他人很有价值。
#             - [x] 我接受此提交可能不会被使用,并根据维护人员的意愿关闭拉取请求。

#             **填写PR内容:**

#             - Update README files version to latest tag by bot 🚀.

#           draft: false
#       - name: Auto approve
#         if: steps.cpr.outputs.pull-request-operation == 'created'
#         uses: juliangruber/approve-pull-request-action@v1
#         with:
#           github-token: ${{ secrets.PAT }}
#           number: ${{ steps.cpr.outputs.pull-request-number }}
#       - id: automerge
#         name: automerge
#         uses: "pascalgn/automerge-action@v0.15.3"
#         env:
#           MERGE_LABELS: "document"
#           MERGE_DELETE_BRANCH: true
#           GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
#           PULL_REQUEST: ${{ steps.cpr.outputs.pull-request-number }}
#           MERGE_RETRIES: 18
#           MERGE_RETRY_SLEEP: 10000

#   release:
#     needs: doc
#     runs-on: ubuntu-latest
#     steps:
#       - uses: actions/checkout@v2
#         with:
#           fetch-depth: 0
#       - name: 'Get Previous tag'
#         id: previoustag
#         uses: "WyriHaximus/github-action-get-previous-tag@v1"
#       - name: replace version
#         run: sed -i "s/SONIC_VERSION/${{ steps.previoustag.outputs.tag }}/g" pom.xml
#       - name: Set up Maven Central Repo
#         uses: actions/setup-java@v1
#         with:
#           java-version: 1.8
#           server-id: sonatype-nexus-staging
#           server-username: ${{ secrets.OSSRH_USER }}
#           server-password: ${{ secrets.OSSRH_PASSWORD }}
#           gpg-passphrase: ${{ secrets.GPG_PASSWORD }}
#       - name: Release Maven package
#         uses: WasiqB/maven-publish-action@v1
#         with:
#           maven_args: -Dmaven.test.skip=true
#           gpg_private_key: ${{ secrets.GPG_SECRET }}
#           gpg_passphrase: ${{ secrets.GPG_PASSWORD }}
#           nexus_username: ${{ secrets.OSSRH_USER }}
#           nexus_password: ${{ secrets.OSSRH_PASSWORD }}
#       - uses: softprops/action-gh-release@v1
#         with:
#           draft: false
#           generate_release_notes: true
# # mvn clean deploy -Pdeploy -Dmaven.test.skip=true

================================================
FILE: .gitignore
================================================
/.idea/
/target/
/logs/
*.iml
*/.DS_Store
.DS_Store

================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright © [SonicCloudOrg] Sonic Project

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
<p align="center">
  <img width="80px" src="https://raw.githubusercontent.com/SonicCloudOrg/sonic-server/main/logo.png">
</p>
<p align="center">🎉The Sonic UIAutomation Driver Core</p>
<p align="center">
  <span>English |</span>
  <a href="https://github.com/SonicCloudOrg/sonic-driver-core/blob/main/README_CN.md">  
     简体中文
  </a>
</p>
<p align="center">
  <a href="#">  
    <img src="https://img.shields.io/maven-central/v/io.github.soniccloudorg/sonic-driver-core">
  </a>
  <a href="#">  
    <img src="https://img.shields.io/github/commit-activity/m/SonicCloudOrg/sonic-driver-core">
  </a>
<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>
  <a href="https://codecov.io/gh/SonicCloudOrg/sonic-driver-core">  
    <img src="https://codecov.io/gh/SonicCloudOrg/sonic-driver-core/branch/main/graph/badge.svg?token=PZ5295WQP1">
  </a>
</p>
<p align="center">
  <a href="https://github.com/SonicCloudOrg/sonic-driver-core">  
    <img src="https://www.oscs1024.com/platform/badge/SonicCloudOrg/sonic-driver-core.svg?size=large">
  </a>
</p>

## What is sonic-driver-core?

sonic-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.

## Use in Java code

### Add dependency
#### Maven Central

```xml

<dependency>
    <groupId>io.github.soniccloudorg</groupId>
    <artifactId>sonic-driver-core</artifactId>
    <version>1.1.30</version>
</dependency>
```

#### Gradle

```
implementation 'io.github.soniccloudorg:sonic-driver-core:1.1.30'
```

### Code

```java
package org.cloud.sonic.driver.ios;

import org.cloud.sonic.driver.ios.enums.IOSSelector;
import org.cloud.sonic.driver.common.tool.SonicRespException;

public class MyTest {

    public void test() throws SonicRespException {
        IOSDriver iosDriver = new IOSDriver("http://localhost:8100");
        iosDriver.showLog();

        //touch
        iosDriver.swipe(100, 256, 50, 256);
        iosDriver.tap(150, 81);
        iosDriver.longPress(150, 281, 1500);
        iosDriver.performTouchAction(new TouchActions().press(50, 256).wait(50).move(100, 256).wait(10).release());

        //element
        iosDriver.findElement(IOSSelector.XPATH, "//XCUIElementTypeTextField").click();

        //more...
    }
}
```

## More Example

See [Here](https://github.com/SonicCloudOrg/sonic-uiautomation-example/tree/main/java-example).

## Document

See [Here](https://sonic-cloud.cn/sdc/re-sdc.html).

## Sponsors

Thank you to all our sponsors!

[<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)

> [霍格沃兹测试开发学社](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)

## LICENSE

[License](LICENSE)


[![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)


================================================
FILE: README_CN.md
================================================
<p align="center">
  <img width="80px" src="https://raw.githubusercontent.com/SonicCloudOrg/sonic-server/main/logo.png">
</p>
<p align="center">🎉Sonic UI自动化Driver核心</p>
<p align="center">
  <a href="https://github.com/SonicCloudOrg/sonic-driver-core/blob/main/README.md">  
    English
  </a>
  <span>| 简体中文</span>
</p>
<p align="center">
  <a href="#">  
    <img src="https://img.shields.io/maven-central/v/io.github.soniccloudorg/sonic-driver-core">
  </a>
  <a href="#">  
    <img src="https://img.shields.io/github/commit-activity/m/SonicCloudOrg/sonic-driver-core">
  </a>
  <a href="https://codecov.io/gh/SonicCloudOrg/sonic-driver-core">  
    <img src="https://codecov.io/gh/SonicCloudOrg/sonic-driver-core/branch/main/graph/badge.svg?token=PZ5295WQP1">
  </a>
</p>
<p align="center">
  <a href="https://github.com/SonicCloudOrg/sonic-driver-core">  
    <img src="https://www.oscs1024.com/platform/badge/SonicCloudOrg/sonic-driver-core.svg?size=large">
  </a>
</p>

## sonic-driver-core是什么?

sonic-driver-core可以脱离Appium,直接与WebDriverAgent或UIautomator2交互,减少了Appium的通信层,让测试更快更稳定。

## 在你的Java代码中使用

### 引用库
#### Maven
```xml
<dependency>
    <groupId>io.github.soniccloudorg</groupId>
    <artifactId>sonic-driver-core</artifactId>
    <version>1.1.30</version>
</dependency>
```
#### Gradle
```
implementation 'io.github.soniccloudorg:sonic-driver-core:1.1.30'
```

### 代码

```java
package org.cloud.sonic.driver.ios;

import org.cloud.sonic.driver.common.tool.SonicRespException;

public class MyTest {

    public void test() throws SonicRespException {
        IOSDriver iosDriver = new IOSDriver("http://localhost:8100");
        iosDriver.showLog();

        //touch
        iosDriver.swipe(100, 256, 50, 256);
        iosDriver.tap(150, 81);
        iosDriver.longPress(150, 281, 1500);
        iosDriver.performTouchAction(new TouchActions().press(50, 256).wait(50).move(100, 256).wait(10).release());

        //element
        iosDriver.findElement(IOSSelector.XPATH, "//XCUIElementTypeTextField").click();

        //更多...
    }
}
```

## 更多例子

查看 [这里](https://github.com/SonicCloudOrg/sonic-uiautomation-example/tree/main/java-example).

## 文档

查看 [这里](https://sonic-cloud.cn/sdc/re-sdc.html).

## 赞助商

感谢所有赞助商!

[<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)

> [霍格沃兹测试开发学社](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)

## 开源许可协议

[License](LICENSE)


================================================
FILE: pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>io.github.soniccloudorg</groupId>
    <artifactId>sonic-driver-core</artifactId>
    <version>1.1.33</version>

    <name>sonic-driver-core</name>
    <description>The Sonic Project UIAutomation Driver Core for Android, iOS, Windows, Mac and so on.</description>
    <url>https://github.com/SonicCloudOrg/sonic-driver-core</url>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>


    <scm>
        <tag>main</tag>
        <url>https://github.com/SonicCloudOrg/sonic-driver-core</url>
        <connection>https://github.com/SonicCloudOrg/sonic-driver-core</connection>
        <developerConnection>https://github.com/SonicCloudOrg/sonic-driver-core</developerConnection>
    </scm>

    <licenses>
        <license>
            <name>APACHE2</name>
            <url>https://github.com/SonicCloudOrg/sonic-driver-core/blob/main/LICENSE</url>
        </license>
    </licenses>

    <issueManagement>
        <system>Github Issue</system>
        <url>https://github.com/SonicCloudOrg/sonic-driver-core/issues</url>
    </issueManagement>

    <distributionManagement>
        <snapshotRepository>
            <id>ossrh</id>
            <url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
        </snapshotRepository>
        <repository>
            <id>ossrh</id>
            <url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
        </repository>
    </distributionManagement>

    <developers>
        <developer>
            <name>SonicCloudOrg</name>
            <email>soniccloudorg@163.com</email>
            <timezone>+8</timezone>
            <roles>
                <role>Developer</role>
            </roles>
        </developer>
    </developers>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.36</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-http</artifactId>
            <version>5.8.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.17.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.56</version>
        </dependency>
        <dependency>
            <groupId>org.java-websocket</groupId>
            <artifactId>Java-WebSocket</artifactId>
            <version>1.5.4</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>4.6.1</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.15.4</version>
        </dependency>

    </dependencies>

    <profiles>
        <profile>
            <id>deploy</id>
            <build>
                <plugins>
                    <!-- Source plugin -->
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-source-plugin</artifactId>
                        <version>2.4</version>
                        <executions>
                            <execution>
                                <id>attach-sources</id>
                                <goals>
                                    <goal>jar-no-fork</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>

                    <!-- Javadoc plugin -->
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-javadoc-plugin</artifactId>
                        <version>2.10.4</version>
                        <configuration>
                            <additionalparam>-Xdoclint:none</additionalparam>
                        </configuration>
                        <executions>
                            <execution>
                                <id>attach-javadocs</id>
                                <goals>
                                    <goal>jar</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>

                    <!-- GPG plugin -->
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-gpg-plugin</artifactId>
                        <version>1.6</version>
                        <executions>
                            <execution>
                                <id>sign-artifacts</id>
                                <phase>verify</phase>
                                <goals>
                                    <goal>sign</goal>
                                </goals>
                                <configuration>
                                    <gpgArguments>
                                        <arg>--pinentry-mode</arg>
                                        <arg>loopback</arg>
                                    </gpgArguments>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>cobertura-maven-plugin</artifactId>
                <version>2.7</version>
                <configuration>
                    <formats>
                        <format>html</format>
                        <format>xml</format>
                    </formats>
                    <check/>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.sonatype.plugins</groupId>
                <artifactId>nexus-staging-maven-plugin</artifactId>
                <version>1.6.8</version>
                <extensions>true</extensions>
                <configuration>
                    <serverId>ossrh</serverId>
                    <nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
                    <autoReleaseAfterClose>true</autoReleaseAfterClose>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>


================================================
FILE: src/main/java/org/cloud/sonic/driver/android/AndroidDriver.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.android;

import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.android.enmus.AndroidSelector;
import org.cloud.sonic.driver.android.service.AndroidElement;
import org.cloud.sonic.driver.android.service.UiaClient;
import org.cloud.sonic.driver.android.service.impl.AndroidElementImpl;
import org.cloud.sonic.driver.android.service.impl.UiaClientImpl;
import org.cloud.sonic.driver.common.enums.PasteboardType;
import org.cloud.sonic.driver.common.models.WindowSize;
import org.cloud.sonic.driver.common.tool.RespHandler;
import org.cloud.sonic.driver.common.tool.SonicRespException;

import java.util.List;

/**
 * @author Eason
 * android driver
 */
public class AndroidDriver {
    private UiaClient uiaClient;

    /**
     * Init android driver.
     *
     * @param url
     * @throws SonicRespException
     */
    public AndroidDriver(String url) throws SonicRespException {
        this(url, RespHandler.DEFAULT_REQUEST_TIMEOUT);
    }

    /**
     * Init android driver.
     *
     * @param url
     * @param timeOut
     * @throws SonicRespException
     */
    public AndroidDriver(String url, int timeOut) throws SonicRespException {
        this(url, timeOut, new JSONObject());
    }

    /**
     * Init android driver.
     *
     * @param url
     * @param cap
     * @throws SonicRespException
     */
    public AndroidDriver(String url, JSONObject cap) throws SonicRespException {
        this(url, RespHandler.DEFAULT_REQUEST_TIMEOUT, cap);
    }

    /**
     * Init android driver.
     *
     * @param url
     * @param timeOut
     * @param cap
     * @throws SonicRespException
     */
    public AndroidDriver(String url, int timeOut, JSONObject cap) throws SonicRespException {
        uiaClient = new UiaClientImpl();
        uiaClient.setRemoteUrl(url);
        uiaClient.setGlobalTimeOut(timeOut);
        uiaClient.newSession(cap);
    }

    /**
     * Get uia2 client.
     *
     * @return
     */
    public UiaClient getUiaClient() {
        return uiaClient;
    }

    /**
     * get uia2 sessionId.
     *
     * @return
     */
    public String getSessionId() {
        return uiaClient.getSessionId();
    }

    /**
     * destroy sessionId.
     *
     * @throws SonicRespException
     */
    public void closeDriver() throws SonicRespException {
        uiaClient.closeSession();
    }

    /**
     * show log.
     */
    public void showLog() {
        uiaClient.showLog();
    }

    /**
     * disable log.
     */
    public void disableLog() {
        uiaClient.disableLog();
    }

    /**
     * get device window size.
     *
     * @return
     * @throws SonicRespException
     */
    public WindowSize getWindowSize() throws SonicRespException {
        return uiaClient.getWindowSize();
    }

    /**
     * send key without element.
     *
     * @param text
     * @throws SonicRespException
     */
    public void sendKeys(String text) throws SonicRespException {
        sendKeys(text, false);
    }

    /**
     * send key without element.
     *
     * @param text
     * @param isCover
     * @throws SonicRespException
     */
    public void sendKeys(String text, boolean isCover) throws SonicRespException {
        uiaClient.sendKeys(text, isCover);
    }

    /**
     * set pasteboard.
     *
     * @param contentType
     * @param content
     * @throws SonicRespException
     */
    public void setPasteboard(String contentType, String content) throws SonicRespException {
        uiaClient.setPasteboard(contentType, content);
    }

    /**
     * set pasteboard.
     *
     * @param pasteboardType
     * @param content
     * @throws SonicRespException
     */
    public void setPasteboard(PasteboardType pasteboardType, String content) throws SonicRespException {
        setPasteboard(pasteboardType.getType(), content);
    }

    /**
     * get pasteboard.
     *
     * @param contentType
     * @return
     * @throws SonicRespException
     */
    public byte[] getPasteboard(String contentType) throws SonicRespException {
        return uiaClient.getPasteboard(contentType);
    }

    /**
     * get pasteboard.
     *
     * @param pasteboardType
     * @return
     * @throws SonicRespException
     */
    public byte[] getPasteboard(PasteboardType pasteboardType) throws SonicRespException {
        return getPasteboard(pasteboardType.getType());
    }

    /**
     * get page source.
     *
     * @return
     * @throws SonicRespException
     */
    public String getPageSource() throws SonicRespException {
        return uiaClient.pageSource();
    }

    /**
     * set default FindElement retry time and interval.
     *
     * @param retry
     * @param interval
     */
    public void setDefaultFindElementInterval(Integer retry, Integer interval) {
        uiaClient.setDefaultFindElementInterval(retry, interval);
    }

    /**
     * find element in device.
     *
     * @param androidSelector
     * @param value
     * @return
     * @throws SonicRespException
     */
    public AndroidElement findElement(AndroidSelector androidSelector, String value) throws SonicRespException {
        return findElement(androidSelector, value, null);
    }

    /**
     * find element in device.
     *
     * @param uiaElementID This ID is the id returned by uia after finding the control
     * @return
     * @throws SonicRespException
     */
    public AndroidElement findElement(String uiaElementID) throws SonicRespException {
        return new AndroidElementImpl(uiaElementID, uiaClient);
    }

    /**
     * find element in device.
     *
     * @param selector
     * @param value
     * @return
     * @throws SonicRespException
     */
    public AndroidElement findElement(String selector, String value) throws SonicRespException {
        return findElement(selector, value, null);
    }

    /**
     * find element in device.
     *
     * @param androidSelector
     * @param value
     * @param retry
     * @return
     * @throws SonicRespException
     */
    public AndroidElement findElement(AndroidSelector androidSelector, String value, Integer retry) throws SonicRespException {
        return findElement(androidSelector, value, retry, null);
    }

    /**
     * find element in device.
     *
     * @param selector
     * @param value
     * @param retry
     * @return
     * @throws SonicRespException
     */
    public AndroidElement findElement(String selector, String value, Integer retry) throws SonicRespException {
        return findElement(selector, value, retry, null);
    }

    /**
     * find element in device.
     *
     * @param androidSelector
     * @param value
     * @param retry
     * @param interval
     * @return
     * @throws SonicRespException
     */
    public AndroidElement findElement(AndroidSelector androidSelector, String value, Integer retry, Integer interval) throws SonicRespException {
        return findElement(androidSelector.getSelector(), value, retry, interval);
    }

    /**
     * find element in device.
     *
     * @param selector
     * @param value
     * @param retry
     * @param interval
     * @return
     * @throws SonicRespException
     */
    public AndroidElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException {
        return uiaClient.findElement(selector, value, retry, interval);
    }

    /**
     * find element list in device.
     *
     * @param androidSelector
     * @param value
     * @return
     * @throws SonicRespException
     */
    public List<AndroidElement> findElementList(AndroidSelector androidSelector, String value) throws SonicRespException {
        return findElementList(androidSelector, value, null);
    }

    /**
     * find element list in device.
     *
     * @param selector
     * @param value
     * @return
     * @throws SonicRespException
     */
    public List<AndroidElement> findElementList(String selector, String value) throws SonicRespException {
        return findElementList(selector, value, null);
    }

    /**
     * find element list in device.
     *
     * @param androidSelector
     * @param value
     * @param retry
     * @return
     * @throws SonicRespException
     */
    public List<AndroidElement> findElementList(AndroidSelector androidSelector, String value, Integer retry) throws SonicRespException {
        return findElementList(androidSelector, value, retry, null);
    }

    /**
     * find element list in device.
     *
     * @param selector
     * @param value
     * @param retry
     * @return
     * @throws SonicRespException
     */
    public List<AndroidElement> findElementList(String selector, String value, Integer retry) throws SonicRespException {
        return findElementList(selector, value, retry, null);
    }

    /**
     * find element list in device.
     *
     * @param androidSelector
     * @param value
     * @param retry
     * @param interval
     * @return
     * @throws SonicRespException
     */
    public List<AndroidElement> findElementList(AndroidSelector androidSelector, String value, Integer retry, Integer interval) throws SonicRespException {
        return findElementList(androidSelector.getSelector(), value, retry, interval);
    }

    /**
     * find element list in device.
     *
     * @param selector
     * @param value
     * @param retry
     * @param interval
     * @return
     * @throws SonicRespException
     */
    public List<AndroidElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException {
        return uiaClient.findElementList(selector, value, retry, interval);
    }

    /**
     * get screenshot.
     *
     * @return
     * @throws SonicRespException
     */
    public byte[] screenshot() throws SonicRespException {
        return uiaClient.screenshot();
    }

    /**
     * set appium settings.
     *
     * @param settings
     * @throws SonicRespException
     */
    public void setAppiumSettings(JSONObject settings) throws SonicRespException {
        uiaClient.setAppiumSettings(settings);
    }

    /**
     * tap position on screen.
     *
     * @param x
     * @param y
     * @throws SonicRespException
     */
    public void tap(int x, int y) throws SonicRespException {
        uiaClient.tap(x, y);
    }

    /**
     * long press position on screen.
     *
     * @param x
     * @param y
     * @param ms
     * @throws SonicRespException
     */
    public void longPress(double x, double y, double ms) throws SonicRespException {
        uiaClient.longPress(x, y, ms);
    }

    /**
     * swipe position on screen.
     *
     * @param fromX
     * @param fromY
     * @param toX
     * @param toY
     * @throws SonicRespException
     */
    public void swipe(int fromX, int fromY, int toX, int toY) throws SonicRespException {
        this.swipe(fromX, fromY, toX, toY, null);
    }

    /**
     * swipe position on screen with target time
     *
     * @param fromX
     * @param fromY
     * @param toX
     * @param toY
     * @param duration
     * @throws SonicRespException
     */
    public void swipe(int fromX, int fromY, int toX, int toY, Integer duration) throws SonicRespException {
        uiaClient.swipe(fromX, fromY, toX, toY, duration);
    }

    /**
     * Performs a long press followed by an immediate drag to a location and releases.
     * fromX(Y) are required if elementId is not provided, so do toX(Y) if destElId is not provided.
     *
     * @param fromX     Starting X coordinate
     * @param fromY     Starting Y coordinate
     * @param toX       Ending X coordinate
     * @param toY       Ending Y coordinate
     * @param duration  Duration of the action in milliseconds
     * @param elementId ID of the original element (optional), for specific interaction scenarios
     * @param destElId  ID of the target element (optional), for specific interaction scenarios
     * @throws SonicRespException Throws when the operation fails
     */
    public void drag(int fromX, int fromY, int toX, int toY, Integer duration, String elementId, String destElId) throws SonicRespException {
        uiaClient.drag(fromX, fromY, toX, toY, duration, elementId, destElId);
    }

    /**
     * Performs a touch action.
     * This method delegates to the UIA client's touchAction method to simulate
     * a touch event at a specified position on the screen with a given action type.
     *
     * @param methodType The type of touch action, enumerate in (down, up, move).
     *                   Specific supported types depend on the UIA client's implementation.
     * @param x          The X coordinate of the touch point.
     * @param y          The Y coordinate of the touch point.
     * @throws SonicRespException If an error occurs while performing the touch action,
     *                            this exception is thrown.
     */
    public void touchAction(String methodType, int x, int y) throws SonicRespException {
        uiaClient.touchAction(methodType, x, y);
    }

}


================================================
FILE: src/main/java/org/cloud/sonic/driver/android/enmus/AndroidSelector.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.android.enmus;

public enum AndroidSelector {
    CLASS_NAME("class name"),
    Id("id"),
    ACCESSIBILITY_ID("accessibility id"),

    XPATH("xpath"),

    UIAUTOMATOR("-android uiautomator");

    private final String selector;

    AndroidSelector(String selector) {
        this.selector = selector;
    }

    public String getSelector() {
        return selector;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/android/service/AndroidElement.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.android.service;

import org.cloud.sonic.driver.common.models.BaseElement;
import org.cloud.sonic.driver.common.tool.SonicRespException;

/**
 * @author Eason
 * web element interface
 */
public interface AndroidElement extends BaseElement {

    void click() throws SonicRespException;

    void sendKeys(String text) throws SonicRespException;

    void sendKeys(String text, boolean isCover) throws SonicRespException;

    void clear() throws SonicRespException;

    String getText() throws SonicRespException;

    byte[] screenshot() throws SonicRespException;
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/android/service/UiaClient.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.android.service;

import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.common.models.WindowSize;
import org.cloud.sonic.driver.common.tool.Logger;
import org.cloud.sonic.driver.common.tool.RespHandler;
import org.cloud.sonic.driver.common.tool.SonicRespException;

import java.util.List;

/**
 * @author Eason
 * uia client interface
 */
public interface UiaClient {
    //Client Setting
    void setGlobalTimeOut(int timeOut);

    RespHandler getRespHandler();

    void setRespHandler(RespHandler respHandler);

    Logger getLogger();

    void showLog();

    void disableLog();

    //Session handler.
    String getRemoteUrl();

    void setRemoteUrl(String remoteUrl);

    String getSessionId();

    void setSessionId(String sessionId);

    void newSession(JSONObject capabilities) throws SonicRespException;

    void closeSession() throws SonicRespException;

    void checkSessionId() throws SonicRespException;

    //window handler.
    WindowSize getWindowSize() throws SonicRespException;

    //keyboard handler.
    void sendKeys(String text, boolean isCover) throws SonicRespException;

    void setPasteboard(String contentType, String content) throws SonicRespException;

    byte[] getPasteboard(String contentType) throws SonicRespException;

    //source handler.
    String pageSource() throws SonicRespException;

    //element handler.
    void setDefaultFindElementInterval(Integer retry, Integer interval);

    AndroidElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException;

    List<AndroidElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException;

    //screen handler.
    byte[] screenshot() throws SonicRespException;

    //appium setting handler.
    void setAppiumSettings(JSONObject settings) throws SonicRespException;

    void tap(int x, int y) throws SonicRespException;

    void longPress(double x, double y, double ms) throws SonicRespException;

    void swipe(int fromX, int fromY, int toX, int toY, Integer duration) throws SonicRespException;

    void drag(int fromX, int fromY, int toX, int toY, Integer duration, String elementId, String destElId) throws SonicRespException;

    // touch handler.
    void touchAction(String methodType, int x, int y) throws SonicRespException;

}


================================================
FILE: src/main/java/org/cloud/sonic/driver/android/service/impl/AndroidElementImpl.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.android.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import org.cloud.sonic.driver.android.service.AndroidElement;
import org.cloud.sonic.driver.android.service.UiaClient;
import org.cloud.sonic.driver.common.models.BaseResp;
import org.cloud.sonic.driver.common.models.ElementRect;
import org.cloud.sonic.driver.common.tool.Logger;
import org.cloud.sonic.driver.common.tool.SonicRespException;

import java.util.Base64;

public class AndroidElementImpl implements AndroidElement {
    private String id;
    private UiaClient uiaClient;
    private Logger logger;

    public AndroidElementImpl(String id, UiaClient uiaClient) {
        this.id = id;
        this.uiaClient = uiaClient;
        logger = uiaClient.getLogger();
    }

    @Override
    public void click() throws SonicRespException {
        uiaClient.checkSessionId();
        BaseResp b = uiaClient.getRespHandler().getResp(
                HttpUtil.createPost(uiaClient.getRemoteUrl() + "/session/"
                        + uiaClient.getSessionId() + "/element/" + id + "/click"));
        if (b.getErr() == null) {
            logger.info("click element %s.", id);
        } else {
            logger.error("click element %s failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void sendKeys(String text) throws SonicRespException {
        sendKeys(text, false);
    }

    @Override
    public void sendKeys(String text, boolean isCover) throws SonicRespException {
        uiaClient.checkSessionId();
        JSONObject data = new JSONObject();
        data.put("text", text);
        data.put("replace", isCover);
        BaseResp b = uiaClient.getRespHandler().getResp(
                HttpUtil.createPost(uiaClient.getRemoteUrl() + "/session/"
                                + uiaClient.getSessionId() + "/element/" + id + "/value")
                        .body(data.toJSONString()), 60000);
        if (b.getErr() == null) {
            logger.info("send key to %s.", id);
        } else {
            logger.error("send key to %s failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void clear() throws SonicRespException {
        uiaClient.checkSessionId();
        BaseResp b = uiaClient.getRespHandler().getResp(
                HttpUtil.createPost(uiaClient.getRemoteUrl() + "/session/"
                        + uiaClient.getSessionId() + "/element/" + id + "/clear"), 60000);
        if (b.getErr() == null) {
            logger.info("clear %s.", id);
        } else {
            logger.error("clear %s failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public String getText() throws SonicRespException {
        uiaClient.checkSessionId();
        BaseResp b = uiaClient.getRespHandler().getResp(
                HttpUtil.createGet(uiaClient.getRemoteUrl() + "/session/"
                        + uiaClient.getSessionId() + "/element/" + id + "/text"));
        if (b.getErr() == null) {
            logger.info("get %s text %s.", id, b.getValue().toString());
            return b.getValue().toString();
        } else {
            logger.error("get %s text failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public String getAttribute(String name) throws SonicRespException {
        uiaClient.checkSessionId();
        BaseResp b = uiaClient.getRespHandler().getResp(
                HttpUtil.createGet(uiaClient.getRemoteUrl() + "/session/"
                        + uiaClient.getSessionId() + "/element/" + id + "/attribute/" + name));
        if (b.getErr() == null) {
            logger.info("get %s attribute %s result %s.", id, name, b.getValue().toString());
            return b.getValue().toString();
        } else {
            logger.error("get %s attribute failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public String getUniquelyIdentifies() throws SonicRespException {
        return id;
    }

    @Override
    public ElementRect getRect() throws SonicRespException {
        uiaClient.checkSessionId();
        BaseResp b = uiaClient.getRespHandler().getResp(
                HttpUtil.createGet(uiaClient.getRemoteUrl() + "/session/"
                        + uiaClient.getSessionId() + "/element/" + id + "/rect"));
        if (b.getErr() == null) {
            ElementRect elementRect = JSON.parseObject(b.getValue().toString(), ElementRect.class);
            logger.info("get %s rect %s.", id, elementRect.toString());
            return elementRect;
        } else {
            logger.error("get %s rect failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public byte[] screenshot() throws SonicRespException {
        uiaClient.checkSessionId();
        BaseResp b = uiaClient.getRespHandler().getResp(
                HttpUtil.createGet(uiaClient.getRemoteUrl() + "/session/"
                        + uiaClient.getSessionId() + "/element/" + id + "/screenshot"), 60000);
        if (b.getErr() == null) {
            logger.info("get element %s screenshot.", id);
            return Base64.getMimeDecoder().decode(b.getValue().toString());
        } else {
            logger.error("get element %s screenshot failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public boolean isDisplayed() throws SonicRespException {
        String result = getAttribute("displayed");
        return Boolean.parseBoolean(result);
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/android/service/impl/UiaClientImpl.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.android.service.impl;

import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.android.service.AndroidElement;
import org.cloud.sonic.driver.android.service.UiaClient;
import org.cloud.sonic.driver.common.models.BaseResp;
import org.cloud.sonic.driver.common.models.SessionInfo;
import org.cloud.sonic.driver.common.models.WindowSize;
import org.cloud.sonic.driver.common.tool.Logger;
import org.cloud.sonic.driver.common.tool.RespHandler;
import org.cloud.sonic.driver.common.tool.SonicRespException;

import java.nio.charset.StandardCharsets;
import java.util.*;

public class UiaClientImpl implements UiaClient {
    private String remoteUrl;
    private String sessionId;
    private RespHandler respHandler;
    private Logger logger;
    private final String LEGACY_WEB_ELEMENT_IDENTIFIER = "ELEMENT";
    private final String WEB_ELEMENT_IDENTIFIER = "element-6066-11e4-a52e-4f735466cecf";
    private int FIND_ELEMENT_INTERVAL = 3000;
    private int FIND_ELEMENT_RETRY = 5;
    private WindowSize size;

    public UiaClientImpl() {
        respHandler = new RespHandler();
        logger = new Logger();
    }

    private void checkBundleId(String bundleId) throws SonicRespException {
        if (bundleId == null || bundleId.length() == 0) {
            logger.error("bundleId not found.");
            throw new SonicRespException("bundleId not found.");
        }
    }

    private String parseElementId(Object o) {
        JSONObject jsonObject = (JSONObject) o;
        List<String> identifier = Arrays.asList(LEGACY_WEB_ELEMENT_IDENTIFIER, WEB_ELEMENT_IDENTIFIER);
        for (String i : identifier) {
            String result = jsonObject.getString(i);
            if (result != null && result.length() > 0) {
                return result;
            }
        }
        return "";
    }

    @Override
    public void setGlobalTimeOut(int timeOut) {
        respHandler.setRequestTimeOut(timeOut);
    }

    @Override
    public RespHandler getRespHandler() {
        return respHandler;
    }

    @Override
    public void setRespHandler(RespHandler respHandler) {
        this.respHandler = respHandler;
    }

    @Override
    public Logger getLogger() {
        return logger;
    }

    @Override
    public void showLog() {
        logger.showLog();
    }

    @Override
    public void disableLog() {
        logger.disableLog();
    }

    @Override
    public String getRemoteUrl() {
        return remoteUrl;
    }

    @Override
    public void setRemoteUrl(String remoteUrl) {
        this.remoteUrl = remoteUrl;
    }

    @Override
    public String getSessionId() {
        return sessionId;
    }

    @Override
    public void setSessionId(String sessionId) {
        this.sessionId = sessionId;
    }

    @Override
    public void newSession(JSONObject capabilities) throws SonicRespException {
        JSONObject data = new JSONObject();
        data.put("capabilities", capabilities);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session").body(data.toJSONString()));
        if (b.getErr() == null) {
            SessionInfo sessionInfo = JSON.parseObject(b.getValue().toString(), SessionInfo.class);
            setSessionId(sessionInfo.getSessionId());
            logger.info("start session successful!");
            logger.info("session : %s", sessionInfo.getSessionId());
        } else {
            logger.error("start session failed.");
            logger.error("cause: %s", b.getErr().toString());
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void closeSession() throws SonicRespException {
        checkSessionId();
        respHandler.getResp(HttpUtil.createRequest(Method.DELETE, remoteUrl + "/session/" + sessionId));
        logger.info("close session successful!");
    }

    @Override
    public void checkSessionId() throws SonicRespException {
        if (sessionId == null || sessionId.length() == 0) {
            logger.error("sessionId not found.");
            throw new SonicRespException("sessionId not found.");
        }
    }

    @Override
    public WindowSize getWindowSize() throws SonicRespException {
        if (size == null) {
            checkSessionId();
            BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + "/session/" + sessionId + "/window/:windowHandle/size"));
            if (b.getErr() == null) {
                size = JSON.parseObject(b.getValue().toString(), WindowSize.class);
                logger.info("get window size %s.", size.toString());
            } else {
                logger.error("get window size failed.");
                throw new SonicRespException(b.getErr().getMessage());
            }
        }
        return size;
    }

    @Override
    public void sendKeys(String text, boolean isCover) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("text", text);
        data.put("replace", isCover);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/keys")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("send key %s.", text);
        } else {
            logger.error("send key failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void setPasteboard(String contentType, String content) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("contentType", contentType.toUpperCase(Locale.ROOT));
        data.put("content", Base64.getEncoder().encodeToString(content.getBytes(StandardCharsets.UTF_8)));
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/appium/device/set_clipboard")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("set pasteboard %s.", content);
        } else {
            logger.error("set pasteboard failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public byte[] getPasteboard(String contentType) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("contentType", contentType.toUpperCase(Locale.ROOT));
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/appium/device/get_clipboard")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            byte[] result = Base64.getMimeDecoder().decode(b.getValue().toString());
            logger.info("get pasteboard length: %d.", result.length);
            return result;
        } else {
            logger.error("get pasteboard failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public String pageSource() throws SonicRespException {
        checkSessionId();
        BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + "/session/" + sessionId + "/source"), 60000);
        if (b.getErr() == null) {
            logger.info("get page source.");
            return b.getValue().toString();
        } else {
            logger.error("get page source failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void setDefaultFindElementInterval(Integer retry, Integer interval) {
        if (retry != null) {
            FIND_ELEMENT_RETRY = retry;
        }
        if (interval != null) {
            FIND_ELEMENT_INTERVAL = interval;
        }
    }

    @Override
    public AndroidElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException {
        AndroidElement androidElement = null;
        int wait = 0;
        int intervalInit = (interval == null ? FIND_ELEMENT_INTERVAL : interval);
        int retryInit = (retry == null ? FIND_ELEMENT_RETRY : retry);
        String errMsg = "";
        while (wait < retryInit) {
            wait++;
            checkSessionId();
            JSONObject data = new JSONObject();
            data.put("strategy", selector);
            data.put("selector", value);
            BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/element")
                    .body(data.toJSONString()));
            if (b.getErr() == null) {
                logger.info("find element successful.");
                String id = parseElementId(b.getValue());
                if (id.length() > 0) {
                    androidElement = new AndroidElementImpl(id, this);
                    break;
                } else {
                    logger.error("parse element id %s failed. retried %d times, retry in %d ms.", b.getValue().toString(), wait, intervalInit);
                }
            } else {
                logger.error("element not found. retried %d times, retry in %d ms.", wait, intervalInit);
                errMsg = b.getErr().getMessage();
            }
            if (wait < retryInit) {
                try {
                    Thread.sleep(intervalInit);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        if (androidElement == null) {
            throw new SonicRespException(errMsg);
        }
        return androidElement;
    }

    @Override
    public List<AndroidElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException {
        List<AndroidElement> androidElementList = new ArrayList<>();
        int wait = 0;
        int intervalInit = (interval == null ? FIND_ELEMENT_INTERVAL : interval);
        int retryInit = (retry == null ? FIND_ELEMENT_RETRY : retry);
        String errMsg = "";
        while (wait < retryInit) {
            wait++;
            checkSessionId();
            JSONObject data = new JSONObject();
            data.put("strategy", selector);
            data.put("selector", value);
            BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/elements")
                    .body(data.toJSONString()));
            if (b.getErr() == null) {
                logger.info("find elements successful.");
                List<JSONObject> ids = JSON.parseObject(b.getValue().toString(), ArrayList.class);
                for (JSONObject ele : ids) {
                    String id = parseElementId(ele);
                    if (id.length() > 0) {
                        androidElementList.add(new AndroidElementImpl(id, this));
                    } else {
                        logger.error("parse element id %s failed.", ele);
                    }
                }
                if (androidElementList.size() > 0) {
                    break;
                }
            } else {
                logger.error("elements not found. retried %d times, retry in %d ms.", wait, intervalInit);
                errMsg = b.getErr().getMessage();
            }
            if (wait < retryInit) {
                try {
                    Thread.sleep(intervalInit);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        if (androidElementList.size() == 0) {
            throw new SonicRespException(errMsg);
        }
        return androidElementList;
    }

    @Override
    public byte[] screenshot() throws SonicRespException {
        checkSessionId();
        BaseResp b = respHandler.getResp(
                HttpUtil.createGet(remoteUrl + "/session/" + sessionId + "/screenshot"), 60000);
        if (b.getErr() == null) {
            logger.info("get screenshot.");
            return Base64.getMimeDecoder().decode(b.getValue().toString());
        } else {
            logger.error("get screenshot failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void setAppiumSettings(JSONObject settings) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("settings", settings);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/appium/settings")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("set appium settings %s.", settings.toJSONString());
        } else {
            logger.error("set appium settings failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void tap(int x, int y) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("x", x);
        data.put("y", y);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/appium/tap")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("perform tap action %s.", data.toString());
        } else {
            logger.error("perform tap action failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void longPress(double x, double y, double ms) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        JSONObject touchEventParams = new JSONObject();
        touchEventParams.put("x", x);
        touchEventParams.put("y", y);
        touchEventParams.put("duration", ms);
        data.put("params", touchEventParams);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/touch/longclick")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("perform longPress action %s.", data.toString());
        } else {
            logger.error("perform longPress action failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void swipe(int fromX, int fromY, int toX, int toY, Integer duration) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("startX", fromX);
        data.put("startY", fromY);
        data.put("endX", toX);
        data.put("endY", toY);
        // steps 参数为uiautomator层定义的单位
        // example:So for a 100 steps, the swipe will take about 1/2 second to complete.
        if (duration == null) {
            data.put("steps", 100);
        } else {
            data.put("steps", duration / 5);
        }
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/touch/perform")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("perform swipe action %s.", data.toString());
        } else {
            logger.error("perform swipe action failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void drag(int fromX, int fromY, int toX, int toY, Integer duration, String elementId, String destElId) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("startX", fromX);
        data.put("startY", fromY);
        data.put("endX", toX);
        data.put("endY", toY);
        data.put("steps", duration != null && duration > 0 ? duration / 5 : 100);
        data.put("elementId", elementId);
        data.put("destElId", destElId);
        BaseResp move = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/touch/drag").body(data.toJSONString()));
        if (move.getErr() == null) {
            logger.info("perform drag action %s.", data.toString());
        } else {
            logger.error("perform drag action failed.");
            throw new SonicRespException(move.getErr().getMessage());
        }
    }

    @Override
    public void touchAction(String methodType, int x, int y) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        JSONObject touchEventParams = new JSONObject();
        touchEventParams.put("x", x);
        touchEventParams.put("y", y);
        data.put("params", touchEventParams);
        String path = "/touch/down";
        switch (methodType) {
            case "down":
                break;
            case "up":
                path = "/touch/up";
                break;
            case "move":
                path = "/touch/move";
                break;
            default:
                throw new RuntimeException("methodType error.");
        }

        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + path)
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info(String.format("perform touch %s action %s.", methodType, data));
        } else {
            logger.error("perform touch %s action failed.", methodType);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }


}


================================================
FILE: src/main/java/org/cloud/sonic/driver/common/enums/PasteboardType.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.common.enums;

public enum PasteboardType {
    PLAIN_TEXT("plaintext"),
    IMAGE("image"),
    URL("url");

    private final String type;

    PasteboardType(String pasteboardType) {
        this.type = pasteboardType;
    }

    public String getType() {
        return type;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/common/models/BaseElement.java
================================================
package org.cloud.sonic.driver.common.models;

import org.cloud.sonic.driver.common.tool.SonicRespException;

public interface BaseElement {
    ElementRect getRect() throws SonicRespException;

    String getAttribute(String name) throws SonicRespException;

    // the xpath or id or csspath...
    String getUniquelyIdentifies() throws SonicRespException;

//    List<BaseElement> getChildren() throws SonicRespException;
    /**
     * Is this element displayed or not?
     * This method avoids the problem of having to parse an element's "style" attribute.
     *
     * @return whether the element is displayed
     */
    boolean isDisplayed() throws SonicRespException;
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/common/models/BaseResp.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.common.models;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class BaseResp<T> {
    private String sessionId;
    private ErrorMsg err;
    private T value;

    public void setErr(ErrorMsg err) {
        this.err = err;
    }

    public void setValue(T value) {
        this.value = value;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/common/models/Capabilities.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.common.models;

import lombok.AllArgsConstructor;
import lombok.ToString;

@ToString
@AllArgsConstructor
public class Capabilities {
    private String device;
    private String browserName;
    private String sdkVersion;
    private String CFBundleIdentifier;
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/common/models/ElementRect.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.common.models;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
@AllArgsConstructor
public class ElementRect {
    private int x;
    private int y;
    private int width;
    private int height;

    @Getter
    @ToString
    @AllArgsConstructor
    public class IOSRectCenter {
        private int x;
        private int y;
    }

    public IOSRectCenter getCenter() {
        return new IOSRectCenter(x / 2, y / 2);
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/common/models/ErrorMsg.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.common.models;

import lombok.AllArgsConstructor;
import lombok.ToString;

@ToString
@AllArgsConstructor
public class ErrorMsg {
    private String error;
    private String message;
    private String traceback;

    public String getMessage() {
        return message;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/common/models/SessionInfo.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.common.models;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
@AllArgsConstructor
public class SessionInfo {
    private String sessionId;
    private Capabilities capabilities;
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/common/models/WindowSize.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.common.models;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
@AllArgsConstructor
public class WindowSize {
    private int width;
    private int height;
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/common/tool/Logger.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.common.tool;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Logger {
    private SimpleDateFormat formatter;
    private boolean isShowLog;

    public Logger() {
        formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        isShowLog = true;
    }

    public void showLog() {
        isShowLog = true;
    }

    public void disableLog() {
        isShowLog = false;
    }

    private void print(String level, String msg, Object... args) {
        if (isShowLog) {
            System.out.println(String.format("[sonic-driver-core] %s [%s] %s",
                    formatter.format(new Date()), level, String.format(msg, args)));
        }
    }

    public void info(String msg, Object... args) {
        print("INFO", msg, args);
    }

    public void error(String msg, Object... args) {
        print("ERROR", msg, args);
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/common/tool/RespHandler.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.common.tool;

import cn.hutool.core.io.IORuntimeException;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.common.models.BaseResp;
import org.cloud.sonic.driver.common.models.ErrorMsg;

import java.util.HashMap;
import java.util.Map;

public class RespHandler {
    public static final int DEFAULT_REQUEST_TIMEOUT = 15000;
    private int requestTimeout = 15000;

    public void setRequestTimeOut(int timeOut) {
        requestTimeout = timeOut;
    }

    public BaseResp getResp(HttpRequest httpRequest) throws SonicRespException {
        return getResp(httpRequest, requestTimeout);
    }

    public BaseResp getResp(HttpRequest httpRequest, int timeout) throws SonicRespException {
        synchronized (this) {
            try {
                return initResp(httpRequest.addHeaders(initHeader()).timeout(timeout).execute().body());
            } catch (HttpException | IORuntimeException e) {
                e.printStackTrace();
                throw new SonicRespException(e.getMessage());
            }
        }
    }

    public BaseResp initResp(String response) {
        if (response.contains("traceback") || response.contains("stacktrace")) {
            return initErrorMsg(response.replace("stacktrace", "traceback"));
        } else {
            return JSON.parseObject(response, BaseResp.class);
        }
    }

    public Map<String, String> initHeader() {
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json; charset=utf-8");
        return headers;
    }

    public BaseResp initErrorMsg(String resp) {
        BaseResp err = JSON.parseObject(resp, BaseResp.class);
        ErrorMsg errorMsg = JSONObject.parseObject(err.getValue().toString(), ErrorMsg.class);
        err.setErr(errorMsg);
        err.setValue(null);
        return err;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/common/tool/SonicRespException.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.common.tool;

public class SonicRespException extends Exception {
    public SonicRespException(String message) {
        super(message);
    }

    public SonicRespException(String message, Throwable cause) {
        super(message, cause);
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/IOSDriver.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios;

import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.common.enums.PasteboardType;
import org.cloud.sonic.driver.common.models.WindowSize;
import org.cloud.sonic.driver.common.tool.RespHandler;
import org.cloud.sonic.driver.common.tool.SonicRespException;
import org.cloud.sonic.driver.ios.enums.*;
import org.cloud.sonic.driver.ios.models.TouchActions;
import org.cloud.sonic.driver.ios.service.IOSElement;
import org.cloud.sonic.driver.ios.service.WdaClient;
import org.cloud.sonic.driver.ios.service.impl.IOSElementImpl;
import org.cloud.sonic.driver.ios.service.impl.WdaClientImpl;

import java.util.List;

/**
 * @author Eason
 * ios driver
 */
public class IOSDriver {
    private WdaClient wdaClient;

    /**
     * Init ios driver.
     *
     * @param url
     * @throws SonicRespException
     */
    public IOSDriver(String url) throws SonicRespException {
        this(url, RespHandler.DEFAULT_REQUEST_TIMEOUT);
    }

    /**
     * Init ios driver.
     *
     * @param url
     * @param timeOut
     * @throws SonicRespException
     */
    public IOSDriver(String url, int timeOut) throws SonicRespException {
        this(url, timeOut, new JSONObject());
    }

    /**
     * Init ios driver.
     *
     * @param url
     * @param cap
     * @throws SonicRespException
     */
    public IOSDriver(String url, JSONObject cap) throws SonicRespException {
        this(url, RespHandler.DEFAULT_REQUEST_TIMEOUT, cap);
    }

    /**
     * Init ios driver.
     *
     * @param url
     * @param timeOut
     * @param cap
     * @throws SonicRespException
     */
    public IOSDriver(String url, int timeOut, JSONObject cap) throws SonicRespException {
        wdaClient = new WdaClientImpl();
        wdaClient.setRemoteUrl(url);
        wdaClient.setGlobalTimeOut(timeOut);
        wdaClient.newSession(cap);
    }

    /**
     * Get wda client.
     *
     * @return
     */
    public WdaClient getWdaClient() {
        return wdaClient;
    }

    /**
     * get wda sessionId.
     *
     * @return
     */
    public String getSessionId() {
        return wdaClient.getSessionId();
    }

    /**
     * destroy sessionId.
     *
     * @throws SonicRespException
     */
    public void closeDriver() throws SonicRespException {
        wdaClient.closeSession();
    }

    /**
     * show log.
     */
    public void showLog() {
        wdaClient.showLog();
    }

    /**
     * disable log.
     */
    public void disableLog() {
        wdaClient.disableLog();
    }

    /**
     * get device window size.
     *
     * @return
     * @throws SonicRespException
     */
    public WindowSize getWindowSize() throws SonicRespException {
        return wdaClient.getWindowSize();
    }

    /**
     * get device lock status.
     *
     * @return
     * @throws SonicRespException
     */
    public boolean isLocked() throws SonicRespException {
        return wdaClient.isLocked();
    }

    /**
     * lock device.
     *
     * @throws SonicRespException
     */
    public void lock() throws SonicRespException {
        wdaClient.lock();
    }

    /**
     * unlock device.
     *
     * @throws SonicRespException
     */
    public void unlock() throws SonicRespException {
        wdaClient.unlock();
    }

    /**
     * tap position on screen.
     *
     * @param x
     * @param y
     * @throws SonicRespException
     */
    public void tap(int x, int y) throws SonicRespException {
        performTouchAction(new TouchActions.FingerTouchAction().press(x, y).release());
    }

    /**
     * double tap position on screen
     * @param x
     * @param y
     * @throws SonicRespException
     */
    public void doubleTap(int x, int y) throws SonicRespException {
        wdaClient.doubleTap(x,y);
    }

    /**
     * long press position on screen.
     *
     * @param x
     * @param y
     * @param ms
     * @throws SonicRespException
     */
    public void longPress(int x, int y, int ms) throws SonicRespException {
        performTouchAction(new TouchActions.FingerTouchAction().press(x, y).wait(ms).release());
    }

    /**
     * swipe position on screen.
     *
     * @param fromX
     * @param fromY
     * @param toX
     * @param toY
     * @throws SonicRespException
     */
    public void swipe(int fromX, int fromY, int toX, int toY) throws SonicRespException {
        performTouchAction(new TouchActions.FingerTouchAction().press(fromX, fromY).wait(300).move(toX, toY).wait(10).release());
    }

    /**
     * swipe position on screen with target time
     *
     * @param fromX
     * @param fromY
     * @param toX
     * @param toY
     * @param duration 滑动完成的时间,单位为毫秒
     * @throws SonicRespException
     */
    public void swipe(double fromX, double fromY, double toX, double toY, double duration) throws SonicRespException {
        wdaClient.swipe(fromX, fromY, toX, toY, duration);
    }

    /**
     * perform touch action.
     *
     * @param touchActions
     * @throws SonicRespException
     */
    public void performTouchAction(TouchActions touchActions) throws SonicRespException {
        wdaClient.performTouchAction(touchActions);
    }

    public void performTouchAction(TouchActions.FingerTouchAction fingerTouchActions) throws SonicRespException {
        performTouchAction(new TouchActions(fingerTouchActions));
    }

    /**
     * press system button.
     *
     * @param systemButton
     * @throws SonicRespException
     */
    public void pressButton(String systemButton) throws SonicRespException {
        wdaClient.pressButton(systemButton);
    }

    /**
     * press system button.
     *
     * @param systemButton
     * @throws SonicRespException
     */
    public void pressButton(SystemButton systemButton) throws SonicRespException {
        pressButton(systemButton.getButton());
    }

    /**
     * send key without element.
     *
     * @param text
     * @throws SonicRespException
     */
    public void sendKeys(String text) throws SonicRespException {
        sendKeys(text, 3);
    }

    /**
     * send key without element.
     *
     * @param text
     * @param frequency
     * @throws SonicRespException
     */
    public void sendKeys(String text, int frequency) throws SonicRespException {
        wdaClient.sendKeys(text, frequency);
    }

    /**
     * send key without element.
     *
     * @param text
     * @throws SonicRespException
     */
    public void sendKeys(TextKey text) throws SonicRespException {
        sendKeys(text, 3);
    }

    /**
     * send key without element.
     *
     * @param text
     * @param frequency
     * @throws SonicRespException
     */
    public void sendKeys(TextKey text, int frequency) throws SonicRespException {
        sendKeys(text.getKey(), frequency);
    }

    /**
     * set pasteboard.
     *
     * @param contentType
     * @param content
     * @throws SonicRespException
     */
    public void setPasteboard(String contentType, String content) throws SonicRespException {
        wdaClient.setPasteboard(contentType, content);
    }

    /**
     * set pasteboard.
     *
     * @param pasteboardType
     * @param content
     * @throws SonicRespException
     */
    public void setPasteboard(PasteboardType pasteboardType, String content) throws SonicRespException {
        setPasteboard(pasteboardType.getType(), content);
    }

    /**
     * get pasteboard.
     *
     * @param contentType
     * @return
     * @throws SonicRespException
     */
    public byte[] getPasteboard(String contentType) throws SonicRespException {
        return wdaClient.getPasteboard(contentType);
    }

    /**
     * get pasteboard.
     *
     * @param pasteboardType
     * @return
     * @throws SonicRespException
     */
    public byte[] getPasteboard(PasteboardType pasteboardType) throws SonicRespException {
        return getPasteboard(pasteboardType.getType());
    }

    /**
     * get page source.
     *
     * @return
     * @throws SonicRespException
     */
    public String getPageSource() throws SonicRespException {
        return wdaClient.pageSource();
    }

    /**
     * send siri command.
     *
     * @param command
     * @throws SonicRespException
     */
    public void sendSiriCommand(String command) throws SonicRespException {
        wdaClient.sendSiriCommand(command);
    }

    /**
     * activate app.
     *
     * @param bundleId
     * @throws SonicRespException
     */
    public void appActivate(String bundleId) throws SonicRespException {
        wdaClient.appActivate(bundleId);
    }

    /**
     * terminate app and get status.
     *
     * @param bundleId
     * @return
     * @throws SonicRespException
     */
    public boolean appTerminate(String bundleId) throws SonicRespException {
        return wdaClient.appTerminate(bundleId);
    }

    /**
     * run app background in seconds.
     *
     * @param duration
     * @throws SonicRespException
     */
    public void appRunBackground(int duration) throws SonicRespException {
        wdaClient.appRunBackground(duration);
    }

    /**
     * reset app auth source.
     *
     * @param resource
     * @throws SonicRespException
     */
    public void appAuthReset(int resource) throws SonicRespException {
        wdaClient.appAuthReset(resource);
    }

    /**
     * reset app auth source.
     *
     * @param authResource
     * @throws SonicRespException
     */
    public void appAuthReset(AuthResource authResource) throws SonicRespException {
        appAuthReset(authResource.getResource());
    }

    /**
     * set default FindElement retry time and interval.
     *
     * @param retry
     * @param interval
     */
    public void setDefaultFindElementInterval(Integer retry, Integer interval) {
        wdaClient.setDefaultFindElementInterval(retry, interval);
    }

    /**
     * find element in device.
     *
     * @param iosSelector
     * @param value
     * @return
     * @throws SonicRespException
     */
    public IOSElement findElement(IOSSelector iosSelector, String value) throws SonicRespException {
        return findElement(iosSelector, value, null);
    }

    /**
     * find element in device.
     *
     * @param wdaElementID This id is the id returned by the wda lookup control
     * @return
     * @throws SonicRespException
     */
    public IOSElement findElement(String wdaElementID) throws SonicRespException {
        return new IOSElementImpl(wdaElementID, wdaClient);
    }

    /**
     * find element in device.
     *
     * @param xcuiElementType
     * @return
     * @throws SonicRespException
     */
    public IOSElement findElement(XCUIElementType xcuiElementType) throws SonicRespException {
        return findElement(xcuiElementType, null);
    }

    /**
     * find element in device.
     *
     * @param selector
     * @param value
     * @return
     * @throws SonicRespException
     */
    public IOSElement findElement(String selector, String value) throws SonicRespException {
        return findElement(selector, value, null);
    }

    /**
     * find element in device.
     *
     * @param iosSelector
     * @param value
     * @param retry
     * @return
     * @throws SonicRespException
     */
    public IOSElement findElement(IOSSelector iosSelector, String value, Integer retry) throws SonicRespException {
        return findElement(iosSelector, value, retry, null);
    }

    /**
     * find element in device.
     *
     * @param xcuiElementType
     * @param retry
     * @return
     * @throws SonicRespException
     */
    public IOSElement findElement(XCUIElementType xcuiElementType, Integer retry) throws SonicRespException {
        return findElement(xcuiElementType, retry, null);
    }

    /**
     * find element in device.
     *
     * @param selector
     * @param value
     * @param retry
     * @return
     * @throws SonicRespException
     */
    public IOSElement findElement(String selector, String value, Integer retry) throws SonicRespException {
        return findElement(selector, value, retry, null);
    }

    /**
     * find element in device.
     *
     * @param iosSelector
     * @param value
     * @param retry
     * @param interval
     * @return
     * @throws SonicRespException
     */
    public IOSElement findElement(IOSSelector iosSelector, String value, Integer retry, Integer interval) throws SonicRespException {
        return findElement(iosSelector.getSelector(), value, retry, interval);
    }

    /**
     * find element in device.
     *
     * @param xcuiElementType
     * @param retry
     * @param interval
     * @return
     * @throws SonicRespException
     */
    public IOSElement findElement(XCUIElementType xcuiElementType, Integer retry, Integer interval) throws SonicRespException {
        return findElement(IOSSelector.CLASS_NAME.getSelector(), xcuiElementType.getType(), retry, interval);
    }

    /**
     * find element in device.
     *
     * @param selector
     * @param value
     * @param retry
     * @param interval
     * @return
     * @throws SonicRespException
     */
    public IOSElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException {
        return wdaClient.findElement(selector, value, retry, interval);
    }

    /**
     * find element list in device.
     *
     * @param iosSelector
     * @param value
     * @return
     * @throws SonicRespException
     */
    public List<IOSElement> findElementList(IOSSelector iosSelector, String value) throws SonicRespException {
        return findElementList(iosSelector, value, null);
    }

    /**
     * find element list in device.
     *
     * @param xcuiElementType
     * @return
     * @throws SonicRespException
     */
    public List<IOSElement> findElementList(XCUIElementType xcuiElementType) throws SonicRespException {
        return findElementList(xcuiElementType, null);
    }

    /**
     * find element list in device.
     *
     * @param selector
     * @param value
     * @return
     * @throws SonicRespException
     */
    public List<IOSElement> findElementList(String selector, String value) throws SonicRespException {
        return findElementList(selector, value, null);
    }

    /**
     * find element list in device.
     *
     * @param iosSelector
     * @param value
     * @param retry
     * @return
     * @throws SonicRespException
     */
    public List<IOSElement> findElementList(IOSSelector iosSelector, String value, Integer retry) throws SonicRespException {
        return findElementList(iosSelector, value, retry, null);
    }

    /**
     * find element list in device.
     *
     * @param xcuiElementType
     * @param retry
     * @return
     * @throws SonicRespException
     */
    public List<IOSElement> findElementList(XCUIElementType xcuiElementType, Integer retry) throws SonicRespException {
        return findElementList(xcuiElementType, retry, null);
    }

    /**
     * find element list in device.
     *
     * @param selector
     * @param value
     * @param retry
     * @return
     * @throws SonicRespException
     */
    public List<IOSElement> findElementList(String selector, String value, Integer retry) throws SonicRespException {
        return findElementList(selector, value, retry, null);
    }

    /**
     * find element list in device.
     *
     * @param iosSelector
     * @param value
     * @param retry
     * @param interval
     * @return
     * @throws SonicRespException
     */
    public List<IOSElement> findElementList(IOSSelector iosSelector, String value, Integer retry, Integer interval) throws SonicRespException {
        return findElementList(iosSelector.getSelector(), value, retry, interval);
    }

    /**
     * find element list in device.
     *
     * @param xcuiElementType
     * @param retry
     * @param interval
     * @return
     * @throws SonicRespException
     */
    public List<IOSElement> findElementList(XCUIElementType xcuiElementType, Integer retry, Integer interval) throws SonicRespException {
        return findElementList(IOSSelector.CLASS_NAME.getSelector(), xcuiElementType.getType(), retry, interval);
    }

    /**
     * find element list in device.
     *
     * @param selector
     * @param value
     * @param retry
     * @param interval
     * @return
     * @throws SonicRespException
     */
    public List<IOSElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException {
        return wdaClient.findElementList(selector, value, retry, interval);
    }

    /**
     * get current active element
     * @return
     * @throws SonicRespException
     */
    public IOSElement activeElement() throws SonicRespException{
        return wdaClient.activeElement();
    }

    /**
     * get screenshot.
     *
     * @return
     * @throws SonicRespException
     */
    public byte[] screenshot() throws SonicRespException {
        return wdaClient.screenshot();
    }

    /**
     * set appium settings.
     *
     * @param settings
     * @throws SonicRespException
     */
    public void setAppiumSettings(JSONObject settings) throws SonicRespException {
        wdaClient.setAppiumSettings(settings);
    }

    /**
     * rotate screen orientation
     * @throws SonicRespException
     */
    public void rotate(Orientation orientation) throws SonicRespException {
        wdaClient.rotate(orientation);
    }

    public Orientation getRotate() throws SonicRespException {
        return wdaClient.getRotate();
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/enums/ActionType.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios.enums;

public enum ActionType {
    PRESS("pointerDown"),
    WAIT("pause"),
    MOVE("pointerMove"),
    RELEASE("pointerUp");

    private final String type;

    ActionType(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/enums/AuthResource.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios.enums;

public enum AuthResource {
    CONTACTS(1),
    CALENDAR(2),
    REMINDERS(3),
    PHOTOS(4),
    MICROPHONE(5),
    CAMERA(6),
    MEDIA_LIBRARY(7),
    HOME_KIT(8),
    SYSTEM_ROOT_DIRECTORY(0x40000000),
    USER_DESKTOP_DIRECTORY(0x40000001),
    USER_DOWNLOADS_DIRECTORY(0x40000002),
    USER_DOCUMENTS_DIRECTORY(0x40000003),
    BLUETOOTH(-0x40000000),
    KEYBOARD_NETWORK(-0x40000001),
    LOCATION(-0x40000002),
    HEALTH(-0x40000003);

    private final int resource;

    AuthResource(int resource) {
        this.resource = resource;
    }

    public int getResource() {
        return resource;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/enums/IOSSelector.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios.enums;

public enum IOSSelector {
    CLASS_NAME("class name"),
    NAME("name"),
    Id("id"),
    ACCESSIBILITY_ID("accessibility id"),

    XPATH("xpath"),
    PREDICATE("predicate string"),

    CLASS_CHAIN("class chain"),
    LINK_TEXT("link text"),
    PARTIAL_LINK_TEXT("partial link text");

    private final String selector;

    IOSSelector(String selector) {
        this.selector = selector;
    }

    public String getSelector() {
        return selector;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/enums/Orientation.java
================================================
package org.cloud.sonic.driver.ios.enums;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.util.HashMap;

public enum Orientation {
    UNKNOWN(0),
    PORTRAIT(1),
    PORTRAITUPSIDEDOWN(2),
    LANDSCAPELEFT(3),
    LANDSCAPERIGHT(4);

    private final int orientation;

    Orientation(int orientation) {
        this.orientation = orientation;
    }

    public JSONObject getValue() {
        JSONObject value = new JSONObject();
        value.put("x",0);
        value.put("y",0);
        switch (this.orientation) {
            case 1:
                value.put("z",0);
                break;
            case 2:
                value.put("z",180);
                break;
            case 3:
                value.put("z",90);
                break;
            case 4:
                value.put("z",270);
                break;
        }

        return value;
    }

}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/enums/SystemButton.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios.enums;

public enum SystemButton {
    HOME("home"),
    VOLUME_UP("volumeUp"),
    VOLUME_DOWN("volumeDown");

    private final String button;

    SystemButton(String button) {
        this.button = button;
    }

    public String getButton() {
        return button;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/enums/TextKey.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios.enums;

public enum TextKey {
    BACK_SPACE("\u0008"),
    DELETE("\u007F");

    private final String key;

    TextKey(String key) {
        this.key = key;
    }

    public String getKey() {
        return key;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/enums/XCUIElementType.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License")),
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios.enums;

public enum XCUIElementType {
    ANY("XCUIElementTypeAny"),
    OTHER("XCUIElementTypeOther"),
    APPLICATION("XCUIElementTypeApplication"),
    GROUP("XCUIElementTypeGroup"),
    WINDOW("XCUIElementTypeWindow"),
    SHEET("XCUIElementTypeSheet"),
    DRAWER("XCUIElementTypeDrawer"),
    ALERT("XCUIElementTypeAlert"),
    DIALOG("XCUIElementTypeDialog"),
    BUTTON("XCUIElementTypeButton"),
    DISCLOSURE_TRIANGLE("XCUIElementTypeDisclosureTriangle"),
    POP_UP_BUTTON("XCUIElementTypePopUpButton"),
    COMBO_BOX("XCUIElementTypeComboBox"),
    MENU_BUTTON("XCUIElementTypeMenuButton"),
    TOOLBAR_BUTTON("XCUIElementTypeToolbarButton"),
    POPOVER("XCUIElementTypePopover"),
    KEYBOARD("XCUIElementTypeKeyboard"),
    NAVIGATION_BAR("XCUIElementTypeNavigationBar"),
    KEY("XCUIElementTypeKey"),
    STATUS_BAR("XCUIElementTypeStatusBar"),
    SWITCH("XCUIElementTypeSwitch"),
    TOGGLE("XCUIElementTypeToggle"),
    LINK("XCUIElementTypeLink"),
    IMAGE("XCUIElementTypeImage"),
    ICON("XCUIElementTypeIcon"),
    SEARCH_FIELD("XCUIElementTypeSearchField"),
    STATIC_TEXT("XCUIElementTypeStaticText"),
    CHECK_BOX("XCUIElementTypeCheckBox"),
    TEXT_VIEW("XCUIElementTypeTextView"),
    SEGMENTED_CONTROL("XCUIElementTypeSegmentedControl"),
    BROWSER("XCUIElementTypeBrowser"),
    COLLECTION_VIEW("XCUIElementTypeCollectionView"),
    SLIDER("XCUIElementTypeSlider"),
    MAP("XCUIElementTypeMap"),
    WEB_VIEW("XCUIElementTypeWebView"),
    TIME_LINE("XCUIElementTypeTimeline"),
    COLOR_WELL("XCUIElementTypeColorWell"),
    HELP_TAG("XCUIElementTypeHelpTag"),
    MATTE("XCUIElementTypeMatte"),
    DOCK_ITEM("XCUIElementTypeDockItem"),
    GRID("XCUIElementTypeGrid"),
    CELL("XCUIElementTypeCell"),
    HANDLE("XCUIElementTypeHandle"),
    STEPPER("XCUIElementTypeStepper"),
    TAB("XCUIElementTypeTab"),
    TOUCH_BAR("XCUIElementTypeTouchBar"),
    STATUS_ITEM("XCUIElementTypeStatusItem"),

    LAYOUT_AREA("XCUIElementTypeLayoutArea"),
    LAYOUT_ITEM("XCUIElementTypeLayoutItem"),

    RULER("XCUIElementTypeRuler"),
    RULER_MARKER("XCUIElementTypeRulerMarker"),

    RADIO_BUTTON("XCUIElementTypeRadioButton"),
    RADIO_GROUP("XCUIElementTypeRadioGroup"),

    TAB_BAR("XCUIElementTypeTabBar"),
    TAB_GROUP("XCUIElementTypeTabGroup"),

    TABLE("XCUIElementTypeTable"),
    TABLE_ROW("XCUIElementTypeTableRow"),
    TABLE_COLUMN("XCUIElementTypeTableColumn"),

    OUTLINE("XCUIElementTypeOutline"),
    OUTLINE_ROW("XCUIElementTypeOutlineRow"),

    PAGE_INDICATOR("XCUIElementTypePageIndicator"),
    PROGRESS_INDICATOR("XCUIElementTypeProgressIndicator"),
    ACTIVITY_INDICATOR("XCUIElementTypeActivityIndicator"),
    RATING_INDICATOR("XCUIElementTypeRatingIndicator"),
    VALUE_INDICATOR("XCUIElementTypeValueIndicator"),
    RELEVANCE_INDICATOR("XCUIElementTypeRelevanceIndicator"),
    LEVEL_INDICATOR("XCUIElementTypeLevelIndicator"),

    SPLIT_GROUP("XCUIElementTypeSplitGroup"),
    SPLITTER("XCUIElementTypeSplitter"),

    PICKER("XCUIElementTypePicker"),
    PICKER_WHEEL("XCUIElementTypePickerWheel"),
    DATE_PICKER("XCUIElementTypeDatePicker"),

    SCROLL_VIEW("XCUIElementTypeScrollView"),
    SCROLL_BAR("XCUIElementTypeScrollBar"),

    TEXT_FIELD("XCUIElementTypeTextField"),
    SECURE_TEXT_FIELD("XCUIElementTypeSecureTextField"),

    MENU("XCUIElementTypeMenu"),
    MENU_ITEM("XCUIElementTypeMenuItem"),
    MENU_BAR("XCUIElementTypeMenuBar"),
    MENU_BAR_ITEM("XCUIElementTypeMenuBarItem"),

    INCREMENT_ARROW("XCUIElementTypeIncrementArrow"),
    DECREMENT_ARROW("XCUIElementTypeDecrementArrow"),
    ;

    private final String type;

    XCUIElementType(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/models/TouchActions.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios.models;

import cn.hutool.core.map.MapUtil;
import lombok.Getter;
import lombok.ToString;
import org.cloud.sonic.driver.ios.enums.ActionType;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Getter
@ToString
public class TouchActions {

    private List<FingerTouchAction> actions;

    @Getter
    @ToString
    public static class FingerTouchAction {
        private final String id;
        private final String type = "pointer";
        private final Map<String, String> parameters = MapUtil.of("pointerType", "touch");
        private final List<TouchAction> actions;

        public FingerTouchAction(String fingerName) {
            this.id = "finger-" + fingerName;
            actions = new ArrayList<>();
        }

        public FingerTouchAction() {
            this("0");
        }

        public FingerTouchAction press(int x, int y) {
            move(x, y);
            TouchAction touchAction = new TouchAction(ActionType.PRESS);
            actions.add(touchAction);
            return this;
        }

        public FingerTouchAction wait(int ms) {
            PauseAction touchAction = new PauseAction();
            touchAction.duration = ms;
            actions.add(touchAction);
            return this;
        }

        public FingerTouchAction move(int x, int y) {
            MoveAction touchAction = new MoveAction();
            touchAction.x = x;
            touchAction.y = y;
            actions.add(touchAction);
            return this;
        }

        public FingerTouchAction release() {
            TouchAction touchAction = new TouchAction(ActionType.RELEASE);
            actions.add(touchAction);
            return this;
        }
    }

    @Getter
    @ToString
    public static class TouchAction {
        private final String type;

        public TouchAction(ActionType actionType) {
            this.type = actionType.getType();
        }
    }

    @Getter
    @ToString(callSuper = true)
    public static class MoveAction extends TouchAction {
        private int x;
        private int y;

        public MoveAction() {
            super(ActionType.MOVE);
        }
    }

    @Getter
    @ToString(callSuper = true)
    public static class PauseAction extends TouchAction {
        private int duration;

        public PauseAction() {
            super(ActionType.WAIT);
        }
    }

    public TouchActions() {
        actions = new ArrayList<>();
    }

    public TouchActions(FingerTouchAction finger) {
        this();
        this.actions.add(finger);
    }

    public FingerTouchAction finger(String name) {
        FingerTouchAction finger = new FingerTouchAction(name);
        actions.add(finger);
        return finger;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/service/IOSElement.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios.service;

import org.cloud.sonic.driver.common.models.BaseElement;
import org.cloud.sonic.driver.common.tool.SonicRespException;

/**
 * @author Eason
 * web element interface
 */
public interface IOSElement extends BaseElement {

    void click() throws SonicRespException;

    void sendKeys(String text) throws SonicRespException;

    void sendKeys(String text, int frequency) throws SonicRespException;

    void clear() throws SonicRespException;

    String getText() throws SonicRespException;

    byte[] screenshot() throws SonicRespException;
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/service/WdaClient.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios.service;

import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.common.models.WindowSize;
import org.cloud.sonic.driver.common.tool.Logger;
import org.cloud.sonic.driver.common.tool.RespHandler;
import org.cloud.sonic.driver.common.tool.SonicRespException;
import org.cloud.sonic.driver.ios.enums.Orientation;
import org.cloud.sonic.driver.ios.models.TouchActions;

import java.util.List;

/**
 * @author Eason
 * wda client interface
 */
public interface WdaClient {
    //Client Setting
    void setGlobalTimeOut(int timeOut);

    RespHandler getRespHandler();

    void setRespHandler(RespHandler respHandler);

    Logger getLogger();

    void showLog();

    void disableLog();

    //Session handler.
    String getRemoteUrl();

    void setRemoteUrl(String remoteUrl);

    String getSessionId();

    void setSessionId(String sessionId);

    void newSession(JSONObject capabilities) throws SonicRespException;

    void closeSession() throws SonicRespException;

    void checkSessionId() throws SonicRespException;

    //window handler.
    WindowSize getWindowSize() throws SonicRespException;

    //lock handler.
    boolean isLocked() throws SonicRespException;

    void lock() throws SonicRespException;

    void unlock() throws SonicRespException;

    //perform handler.
    void performTouchAction(TouchActions touchActions) throws SonicRespException;
    
    //button handler.
    void pressButton(String buttonName) throws SonicRespException;

    void doubleTap(int x, int y) throws SonicRespException;

    //keyboard handler.
    void sendKeys(String text, Integer frequency) throws SonicRespException;

    void setPasteboard(String contentType, String content) throws SonicRespException;

    byte[] getPasteboard(String contentType) throws SonicRespException;

    //source handler.
    String pageSource() throws SonicRespException;

    //siri handler.
    void sendSiriCommand(String command) throws SonicRespException;

    //app handler.
    void appActivate(String bundleId) throws SonicRespException;

    boolean appTerminate(String bundleId) throws SonicRespException;

    void appRunBackground(int duration) throws SonicRespException;

    void appAuthReset(int resource) throws SonicRespException;

    //element handler.
    void setDefaultFindElementInterval(Integer retry, Integer interval);

    IOSElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException;

    List<IOSElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException;

    // get current active element
    IOSElement activeElement() throws SonicRespException;

    //screen handler.
    byte[] screenshot() throws SonicRespException;

    //appium setting handler.
    void setAppiumSettings(JSONObject settings) throws SonicRespException;

    void swipe(double fromX, double fromY, double toX, double toY, double duration) throws SonicRespException;
    void rotate(Orientation orientation) throws SonicRespException;
    Orientation getRotate() throws SonicRespException;
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/service/impl/IOSElementImpl.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import org.cloud.sonic.driver.common.models.BaseResp;
import org.cloud.sonic.driver.common.models.ElementRect;
import org.cloud.sonic.driver.common.tool.Logger;
import org.cloud.sonic.driver.common.tool.SonicRespException;
import org.cloud.sonic.driver.ios.service.IOSElement;
import org.cloud.sonic.driver.ios.service.WdaClient;

import java.util.Base64;

public class IOSElementImpl implements IOSElement {
    private String id;
    private WdaClient wdaClient;
    private Logger logger;

    public IOSElementImpl(String id, WdaClient wdaClient) {
        this.id = id;
        this.wdaClient = wdaClient;
        logger = wdaClient.getLogger();
    }

    @Override
    public void click() throws SonicRespException {
        wdaClient.checkSessionId();
        BaseResp b = wdaClient.getRespHandler().getResp(
                HttpUtil.createPost(wdaClient.getRemoteUrl() + "/session/"
                        + wdaClient.getSessionId() + "/element/" + id + "/click"));
        if (b.getErr() == null) {
            logger.info("click element %s.", id);
        } else {
            logger.error("click element %s failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void sendKeys(String text) throws SonicRespException {
        sendKeys(text, 3);
    }

    @Override
    public void sendKeys(String text, int frequency) throws SonicRespException {
        wdaClient.checkSessionId();
        JSONObject data = new JSONObject();
        data.put("value", text.split(""));
        data.put("frequency", frequency);
        BaseResp b = wdaClient.getRespHandler().getResp(
                HttpUtil.createPost(wdaClient.getRemoteUrl() + "/session/"
                                + wdaClient.getSessionId() + "/element/" + id + "/value")
                        .body(data.toJSONString()), 60000);
        if (b.getErr() == null) {
            logger.info("send key to %s.", id);
        } else {
            logger.error("send key to %s failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void clear() throws SonicRespException {
        wdaClient.checkSessionId();
        BaseResp b = wdaClient.getRespHandler().getResp(
                HttpUtil.createPost(wdaClient.getRemoteUrl() + "/session/"
                        + wdaClient.getSessionId() + "/element/" + id + "/clear"), 60000);
        if (b.getErr() == null) {
            logger.info("clear %s.", id);
        } else {
            logger.error("clear %s failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public String getText() throws SonicRespException {
        wdaClient.checkSessionId();
        BaseResp b = wdaClient.getRespHandler().getResp(
                HttpUtil.createGet(wdaClient.getRemoteUrl() + "/session/"
                        + wdaClient.getSessionId() + "/element/" + id + "/text"));
        if (b.getErr() == null) {
            logger.info("get %s text %s.", id, b.getValue().toString());
            return b.getValue().toString();
        } else {
            logger.error("get %s text failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public ElementRect getRect() throws SonicRespException {
        wdaClient.checkSessionId();
        BaseResp b = wdaClient.getRespHandler().getResp(
                HttpUtil.createGet(wdaClient.getRemoteUrl() + "/session/"
                        + wdaClient.getSessionId() + "/element/" + id + "/rect"));
        if (b.getErr() == null) {
            ElementRect elementRect = JSON.parseObject(b.getValue().toString(), ElementRect.class);
            logger.info("get %s rect %s.", id, elementRect.toString());
            return elementRect;
        } else {
            logger.error("get %s rect failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public String getAttribute(String name) throws SonicRespException {
        wdaClient.checkSessionId();
        BaseResp b = wdaClient.getRespHandler().getResp(
                HttpUtil.createGet(wdaClient.getRemoteUrl() + "/session/"
                        + wdaClient.getSessionId() + "/element/" + id + "/attribute/" + name), 60000);
        if (b.getErr() == null) {
            logger.info("get %s attribute %s.", id, name);
            return b.getValue().toString();
        } else {
            logger.info("get %s attribute %s.", id, name);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public String getUniquelyIdentifies() throws SonicRespException {
        return id;
    }

    @Override
    public byte[] screenshot() throws SonicRespException {
        wdaClient.checkSessionId();
        BaseResp b = wdaClient.getRespHandler().getResp(
                HttpUtil.createGet(wdaClient.getRemoteUrl() + "/session/"
                        + wdaClient.getSessionId() + "/element/" + id + "/screenshot"), 60000);
        if (b.getErr() == null) {
            logger.info("get element %s screenshot.", id);
            return Base64.getMimeDecoder().decode(b.getValue().toString());
        } else {
            logger.error("get element %s screenshot failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public boolean isDisplayed() throws SonicRespException {
        wdaClient.checkSessionId();
        BaseResp b = wdaClient.getRespHandler().getResp(
                HttpUtil.createGet(wdaClient.getRemoteUrl() + "/session/"
                        + wdaClient.getSessionId() + "/element/" + id + "/displayed"));
        if (b.getErr() == null) {
            logger.info("get %s displayed,result is %s.", id, b.getValue().toString());
            return Boolean.parseBoolean(b.getValue().toString());
        } else {
            logger.error("get %s displayed failed.", id);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/ios/service/impl/WdaClientImpl.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.ios.service.impl;

import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.common.models.BaseResp;
import org.cloud.sonic.driver.common.models.SessionInfo;
import org.cloud.sonic.driver.common.models.WindowSize;
import org.cloud.sonic.driver.common.tool.Logger;
import org.cloud.sonic.driver.common.tool.RespHandler;
import org.cloud.sonic.driver.common.tool.SonicRespException;
import org.cloud.sonic.driver.ios.enums.Orientation;
import org.cloud.sonic.driver.ios.models.TouchActions;
import org.cloud.sonic.driver.ios.service.IOSElement;
import org.cloud.sonic.driver.ios.service.WdaClient;
import org.jsoup.select.CombiningEvaluator;

import java.nio.charset.StandardCharsets;
import java.util.*;

public class WdaClientImpl implements WdaClient {
    private String remoteUrl;
    private String sessionId;
    private RespHandler respHandler;
    private Logger logger;
    private final String LEGACY_WEB_ELEMENT_IDENTIFIER = "ELEMENT";
    private final String WEB_ELEMENT_IDENTIFIER = "element-6066-11e4-a52e-4f735466cecf";
    private int FIND_ELEMENT_INTERVAL = 3000;
    private int FIND_ELEMENT_RETRY = 5;

    public WdaClientImpl() {
        respHandler = new RespHandler();
        logger = new Logger();
    }

    private void checkBundleId(String bundleId) throws SonicRespException {
        if (bundleId == null || bundleId.length() == 0) {
            logger.error("bundleId not found.");
            throw new SonicRespException("bundleId not found.");
        }
    }

    private String parseElementId(Object o) {
        JSONObject jsonObject = (JSONObject) o;
        List<String> identifier = Arrays.asList(LEGACY_WEB_ELEMENT_IDENTIFIER, WEB_ELEMENT_IDENTIFIER);
        for (String i : identifier) {
            String result = jsonObject.getString(i);
            if (result != null && result.length() > 0) {
                return result;
            }
        }
        return "";
    }

    @Override
    public void setGlobalTimeOut(int timeOut) {
        respHandler.setRequestTimeOut(timeOut);
    }

    @Override
    public RespHandler getRespHandler() {
        return respHandler;
    }

    @Override
    public void setRespHandler(RespHandler respHandler) {
        this.respHandler = respHandler;
    }

    @Override
    public Logger getLogger() {
        return logger;
    }

    @Override
    public void showLog() {
        logger.showLog();
    }

    @Override
    public void disableLog() {
        logger.disableLog();
    }

    @Override
    public String getRemoteUrl() {
        return remoteUrl;
    }

    @Override
    public void setRemoteUrl(String remoteUrl) {
        this.remoteUrl = remoteUrl;
    }

    @Override
    public String getSessionId() {
        return sessionId;
    }

    @Override
    public void setSessionId(String sessionId) {
        this.sessionId = sessionId;
    }

    @Override
    public void newSession(JSONObject capabilities) throws SonicRespException {
        JSONObject data = new JSONObject();
        data.put("capabilities", capabilities);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session").body(data.toJSONString()));
        if (b.getErr() == null) {
            SessionInfo sessionInfo = JSON.parseObject(b.getValue().toString(), SessionInfo.class);
            setSessionId(sessionInfo.getSessionId());
            logger.info("start session successful!");
            logger.info("session : %s", sessionInfo.getSessionId());
            logger.info("session capabilities : %s", sessionInfo.getCapabilities().toString());
        } else {
            logger.error("start session failed.");
            logger.error("cause: %s", b.getErr().toString());
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void closeSession() throws SonicRespException {
        checkSessionId();
        respHandler.getResp(HttpUtil.createRequest(Method.DELETE, remoteUrl + "/session/" + sessionId));
        logger.info("close session successful!");
    }

    @Override
    public void checkSessionId() throws SonicRespException {
        if (sessionId == null || sessionId.length() == 0) {
            logger.error("sessionId not found.");
            throw new SonicRespException("sessionId not found.");
        }
    }

    @Override
    public WindowSize getWindowSize() throws SonicRespException {
        checkSessionId();
        BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + "/session/" + sessionId + "/window/size"));
        if (b.getErr() == null) {
            logger.info("get window size %s.", b.getValue().toString());
            return JSON.parseObject(b.getValue().toString(), WindowSize.class);
        } else {
            logger.error("get window size failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public boolean isLocked() throws SonicRespException {
        checkSessionId();
        BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + "/session/" + sessionId + "/wda/locked"));
        if (b.getErr() == null) {
            logger.info("device lock status: %b.", b.getValue());
            return (boolean) b.getValue();
        } else {
            logger.error("get device lock status failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void lock() throws SonicRespException {
        checkSessionId();
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/lock"));
        if (b.getErr() == null) {
            logger.info("lock device.");
        } else {
            logger.error("lock device failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void unlock() throws SonicRespException {
        checkSessionId();
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/unlock"));
        if (b.getErr() == null) {
            logger.info("unlock device.");
        } else {
            logger.error("unlock device failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void performTouchAction(TouchActions touchActions) throws SonicRespException {
        checkSessionId();
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/actions")
                .body(JSON.toJSONString(touchActions)));
        if (b.getErr() == null) {
            logger.info("perform action %s.", touchActions.toString());
        } else {
            logger.error("perform failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void pressButton(String buttonName) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("name", buttonName);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/pressButton")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("press button %s.", buttonName);
        } else {
            logger.error("press button failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void doubleTap(int x, int y) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("x",x);
        data.put("y",y);

        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/doubleTap")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("double tap success. %s", data.toJSONString());
        } else {
            logger.error("double tap failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void sendKeys(String text, Integer frequency) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("value", text.split(""));
        data.put("frequency", frequency);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/keys")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("send key %s.", text);
        } else {
            logger.error("send key failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void setPasteboard(String contentType, String content) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("contentType", contentType);
        data.put("content", Base64.getEncoder().encodeToString(content.getBytes(StandardCharsets.UTF_8)));
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/setPasteboard")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("set pasteboard %s.", content);
        } else {
            logger.error("set pasteboard failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public byte[] getPasteboard(String contentType) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("contentType", contentType);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/getPasteboard")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            byte[] result = Base64.getMimeDecoder().decode(b.getValue().toString());
            logger.info("get pasteboard length: %d.", result.length);
            return result;
        } else {
            logger.error("get pasteboard failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public String pageSource() throws SonicRespException {
        checkSessionId();
        BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + "/session/" + sessionId + "/source"), 5 * 60 * 1000);
        if (b.getErr() == null) {
            logger.info("get page source.");
            return b.getValue().toString();
        } else {
            logger.error("get page source failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void sendSiriCommand(String command) throws SonicRespException {
        if (command != null && command.length() != 0) {
            checkSessionId();
            JSONObject data = new JSONObject();
            data.put("text", command);
            BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/siri/activate")
                    .body(data.toJSONString()));
            if (b.getErr() == null) {
                logger.info("send siri command: %s", command);
            } else {
                logger.error("send siri command [%s] failed.", command);
                throw new SonicRespException(b.getErr().getMessage());
            }
        } else {
            logger.error("siri command is null!");
            throw new SonicRespException("siri command is null!");
        }
    }

    @Override
    public void appActivate(String bundleId) throws SonicRespException {
        checkSessionId();
        checkBundleId(bundleId);
        JSONObject data = new JSONObject();
        data.put("bundleId", bundleId);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/apps/activate")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("activate app %s.", bundleId);
        } else {
            logger.error("activate app %s failed.", bundleId);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public boolean appTerminate(String bundleId) throws SonicRespException {
        checkSessionId();
        checkBundleId(bundleId);
        JSONObject data = new JSONObject();
        data.put("bundleId", bundleId);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/apps/terminate")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("terminate app %s status: %b.", bundleId, b.getValue());
            return (boolean) b.getValue();
        } else {
            logger.error("terminate app failed.", bundleId);
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void appRunBackground(int duration) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("duration", duration);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/deactivateApp")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("run app background in %d seconds.", duration);
        } else {
            logger.error("run app background failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void appAuthReset(int resource) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("resource", resource);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/resetAppAuth")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("reset app auth %s.", resource);
        } else {
            logger.error("reset app auth failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void setDefaultFindElementInterval(Integer retry, Integer interval) {
        if (retry != null) {
            FIND_ELEMENT_RETRY = retry;
        }
        if (interval != null) {
            FIND_ELEMENT_INTERVAL = interval;
        }
    }

    @Override
    public IOSElement findElement(String selector, String value, Integer retry, Integer interval) throws SonicRespException {
        IOSElement iosElement = null;
        int wait = 0;
        int intervalInit = (interval == null ? FIND_ELEMENT_INTERVAL : interval);
        int retryInit = (retry == null ? FIND_ELEMENT_RETRY : retry);
        String errMsg = "";
        while (wait < retryInit) {
            wait++;
            checkSessionId();
            JSONObject data = new JSONObject();
            data.put("using", selector);
            data.put("value", value);
            BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/element")
                    .body(data.toJSONString()));
            if (b.getErr() == null) {
                logger.info("find element successful.");
                String id = parseElementId(b.getValue());
                if (id.length() > 0) {
                    iosElement = new IOSElementImpl(id, this);
                    break;
                } else {
                    logger.error("parse element id %s failed. retried %d times, retry in %d ms.", b.getValue().toString(), wait, intervalInit);
                }
            } else {
                logger.error("element not found. retried %d times, retry in %d ms.", wait, intervalInit);
                errMsg = b.getErr().getMessage();
            }
            if (wait < retryInit) {
                try {
                    Thread.sleep(intervalInit);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        if (iosElement == null) {
            throw new SonicRespException(errMsg);
        }
        return iosElement;
    }

    @Override
    public List<IOSElement> findElementList(String selector, String value, Integer retry, Integer interval) throws SonicRespException {
        List<IOSElement> iosElementList = new ArrayList<>();
        int wait = 0;
        int intervalInit = (interval == null ? FIND_ELEMENT_INTERVAL : interval);
        int retryInit = (retry == null ? FIND_ELEMENT_RETRY : retry);
        String errMsg = "";
        while (wait < retryInit) {
            wait++;
            checkSessionId();
            JSONObject data = new JSONObject();
            data.put("using", selector);
            data.put("value", value);
            BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/elements")
                    .body(data.toJSONString()));
            if (b.getErr() == null) {
                logger.info("find elements successful.");
                List<JSONObject> ids = JSON.parseObject(b.getValue().toString(), ArrayList.class);
                for (JSONObject ele : ids) {
                    String id = parseElementId(ele);
                    if (id.length() > 0) {
                        iosElementList.add(new IOSElementImpl(id, this));
                    } else {
                        logger.error("parse element id %s failed.", ele);
                    }
                }
                if (iosElementList.size() > 0) {
                    break;
                }
            } else {
                logger.error("elements not found. retried %d times, retry in %d ms.", wait, intervalInit);
                errMsg = b.getErr().getMessage();
            }
            if (wait < retryInit) {
                try {
                    Thread.sleep(intervalInit);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        if (iosElementList.size() == 0) {
            throw new SonicRespException(errMsg);
        }
        return iosElementList;
    }

    @Override
    public IOSElement activeElement() throws SonicRespException {
        checkSessionId();
        BaseResp b = respHandler.getResp(
                HttpUtil.createGet(remoteUrl + "/session/" + sessionId + "/element/active"));
        if (b.getErr() == null) {
            logger.info("find active elements successful.");
            JSONObject value = JSON.parseObject(b.getValue().toString(), JSONObject.class);
            List<String> identifier = Arrays.asList("ELEMENT", "element-6066-11e4-a52e-4f735466cecf");
            Iterator var4 = identifier.iterator();
            String eleId;
            do {
                if (!var4.hasNext()) {
                    return null;
                }
                String i = (String)var4.next();
                eleId = value.getString(i);
            } while(eleId == null || eleId.length() <= 0);

            return new IOSElementImpl(eleId,this);
        } else {
            logger.error("active element not found.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public byte[] screenshot() throws SonicRespException {
        checkSessionId();
        BaseResp b = respHandler.getResp(
                HttpUtil.createGet(remoteUrl + "/session/" + sessionId + "/screenshot"), 60000);
        if (b.getErr() == null) {
            logger.info("get screenshot.");
            return Base64.getMimeDecoder().decode(b.getValue().toString());
        } else {
            logger.error("get screenshot failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void setAppiumSettings(JSONObject settings) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();
        data.put("settings", settings);
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/appium/settings")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("set appium settings %s.", settings.toJSONString());
        } else {
            logger.error("set appium settings failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void swipe(double fromX, double fromY, double toX, double toY, double duration) throws SonicRespException {
        checkSessionId();
        JSONObject data = new JSONObject();

        double maxSwipeDistance = Math.max(Math.abs(toX - fromX), Math.abs(toY - fromY));
        double velocity = maxSwipeDistance / duration * 1000;
        // velocity 单位为像素/秒,这里做个限制,如果超过1000则滑动的过快,很容易触发fling行为
        velocity = Math.min(velocity, 1000);
        data.put("fromX", fromX);
        data.put("fromY", fromY);
        data.put("toX", toX);
        data.put("toY", toY);
        data.put("velocity", velocity);

        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/session/" + sessionId + "/wda/pressAndDragWithVelocity")
                .body(data.toJSONString()));
        if (b.getErr() == null) {
            logger.info("perform swipe %s successful.", data.toString());
        } else {
            logger.error("perform swipe %s failed.", data.toString());
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public void rotate(Orientation orientation) throws SonicRespException {
        JSONObject xyz = orientation.getValue();
        BaseResp b = respHandler.getResp(HttpUtil.createPost(remoteUrl + "/rotation").body(String.valueOf(xyz.toJSONString())));
        if (b.getErr() == null) {
            logger.info("set orientation success. %s", xyz.toJSONString());
        } else {
            logger.error("set orientation failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }

    @Override
    public Orientation getRotate() throws SonicRespException {
        BaseResp b = respHandler.getResp(HttpUtil.createGet(remoteUrl + "/rotation"));
        if (b.getErr() == null) {
            logger.info("get orientation %s.",b.getValue());
            JSONObject value = JSON.parseObject(b.getValue().toString(), JSONObject.class);
            Integer zValue = Integer.valueOf(value.getString("z"));
            switch (zValue) {
                case 0:
                    return Orientation.PORTRAIT;
                case 180:
                    return Orientation.PORTRAITUPSIDEDOWN;
                case 90:
                    return Orientation.LANDSCAPELEFT;
                case 270:
                    return Orientation.LANDSCAPERIGHT;
                default:
                    return Orientation.UNKNOWN;
            }
        } else {
            logger.error("get orientation failed.");
            throw new SonicRespException(b.getErr().getMessage());
        }
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/poco/PocoDriver.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.poco;

import org.cloud.sonic.driver.common.models.WindowSize;
import org.cloud.sonic.driver.common.tool.SonicRespException;
import org.cloud.sonic.driver.poco.enums.PocoEngine;
import org.cloud.sonic.driver.poco.enums.PocoSelector;
import org.cloud.sonic.driver.poco.models.PocoElement;
import org.cloud.sonic.driver.poco.service.PocoClient;
import org.cloud.sonic.driver.poco.service.impl.PocoClientImpl;
import org.jsoup.nodes.Element;

import java.util.List;

/**
 * @author Eason
 * poco driver
 */
public class PocoDriver {
    private PocoClient pocoClient;

    /**
     * init poco driver
     *
     * @param pocoEngine
     */
    public PocoDriver(PocoEngine pocoEngine) {
        this(pocoEngine, pocoEngine.getDefaultPort());
    }

    /**
     * init poco driver
     *
     * @param pocoEngine
     * @param port
     */
    public PocoDriver(PocoEngine pocoEngine, int port) {
        pocoClient = new PocoClientImpl();
        pocoClient.newClient(pocoEngine, port);
    }

    /**
     * close driver
     */
    public void closeDriver() {
        pocoClient.closeClient();
    }

    /**
     * show log.
     */
    public void showLog() {
        pocoClient.showLog();
    }

    /**
     * disable log.
     */
    public void disableLog() {
        pocoClient.disableLog();
    }

    /**
     * get Poco element
     *
     * @return
     * @throws SonicRespException
     */
    public PocoElement getPageSource() throws SonicRespException {
        return pocoClient.pageSource();
    }

    /**
     * get Poco element for Json
     *
     * @return
     * @throws SonicRespException
     */
    public String getPageSourceForJsonString() throws SonicRespException {
        return pocoClient.pageSourceForJsonString();
    }

    /**
     * get Poco element for jsoup.Element
     *
     * @return
     * @throws SonicRespException
     */
    public Element getPageSourceForXmlElement() throws SonicRespException {
        return pocoClient.pageSourceForXmlElement();
    }

    /**
     * find poco elements
     *
     * @param expression
     * @return
     */
    public List<PocoElement> findElements(String selector, String expression) throws SonicRespException {
        return pocoClient.findElements(selector, expression);
    }

    /**
     * find poco elements
     *
     * @param expression
     * @return
     */
    public List<PocoElement> findElements(PocoSelector selector, String expression) throws SonicRespException {
        return findElements(selector.getSelector(), expression);
    }

    /**
     * find poco element
     *
     * @param expression
     * @return
     */
    public PocoElement findElement(String selector, String expression) throws SonicRespException {
        return pocoClient.findElement(selector, expression);
    }

    /**
     * find poco element
     *
     * @param expression
     * @return
     */
    public PocoElement findElement(PocoSelector selector, String expression) throws SonicRespException {
        return findElement(selector.getSelector(), expression);
    }

    /**
     * Freeze page source
     */
    public void freezeSource() {
        pocoClient.freezeSource();
    }

    /**
     * Thaw page source
     */
    public void thawSource() {
        pocoClient.thawSource();
    }

    /**
     * get windows size
     *
     * @return
     * @throws SonicRespException
     */
    public WindowSize getScreenSize() throws SonicRespException {
        return pocoClient.getScreenSize();
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/poco/enums/PocoEngine.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.poco.enums;

public enum PocoEngine {
    UNITY_3D("Unity3d", 5001),
    UE4("UE4", 5001),
    COCOS_2DX_JS("Cocos2dx-js", 5003),
    COCOS_2DX_LUA("Cocos2dx-lua", 15004),
    COCOS_2DX_C_PLUS_1("Cocos2dx-c++", 18888),
    COCOS_CREATOR("cocos-creator", 5003),
    EGRET("Egret", 5003);

    private final String name;
    private final int defaultPort;

    PocoEngine(String name, int defaultPort) {
        this.name = name;
        this.defaultPort = defaultPort;
    }

    public int getDefaultPort() {
        return defaultPort;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/poco/enums/PocoSelector.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.poco.enums;

public enum PocoSelector {
    POCO("poco"),
    XPATH("xpath"),
    CSS_SELECTOR("cssSelector");

    private final String selector;

    PocoSelector(String selector) {
        this.selector = selector;
    }

    public String getSelector() {
        return selector;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/poco/models/PocoElement.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.poco.models;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import org.cloud.sonic.driver.common.models.BaseElement;
import org.cloud.sonic.driver.common.models.ElementRect;
import org.cloud.sonic.driver.common.tool.SonicRespException;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Getter
@ToString
@AllArgsConstructor
public class PocoElement implements BaseElement {
    public String currentNodeSelector = "Root";
    private Payload payload;
    private List<PocoElement> children;
    RootElement rootElement;
    private Element currentNodeXmlElement;

    private long version;

    @Override
    // the poco given pos is a point where the origin of the coordinates will change with
    // the rotation of the screen. The function and others are not uniform, so the implementation
    // is not considered, you can get the pos point through the payload, and then convert it at
    // the application layer
    public ElementRect getRect() throws SonicRespException {
        throw new SonicRespException("poco element unrealized");
    }

    @Override
    public String getAttribute(String name) throws SonicRespException {
        return currentNodeXmlElement.attr(name);
    }

    @Override
    public String getUniquelyIdentifies() throws SonicRespException {
        return currentNodeSelector;
    }

    @Override
    public boolean isDisplayed() throws SonicRespException {
        throw new SonicRespException("poco element unrealized");
    }

    @Getter
    @ToString
    @AllArgsConstructor
    public class Payload {
        private String layer;
        private String name;
        private String tag;
        private String text;
        private String texture;
        private Integer _instanceId;
        private Integer _ilayer;
        private String type;
        private Boolean visible;
        private ZOrders zOrders;
        private List<String> components;
        private List<Float> anchorPoint;
        private List<Float> scale;
        private List<Float> size;
        private List<Float> pos;
        private Boolean clickable;

        @Getter
        @ToString
        @AllArgsConstructor
        public class ZOrders {
            private Float global;
            private Float local;

            public ZOrders() {
            }
        }

        public Payload() {
            zOrders = new ZOrders();
        }
    }

    public PocoElement(RootElement root) {
        this.rootElement = root;
        this.currentNodeSelector = root.getXmlElement().cssSelector();
        payload = new Payload();
        children = new ArrayList<>();
    }

    public PocoElement(RootElement root, Element currentNodeXmlElement) {
        this(root);
        this.currentNodeXmlElement = currentNodeXmlElement;
        this.currentNodeSelector = currentNodeXmlElement.cssSelector();
        parseXmlNode(currentNodeXmlElement);
    }

    public Payload getPayload() {

        if (rootElement.getVersion() != getVersion()) {
            Element xmlPocoNode = rootElement.getXmlElement().select(currentNodeSelector).first();
            if (xmlPocoNode == null) {
                return null;
            }
            parseXmlNode(xmlPocoNode);
            version = rootElement.getVersion();
        }
        return payload;
    }

    public List<PocoElement> getChildren() {

        if (rootElement.getVersion() != getVersion()) {
            Element xmlPocoNode = rootElement.getXmlElement().select(currentNodeSelector).first();

            if (xmlPocoNode == null) {
                return null;
            }

            children.clear();

            currentNodeXmlElement = xmlPocoNode;

            Elements childList = xmlPocoNode.children();

            for (Element child : childList) {
                children.add(new PocoElement(rootElement, child));
            }

            version = rootElement.getVersion();
        }
        return children;
    }


    public void parseXmlNode(Element xmlNode) {

        payload.layer = xmlNode.attr("layer");
        payload.name = xmlNode.attr("name");
        payload.tag = xmlNode.attr("tag");
        payload.text = xmlNode.attr("text");
        payload.texture = xmlNode.attr("texture");

        String _instanceId = xmlNode.attr("_instanceId");
        payload._instanceId = _instanceId.isEmpty() ? 0 : Integer.parseInt(_instanceId);

        String _ilayer = xmlNode.attr("_ilayer");
        payload._ilayer = _ilayer.isEmpty() ? 0 : Integer.parseInt(_ilayer);

        payload.type = xmlNode.attr("type");

        String visible = xmlNode.attr("visible");
        payload.visible = Boolean.parseBoolean(visible);

        String global = xmlNode.attr("global");
        payload.zOrders.global = global.isEmpty() ? 0 : Float.parseFloat(global);
        String local = xmlNode.attr("local");
        payload.zOrders.local = local.isEmpty() ? 0 : Float.parseFloat(local);

        String components = xmlNode.attr("components");
        if (components.length() > 2) {
            components = components.substring(1, components.length() - 1);
            payload.components = Arrays.asList(components.split(","));
        }
        payload.anchorPoint = parseFloatAttrList(xmlNode.attr("anchorPoint"));
        payload.scale = parseFloatAttrList(xmlNode.attr("scale"));
        payload.size = parseFloatAttrList(xmlNode.attr("size"));
        payload.pos = parseFloatAttrList(xmlNode.attr("pos"));
        payload.clickable = Boolean.parseBoolean(xmlNode.attr("clickable"));
    }

    private List<Float> parseFloatAttrList(String floatAttrStr) {
        if (floatAttrStr.length() <= 2) return null;
        List<Float> result = new ArrayList<Float>();
        floatAttrStr = floatAttrStr.substring(1, floatAttrStr.length() - 1);
        for (String numStr : floatAttrStr.split(",")) {
            result.add(Float.parseFloat(numStr));
        }
        return result;
    }

    public Boolean currentTheNodeExists() {
        return rootElement.getXmlElement().select(currentNodeSelector).first() != null;
    }

    public PocoElement getParentNode() {
        Element xmlPocoNode = rootElement.getXmlElement().select(currentNodeSelector).first();

        if (xmlPocoNode == null) {
            return null;
        }
        Element parentNode = xmlPocoNode.parent();
        if (parentNode == null) {
            return null;
        }
        return new PocoElement(rootElement, parentNode);
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/poco/models/RootElement.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.poco.models;

import org.jsoup.nodes.Element;

public class RootElement {
    private Element XmlElement;
    private long version;

    public RootElement() {
    }

    public RootElement(Element XmlElement) {
        this.XmlElement = XmlElement;
    }

    public RootElement(Element XmlElement, long version) {
        this(XmlElement);
        this.version = version;
    }


    public Element getXmlElement() {
        return XmlElement;
    }

    public void setXmlElement(Element xmlElement) {
        this.XmlElement = xmlElement;
    }

    public long getVersion() {
        return version;
    }

    public void setVersion(long version) {
        this.version = version;
    }

    public synchronized void updateVersion(Element rootXmlElement) {
        this.XmlElement = rootXmlElement;
        this.version += 1;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/poco/service/PocoClient.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.poco.service;

import org.cloud.sonic.driver.common.models.WindowSize;
import org.cloud.sonic.driver.common.tool.Logger;
import org.cloud.sonic.driver.common.tool.SonicRespException;
import org.cloud.sonic.driver.poco.enums.PocoEngine;
import org.cloud.sonic.driver.poco.models.PocoElement;
import org.jsoup.nodes.Element;

import java.util.List;

/**
 * @author Eason
 * poco client interface
 */
public interface PocoClient {
    //Client Setting
    Logger getLogger();

    void showLog();

    void disableLog();

    //Client handler.
    void newClient(PocoEngine engine, int port);

    void closeClient();

    //source handler.
    PocoElement pageSource() throws SonicRespException;

    String pageSourceForJsonString() throws SonicRespException;

    Element pageSourceForXmlElement() throws SonicRespException;

    PocoElement findElement(String selector, String expression) throws SonicRespException;

    List<PocoElement> findElements(String selector, String expression) throws SonicRespException;

    void freezeSource();

    void thawSource();

    //other
    WindowSize getScreenSize() throws SonicRespException;
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/poco/service/PocoConnection.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.poco.service;

import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.common.tool.SonicRespException;

public interface PocoConnection {
    void connect();

    void disconnect();

    String sendAndReceive(JSONObject jsonObject) throws SonicRespException;
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/poco/service/impl/PocoClientImpl.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.poco.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.common.models.WindowSize;
import org.cloud.sonic.driver.common.tool.Logger;
import org.cloud.sonic.driver.common.tool.SonicRespException;
import org.cloud.sonic.driver.poco.enums.PocoEngine;
import org.cloud.sonic.driver.poco.models.PocoElement;
import org.cloud.sonic.driver.poco.models.RootElement;
import org.cloud.sonic.driver.poco.service.PocoClient;
import org.cloud.sonic.driver.poco.service.PocoConnection;
import org.cloud.sonic.driver.poco.util.PocoJsonToXml;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;

import java.util.*;

import static org.cloud.sonic.driver.poco.enums.PocoEngine.*;

public class PocoClientImpl implements PocoClient {
    private Logger logger;
    private PocoConnection pocoConnection;
    private PocoEngine engine;
    private String source;

    public RootElement rootNode;
    private boolean isFrozen = false;

    public PocoClientImpl() {
        logger = new Logger();
        rootNode = new RootElement();
    }

    @Override
    public Logger getLogger() {
        return logger;
    }

    @Override
    public void showLog() {
        logger.showLog();
    }

    @Override
    public void disableLog() {
        logger.disableLog();
    }

    @Override
    public void newClient(PocoEngine engine, int port) {
        this.engine = engine;
        if (engine.equals(COCOS_2DX_JS) || engine.equals(COCOS_CREATOR) || engine.equals(EGRET)) {
            pocoConnection = new WebSocketClientImpl(port, logger);
        } else {
            pocoConnection = new SocketClientImpl(port, logger);
        }
        pocoConnection.connect();
    }

    @Override
    public void closeClient() {
        pocoConnection.disconnect();
    }

    @Override
    public PocoElement pageSource() throws SonicRespException {
        pageSourceForXmlElement();
        return new PocoElement(rootNode);
    }

    @Override
    public String pageSourceForJsonString() throws SonicRespException {
        if (isFrozen) {
            return source;
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("jsonrpc", "2.0");
        jsonObject.put("params", Arrays.asList(true));
        jsonObject.put("id", UUID.randomUUID().toString());
        jsonObject.put("method", "Dump");
        if (engine.equals(COCOS_CREATOR) || engine.equals(COCOS_2DX_JS)) {
            jsonObject.put("method", "dump");
        }
        source = pocoConnection.sendAndReceive(jsonObject);
        return source;
    }

    @Override
    public Element pageSourceForXmlElement() throws SonicRespException {
        pageSourceForJsonString();
//        String pocoJson = "{\"Root\"" + source.substring("{\"result\"".length());
        Element rootXmlElement = Jsoup.parse(PocoJsonToXml.jsonObjToXml(JSON.parseObject(source).getJSONObject("result")), "", Parser.xmlParser());

        rootNode.updateVersion(rootXmlElement);

        return rootNode.getXmlElement();
    }

    @Override
    public PocoElement findElement(String selector, String expression) throws SonicRespException {
        List<PocoElement> pocoElements = findElements(selector, expression);
        return pocoElements.get(0);
    }

    @Override
    public List<PocoElement> findElements(String selector, String expression) throws SonicRespException {
        if (rootNode.getXmlElement() == null) {
            pageSourceForXmlElement();
        }
        Elements xmlNodes = null;
        switch (selector) {
            case "poco":
                String newExpress = "";
                String[] steps = expression.split("\\.");
                for (String step : steps) {
                    if (step.startsWith("poco")) {
                        newExpress += ("//*" + parseAttr(step));
                    } else if (step.startsWith("child")) {
                        newExpress += ("/*" + parseAttr(step));
                    }
                    if (step.endsWith("]") && step.contains("[")) {
                        int index = Integer.parseInt(step.substring(step.indexOf("[") + 1, step.indexOf("]")));
                        newExpress = String.format("(%s)[%d]", newExpress, (index + 1));
                    }
                }
                xmlNodes = rootNode.getXmlElement().selectXpath(newExpress);
                break;
            case "xpath":
                xmlNodes = rootNode.getXmlElement().selectXpath(expression);
                break;
            case "cssSelector":
                xmlNodes = rootNode.getXmlElement().select(expression);
                break;
        }
        if (xmlNodes == null || xmlNodes.isEmpty()) {
            throw new SonicRespException(String.format("poco element not found for selector:%s, value:%s", selector, expression));
        }
        List<PocoElement> result = new ArrayList<>();
        for (Element node : xmlNodes) {
            PocoElement pocoElement = new PocoElement(rootNode, node);
            result.add(pocoElement);
        }
        return result;
    }

    private String parseAttr(String express) {
        String result = "[";
        String attrExpression = express.substring(express.indexOf("(") + 1, express.lastIndexOf(")"));
        if (attrExpression.startsWith("\"") && attrExpression.endsWith("\"")) {
            attrExpression = "name=" + attrExpression.replace("\"", "");
        }
        String[] attrs = attrExpression.split(",");
        for (String attr : attrs) {
            String field = attr.substring(0, attr.indexOf("="));
            String value = attr.substring(attr.indexOf("=") + 1).replace("\"", "");
            if ("visible".equals(value) || "clickable".equals(value)) {
                value = value.toLowerCase(Locale.ROOT);
            }
            result += ("@" + field + "=\"" + value + "\" and ");
        }
        result += "]";
        return result.replace(" and ]", "]");
    }

    @Override
    public void freezeSource() {
        isFrozen = true;
    }

    @Override
    public void thawSource() {
        isFrozen = false;
    }

    @Override
    public WindowSize getScreenSize() throws SonicRespException {
        if (engine.equals(UNITY_3D) || engine.equals(COCOS_2DX_LUA)) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("jsonrpc", "2.0");
            jsonObject.put("params", Arrays.asList(true));
            jsonObject.put("id", UUID.randomUUID().toString());
            jsonObject.put("method", "GetScreenSize");
            List<Integer> result = ((JSONArray) JSONObject.parseObject(
                            pocoConnection.sendAndReceive(jsonObject))
                    .get("result"))
                    .toJavaList(Integer.class);
            return new WindowSize(result.get(0), result.get(1));
        }
        return null;
    }

}


================================================
FILE: src/main/java/org/cloud/sonic/driver/poco/service/impl/SocketClientImpl.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.poco.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.common.tool.Logger;
import org.cloud.sonic.driver.common.tool.SonicRespException;
import org.cloud.sonic.driver.poco.service.PocoConnection;
import org.cloud.sonic.driver.poco.util.PocoTool;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

public class SocketClientImpl implements PocoConnection {

    private Socket poco = null;
    private InputStream inputStream = null;
    private OutputStream outputStream = null;

    private int port;
    private Logger logger;

    public SocketClientImpl(int port, Logger logger) {
        this.port = port;
        this.logger = logger;
    }

    @Override
    public String sendAndReceive(JSONObject jsonObject) throws SonicRespException {
        byte[] data = jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8);
        byte[] header = intToByteArray(data.length);

        synchronized (SocketClientImpl.class) {
            try {
                outputStream.write(header);
                outputStream.write(data);
                outputStream.flush();
                byte[] head = new byte[4];
                byte[] buffer = new byte[8192];
                inputStream.read(head);
                int headLen = toInt(head);
                ByteBuffer rData = ByteBuffer.allocate(headLen);
                while (poco.isConnected() && !Thread.interrupted()) {

                    int realLen;
                    realLen = inputStream.read(buffer);
                    if (realLen > 0) {
                        rData.put(buffer, 0, realLen);
                    }

                    if (rData.position() == headLen) {
                        rData.flip();
                        String pocoResult = new String(rData.array(), StandardCharsets.UTF_8);
                        int subStartIndex = pocoResult.indexOf("\"result\"");
                        // when cocos is integrated there will be no id
//                        String pocoPrefix = pocoResult.substring(0, subStartIndex) + "}";
//                        if (PocoTool.checkPocoRpcResultID(pocoPrefix, jsonObject.getString("id"))) {
                            return "{" + pocoResult.substring(subStartIndex);
//                        } else {
//                            throw new SonicRespException("id not found!");
//                        }
                    }
                }
            } catch (Exception e) {
                throw new SonicRespException(e.getMessage());
            }
        }
        return null;
    }

    @Override
    public void connect() {
        int waitConnect = 0;
        while (poco == null || !poco.isConnected()) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            waitConnect++;
            if (waitConnect >= 20) {
                break;
            }
            try {
                poco = new Socket("localhost", port);
                inputStream = poco.getInputStream();
                outputStream = poco.getOutputStream();
            } catch (Exception e) {
                logger.info(e.getMessage());
            }
        }
        if (poco != null) {
            logger.info("poco socket connected.");
        } else {
            logger.info("poco socket disconnected.");
        }
    }

    @Override
    public void disconnect() {
        if (poco != null && poco.isConnected()) {
            try {
                poco.close();
                logger.info("poco socket closed.");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (inputStream != null) {
            try {
                inputStream.close();
                logger.info("poco input stream closed.");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (outputStream != null) {
            try {
                outputStream.close();
                logger.info("poco output stream closed.");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private byte[] intToByteArray(int i) {
        byte[] result = new byte[4];
        result[0] = (byte) (i & 0xff);
        result[1] = (byte) (i >> 8 & 0xff);
        result[2] = (byte) (i >> 16 & 0xff);
        result[3] = (byte) (i >> 24 & 0xff);
        return result;
    }

    private int toInt(byte[] b) {
        int res = 0;
        for (int i = 0; i < b.length; i++) {
            res += (b[i] & 0xff) << (i * 8);
        }
        return res;
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/poco/service/impl/WebSocketClientImpl.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.poco.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.common.tool.Logger;
import org.cloud.sonic.driver.common.tool.SonicRespException;
import org.cloud.sonic.driver.poco.service.PocoConnection;
import org.cloud.sonic.driver.poco.util.PocoTool;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;
import java.net.URISyntaxException;

public class WebSocketClientImpl implements PocoConnection {
    private int port;
    private Logger logger;

    private org.java_websocket.client.WebSocketClient webSocketClient;
    private String result = null;

    public WebSocketClientImpl(int port, Logger logger) {
        this.port = port;
        this.logger = logger;
    }

    @Override
    public String sendAndReceive(JSONObject jsonObject) throws SonicRespException {
        synchronized (WebSocketClientImpl.class) {
            webSocketClient.send(jsonObject.toString());
            int wait = 0;
            while (result == null) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                wait++;
                if (wait >= 20) {
                    break;
                }
            }
            if (result != null) {
                int subStartIndex = result.indexOf("\"result\"");

                String pocoPrefix = result.substring(0, subStartIndex) + "}";

                if (PocoTool.checkPocoRpcResultID(pocoPrefix, jsonObject.getString("id"))) {
                    String re = "{" + result.substring(subStartIndex);
                    result = null;
                    return re;
                } else {
                    throw new SonicRespException("id not found!");
                }
            }
        }
        return null;
    }

    @Override
    public void connect() {
        URI ws = null;
        try {
            ws = new URI("ws://localhost:" + port);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        webSocketClient = new org.java_websocket.client.WebSocketClient(ws) {
            @Override
            public void onOpen(ServerHandshake serverHandshake) {
                logger.info("poco ws connected.");
            }

            @Override
            public void onMessage(String s) {
                logger.info(s);
                result = s;
            }

            @Override
            public void onClose(int i, String s, boolean b) {
                logger.info("poco ws close.");
            }

            @Override
            public void onError(Exception e) {

            }
        };
        webSocketClient.connect();
        int waitConnect = 0;
        while (!webSocketClient.isOpen()) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            waitConnect++;
            if (waitConnect >= 20) {
                break;
            }
        }
    }

    @Override
    public void disconnect() {
        webSocketClient.close();
    }
}


================================================
FILE: src/main/java/org/cloud/sonic/driver/poco/util/PocoJsonToXml.java
================================================
/*
 *  Copyright (C) [SonicCloudOrg] Sonic Project
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package org.cloud.sonic.driver.poco.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.cloud.sonic.driver.common.tool.SonicRespException;

import java.util.Map;

public class PocoJsonToXml {
    /**
     * convert json to xml
     *
     * @param jo JSONObject
     * @return xml
     */
    public static String jsonObjToXml(JSONObject jo) throws SonicRespException {
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + jsonToXml(jo, "");
        return xml;
    }

    /**
     * json object to xml
     *
     * @param jo JSONObject
     * @param gt "\n" shifter
     * @return XML
     */
    @SuppressWarnings("rawtypes")
    private static String jsonToXml(JSONObject jo, String gt) throws SonicRespException {
        StringBuffer xmlStr = new StringBuffer();
        try {
            JSONObject payload = jo.getJSONObject("payload");
            JSONArray children = jo.getJSONArray("children");

            xmlStr.append(gt);
            xmlStr.append("<");
            String name = String.join("__", payload.get("name").toString().split(" "));

            if (name.equals("<Root>")) name = "Root";
            if (!checkName(name)) {
                name = "invalidName";
            }
            xmlStr.append(name);
            xmlStr.append(getAttrStr(payload));
            xmlStr.append(">\n");
            if (children != null) {
                for (int i = 0; i < children.size(); i++) {
                    JSONObject child = children.getJSONObject(i);
                    xmlStr.append(jsonToXml(child, gt + "\t"));
                }
            }
            addTag(xmlStr, gt, name, true);
        } catch (Exception e) {
            throw new SonicRespException(e.getMessage());
        }
        return xmlStr.toString();
    }

    public static void addTag(StringBuffer xmlStr, String gt, String tagName, boolean isItTheEnd) {
        xmlStr.append(gt);
        if (isItTheEnd) {
            xmlStr.append("</");
        } else {
            xmlStr.append("<");
        }
        xmlStr.append(tagName);
        xmlStr.append(">\n");
    }

    public static StringBuilder getAttrStr(JSONObject payload) {
        StringBuilder sb = new StringBuilder();
        sb.append(" ");
        for (Map.Entry<String, Object> stringObjectEntry : payload.entrySet()) {
            Map.Entry entry = (Map.Entry) stringObjectEntry;
            String key = entry.getKey().toString();
            String val = entry.getValue().toString();
            if (key.equals("zOrders")) {
                JSONObject zOrders = JSONObject.parseObject(val);
                sb.append(String.format("global=\"%s\" ", zOrders.get("global")));
                sb.append(String.format("local=\"%s\" ", zOrders.get("local")));
            } else if (key.equals("components")) {
                val = val.replace("\"", "");
                sb.append(String.format("%s=\"%s\" ", key, val));
            } else {
                val = escapingSpecialCharacters(val);
                sb.append(String.format("%s=\"%s\" ", k
Download .txt
gitextract_j_5ezhz9/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── maven.yml
│       └── release.yml
├── .gitignore
├── LICENSE
├── README.md
├── README_CN.md
├── pom.xml
└── src/
    ├── main/
    │   └── java/
    │       └── org/
    │           └── cloud/
    │               └── sonic/
    │                   └── driver/
    │                       ├── android/
    │                       │   ├── AndroidDriver.java
    │                       │   ├── enmus/
    │                       │   │   └── AndroidSelector.java
    │                       │   └── service/
    │                       │       ├── AndroidElement.java
    │                       │       ├── UiaClient.java
    │                       │       └── impl/
    │                       │           ├── AndroidElementImpl.java
    │                       │           └── UiaClientImpl.java
    │                       ├── common/
    │                       │   ├── enums/
    │                       │   │   └── PasteboardType.java
    │                       │   ├── models/
    │                       │   │   ├── BaseElement.java
    │                       │   │   ├── BaseResp.java
    │                       │   │   ├── Capabilities.java
    │                       │   │   ├── ElementRect.java
    │                       │   │   ├── ErrorMsg.java
    │                       │   │   ├── SessionInfo.java
    │                       │   │   └── WindowSize.java
    │                       │   └── tool/
    │                       │       ├── Logger.java
    │                       │       ├── RespHandler.java
    │                       │       └── SonicRespException.java
    │                       ├── ios/
    │                       │   ├── IOSDriver.java
    │                       │   ├── enums/
    │                       │   │   ├── ActionType.java
    │                       │   │   ├── AuthResource.java
    │                       │   │   ├── IOSSelector.java
    │                       │   │   ├── Orientation.java
    │                       │   │   ├── SystemButton.java
    │                       │   │   ├── TextKey.java
    │                       │   │   └── XCUIElementType.java
    │                       │   ├── models/
    │                       │   │   └── TouchActions.java
    │                       │   └── service/
    │                       │       ├── IOSElement.java
    │                       │       ├── WdaClient.java
    │                       │       └── impl/
    │                       │           ├── IOSElementImpl.java
    │                       │           └── WdaClientImpl.java
    │                       └── poco/
    │                           ├── PocoDriver.java
    │                           ├── enums/
    │                           │   ├── PocoEngine.java
    │                           │   └── PocoSelector.java
    │                           ├── models/
    │                           │   ├── PocoElement.java
    │                           │   └── RootElement.java
    │                           ├── service/
    │                           │   ├── PocoClient.java
    │                           │   ├── PocoConnection.java
    │                           │   └── impl/
    │                           │       ├── PocoClientImpl.java
    │                           │       ├── SocketClientImpl.java
    │                           │       └── WebSocketClientImpl.java
    │                           └── util/
    │                               ├── PocoJsonToXml.java
    │                               ├── PocoTool.java
    │                               └── PocoXYTransformer.java
    └── test/
        └── java/
            └── org/
                └── cloud/
                    └── sonic/
                        └── driver/
                            ├── android/
                            │   ├── AndroidDriverTest.java
                            │   └── service/
                            │       └── UiaClientTest.java
                            ├── common/
                            │   └── tool/
                            │       ├── RespHandlerTest.java
                            │       └── SonicRespExceptionTest.java
                            ├── ios/
                            │   ├── IOSDriverTest.java
                            │   └── service/
                            │       └── WdaClientTest.java
                            └── poco/
                                ├── PocoDriverTest.java
                                ├── PocoJsonToXmlTest.java
                                └── PocoXYTransformerTest.java
Download .txt
SYMBOL INDEX (568 symbols across 52 files)

FILE: src/main/java/org/cloud/sonic/driver/android/AndroidDriver.java
  class AndroidDriver (line 36) | public class AndroidDriver {
    method AndroidDriver (line 45) | public AndroidDriver(String url) throws SonicRespException {
    method AndroidDriver (line 56) | public AndroidDriver(String url, int timeOut) throws SonicRespException {
    method AndroidDriver (line 67) | public AndroidDriver(String url, JSONObject cap) throws SonicRespExcep...
    method AndroidDriver (line 79) | public AndroidDriver(String url, int timeOut, JSONObject cap) throws S...
    method getUiaClient (line 91) | public UiaClient getUiaClient() {
    method getSessionId (line 100) | public String getSessionId() {
    method closeDriver (line 109) | public void closeDriver() throws SonicRespException {
    method showLog (line 116) | public void showLog() {
    method disableLog (line 123) | public void disableLog() {
    method getWindowSize (line 133) | public WindowSize getWindowSize() throws SonicRespException {
    method sendKeys (line 143) | public void sendKeys(String text) throws SonicRespException {
    method sendKeys (line 154) | public void sendKeys(String text, boolean isCover) throws SonicRespExc...
    method setPasteboard (line 165) | public void setPasteboard(String contentType, String content) throws S...
    method setPasteboard (line 176) | public void setPasteboard(PasteboardType pasteboardType, String conten...
    method getPasteboard (line 187) | public byte[] getPasteboard(String contentType) throws SonicRespExcept...
    method getPasteboard (line 198) | public byte[] getPasteboard(PasteboardType pasteboardType) throws Soni...
    method getPageSource (line 208) | public String getPageSource() throws SonicRespException {
    method setDefaultFindElementInterval (line 218) | public void setDefaultFindElementInterval(Integer retry, Integer inter...
    method findElement (line 230) | public AndroidElement findElement(AndroidSelector androidSelector, Str...
    method findElement (line 241) | public AndroidElement findElement(String uiaElementID) throws SonicRes...
    method findElement (line 253) | public AndroidElement findElement(String selector, String value) throw...
    method findElement (line 266) | public AndroidElement findElement(AndroidSelector androidSelector, Str...
    method findElement (line 279) | public AndroidElement findElement(String selector, String value, Integ...
    method findElement (line 293) | public AndroidElement findElement(AndroidSelector androidSelector, Str...
    method findElement (line 307) | public AndroidElement findElement(String selector, String value, Integ...
    method findElementList (line 319) | public List<AndroidElement> findElementList(AndroidSelector androidSel...
    method findElementList (line 331) | public List<AndroidElement> findElementList(String selector, String va...
    method findElementList (line 344) | public List<AndroidElement> findElementList(AndroidSelector androidSel...
    method findElementList (line 357) | public List<AndroidElement> findElementList(String selector, String va...
    method findElementList (line 371) | public List<AndroidElement> findElementList(AndroidSelector androidSel...
    method findElementList (line 385) | public List<AndroidElement> findElementList(String selector, String va...
    method screenshot (line 395) | public byte[] screenshot() throws SonicRespException {
    method setAppiumSettings (line 405) | public void setAppiumSettings(JSONObject settings) throws SonicRespExc...
    method tap (line 416) | public void tap(int x, int y) throws SonicRespException {
    method longPress (line 428) | public void longPress(double x, double y, double ms) throws SonicRespE...
    method swipe (line 441) | public void swipe(int fromX, int fromY, int toX, int toY) throws Sonic...
    method swipe (line 455) | public void swipe(int fromX, int fromY, int toX, int toY, Integer dura...
    method drag (line 472) | public void drag(int fromX, int fromY, int toX, int toY, Integer durat...
    method touchAction (line 488) | public void touchAction(String methodType, int x, int y) throws SonicR...

FILE: src/main/java/org/cloud/sonic/driver/android/enmus/AndroidSelector.java
  type AndroidSelector (line 19) | public enum AndroidSelector {
    method AndroidSelector (line 30) | AndroidSelector(String selector) {
    method getSelector (line 34) | public String getSelector() {

FILE: src/main/java/org/cloud/sonic/driver/android/service/AndroidElement.java
  type AndroidElement (line 26) | public interface AndroidElement extends BaseElement {
    method click (line 28) | void click() throws SonicRespException;
    method sendKeys (line 30) | void sendKeys(String text) throws SonicRespException;
    method sendKeys (line 32) | void sendKeys(String text, boolean isCover) throws SonicRespException;
    method clear (line 34) | void clear() throws SonicRespException;
    method getText (line 36) | String getText() throws SonicRespException;
    method screenshot (line 38) | byte[] screenshot() throws SonicRespException;

FILE: src/main/java/org/cloud/sonic/driver/android/service/UiaClient.java
  type UiaClient (line 31) | public interface UiaClient {
    method setGlobalTimeOut (line 33) | void setGlobalTimeOut(int timeOut);
    method getRespHandler (line 35) | RespHandler getRespHandler();
    method setRespHandler (line 37) | void setRespHandler(RespHandler respHandler);
    method getLogger (line 39) | Logger getLogger();
    method showLog (line 41) | void showLog();
    method disableLog (line 43) | void disableLog();
    method getRemoteUrl (line 46) | String getRemoteUrl();
    method setRemoteUrl (line 48) | void setRemoteUrl(String remoteUrl);
    method getSessionId (line 50) | String getSessionId();
    method setSessionId (line 52) | void setSessionId(String sessionId);
    method newSession (line 54) | void newSession(JSONObject capabilities) throws SonicRespException;
    method closeSession (line 56) | void closeSession() throws SonicRespException;
    method checkSessionId (line 58) | void checkSessionId() throws SonicRespException;
    method getWindowSize (line 61) | WindowSize getWindowSize() throws SonicRespException;
    method sendKeys (line 64) | void sendKeys(String text, boolean isCover) throws SonicRespException;
    method setPasteboard (line 66) | void setPasteboard(String contentType, String content) throws SonicRes...
    method getPasteboard (line 68) | byte[] getPasteboard(String contentType) throws SonicRespException;
    method pageSource (line 71) | String pageSource() throws SonicRespException;
    method setDefaultFindElementInterval (line 74) | void setDefaultFindElementInterval(Integer retry, Integer interval);
    method findElement (line 76) | AndroidElement findElement(String selector, String value, Integer retr...
    method findElementList (line 78) | List<AndroidElement> findElementList(String selector, String value, In...
    method screenshot (line 81) | byte[] screenshot() throws SonicRespException;
    method setAppiumSettings (line 84) | void setAppiumSettings(JSONObject settings) throws SonicRespException;
    method tap (line 86) | void tap(int x, int y) throws SonicRespException;
    method longPress (line 88) | void longPress(double x, double y, double ms) throws SonicRespException;
    method swipe (line 90) | void swipe(int fromX, int fromY, int toX, int toY, Integer duration) t...
    method drag (line 92) | void drag(int fromX, int fromY, int toX, int toY, Integer duration, St...
    method touchAction (line 95) | void touchAction(String methodType, int x, int y) throws SonicRespExce...

FILE: src/main/java/org/cloud/sonic/driver/android/service/impl/AndroidElementImpl.java
  class AndroidElementImpl (line 31) | public class AndroidElementImpl implements AndroidElement {
    method AndroidElementImpl (line 36) | public AndroidElementImpl(String id, UiaClient uiaClient) {
    method click (line 42) | @Override
    method sendKeys (line 56) | @Override
    method sendKeys (line 61) | @Override
    method clear (line 79) | @Override
    method getText (line 93) | @Override
    method getAttribute (line 108) | @Override
    method getUniquelyIdentifies (line 123) | @Override
    method getRect (line 128) | @Override
    method screenshot (line 144) | @Override
    method isDisplayed (line 159) | @Override

FILE: src/main/java/org/cloud/sonic/driver/android/service/impl/UiaClientImpl.java
  class UiaClientImpl (line 35) | public class UiaClientImpl implements UiaClient {
    method UiaClientImpl (line 46) | public UiaClientImpl() {
    method checkBundleId (line 51) | private void checkBundleId(String bundleId) throws SonicRespException {
    method parseElementId (line 58) | private String parseElementId(Object o) {
    method setGlobalTimeOut (line 70) | @Override
    method getRespHandler (line 75) | @Override
    method setRespHandler (line 80) | @Override
    method getLogger (line 85) | @Override
    method showLog (line 90) | @Override
    method disableLog (line 95) | @Override
    method getRemoteUrl (line 100) | @Override
    method setRemoteUrl (line 105) | @Override
    method getSessionId (line 110) | @Override
    method setSessionId (line 115) | @Override
    method newSession (line 120) | @Override
    method closeSession (line 137) | @Override
    method checkSessionId (line 144) | @Override
    method getWindowSize (line 152) | @Override
    method sendKeys (line 168) | @Override
    method setPasteboard (line 184) | @Override
    method getPasteboard (line 200) | @Override
    method pageSource (line 217) | @Override
    method setDefaultFindElementInterval (line 230) | @Override
    method findElement (line 240) | @Override
    method findElementList (line 282) | @Override
    method screenshot (line 329) | @Override
    method setAppiumSettings (line 343) | @Override
    method tap (line 358) | @Override
    method longPress (line 374) | @Override
    method swipe (line 393) | @Override
    method drag (line 418) | @Override
    method touchAction (line 438) | @Override

FILE: src/main/java/org/cloud/sonic/driver/common/enums/PasteboardType.java
  type PasteboardType (line 19) | public enum PasteboardType {
    method PasteboardType (line 26) | PasteboardType(String pasteboardType) {
    method getType (line 30) | public String getType() {

FILE: src/main/java/org/cloud/sonic/driver/common/models/BaseElement.java
  type BaseElement (line 5) | public interface BaseElement {
    method getRect (line 6) | ElementRect getRect() throws SonicRespException;
    method getAttribute (line 8) | String getAttribute(String name) throws SonicRespException;
    method getUniquelyIdentifies (line 11) | String getUniquelyIdentifies() throws SonicRespException;
    method isDisplayed (line 20) | boolean isDisplayed() throws SonicRespException;

FILE: src/main/java/org/cloud/sonic/driver/common/models/BaseResp.java
  class BaseResp (line 22) | @Getter
    method setErr (line 29) | public void setErr(ErrorMsg err) {
    method setValue (line 33) | public void setValue(T value) {

FILE: src/main/java/org/cloud/sonic/driver/common/models/Capabilities.java
  class Capabilities (line 22) | @ToString

FILE: src/main/java/org/cloud/sonic/driver/common/models/ElementRect.java
  class ElementRect (line 23) | @Getter
    class IOSRectCenter (line 32) | @Getter
    method getCenter (line 40) | public IOSRectCenter getCenter() {

FILE: src/main/java/org/cloud/sonic/driver/common/models/ErrorMsg.java
  class ErrorMsg (line 22) | @ToString
    method getMessage (line 29) | public String getMessage() {

FILE: src/main/java/org/cloud/sonic/driver/common/models/SessionInfo.java
  class SessionInfo (line 23) | @Getter

FILE: src/main/java/org/cloud/sonic/driver/common/models/WindowSize.java
  class WindowSize (line 23) | @Getter

FILE: src/main/java/org/cloud/sonic/driver/common/tool/Logger.java
  class Logger (line 22) | public class Logger {
    method Logger (line 26) | public Logger() {
    method showLog (line 31) | public void showLog() {
    method disableLog (line 35) | public void disableLog() {
    method print (line 39) | private void print(String level, String msg, Object... args) {
    method info (line 46) | public void info(String msg, Object... args) {
    method error (line 50) | public void error(String msg, Object... args) {

FILE: src/main/java/org/cloud/sonic/driver/common/tool/RespHandler.java
  class RespHandler (line 30) | public class RespHandler {
    method setRequestTimeOut (line 34) | public void setRequestTimeOut(int timeOut) {
    method getResp (line 38) | public BaseResp getResp(HttpRequest httpRequest) throws SonicRespExcep...
    method getResp (line 42) | public BaseResp getResp(HttpRequest httpRequest, int timeout) throws S...
    method initResp (line 53) | public BaseResp initResp(String response) {
    method initHeader (line 61) | public Map<String, String> initHeader() {
    method initErrorMsg (line 67) | public BaseResp initErrorMsg(String resp) {

FILE: src/main/java/org/cloud/sonic/driver/common/tool/SonicRespException.java
  class SonicRespException (line 19) | public class SonicRespException extends Exception {
    method SonicRespException (line 20) | public SonicRespException(String message) {
    method SonicRespException (line 24) | public SonicRespException(String message, Throwable cause) {

FILE: src/main/java/org/cloud/sonic/driver/ios/IOSDriver.java
  class IOSDriver (line 37) | public class IOSDriver {
    method IOSDriver (line 46) | public IOSDriver(String url) throws SonicRespException {
    method IOSDriver (line 57) | public IOSDriver(String url, int timeOut) throws SonicRespException {
    method IOSDriver (line 68) | public IOSDriver(String url, JSONObject cap) throws SonicRespException {
    method IOSDriver (line 80) | public IOSDriver(String url, int timeOut, JSONObject cap) throws Sonic...
    method getWdaClient (line 92) | public WdaClient getWdaClient() {
    method getSessionId (line 101) | public String getSessionId() {
    method closeDriver (line 110) | public void closeDriver() throws SonicRespException {
    method showLog (line 117) | public void showLog() {
    method disableLog (line 124) | public void disableLog() {
    method getWindowSize (line 134) | public WindowSize getWindowSize() throws SonicRespException {
    method isLocked (line 144) | public boolean isLocked() throws SonicRespException {
    method lock (line 153) | public void lock() throws SonicRespException {
    method unlock (line 162) | public void unlock() throws SonicRespException {
    method tap (line 173) | public void tap(int x, int y) throws SonicRespException {
    method doubleTap (line 183) | public void doubleTap(int x, int y) throws SonicRespException {
    method longPress (line 195) | public void longPress(int x, int y, int ms) throws SonicRespException {
    method swipe (line 208) | public void swipe(int fromX, int fromY, int toX, int toY) throws Sonic...
    method swipe (line 222) | public void swipe(double fromX, double fromY, double toX, double toY, ...
    method performTouchAction (line 232) | public void performTouchAction(TouchActions touchActions) throws Sonic...
    method performTouchAction (line 236) | public void performTouchAction(TouchActions.FingerTouchAction fingerTo...
    method pressButton (line 246) | public void pressButton(String systemButton) throws SonicRespException {
    method pressButton (line 256) | public void pressButton(SystemButton systemButton) throws SonicRespExc...
    method sendKeys (line 266) | public void sendKeys(String text) throws SonicRespException {
    method sendKeys (line 277) | public void sendKeys(String text, int frequency) throws SonicRespExcep...
    method sendKeys (line 287) | public void sendKeys(TextKey text) throws SonicRespException {
    method sendKeys (line 298) | public void sendKeys(TextKey text, int frequency) throws SonicRespExce...
    method setPasteboard (line 309) | public void setPasteboard(String contentType, String content) throws S...
    method setPasteboard (line 320) | public void setPasteboard(PasteboardType pasteboardType, String conten...
    method getPasteboard (line 331) | public byte[] getPasteboard(String contentType) throws SonicRespExcept...
    method getPasteboard (line 342) | public byte[] getPasteboard(PasteboardType pasteboardType) throws Soni...
    method getPageSource (line 352) | public String getPageSource() throws SonicRespException {
    method sendSiriCommand (line 362) | public void sendSiriCommand(String command) throws SonicRespException {
    method appActivate (line 372) | public void appActivate(String bundleId) throws SonicRespException {
    method appTerminate (line 383) | public boolean appTerminate(String bundleId) throws SonicRespException {
    method appRunBackground (line 393) | public void appRunBackground(int duration) throws SonicRespException {
    method appAuthReset (line 403) | public void appAuthReset(int resource) throws SonicRespException {
    method appAuthReset (line 413) | public void appAuthReset(AuthResource authResource) throws SonicRespEx...
    method setDefaultFindElementInterval (line 423) | public void setDefaultFindElementInterval(Integer retry, Integer inter...
    method findElement (line 435) | public IOSElement findElement(IOSSelector iosSelector, String value) t...
    method findElement (line 446) | public IOSElement findElement(String wdaElementID) throws SonicRespExc...
    method findElement (line 457) | public IOSElement findElement(XCUIElementType xcuiElementType) throws ...
    method findElement (line 469) | public IOSElement findElement(String selector, String value) throws So...
    method findElement (line 482) | public IOSElement findElement(IOSSelector iosSelector, String value, I...
    method findElement (line 494) | public IOSElement findElement(XCUIElementType xcuiElementType, Integer...
    method findElement (line 507) | public IOSElement findElement(String selector, String value, Integer r...
    method findElement (line 521) | public IOSElement findElement(IOSSelector iosSelector, String value, I...
    method findElement (line 534) | public IOSElement findElement(XCUIElementType xcuiElementType, Integer...
    method findElement (line 548) | public IOSElement findElement(String selector, String value, Integer r...
    method findElementList (line 560) | public List<IOSElement> findElementList(IOSSelector iosSelector, Strin...
    method findElementList (line 571) | public List<IOSElement> findElementList(XCUIElementType xcuiElementTyp...
    method findElementList (line 583) | public List<IOSElement> findElementList(String selector, String value)...
    method findElementList (line 596) | public List<IOSElement> findElementList(IOSSelector iosSelector, Strin...
    method findElementList (line 608) | public List<IOSElement> findElementList(XCUIElementType xcuiElementTyp...
    method findElementList (line 621) | public List<IOSElement> findElementList(String selector, String value,...
    method findElementList (line 635) | public List<IOSElement> findElementList(IOSSelector iosSelector, Strin...
    method findElementList (line 648) | public List<IOSElement> findElementList(XCUIElementType xcuiElementTyp...
    method findElementList (line 662) | public List<IOSElement> findElementList(String selector, String value,...
    method activeElement (line 671) | public IOSElement activeElement() throws SonicRespException{
    method screenshot (line 681) | public byte[] screenshot() throws SonicRespException {
    method setAppiumSettings (line 691) | public void setAppiumSettings(JSONObject settings) throws SonicRespExc...
    method rotate (line 699) | public void rotate(Orientation orientation) throws SonicRespException {
    method getRotate (line 703) | public Orientation getRotate() throws SonicRespException {

FILE: src/main/java/org/cloud/sonic/driver/ios/enums/ActionType.java
  type ActionType (line 19) | public enum ActionType {
    method ActionType (line 27) | ActionType(String type) {
    method getType (line 31) | public String getType() {

FILE: src/main/java/org/cloud/sonic/driver/ios/enums/AuthResource.java
  type AuthResource (line 19) | public enum AuthResource {
    method AuthResource (line 39) | AuthResource(int resource) {
    method getResource (line 43) | public int getResource() {

FILE: src/main/java/org/cloud/sonic/driver/ios/enums/IOSSelector.java
  type IOSSelector (line 19) | public enum IOSSelector {
    method IOSSelector (line 34) | IOSSelector(String selector) {
    method getSelector (line 38) | public String getSelector() {

FILE: src/main/java/org/cloud/sonic/driver/ios/enums/Orientation.java
  type Orientation (line 8) | public enum Orientation {
    method Orientation (line 17) | Orientation(int orientation) {
    method getValue (line 21) | public JSONObject getValue() {

FILE: src/main/java/org/cloud/sonic/driver/ios/enums/SystemButton.java
  type SystemButton (line 19) | public enum SystemButton {
    method SystemButton (line 26) | SystemButton(String button) {
    method getButton (line 30) | public String getButton() {

FILE: src/main/java/org/cloud/sonic/driver/ios/enums/TextKey.java
  type TextKey (line 19) | public enum TextKey {
    method TextKey (line 25) | TextKey(String key) {
    method getKey (line 29) | public String getKey() {

FILE: src/main/java/org/cloud/sonic/driver/ios/enums/XCUIElementType.java
  type XCUIElementType (line 19) | public enum XCUIElementType {
    method XCUIElementType (line 119) | XCUIElementType(String type) {
    method getType (line 123) | public String getType() {

FILE: src/main/java/org/cloud/sonic/driver/ios/models/TouchActions.java
  class TouchActions (line 28) | @Getter
    class FingerTouchAction (line 34) | @Getter
      method FingerTouchAction (line 42) | public FingerTouchAction(String fingerName) {
      method FingerTouchAction (line 47) | public FingerTouchAction() {
      method press (line 51) | public FingerTouchAction press(int x, int y) {
      method wait (line 58) | public FingerTouchAction wait(int ms) {
      method move (line 65) | public FingerTouchAction move(int x, int y) {
      method release (line 73) | public FingerTouchAction release() {
    class TouchAction (line 80) | @Getter
      method TouchAction (line 85) | public TouchAction(ActionType actionType) {
    class MoveAction (line 90) | @Getter
      method MoveAction (line 96) | public MoveAction() {
    class PauseAction (line 101) | @Getter
      method PauseAction (line 106) | public PauseAction() {
    method TouchActions (line 111) | public TouchActions() {
    method TouchActions (line 115) | public TouchActions(FingerTouchAction finger) {
    method finger (line 120) | public FingerTouchAction finger(String name) {

FILE: src/main/java/org/cloud/sonic/driver/ios/service/IOSElement.java
  type IOSElement (line 26) | public interface IOSElement extends BaseElement {
    method click (line 28) | void click() throws SonicRespException;
    method sendKeys (line 30) | void sendKeys(String text) throws SonicRespException;
    method sendKeys (line 32) | void sendKeys(String text, int frequency) throws SonicRespException;
    method clear (line 34) | void clear() throws SonicRespException;
    method getText (line 36) | String getText() throws SonicRespException;
    method screenshot (line 38) | byte[] screenshot() throws SonicRespException;

FILE: src/main/java/org/cloud/sonic/driver/ios/service/WdaClient.java
  type WdaClient (line 33) | public interface WdaClient {
    method setGlobalTimeOut (line 35) | void setGlobalTimeOut(int timeOut);
    method getRespHandler (line 37) | RespHandler getRespHandler();
    method setRespHandler (line 39) | void setRespHandler(RespHandler respHandler);
    method getLogger (line 41) | Logger getLogger();
    method showLog (line 43) | void showLog();
    method disableLog (line 45) | void disableLog();
    method getRemoteUrl (line 48) | String getRemoteUrl();
    method setRemoteUrl (line 50) | void setRemoteUrl(String remoteUrl);
    method getSessionId (line 52) | String getSessionId();
    method setSessionId (line 54) | void setSessionId(String sessionId);
    method newSession (line 56) | void newSession(JSONObject capabilities) throws SonicRespException;
    method closeSession (line 58) | void closeSession() throws SonicRespException;
    method checkSessionId (line 60) | void checkSessionId() throws SonicRespException;
    method getWindowSize (line 63) | WindowSize getWindowSize() throws SonicRespException;
    method isLocked (line 66) | boolean isLocked() throws SonicRespException;
    method lock (line 68) | void lock() throws SonicRespException;
    method unlock (line 70) | void unlock() throws SonicRespException;
    method performTouchAction (line 73) | void performTouchAction(TouchActions touchActions) throws SonicRespExc...
    method pressButton (line 76) | void pressButton(String buttonName) throws SonicRespException;
    method doubleTap (line 78) | void doubleTap(int x, int y) throws SonicRespException;
    method sendKeys (line 81) | void sendKeys(String text, Integer frequency) throws SonicRespException;
    method setPasteboard (line 83) | void setPasteboard(String contentType, String content) throws SonicRes...
    method getPasteboard (line 85) | byte[] getPasteboard(String contentType) throws SonicRespException;
    method pageSource (line 88) | String pageSource() throws SonicRespException;
    method sendSiriCommand (line 91) | void sendSiriCommand(String command) throws SonicRespException;
    method appActivate (line 94) | void appActivate(String bundleId) throws SonicRespException;
    method appTerminate (line 96) | boolean appTerminate(String bundleId) throws SonicRespException;
    method appRunBackground (line 98) | void appRunBackground(int duration) throws SonicRespException;
    method appAuthReset (line 100) | void appAuthReset(int resource) throws SonicRespException;
    method setDefaultFindElementInterval (line 103) | void setDefaultFindElementInterval(Integer retry, Integer interval);
    method findElement (line 105) | IOSElement findElement(String selector, String value, Integer retry, I...
    method findElementList (line 107) | List<IOSElement> findElementList(String selector, String value, Intege...
    method activeElement (line 110) | IOSElement activeElement() throws SonicRespException;
    method screenshot (line 113) | byte[] screenshot() throws SonicRespException;
    method setAppiumSettings (line 116) | void setAppiumSettings(JSONObject settings) throws SonicRespException;
    method swipe (line 118) | void swipe(double fromX, double fromY, double toX, double toY, double ...
    method rotate (line 119) | void rotate(Orientation orientation) throws SonicRespException;
    method getRotate (line 120) | Orientation getRotate() throws SonicRespException;

FILE: src/main/java/org/cloud/sonic/driver/ios/service/impl/IOSElementImpl.java
  class IOSElementImpl (line 31) | public class IOSElementImpl implements IOSElement {
    method IOSElementImpl (line 36) | public IOSElementImpl(String id, WdaClient wdaClient) {
    method click (line 42) | @Override
    method sendKeys (line 56) | @Override
    method sendKeys (line 61) | @Override
    method clear (line 79) | @Override
    method getText (line 93) | @Override
    method getRect (line 108) | @Override
    method getAttribute (line 124) | @Override
    method getUniquelyIdentifies (line 139) | @Override
    method screenshot (line 144) | @Override
    method isDisplayed (line 159) | @Override

FILE: src/main/java/org/cloud/sonic/driver/ios/service/impl/WdaClientImpl.java
  class WdaClientImpl (line 38) | public class WdaClientImpl implements WdaClient {
    method WdaClientImpl (line 48) | public WdaClientImpl() {
    method checkBundleId (line 53) | private void checkBundleId(String bundleId) throws SonicRespException {
    method parseElementId (line 60) | private String parseElementId(Object o) {
    method setGlobalTimeOut (line 72) | @Override
    method getRespHandler (line 77) | @Override
    method setRespHandler (line 82) | @Override
    method getLogger (line 87) | @Override
    method showLog (line 92) | @Override
    method disableLog (line 97) | @Override
    method getRemoteUrl (line 102) | @Override
    method setRemoteUrl (line 107) | @Override
    method getSessionId (line 112) | @Override
    method setSessionId (line 117) | @Override
    method newSession (line 122) | @Override
    method closeSession (line 140) | @Override
    method checkSessionId (line 147) | @Override
    method getWindowSize (line 155) | @Override
    method isLocked (line 168) | @Override
    method lock (line 181) | @Override
    method unlock (line 193) | @Override
    method performTouchAction (line 205) | @Override
    method pressButton (line 218) | @Override
    method doubleTap (line 233) | @Override
    method sendKeys (line 250) | @Override
    method setPasteboard (line 266) | @Override
    method getPasteboard (line 282) | @Override
    method pageSource (line 299) | @Override
    method sendSiriCommand (line 312) | @Override
    method appActivate (line 332) | @Override
    method appTerminate (line 348) | @Override
    method appRunBackground (line 365) | @Override
    method appAuthReset (line 380) | @Override
    method setDefaultFindElementInterval (line 395) | @Override
    method findElement (line 405) | @Override
    method findElementList (line 447) | @Override
    method activeElement (line 494) | @Override
    method screenshot (line 520) | @Override
    method setAppiumSettings (line 534) | @Override
    method swipe (line 549) | @Override
    method rotate (line 574) | @Override
    method getRotate (line 586) | @Override

FILE: src/main/java/org/cloud/sonic/driver/poco/PocoDriver.java
  class PocoDriver (line 34) | public class PocoDriver {
    method PocoDriver (line 42) | public PocoDriver(PocoEngine pocoEngine) {
    method PocoDriver (line 52) | public PocoDriver(PocoEngine pocoEngine, int port) {
    method closeDriver (line 60) | public void closeDriver() {
    method showLog (line 67) | public void showLog() {
    method disableLog (line 74) | public void disableLog() {
    method getPageSource (line 84) | public PocoElement getPageSource() throws SonicRespException {
    method getPageSourceForJsonString (line 94) | public String getPageSourceForJsonString() throws SonicRespException {
    method getPageSourceForXmlElement (line 104) | public Element getPageSourceForXmlElement() throws SonicRespException {
    method findElements (line 114) | public List<PocoElement> findElements(String selector, String expressi...
    method findElements (line 124) | public List<PocoElement> findElements(PocoSelector selector, String ex...
    method findElement (line 134) | public PocoElement findElement(String selector, String expression) thr...
    method findElement (line 144) | public PocoElement findElement(PocoSelector selector, String expressio...
    method freezeSource (line 151) | public void freezeSource() {
    method thawSource (line 158) | public void thawSource() {
    method getScreenSize (line 168) | public WindowSize getScreenSize() throws SonicRespException {

FILE: src/main/java/org/cloud/sonic/driver/poco/enums/PocoEngine.java
  type PocoEngine (line 19) | public enum PocoEngine {
    method PocoEngine (line 31) | PocoEngine(String name, int defaultPort) {
    method getDefaultPort (line 36) | public int getDefaultPort() {

FILE: src/main/java/org/cloud/sonic/driver/poco/enums/PocoSelector.java
  type PocoSelector (line 19) | public enum PocoSelector {
    method PocoSelector (line 26) | PocoSelector(String selector) {
    method getSelector (line 30) | public String getSelector() {

FILE: src/main/java/org/cloud/sonic/driver/poco/models/PocoElement.java
  class PocoElement (line 32) | @Getter
    method getRect (line 44) | @Override
    method getAttribute (line 53) | @Override
    method getUniquelyIdentifies (line 58) | @Override
    method isDisplayed (line 63) | @Override
    class Payload (line 68) | @Getter
      class ZOrders (line 89) | @Getter
        method ZOrders (line 96) | public ZOrders() {
      method Payload (line 100) | public Payload() {
    method PocoElement (line 105) | public PocoElement(RootElement root) {
    method PocoElement (line 112) | public PocoElement(RootElement root, Element currentNodeXmlElement) {
    method getPayload (line 119) | public Payload getPayload() {
    method getChildren (line 132) | public List<PocoElement> getChildren() {
    method parseXmlNode (line 157) | public void parseXmlNode(Element xmlNode) {
    method parseFloatAttrList (line 193) | private List<Float> parseFloatAttrList(String floatAttrStr) {
    method currentTheNodeExists (line 203) | public Boolean currentTheNodeExists() {
    method getParentNode (line 207) | public PocoElement getParentNode() {

FILE: src/main/java/org/cloud/sonic/driver/poco/models/RootElement.java
  class RootElement (line 21) | public class RootElement {
    method RootElement (line 25) | public RootElement() {
    method RootElement (line 28) | public RootElement(Element XmlElement) {
    method RootElement (line 32) | public RootElement(Element XmlElement, long version) {
    method getXmlElement (line 38) | public Element getXmlElement() {
    method setXmlElement (line 42) | public void setXmlElement(Element xmlElement) {
    method getVersion (line 46) | public long getVersion() {
    method setVersion (line 50) | public void setVersion(long version) {
    method updateVersion (line 54) | public synchronized void updateVersion(Element rootXmlElement) {

FILE: src/main/java/org/cloud/sonic/driver/poco/service/PocoClient.java
  type PocoClient (line 32) | public interface PocoClient {
    method getLogger (line 34) | Logger getLogger();
    method showLog (line 36) | void showLog();
    method disableLog (line 38) | void disableLog();
    method newClient (line 41) | void newClient(PocoEngine engine, int port);
    method closeClient (line 43) | void closeClient();
    method pageSource (line 46) | PocoElement pageSource() throws SonicRespException;
    method pageSourceForJsonString (line 48) | String pageSourceForJsonString() throws SonicRespException;
    method pageSourceForXmlElement (line 50) | Element pageSourceForXmlElement() throws SonicRespException;
    method findElement (line 52) | PocoElement findElement(String selector, String expression) throws Son...
    method findElements (line 54) | List<PocoElement> findElements(String selector, String expression) thr...
    method freezeSource (line 56) | void freezeSource();
    method thawSource (line 58) | void thawSource();
    method getScreenSize (line 61) | WindowSize getScreenSize() throws SonicRespException;

FILE: src/main/java/org/cloud/sonic/driver/poco/service/PocoConnection.java
  type PocoConnection (line 22) | public interface PocoConnection {
    method connect (line 23) | void connect();
    method disconnect (line 25) | void disconnect();
    method sendAndReceive (line 27) | String sendAndReceive(JSONObject jsonObject) throws SonicRespException;

FILE: src/main/java/org/cloud/sonic/driver/poco/service/impl/PocoClientImpl.java
  class PocoClientImpl (line 40) | public class PocoClientImpl implements PocoClient {
    method PocoClientImpl (line 49) | public PocoClientImpl() {
    method getLogger (line 54) | @Override
    method showLog (line 59) | @Override
    method disableLog (line 64) | @Override
    method newClient (line 69) | @Override
    method closeClient (line 80) | @Override
    method pageSource (line 85) | @Override
    method pageSourceForJsonString (line 91) | @Override
    method pageSourceForXmlElement (line 108) | @Override
    method findElement (line 119) | @Override
    method findElements (line 125) | @Override
    method parseAttr (line 166) | private String parseAttr(String express) {
    method freezeSource (line 185) | @Override
    method thawSource (line 190) | @Override
    method getScreenSize (line 195) | @Override

FILE: src/main/java/org/cloud/sonic/driver/poco/service/impl/SocketClientImpl.java
  class SocketClientImpl (line 32) | public class SocketClientImpl implements PocoConnection {
    method SocketClientImpl (line 41) | public SocketClientImpl(int port, Logger logger) {
    method sendAndReceive (line 46) | @Override
    method connect (line 89) | @Override
    method disconnect (line 117) | @Override
    method intToByteArray (line 145) | private byte[] intToByteArray(int i) {
    method toInt (line 154) | private int toInt(byte[] b) {

FILE: src/main/java/org/cloud/sonic/driver/poco/service/impl/WebSocketClientImpl.java
  class WebSocketClientImpl (line 29) | public class WebSocketClientImpl implements PocoConnection {
    method WebSocketClientImpl (line 36) | public WebSocketClientImpl(int port, Logger logger) {
    method sendAndReceive (line 41) | @Override
    method connect (line 74) | @Override
    method disconnect (line 119) | @Override

FILE: src/main/java/org/cloud/sonic/driver/poco/util/PocoJsonToXml.java
  class PocoJsonToXml (line 25) | public class PocoJsonToXml {
    method jsonObjToXml (line 32) | public static String jsonObjToXml(JSONObject jo) throws SonicRespExcep...
    method jsonToXml (line 44) | @SuppressWarnings("rawtypes")
    method addTag (line 75) | public static void addTag(StringBuffer xmlStr, String gt, String tagNa...
    method getAttrStr (line 86) | public static StringBuilder getAttrStr(JSONObject payload) {
    method checkName (line 108) | private static boolean checkName(String name) {
    method escapingSpecialCharacters (line 119) | private static String escapingSpecialCharacters(String originStr) {

FILE: src/main/java/org/cloud/sonic/driver/poco/util/PocoTool.java
  class PocoTool (line 21) | public class PocoTool {
    method checkPocoRpcResultID (line 22) | public static boolean checkPocoRpcResultID(String pocoRpcResult, Strin...

FILE: src/main/java/org/cloud/sonic/driver/poco/util/PocoXYTransformer.java
  class PocoXYTransformer (line 22) | public class PocoXYTransformer {
    method PocoTransformerVertical (line 35) | public static double[] PocoTransformerVertical(double x, double y, dou...
    method VerticalTransformerPoco (line 63) | public static double[] VerticalTransformerPoco(double x, double y, dou...

FILE: src/test/java/org/cloud/sonic/driver/android/AndroidDriverTest.java
  class AndroidDriverTest (line 19) | @RunWith(Parameterized.class)
    method data (line 24) | @Parameterized.Parameters
    method before (line 29) | @Before
    method beforeClass (line 34) | @BeforeClass
    method afterClass (line 52) | @AfterClass
    method testSession (line 57) | @Test
    method testSource (line 83) | @Test
    method testGetWindowSize (line 88) | @Test
    method testClipboard (line 96) | @Test
    method testFindElement (line 102) | @Test
    method testFindElementList (line 142) | @Test
    method testScreenshot (line 148) | @Test
    method testSetAppiumSettings (line 158) | @Test
    method testSwipeAction (line 163) | @Test
    method testTapAction (line 171) | @Test
    method testLongPressAction (line 180) | @Test
    method testDragAction (line 191) | @Test
    method testTouchActionDown (line 197) | @Test
    method testTouchActionMove (line 203) | @Test
    method testTouchActionUp (line 209) | @Test

FILE: src/test/java/org/cloud/sonic/driver/android/service/UiaClientTest.java
  class UiaClientTest (line 18) | public class UiaClientTest {
    method before (line 23) | @BeforeClass
    method testGetWindowSize (line 39) | @Test
    method testSendKeys (line 53) | @Test
    method testSetPasteboard (line 66) | @Test
    method testGetPasteboard (line 79) | @Test
    method testPageSource (line 92) | @Test
    method testFindElement (line 105) | @Test
    method testFindElements (line 118) | @Test
    method testSetAppiumSettings (line 131) | @Test
    method testScreenShot (line 144) | @Test

FILE: src/test/java/org/cloud/sonic/driver/common/tool/RespHandlerTest.java
  class RespHandlerTest (line 7) | public class RespHandlerTest {
    method testTimeOut (line 8) | @Test

FILE: src/test/java/org/cloud/sonic/driver/common/tool/SonicRespExceptionTest.java
  class SonicRespExceptionTest (line 22) | public class SonicRespExceptionTest {
    method testSonicRespExceptionTest (line 24) | @Test
    method testSonicRespExceptionTestWithMsg (line 30) | @Test

FILE: src/test/java/org/cloud/sonic/driver/ios/IOSDriverTest.java
  class IOSDriverTest (line 37) | @RunWith(Parameterized.class)
    method data (line 42) | @Parameterized.Parameters
    method before (line 47) | @Before
    method beforeClass (line 52) | @BeforeClass
    method testApp (line 75) | @Test
    method testSiriAndSendKeys (line 102) | @Test
    method testPasteboard (line 131) | @Test
    method testSwipe (line 143) | @Test
    method testSwipeWithTime (line 150) | @Test
    method testTap (line 155) | @Test
    method testLongPress (line 162) | @Test
    method testPerformTouchAction (line 168) | @Test
    method testMultiFingerTouchAction (line 175) | @Test
    method testPressButton (line 192) | @Test
    method testGetPageSource (line 203) | @Test
    method testLock (line 208) | @Test
    method testSession (line 216) | @Test
    method testFindElement (line 242) | @Test
    method testFindElementList (line 290) | @Test
    method testScreenshot (line 297) | @Test
    method testGetWindowSize (line 307) | @Test
    method testSetAppiumSettings (line 315) | @Test
    method testIsDisplayed (line 320) | @Test
    method testDoubleTap (line 332) | @Test
    method testActiveElement (line 338) | @Test
    method testOrientation (line 345) | @Test
    method after (line 357) | @AfterClass

FILE: src/test/java/org/cloud/sonic/driver/ios/service/WdaClientTest.java
  class WdaClientTest (line 35) | public class WdaClientTest {
    method before (line 40) | @BeforeClass
    method testGetWindowSize (line 56) | @Test
    method testLocked (line 69) | @Test
    method testLock (line 82) | @Test
    method testUnLock (line 95) | @Test
    method testPerformTouchAction (line 108) | @Test
    method testSendKeys (line 121) | @Test
    method testSetPasteboard (line 134) | @Test
    method testGetPasteboard (line 147) | @Test
    method testPageSource (line 160) | @Test
    method testPressButton (line 173) | @Test
    method testSiriCommand (line 186) | @Test
    method testAppActivate (line 199) | @Test
    method testAppTerminate (line 212) | @Test
    method testAppRunBackground (line 225) | @Test
    method testAppAuthReset (line 238) | @Test
    method testFindElement (line 251) | @Test
    method testFindElements (line 264) | @Test
    method testSetAppiumSettings (line 277) | @Test
    method testScreenShot (line 290) | @Test

FILE: src/test/java/org/cloud/sonic/driver/poco/PocoDriverTest.java
  class PocoDriverTest (line 15) | public class PocoDriverTest {
    method beforeClass (line 18) | @BeforeClass
    method testPageSource (line 25) | @Test
    method testFindElement (line 33) | @Test
    method testFreeze (line 47) | @Test
    method testWindowsSize (line 61) | @Test
    method testNodeExist (line 68) | @Test
    method testGetParent (line 78) | @Test
    method testElementGetChild (line 89) | @Test
    method testUpdateRootCase (line 97) | @Test
    method testGetAttribute (line 113) | @Test
    method afterClass (line 121) | @AfterClass

FILE: src/test/java/org/cloud/sonic/driver/poco/PocoJsonToXmlTest.java
  class PocoJsonToXmlTest (line 17) | public class PocoJsonToXmlTest {
    method testToXml (line 19) | @Test
    method mockPocoResultTest (line 24) | @Test

FILE: src/test/java/org/cloud/sonic/driver/poco/PocoXYTransformerTest.java
  class PocoXYTransformerTest (line 7) | public class PocoXYTransformerTest {
    method testOriToUP (line 10) | @Test
    method testUPToOri (line 26) | @Test
Condensed preview — 60 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (277K chars).
[
  {
    "path": ".github/dependabot.yml",
    "chars": 109,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"maven\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/maven.yml",
    "chars": 361,
    "preview": "name: maven compile test\n\non: [pull_request]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 3391,
    "preview": "# name: release to github\n\n# on:\n#   push:\n#     tags:\n#       - \"*.*.*\"\n\n# jobs:\n#   doc:\n#     runs-on: ubuntu-latest\n"
  },
  {
    "path": ".gitignore",
    "chars": 51,
    "preview": "/.idea/\n/target/\n/logs/\n*.iml\n*/.DS_Store\n.DS_Store"
  },
  {
    "path": "LICENSE",
    "chars": 11356,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 3711,
    "preview": "<p align=\"center\">\n  <img width=\"80px\" src=\"https://raw.githubusercontent.com/SonicCloudOrg/sonic-server/main/logo.png\">"
  },
  {
    "path": "README_CN.md",
    "chars": 2967,
    "preview": "<p align=\"center\">\n  <img width=\"80px\" src=\"https://raw.githubusercontent.com/SonicCloudOrg/sonic-server/main/logo.png\">"
  },
  {
    "path": "pom.xml",
    "chars": 7617,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/AndroidDriver.java",
    "chars": 13717,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/enmus/AndroidSelector.java",
    "chars": 1038,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/service/AndroidElement.java",
    "chars": 1229,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/service/UiaClient.java",
    "chars": 3026,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/service/impl/AndroidElementImpl.java",
    "chars": 6454,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/android/service/impl/UiaClientImpl.java",
    "chars": 18095,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/enums/PasteboardType.java",
    "chars": 947,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/BaseElement.java",
    "chars": 681,
    "preview": "package org.cloud.sonic.driver.common.models;\n\nimport org.cloud.sonic.driver.common.tool.SonicRespException;\n\npublic int"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/BaseResp.java",
    "chars": 1005,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/Capabilities.java",
    "chars": 923,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/ElementRect.java",
    "chars": 1138,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/ErrorMsg.java",
    "chars": 938,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/SessionInfo.java",
    "chars": 892,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/models/WindowSize.java",
    "chars": 869,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/tool/Logger.java",
    "chars": 1532,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/tool/RespHandler.java",
    "chars": 2620,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/common/tool/SonicRespException.java",
    "chars": 908,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/IOSDriver.java",
    "chars": 18260,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/ActionType.java",
    "chars": 947,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/AuthResource.java",
    "chars": 1288,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/IOSSelector.java",
    "chars": 1142,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/Orientation.java",
    "chars": 918,
    "preview": "package org.cloud.sonic.driver.ios.enums;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\n\nim"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/SystemButton.java",
    "chars": 943,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/TextKey.java",
    "chars": 887,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/enums/XCUIElementType.java",
    "chars": 4419,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/models/TouchActions.java",
    "chars": 3395,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/service/IOSElement.java",
    "chars": 1219,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/service/WdaClient.java",
    "chars": 3776,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/service/impl/IOSElementImpl.java",
    "chars": 6907,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/ios/service/impl/WdaClientImpl.java",
    "chars": 23844,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/PocoDriver.java",
    "chars": 4163,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/enums/PocoEngine.java",
    "chars": 1204,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/enums/PocoSelector.java",
    "chars": 951,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/models/PocoElement.java",
    "chars": 7210,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/models/RootElement.java",
    "chars": 1499,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/service/PocoClient.java",
    "chars": 1798,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/service/PocoConnection.java",
    "chars": 934,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/service/impl/PocoClientImpl.java",
    "chars": 7651,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/service/impl/SocketClientImpl.java",
    "chars": 5445,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/service/impl/WebSocketClientImpl.java",
    "chars": 3813,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/util/PocoJsonToXml.java",
    "chars": 4845,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/util/PocoTool.java",
    "chars": 951,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/main/java/org/cloud/sonic/driver/poco/util/PocoXYTransformer.java",
    "chars": 2928,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/android/AndroidDriverTest.java",
    "chars": 8229,
    "preview": "package org.cloud.sonic.driver.android;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.android.e"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/android/service/UiaClientTest.java",
    "chars": 5192,
    "preview": "package org.cloud.sonic.driver.android.service;\n\nimport com.alibaba.fastjson.JSONObject;\nimport org.cloud.sonic.driver.a"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/common/tool/RespHandlerTest.java",
    "chars": 669,
    "preview": "package org.cloud.sonic.driver.common.tool;\n\nimport cn.hutool.http.HttpUtil;\nimport org.junit.Assert;\nimport org.junit.T"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/common/tool/SonicRespExceptionTest.java",
    "chars": 1218,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/ios/IOSDriverTest.java",
    "chars": 14320,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/ios/service/WdaClientTest.java",
    "chars": 9745,
    "preview": "/*\n *  Copyright (C) [SonicCloudOrg] Sonic Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\")"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/poco/PocoDriverTest.java",
    "chars": 5119,
    "preview": "package org.cloud.sonic.driver.poco;\n\nimport org.cloud.sonic.driver.common.models.WindowSize;\nimport org.cloud.sonic.dri"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/poco/PocoJsonToXmlTest.java",
    "chars": 14617,
    "preview": "package org.cloud.sonic.driver.poco;\n\nimport com.alibaba.fastjson.JSON;\nimport org.cloud.sonic.driver.common.tool.SonicR"
  },
  {
    "path": "src/test/java/org/cloud/sonic/driver/poco/PocoXYTransformerTest.java",
    "chars": 1562,
    "preview": "package org.cloud.sonic.driver.poco;\n\nimport org.cloud.sonic.driver.poco.util.PocoXYTransformer;\nimport org.junit.Assert"
  }
]

About this extraction

This page contains the full source code of the SonicCloudOrg/sonic-driver-core GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 60 files (251.5 KB), approximately 61.9k tokens, and a symbol index with 568 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!