├── .remarkrc
├── jitpack.yml
├── xyo-android-library
├── consumer-rules.pro
├── .gitignore
├── version.properties
├── .settings
│ └── org.eclipse.buildship.core.prefs
├── src
│ └── main
│ │ ├── res
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── java
│ │ └── network
│ │ │ └── xyo
│ │ │ └── sdk
│ │ │ ├── XyoSdk.kt
│ │ │ ├── XyoServer.kt
│ │ │ ├── XyoNetwork.kt
│ │ │ ├── XyoNode.kt
│ │ │ ├── XyoClient.kt
│ │ │ ├── XyoTcpIpServer.kt
│ │ │ ├── XyoTcpIpNetwork.kt
│ │ │ ├── XyoBleNetwork.kt
│ │ │ ├── bluetooth
│ │ │ ├── XyoUuids.kt
│ │ │ ├── packet
│ │ │ │ ├── XyoInputStream.kt
│ │ │ │ ├── XyoBluetoothOutgoingPacket.kt
│ │ │ │ └── XyoBluetoothIncomingPacket.kt
│ │ │ ├── client
│ │ │ │ ├── XyoBridgeX.kt
│ │ │ │ ├── XyoIosAppX.kt
│ │ │ │ ├── XyoAndroidAppX.kt
│ │ │ │ ├── XyoBluetoothClientPipe.kt
│ │ │ │ ├── XyoSentinelX.kt
│ │ │ │ └── XyoBluetoothClient.kt
│ │ │ ├── XyoBleSdk.kt
│ │ │ ├── node
│ │ │ │ └── XyoBleNode.kt
│ │ │ └── advertiser
│ │ │ │ └── XyoBluetoothAdvertiser.kt
│ │ │ ├── XyoBoundWitnessTarget.kt
│ │ │ ├── XyoBleServer.kt
│ │ │ ├── XyoSnappyDBStorageProvider.kt
│ │ │ ├── XyoTcpIpClient.kt
│ │ │ ├── Base58.kt
│ │ │ ├── XyoBleClient.kt
│ │ │ └── XyoNodeBuilder.kt
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── xyo-android-sample
├── .gitignore
├── .settings
│ └── org.eclipse.buildship.core.prefs
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── src
│ └── main
│ │ ├── res
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── styles.xml
│ │ │ └── strings.xml
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── drawable
│ │ │ ├── ic_dashboard_black_24dp.xml
│ │ │ └── ic_launcher_background.xml
│ │ ├── menu
│ │ │ └── bottom_nav_menu.xml
│ │ ├── layout
│ │ │ ├── fragment_tcpip_server.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── fragment_ble_server.xml
│ │ │ ├── fragment_tcpip_client.xml
│ │ │ └── fragment_ble_client.xml
│ │ ├── navigation
│ │ │ └── mobile_navigation.xml
│ │ └── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ └── network
│ │ │ └── xyo
│ │ │ └── sdk
│ │ │ └── sample
│ │ │ ├── UIThread.kt
│ │ │ ├── ui
│ │ │ ├── tcpip_server
│ │ │ │ └── TcpIpServerFragment.kt
│ │ │ ├── ble_server
│ │ │ │ └── BleServerFragment.kt
│ │ │ ├── tcpip_client
│ │ │ │ └── TcpIpClientFragment.kt
│ │ │ └── ble_client
│ │ │ │ └── BleClientFragment.kt
│ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
├── build.gradle
├── gradlew.bat
└── gradlew
├── .vscode
└── settings.json
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .github
├── workflows
│ ├── minorBuild.yml
│ ├── build.yml
│ ├── prepRelease.yml
│ └── release.yml
└── ISSUE_TEMPLATE
│ ├── documentation-needed.md
│ ├── feature_request.md
│ └── bug_report.md
├── .settings
└── org.eclipse.buildship.core.prefs
├── .gitignore
├── sonar-project.properties
├── gradle.properties
├── gradlew.bat
├── gradlew
├── LICENSE
└── README.md
/.remarkrc:
--------------------------------------------------------------------------------
1 | {
2 |
3 | }
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk11
3 |
--------------------------------------------------------------------------------
/xyo-android-library/consumer-rules.pro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/xyo-android-library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/xyo-android-sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "java.configuration.updateBuildConfiguration": "automatic"
3 | }
--------------------------------------------------------------------------------
/xyo-android-library/version.properties:
--------------------------------------------------------------------------------
1 | #Fri Dec 03 15:15:41 PST 2021
2 | VERSION_PATCH=44
3 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':xyo-android-sample', ':xyo-android-library'
2 | rootProject.name='sdk-xyo-android'
3 |
--------------------------------------------------------------------------------
/xyo-android-library/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | connection.project.dir=..
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/xyo-android-sample/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | connection.project.dir=..
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/xyo-android-library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | xyo-android-library
3 |
4 |
--------------------------------------------------------------------------------
/xyo-android-sample/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/xyo-android-sample/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/xyo-android-sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/xyo-android-sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/xyo-android-sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/xyo-android-sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/xyo-android-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/xyo-android-sample/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/xyo-android-sample/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/xyo-android-sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/xyo-android-sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XYOracleNetwork-v1/sdk-xyo-android/HEAD/xyo-android-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoSdk.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import network.xyo.base.XYBase
3 |
4 | class XyoSdk : XYBase() {
5 | companion object {
6 | val nodes = mutableListOf()
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Oct 13 10:29:53 PDT 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
7 |
--------------------------------------------------------------------------------
/xyo-android-sample/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Apr 13 13:11:06 PDT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoServer.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalog
3 | import network.xyo.sdkcorekotlin.node.XyoRelayNode
4 |
5 | abstract class XyoServer(relayNode: XyoRelayNode, procedureCatalog: XyoProcedureCatalog) : XyoBoundWitnessTarget(relayNode, procedureCatalog)
6 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoNetwork.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import network.xyo.base.XYBase
3 |
4 | abstract class XyoNetwork(val type: Type) : XYBase() {
5 |
6 | enum class Type {
7 | BluetoothLE,
8 | TcpIp,
9 | Other
10 | }
11 |
12 | abstract val client: XyoClient
13 | abstract val server: XyoServer
14 | }
15 |
--------------------------------------------------------------------------------
/.github/workflows/minorBuild.yml:
--------------------------------------------------------------------------------
1 | name: Minor Branch Build
2 |
3 | on:
4 | push:
5 | branches-ignore:
6 | - 'develop'
7 | - 'release'
8 | - 'master'
9 |
10 | jobs:
11 | build:
12 |
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: pre-build
17 | run: chmod +x ./gradlew
18 | - name: build
19 | run: ./gradlew assemble
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'develop'
7 | pull_request:
8 | branches:
9 | - 'master'
10 |
11 | jobs:
12 | build:
13 |
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: pre-build
18 | run: chmod +x ./gradlew
19 | - name: build
20 | run: ./gradlew clean assemble
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/drawable/ic_dashboard_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoNode.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import network.xyo.base.XYBase
3 |
4 | class XyoNode(val networks: Map) : XYBase() {
5 | fun setAllListeners(name: String, listener: XyoBoundWitnessTarget.Listener) {
6 | networks.forEach {
7 | it.value.client.listeners[name] = listener
8 | it.value.server.listeners[name] = listener
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | arguments=
2 | auto.sync=false
3 | build.scans.enabled=false
4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
5 | connection.project.dir=
6 | eclipse.preferences.version=1
7 | gradle.user.home=
8 | java.home=/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home
9 | jvm.arguments=
10 | offline.mode=false
11 | override.workspace.settings=true
12 | show.console.view=true
13 | show.executions.view=true
14 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows thumbnail db
2 | Thumbs.db
3 |
4 | # OSX files
5 | .DS_Store
6 |
7 | # built application files
8 | *.apk
9 | *.ap_
10 |
11 | # files for the dex VM
12 | *.dex
13 |
14 | # Java class files
15 | *.class
16 |
17 | # generated files
18 | bin/
19 | gen/
20 | build/
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Eclipse project files
26 | .classpath
27 | .project
28 |
29 | # Android Studio
30 | .idea
31 | .gradle
32 | /*/local.properties
33 | /*/out
34 | /*/*/build
35 | build
36 | /*/*/production
37 | *.iml
38 | *.iws
39 | *.ipr
40 | *~
41 | *.swp
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoClient.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalog
3 | import network.xyo.sdkcorekotlin.node.XyoRelayNode
4 |
5 | abstract class XyoClient(
6 | relayNode: XyoRelayNode,
7 | procedureCatalog: XyoProcedureCatalog,
8 | open var autoBoundWitness: Boolean,
9 | open var knownBridges: List? = null
10 | ) : XyoBoundWitnessTarget(relayNode, procedureCatalog) {
11 | // this is not a parameter since scanning has to start off of false
12 | open var scan: Boolean = false
13 | open var deviceCount = 0
14 | open var xyoDeviceCount = 0
15 | open var nearbyXyoDeviceCount = 0
16 | }
17 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/documentation-needed.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Documentation needed
3 | about: Suggest documentation that is needed
4 | title: "[DOCUMENTATION]:"
5 | labels: documentation
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your documentation request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the documentation and format you would like**
14 | A clear and concise description of what documentation you would like to see and what type for format.
15 | Ex. Step-by-step, Paragraph explainer, screenshots, etc.
16 |
17 | **Additional context**
18 | Add any other context or screenshots about the document request here.
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for XYO SDK Android
4 | title: "[FEATURE]"
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here. This could include specific devices, android versions, etc.
21 |
--------------------------------------------------------------------------------
/xyo-android-sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve the XYO Android SDK
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Observed behavior**
11 | A clear and concise description of what exactly happened.
12 |
13 | **Expected behavior**
14 | A clear and concise description of what you expected to happen.
15 |
16 | **To Reproduce**
17 | Steps to reproduce the behavior:
18 | 1. Go to '...'
19 | 2. Click on '....'
20 | 3. Scroll down to '....'
21 | 4. See error
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Smartphone (please complete the following information):**
27 | - Device: [e.g. Samsung Galaxy, Google Pixel]
28 | - OS: [e.g. Android 10]
29 | - Browser [e.g. stock browser, chrome]
30 |
31 | **Additional context**
32 | Add any other context about the problem here.
33 |
--------------------------------------------------------------------------------
/.github/workflows/prepRelease.yml:
--------------------------------------------------------------------------------
1 | name: Prepare Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'release'
7 |
8 | jobs:
9 | build:
10 |
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: pre-build
15 | run: chmod +x ./gradlew
16 | - name: build
17 | run: ./gradlew :xyo-android-library:assemble
18 | - name: check in local changes
19 | run: |
20 | git status
21 | git add xyo-android-library/version.properties
22 | - name: commit file
23 | run: |
24 | git config --local user.email "action@github.com"
25 | git config --local user.name "GitHub Action"
26 | git commit -m "version bump" -a
27 | - name: push changes
28 | uses: ad-m/github-push-action@master
29 | with:
30 | github_token: ${{ secrets.GITHUB_TOKEN }}
31 | branch: 'release'
32 |
--------------------------------------------------------------------------------
/sonar-project.properties:
--------------------------------------------------------------------------------
1 | sonar.projectKey=XYOracleNetwork_sdk-xyo-android
2 | sonar.projectName=sdk-xyo-android
3 |
4 | # =====================================================
5 | # Meta-data for the project
6 | # =====================================================
7 |
8 | sonar.links.homepage=https://github.com/XYOracleNetwork/sdk-xyo-android
9 | sonar.links.ci=https://github.com/XYOracleNetwork/sdk-xyo-android
10 | sonar.links.scm=https://github.com/XYOracleNetwork/sdk-xyo-android
11 | sonar.links.issue=https://github.com/XYOracleNetwork/sdk-xyo-android/issues
12 |
13 |
14 | # =====================================================
15 | # Properties that will be shared amongst all modules
16 | # =====================================================
17 |
18 | sonar.host.url=https://sonarcloud.io
19 | sonar.organization=xyo-network
20 | sonar.login=${SONAR_TOKEN}
21 | sonar.sources=.
22 |
23 | sonar.java.binaries=.
24 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | XYO SDK Sample
3 | BLE Client
4 | BLE Server
5 | TcpIp Client
6 | TcpIp Server
7 | Auto BoundWitness
8 | Auto Bridge
9 | Accept Bridging
10 | Scan
11 | Listen
12 | Public Key:
13 | Detected Devices:
14 | Detected XYO Devices:
15 | Nearby XYO Devices:
16 |
17 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoTcpIpServer.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalog
3 | import network.xyo.sdkcorekotlin.node.XyoRelayNode
4 |
5 | class XyoTcpIpServer(
6 | relayNode: XyoRelayNode,
7 | procedureCatalog: XyoProcedureCatalog,
8 | autoBridge: Boolean,
9 | acceptBridging: Boolean,
10 | listen: Boolean
11 | ) : XyoServer(relayNode, procedureCatalog) {
12 |
13 | override var autoBridge: Boolean
14 | get() { return false }
15 | set(_) { }
16 |
17 | override var acceptBridging: Boolean
18 | get() { return false }
19 | set(_) { }
20 |
21 | var listen: Boolean
22 | get() { return false }
23 | set(_) { }
24 |
25 | init {
26 | this.autoBridge = autoBridge
27 | this.acceptBridging = acceptBridging
28 | this.listen = listen
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/menu/bottom_nav_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
25 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/layout/fragment_tcpip_server.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
19 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoTcpIpNetwork.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalog
3 | import network.xyo.sdkcorekotlin.node.XyoRelayNode
4 |
5 | @kotlin.ExperimentalUnsignedTypes
6 | class XyoTcpIpNetwork(
7 | relayNode: XyoRelayNode,
8 | procedureCatalog: XyoProcedureCatalog,
9 | override val client: XyoTcpIpClient = XyoTcpIpClient(
10 | relayNode,
11 | procedureCatalog,
12 | autoBoundWitness = true,
13 | autoBridge = true,
14 | acceptBridging = false
15 | ),
16 | override val server: XyoTcpIpServer = XyoTcpIpServer(
17 | relayNode,
18 | procedureCatalog,
19 | autoBridge = true,
20 | acceptBridging = false,
21 | listen = false
22 | )
23 | ) : XyoNetwork(Type.TcpIp) {
24 | init {
25 | client.knownBridges = client.knownBridges ?: listOf("ws://alpha-peers.xyo.network:11000")
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoBleNetwork.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import android.content.Context
3 | import kotlinx.coroutines.InternalCoroutinesApi
4 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalog
5 | import network.xyo.sdkcorekotlin.node.XyoRelayNode
6 |
7 | @InternalCoroutinesApi
8 | @kotlin.ExperimentalUnsignedTypes
9 | class XyoBleNetwork(
10 | context: Context,
11 | relayNode: XyoRelayNode,
12 | procedureCatalog: XyoProcedureCatalog,
13 | override val client: XyoBleClient = XyoBleClient(
14 | context,
15 | relayNode,
16 | procedureCatalog,
17 | autoBoundWitness = true,
18 | autoBridge = false,
19 | acceptBridging = false,
20 | scan = true
21 | ),
22 | override val server: XyoBleServer = XyoBleServer(
23 | context,
24 | relayNode,
25 | procedureCatalog,
26 | autoBridge = false,
27 | acceptBridging = false,
28 | listen = true
29 | )
30 | ) : XyoNetwork(Type.BluetoothLE)
31 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/XyoUuids.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth
2 |
3 | import java.util.*
4 |
5 | /**
6 | * All the XYO Bluetooth UUIDs.
7 | */
8 | object XyoUuids {
9 | // The descriptor to use when to manage subscribing to notifications.
10 | val NOTIFY_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")!!
11 |
12 | // The primary GATT service that will be advertised.
13 | var XYO_SERVICE = UUID.fromString("d684352e-df36-484e-bc98-2d5398c5593e")!!
14 |
15 | // The GATT characteristic to be written to when creating pipes. This will be in the XYO_SERVICE.
16 | val XYO_PIPE = UUID.fromString("727a3639-0eb4-4525-b1bc-7fa456490b2d")!!
17 |
18 | // The password GATT characteristic to write to when changing the password.
19 | val XYO_PASSWORD = UUID.fromString("727a3639-0eb4-4525-b1bc-7fa4564A0b2d")!!
20 |
21 | // The password bound witness data characteristic characteristic to be written to.
22 | val XYO_CHANGE_BW_DATA = UUID.fromString("727a3639-0eb4-4525-b1bc-7fa4564B0b2d")!!
23 |
24 | val XYO_RESET_DEVICE = UUID.fromString("727a3639-0eb4-4525-b1bc-7fa4564C0b2d")!!
25 | val XYO_PUBLIC_KEY = UUID.fromString("727a3639-0eb4-4525-b1bc-7fa4564D0b2d")!!
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/java/network/xyo/sdk/sample/UIThread.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.sample
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 | import kotlinx.coroutines.*
6 | import kotlin.coroutines.AbstractCoroutineContextElement
7 | import kotlin.coroutines.Continuation
8 | import kotlin.coroutines.ContinuationInterceptor
9 | import kotlin.coroutines.CoroutineContext
10 |
11 | // this needs to be imported by sdk-ui-android (https://github.com/XYOracleNetwork/sdk-ui-android)
12 | private class AndroidContinuation(val cont: Continuation) : Continuation by cont {
13 | override fun resumeWith(result: Result) {
14 | if (Looper.myLooper() == Looper.getMainLooper()) cont.resumeWith(result)
15 | else Handler(Looper.getMainLooper()).post { cont.resumeWith(result) }
16 | }
17 |
18 | }
19 |
20 | object UIThread : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
21 | override fun interceptContinuation(continuation: Continuation): Continuation =
22 | AndroidContinuation(continuation)
23 | }
24 |
25 | fun ui(
26 | context: CoroutineContext = UIThread,
27 | start: CoroutineStart = CoroutineStart.DEFAULT,
28 | block: suspend CoroutineScope.() -> Unit
29 | ): Job {
30 | return GlobalScope.launch(context, start, block)
31 | }
--------------------------------------------------------------------------------
/xyo-android-library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/arietrouw/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
27 | -keepattributes InnerClasses
28 | -keepattributes Signature
29 | -keepattributes Exceptions
30 | -keepattributes *Annotation*
31 | -keepattributes EnclosingMethod
32 |
33 | -keep public class network.xyo.** { *; }
34 | -keep class androidx.core.app.CoreComponentFactory { *; }
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/packet/XyoInputStream.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth.packet
2 |
3 | class XyoInputStream {
4 | private val donePackets = ArrayList()
5 | private var currentBuffer: XyoBluetoothIncomingPacket? = null
6 | var onComplete : ((done: ByteArray) -> Any?)? = null
7 |
8 | fun addChunk (data: ByteArray) {
9 | if (currentBuffer == null) {
10 | currentBuffer = XyoBluetoothIncomingPacket(data)
11 |
12 | if (currentBuffer?.done == true) {
13 | val finished = currentBuffer?.getCurrentBuffer() ?: return
14 | donePackets.add(finished)
15 | onDone()
16 | currentBuffer = null
17 | }
18 |
19 | return
20 | }
21 |
22 | val finished = currentBuffer?.addPacket(data) ?: return
23 | donePackets.add(finished)
24 | onDone()
25 | currentBuffer = null
26 | }
27 |
28 | private fun onDone () {
29 | val cb = onComplete
30 |
31 | if (cb != null && donePackets.size > 0) {
32 | cb(donePackets[0])
33 | donePackets.removeAt(0)
34 | }
35 | }
36 |
37 | fun getOldestPacket () : ByteArray? {
38 | if (donePackets.isEmpty()) {
39 | return null
40 | }
41 |
42 | val done = donePackets.first()
43 |
44 | donePackets.removeAt(0)
45 |
46 | return done
47 | }
48 |
49 | }
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/java/network/xyo/sdk/sample/ui/tcpip_server/TcpIpServerFragment.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.sample.ui.tcpip_server
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import network.xyo.sdk.sample.databinding.FragmentTcpipServerBinding
9 | import network.xyo.sdk.sample.ui
10 |
11 | class TcpIpServerFragment : Fragment() {
12 |
13 | private var _binding: FragmentTcpipServerBinding? = null
14 | private val binding get() = _binding!!
15 |
16 | override fun onCreateView(
17 | inflater: LayoutInflater,
18 | container: ViewGroup?,
19 | savedInstanceState: Bundle?
20 | ): View? {
21 | _binding = FragmentTcpipServerBinding.inflate(inflater, container, false)
22 | val view = binding.root
23 | return view
24 | }
25 |
26 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
27 | super.onViewCreated(view, savedInstanceState)
28 | addStatus("Not Implemented for Android")
29 | }
30 |
31 | fun addStatus(status: String) {
32 | ui {
33 | binding.textTcpipServer.let {
34 | val sb = StringBuilder()
35 | sb.append(it.text)
36 | sb.append("\r\n")
37 | sb.append(status)
38 | it.text = sb.toString()
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/navigation/mobile_navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'master'
7 |
8 | jobs:
9 | publish:
10 |
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - uses: actions/checkout@master
15 | - uses: actions/setup-java@v1
16 | with:
17 | java-version: 1.8
18 | - name: pre-build
19 | run: chmod +x gradlew
20 | - name: install
21 | run: ./gradlew install
22 | - name: assemble
23 | run: ./gradlew :xyo-android-library:assembleRelease
24 | - name: print version
25 | run: |
26 | echo "##[set-output name=version;]$(gradle -q printVersion)"
27 | id: release_version
28 | - name: bintray upload
29 | env:
30 | BINTRAY_USER: ${{ secrets.BINTRAY_USER }}
31 | BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }}
32 | run: ./gradlew :xyo-android-library:bintrayUpload
33 | - name: get commit message
34 | run: |
35 | echo ::set-env name=commitmsg::$(git log --format=%B -n 1 ${{ github.event.after }})
36 | - name: show commit message
37 | run: echo $commitmsg
38 | - name: Create Release
39 | id: create_release
40 | uses: actions/create-release@v1
41 | env:
42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43 | with:
44 | tag_name: ${{ steps.release_version.outputs.version }}
45 | release_name: Release ${{ env.commitmsg }}
46 | draft: true
47 | prerelease: false
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/packet/XyoBluetoothOutgoingPacket.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth.packet
2 |
3 | import java.nio.ByteBuffer
4 |
5 | /**
6 | * A class to chunk bluetooth data when writing to a GATT.
7 | *
8 | * @param chunkSize The number of bytes per chunk.
9 | * @param bytes The bytes to chunk.
10 | * @param sizeOfSize The number of bytes to prepend the size with
11 | */
12 | class XyoBluetoothOutgoingPacket(private val chunkSize: Int, bytes: ByteArray, private val sizeOfSize: Int) {
13 | private var currentIndex = 0
14 | private val sizeWithBytes = getSizeWithBytes(bytes)
15 |
16 | private fun getSizeWithBytes(bytes: ByteArray): ByteArray {
17 | val buff = ByteBuffer.allocate(bytes.size + sizeOfSize)
18 |
19 | when (sizeOfSize) {
20 | 1 -> buff.put((bytes.size + 1).toByte())
21 | 2 -> buff.putShort((bytes.size + 2).toShort())
22 | 4 -> buff.putInt(bytes.size + 4)
23 | }
24 |
25 | buff.put(bytes)
26 | return buff.array()
27 | }
28 |
29 | /**
30 | * If there are more packets to send.
31 | */
32 | val canSendNext: Boolean
33 | get() {
34 | return sizeWithBytes.size != currentIndex
35 | }
36 |
37 |
38 | /**
39 | * Gets the next packet to send.
40 | */
41 | fun getNext(): ByteArray {
42 | var size = Math.min(chunkSize, (sizeWithBytes.size - currentIndex))
43 | //possible to get a negative size -- will cause NegativeArraySizeException
44 | if (size < 0 ) {
45 | size = 0
46 | }
47 |
48 | val packet = ByteArray(size)
49 |
50 | for (i in packet.indices) {
51 | packet[i] = sizeWithBytes[currentIndex]
52 | currentIndex++
53 | }
54 |
55 | return packet
56 | }
57 | }
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/packet/XyoBluetoothIncomingPacket.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth.packet
2 |
3 |
4 | import java.nio.ByteBuffer
5 |
6 | /**
7 | * A class to help receive chucked data that came from XyoBluetoothOutgoingPacket.
8 | *
9 | * @param firstPacket The first chunk sent by the other party.
10 | */
11 | class XyoBluetoothIncomingPacket(firstPacket: ByteArray) {
12 | private var packets = ArrayList()
13 | private var currentSize = 0
14 | private var totalSize = 0
15 |
16 | /**
17 | * If there is no more packets to add.
18 | */
19 | val done: Boolean
20 | get() = (currentSize >= totalSize && totalSize != 0)
21 |
22 | /**
23 | * Adds a chunk to incoming packet.
24 | *
25 | * @param toAdd The chunk to add.
26 | * @return If the packet if finished, it will return the completed packet.
27 | */
28 | fun addPacket(toAdd: ByteArray): ByteArray? {
29 | if (totalSize == 0 && currentSize == 0) {
30 | totalSize = ByteBuffer.wrap(toAdd.copyOfRange(0, 4)).int
31 | packets.add(toAdd.copyOfRange(4, toAdd.size))
32 | currentSize += toAdd.size
33 | return null
34 | }
35 |
36 | packets.add(toAdd)
37 | currentSize += toAdd.size
38 |
39 | if (totalSize == currentSize) {
40 | return getCurrentBuffer()
41 | }
42 |
43 | return null
44 | }
45 |
46 | /**
47 | * Get the current packet buffer.
48 | */
49 | fun getCurrentBuffer(): ByteArray {
50 | val buff = ByteBuffer.allocate(currentSize - 4)
51 |
52 | for (i in 0 until packets.size) {
53 | buff.put(packets[i])
54 | }
55 |
56 | return buff.array()
57 | }
58 |
59 | init {
60 | addPacket(firstPacket)
61 | }
62 | }
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoBoundWitnessTarget.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import network.xyo.base.XYBase
3 | import network.xyo.sdkcorekotlin.boundWitness.XyoBoundWitness
4 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalog
5 | import network.xyo.sdkcorekotlin.node.XyoRelayNode
6 |
7 | abstract class XyoBoundWitnessTarget(
8 | val relayNode: XyoRelayNode,
9 | val procedureCatalog: XyoProcedureCatalog
10 | ) : XYBase() {
11 |
12 | val publicKey: String?
13 | get() {
14 | if (relayNode.originState.signers.isEmpty()) {
15 | return null
16 | }
17 |
18 | return relayNode.originState.signers.first().publicKey.bytesCopy.toBase58String()
19 | }
20 |
21 | open class Listener : XYBase() {
22 | open fun boundWitnessStarted(source: Any?, target: XyoBoundWitnessTarget) {
23 | log.info("boundWitnessStarted")
24 | }
25 |
26 | open fun boundWitnessCompleted(source: Any?, target: XyoBoundWitnessTarget, boundWitness: XyoBoundWitness?, error: String?) {
27 | log.info("boundWitnessCompleted")
28 | }
29 | }
30 |
31 | // the interaction listener
32 | val listeners = mutableMapOf()
33 |
34 | fun boundWitnessStarted(source: Any?) {
35 | listeners.forEach {
36 | it.value.boundWitnessStarted(source, this)
37 | }
38 | }
39 |
40 | fun boundWitnessCompleted(source: Any?, boundWitness: XyoBoundWitness?, error: String?) {
41 | listeners.forEach {
42 | it.value.boundWitnessCompleted(source, this, boundWitness, error)
43 | }
44 | }
45 |
46 | // accept bound witnesses that have bridges payloads
47 | abstract var acceptBridging: Boolean
48 |
49 | // when auto bound witnessing, should we bridge our chain
50 | abstract var autoBridge: Boolean
51 | }
52 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
24 |
25 |
36 |
37 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/xyo-android-sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'maven-publish'
4 |
5 | android {
6 | compileSdkVersion 31
7 | buildToolsVersion "30.0.3"
8 | ndkVersion "21.1.6352462"
9 | defaultConfig {
10 | applicationId "network.xyo.sdk.sample"
11 | minSdkVersion 21
12 | targetSdkVersion 31
13 | versionCode 1
14 | versionName "1.0"
15 | }
16 |
17 | compileOptions {
18 | sourceCompatibility = 1.8
19 | targetCompatibility = 1.8
20 | }
21 |
22 | buildFeatures {
23 | viewBinding true
24 | }
25 |
26 | lintOptions {
27 | htmlReport false
28 | abortOnError false
29 | warningsAsErrors false
30 | disable "HardwareIds", "MissingPermission"
31 | }
32 |
33 | kotlinOptions {
34 | jvmTarget = "1.8"
35 | }
36 |
37 | buildTypes {
38 | release {
39 | minifyEnabled false
40 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
41 | }
42 | debug {
43 | debuggable true
44 | }
45 | }
46 |
47 | configurations {
48 | all {
49 | exclude group: 'org.json', module: 'json'
50 | }
51 | }
52 | }
53 |
54 | dependencies {
55 | implementation fileTree(dir: 'libs', include: ['*.jar'])
56 | implementation "org.jetbrains.kotlin:kotlin-stdlib:1.6.0"
57 | implementation "org.jetbrains.kotlin:kotlin-reflect:1.6.0"
58 | implementation 'androidx.appcompat:appcompat:1.4.0'
59 | implementation 'androidx.core:core-ktx:1.7.0'
60 | implementation 'com.google.android.material:material:1.4.0'
61 | implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
62 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
63 | implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
64 | implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
65 | api 'com.github.xyoraclenetwork:sdk-ble-android:4.1.3'
66 | api 'com.github.xyoraclenetwork:sdk-core-kotlin:3.1.4'
67 | implementation project(':xyo-android-library')
68 | }
69 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/client/XyoBridgeX.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth.client
2 |
3 | import android.bluetooth.BluetoothDevice
4 | import android.content.Context
5 | import android.os.Build
6 | import network.xyo.ble.generic.devices.XYBluetoothDevice
7 | import network.xyo.ble.generic.devices.XYCreator
8 | import network.xyo.ble.generic.scanner.XYScanResult
9 | import java.util.*
10 | import java.util.concurrent.ConcurrentHashMap
11 |
12 | @kotlin.ExperimentalUnsignedTypes
13 | open class XyoBridgeX: XyoBluetoothClient {
14 |
15 | constructor(context: Context, scanResult: XYScanResult, hash: String) : super(context, scanResult, hash)
16 |
17 | constructor(context: Context, scanResult: XYScanResult, hash: String, transport: Int) : super(context, scanResult, hash, transport)
18 |
19 | companion object : XYCreator() {
20 |
21 | fun enable(enable: Boolean) {
22 | if (enable) {
23 | xyoManufactureIdToCreator[XyoBluetoothClientDeviceType.BridgeX.raw] = this
24 | } else {
25 | xyoManufactureIdToCreator.remove(XyoBluetoothClientDeviceType.BridgeX.raw)
26 | }
27 | }
28 |
29 | override fun getDevicesFromScanResult(
30 | context: Context,
31 | scanResult: XYScanResult,
32 | globalDevices: ConcurrentHashMap,
33 | foundDevices: HashMap
35 | ) {
36 | val hash = hashFromScanResult(scanResult)
37 | val existingDevice = globalDevices[hash]
38 | if (existingDevice != null) {
39 | existingDevice.rssi = scanResult.rssi
40 | existingDevice.updateBluetoothDevice(scanResult.device)
41 | } else {
42 | val createdDevice = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
43 | XyoBridgeX(context, scanResult, hash, BluetoothDevice.TRANSPORT_LE)
44 | } else {
45 | XyoBridgeX(context, scanResult, hash)
46 | }
47 | foundDevices[hash] = createdDevice
48 | globalDevices[hash] = createdDevice
49 | }
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/client/XyoIosAppX.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth.client
2 |
3 | import android.bluetooth.BluetoothDevice
4 | import android.content.Context
5 | import android.os.Build
6 | import network.xyo.ble.generic.devices.XYBluetoothDevice
7 | import network.xyo.ble.generic.devices.XYCreator
8 | import network.xyo.ble.generic.scanner.XYScanResult
9 | import java.util.*
10 | import java.util.concurrent.ConcurrentHashMap
11 |
12 | @kotlin.ExperimentalUnsignedTypes
13 | open class XyoIosAppX : XyoBluetoothClient {
14 |
15 | constructor(context: Context, scanResult: XYScanResult, hash: String) : super(context, scanResult, hash)
16 |
17 | constructor(context: Context, scanResult: XYScanResult, hash: String, transport: Int) : super(context, scanResult, hash, transport)
18 |
19 | companion object : XYCreator() {
20 |
21 | fun enable(enable: Boolean) {
22 | if (enable) {
23 | xyoManufactureIdToCreator[XyoBluetoothClientDeviceType.IosAppX.raw] = this
24 | } else {
25 | xyoManufactureIdToCreator.remove(XyoBluetoothClientDeviceType.IosAppX.raw)
26 | }
27 | }
28 |
29 | override fun getDevicesFromScanResult(
30 | context: Context,
31 | scanResult: XYScanResult,
32 | globalDevices: ConcurrentHashMap,
33 | foundDevices: HashMap
35 | ) {
36 | val hash = hashFromScanResult(scanResult)
37 |
38 | val existingDevice = globalDevices[hash]
39 | if (existingDevice != null) {
40 | existingDevice.rssi = scanResult.rssi
41 | existingDevice.updateBluetoothDevice(scanResult.device)
42 | } else {
43 | val createdDevice = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
44 | XyoIosAppX(context, scanResult, hash, BluetoothDevice.TRANSPORT_LE)
45 | } else {
46 | XyoIosAppX(context, scanResult, hash)
47 | }
48 | foundDevices[hash] = createdDevice
49 | globalDevices[hash] = createdDevice
50 | }
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/client/XyoAndroidAppX.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth.client
2 |
3 | import android.bluetooth.BluetoothDevice
4 | import android.content.Context
5 | import android.os.Build
6 | import network.xyo.ble.generic.devices.XYBluetoothDevice
7 | import network.xyo.ble.generic.devices.XYCreator
8 | import network.xyo.ble.generic.scanner.XYScanResult
9 | import java.util.*
10 | import java.util.concurrent.ConcurrentHashMap
11 |
12 | @kotlin.ExperimentalUnsignedTypes
13 | open class XyoAndroidAppX: XyoBluetoothClient{
14 |
15 | constructor(context: Context, scanResult: XYScanResult, hash: String) : super(context, scanResult, hash)
16 |
17 | constructor(context: Context, scanResult: XYScanResult, hash: String, transport: Int) : super(context, scanResult, hash, transport)
18 |
19 | companion object : XYCreator() {
20 |
21 | fun enable(enable: Boolean) {
22 | if (enable) {
23 | xyoManufactureIdToCreator[XyoBluetoothClientDeviceType.AndroidAppX.raw] = this
24 | } else {
25 | xyoManufactureIdToCreator.remove(XyoBluetoothClientDeviceType.AndroidAppX.raw)
26 | }
27 | }
28 |
29 | override fun getDevicesFromScanResult(
30 | context: Context,
31 | scanResult: XYScanResult,
32 | globalDevices: ConcurrentHashMap,
33 | foundDevices: HashMap
35 | ) {
36 | val hash = hashFromScanResult(scanResult)
37 | val existingDevice = globalDevices[hash]
38 | if (existingDevice != null) {
39 | existingDevice.rssi = scanResult.rssi
40 | existingDevice.updateBluetoothDevice(scanResult.device)
41 | } else {
42 | val createdDevice = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
43 | XyoAndroidAppX(context, scanResult, hash, BluetoothDevice.TRANSPORT_LE)
44 | } else {
45 | XyoAndroidAppX(context, scanResult, hash)
46 | }
47 | foundDevices[hash] = createdDevice
48 | globalDevices[hash] = createdDevice
49 | }
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/XyoBleSdk.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth
2 |
3 | import android.content.Context
4 | import kotlinx.coroutines.InternalCoroutinesApi
5 | import kotlinx.coroutines.sync.Mutex
6 | import network.xyo.ble.generic.gatt.server.XYBluetoothAdvertiser
7 | import network.xyo.ble.generic.gatt.server.XYBluetoothGattServer
8 | import network.xyo.sdk.bluetooth.advertiser.XyoBluetoothAdvertiser
9 | import network.xyo.sdk.bluetooth.server.XyoBluetoothServer
10 | import java.util.*
11 |
12 | @kotlin.ExperimentalUnsignedTypes
13 | class XyoBleSdk {
14 | @InternalCoroutinesApi
15 | companion object {
16 | private var server: XyoBluetoothServer? = null
17 | private var advertiser: XyoBluetoothAdvertiser? = null
18 | private val initServerMutex = Mutex(false)
19 | private val initAdvertiserMutex = Mutex(false)
20 |
21 | private fun createNewAdvertiser(context: Context, major: UShort?, minor: UShort?): XyoBluetoothAdvertiser {
22 | val newAdvertiser = XyoBluetoothAdvertiser(
23 | major ?: Random().nextInt(Short.MAX_VALUE * 2 + 1).toUShort(),
24 | minor ?: Random().nextInt(Short.MAX_VALUE * 2 + 1).toUShort(),
25 | XYBluetoothAdvertiser(context))
26 | newAdvertiser.configureAdvertiser()
27 | advertiser = newAdvertiser
28 | return newAdvertiser
29 | }
30 |
31 | private suspend fun initServer(context: Context): XyoBluetoothServer {
32 | val newServer = XyoBluetoothServer(XYBluetoothGattServer(context))
33 | newServer.initServer()
34 | server = newServer
35 | return newServer
36 | }
37 |
38 | suspend fun server(context: Context): XyoBluetoothServer {
39 | initServerMutex.lock(this)
40 | val result = server ?: initServer(context)
41 | initServerMutex.unlock(this)
42 | return result
43 | }
44 |
45 | suspend fun advertiser(context: Context, major: UShort? = null, minor: UShort? = null): XyoBluetoothAdvertiser {
46 | initAdvertiserMutex.lock(this)
47 | val result = advertiser ?: createNewAdvertiser(context, major, minor)
48 | initAdvertiserMutex.unlock(this)
49 | return result
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/xyo-android-sample/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/layout/fragment_ble_server.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
20 |
21 |
28 |
29 |
35 |
36 |
42 |
43 |
49 |
50 |
56 |
57 |
58 |
59 |
68 |
81 |
82 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/node/XyoBleNode.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth.node
2 |
3 | import kotlinx.coroutines.GlobalScope
4 | import kotlinx.coroutines.InternalCoroutinesApi
5 | import kotlinx.coroutines.launch
6 | import network.xyo.ble.generic.devices.XYBluetoothDevice
7 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResult
8 | import network.xyo.ble.generic.scanner.XYSmartScan
9 | import network.xyo.ble.generic.scanner.XYSmartScanListener
10 | import network.xyo.sdk.bluetooth.client.XyoBluetoothClient
11 | import network.xyo.sdk.bluetooth.server.XyoBluetoothServer
12 | import network.xyo.sdkcorekotlin.hashing.XyoHash
13 | import network.xyo.sdkcorekotlin.network.XyoNetworkHandler
14 | import network.xyo.sdkcorekotlin.network.XyoNetworkPipe
15 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalog
16 | import network.xyo.sdkcorekotlin.node.XyoRelayNode
17 | import network.xyo.sdkcorekotlin.repositories.XyoBridgeQueueRepository
18 | import network.xyo.sdkcorekotlin.repositories.XyoOriginBlockRepository
19 | import network.xyo.sdkcorekotlin.repositories.XyoOriginChainStateRepository
20 |
21 | @kotlin.ExperimentalUnsignedTypes
22 | open class XyoBleNode(private val procedureCatalog: XyoProcedureCatalog,
23 | blockRepository: XyoOriginBlockRepository,
24 | stateRepository: XyoOriginChainStateRepository,
25 | bridgeQueueRepository: XyoBridgeQueueRepository,
26 | hashingProvider: XyoHash.XyoHashProvider) :
27 | XyoRelayNode(blockRepository, stateRepository, bridgeQueueRepository, hashingProvider) {
28 |
29 | private var canBoundWitness = true
30 |
31 | val scanCallback = object : XYSmartScanListener() {
32 | override fun entered(device: XYBluetoothDevice) {
33 | super.entered(device)
34 |
35 | if (device is XyoBluetoothClient) {
36 | GlobalScope.launch {
37 | tryBoundWitnessWithDevice(device)
38 | }
39 | }
40 | }
41 | }
42 |
43 | @InternalCoroutinesApi
44 | val serverCallback = object : XyoBluetoothServer.Listener {
45 | override fun onPipe(pipe: XyoNetworkPipe) {
46 | GlobalScope.launch {
47 | if (canBoundWitness) {
48 | canBoundWitness = false
49 | val handler = XyoNetworkHandler(pipe)
50 |
51 | boundWitness(handler, procedureCatalog)
52 |
53 | canBoundWitness = true
54 | return@launch
55 | }
56 |
57 | pipe.close()
58 | }
59 | }
60 | }
61 |
62 | suspend fun tryBoundWitnessWithDevice(device: XyoBluetoothClient) {
63 | if (canBoundWitness) {
64 | canBoundWitness = false
65 |
66 | device.connection {
67 | val pipe = device.createPipe()
68 |
69 | if (pipe != null) {
70 | val handler = XyoNetworkHandler(pipe)
71 |
72 | val bw = boundWitness(handler, procedureCatalog)
73 | return@connection XYBluetoothResult(bw != null)
74 | }
75 |
76 | return@connection XYBluetoothResult(false)
77 | }
78 |
79 |
80 | canBoundWitness = true
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoBleServer.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import android.content.Context
3 | import kotlinx.coroutines.GlobalScope
4 | import kotlinx.coroutines.InternalCoroutinesApi
5 | import kotlinx.coroutines.launch
6 | import kotlinx.coroutines.runBlocking
7 | import network.xyo.sdk.bluetooth.XyoBleSdk
8 | import network.xyo.sdk.bluetooth.advertiser.XyoBluetoothAdvertiser
9 | import network.xyo.sdk.bluetooth.server.XyoBluetoothServer
10 | import network.xyo.sdkcorekotlin.network.XyoNetworkHandler
11 | import network.xyo.sdkcorekotlin.network.XyoNetworkPipe
12 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalog
13 | import network.xyo.sdkcorekotlin.node.XyoNodeListener
14 | import network.xyo.sdkcorekotlin.node.XyoRelayNode
15 |
16 | @InternalCoroutinesApi
17 | @kotlin.ExperimentalUnsignedTypes
18 | class XyoBleServer(
19 | context: Context,
20 | relayNode: XyoRelayNode,
21 | procedureCatalog: XyoProcedureCatalog,
22 | autoBridge: Boolean,
23 | acceptBridging: Boolean,
24 | listen: Boolean
25 | ) : XyoServer(relayNode, procedureCatalog) {
26 |
27 | override var autoBridge: Boolean = false
28 | override var acceptBridging: Boolean = false
29 |
30 | var advertiser: XyoBluetoothAdvertiser? = null
31 | lateinit var server: XyoBluetoothServer
32 |
33 | var listen: Boolean
34 | get() { return advertiser?.started ?: false }
35 | set(value) {
36 | runBlocking {
37 | advertiser?.let { advertiser ->
38 | if (value) {
39 | log.info("Starting Advertiser")
40 | advertiser.startAdvertiser()
41 | } else {
42 | log.info("Stopping Advertiser")
43 | advertiser.stopAdvertiser()
44 | }
45 | }
46 | }
47 | }
48 |
49 | init {
50 | this.autoBridge = autoBridge
51 | this.acceptBridging = acceptBridging
52 | GlobalScope.launch {
53 | initServer(context)
54 | this@XyoBleServer.listen = listen
55 | }
56 | }
57 |
58 | private suspend fun initServer(context: Context): Boolean {
59 | advertiser = XyoBleSdk.advertiser(context)
60 | server = XyoBleSdk.server(context)
61 | var errorMessage: String? = null
62 | server.listener = object : XyoBluetoothServer.Listener {
63 | override fun onPipe(pipe: XyoNetworkPipe) {
64 | log.info("onPipe")
65 | GlobalScope.launch {
66 | boundWitnessStarted(null)
67 | val handler = XyoNetworkHandler(pipe)
68 | relayNode.addListener("XyoBleServer", object : XyoNodeListener() {
69 | override fun onBoundWitnessEndFailure(error: Exception?) {
70 | errorMessage = error?.message ?: error?.toString() ?: "Unknown Error"
71 | }
72 | })
73 | val bw = relayNode.boundWitness(handler, procedureCatalog)
74 | relayNode.removeListener("XyoBleServer")
75 | boundWitnessCompleted(null, bw, errorMessage)
76 | pipe.close()
77 | return@launch
78 | }
79 | }
80 | }
81 | log.info("Initialized Server")
82 | return true
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoSnappyDBStorageProvider.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import android.content.Context
3 | import android.util.Base64
4 | import com.snappydb.DB
5 | import com.snappydb.DBFactory
6 | import com.snappydb.SnappydbException
7 | import kotlinx.coroutines.Deferred
8 | import kotlinx.coroutines.GlobalScope
9 | import kotlinx.coroutines.async
10 | import network.xyo.sdkcorekotlin.persist.XyoKeyValueStore
11 | import network.xyo.sdkcorekotlin.persist.XyoStorageException
12 |
13 | /**
14 | * A key value store implementation of the XyoStorageProviderInterface, in android using SnappyDB.
15 | *
16 | * For more information about SnappyDB: http://snappydb.com/
17 | *
18 | * @param context The android context to open the SnappyDB with.
19 | */
20 | open class XyoSnappyDBStorageProvider(private var context: Context) : XyoKeyValueStore {
21 |
22 | private val db = DBFactory.open(context)
23 | // TODO -DB is not being closed
24 | // TODO - use sync blocks - snappyDB is not thread safe
25 | // TODO - move save functions here instead of using getDB ?
26 |
27 | // get an instance of the opened DB
28 | fun getDB(): DB? {
29 | return db
30 | }
31 |
32 | override suspend fun containsKey(key: ByteArray): Boolean {
33 | try {
34 | return db.exists(makeKey(key))
35 | } catch (dbException: SnappydbException) {
36 | throw XyoStorageException("Failed to read: $dbException")
37 | }
38 | }
39 |
40 | override suspend fun delete(key: ByteArray) {
41 | try {
42 | db.del(makeKey(key))
43 | } catch (dbException: SnappydbException) {
44 | throw XyoStorageException("Failed to delete: $dbException")
45 | }
46 | }
47 |
48 | override suspend fun getAllKeys(): Iterator {
49 | try {
50 | val i = db.allKeysIterator()
51 |
52 | return object : Iterator {
53 | override fun hasNext(): Boolean {
54 | val hasNext = i.hasNext()
55 |
56 | if (!hasNext) {
57 | i.close()
58 | }
59 |
60 | return hasNext
61 | }
62 |
63 | override fun next(): ByteArray {
64 | return getKey(i.next(1)[0])
65 | }
66 | }
67 | } catch (dbException: SnappydbException) {
68 | return arrayOf().iterator()
69 | }
70 | }
71 |
72 | override suspend fun read(key: ByteArray): ByteArray? {
73 | var result: ByteArray? = null
74 | try {
75 | result = db.getBytes(makeKey(key))
76 | } catch (dbException: SnappydbException) {
77 |
78 | }
79 | return result
80 | }
81 |
82 | override suspend fun write(key: ByteArray, value: ByteArray) {
83 | try {
84 | db.put(makeKey(key), value)
85 | } catch (dbException: SnappydbException) {
86 | throw XyoStorageException("Failed to write: $dbException")
87 | }
88 | }
89 |
90 | fun reset() {
91 | db.destroy()
92 | }
93 |
94 | private fun makeKey(byteArray: ByteArray): String {
95 | return String(Base64.encode(byteArray, 0))
96 | }
97 |
98 | private fun getKey(string: String): ByteArray {
99 | return Base64.decode(string, 0)
100 | }
101 |
102 | companion object {
103 | const val ARCHIVIST_LIST = "archlist"
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/layout/fragment_tcpip_client.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
20 |
21 |
28 |
29 |
35 |
36 |
42 |
43 |
49 |
50 |
56 |
57 |
63 |
64 |
65 |
74 |
87 |
88 |
--------------------------------------------------------------------------------
/xyo-android-library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'maven-publish'
4 |
5 | group = 'network.xyo'
6 |
7 | buildscript {
8 | repositories {
9 | mavenCentral()
10 | mavenLocal()
11 | google()
12 | maven {
13 | url "https://plugins.gradle.org/m2/"
14 | }
15 | }
16 | }
17 |
18 | Properties versionProps = new Properties()
19 | def versionPropsFile = file('version.properties')
20 |
21 | if (versionPropsFile.exists())
22 | versionProps.load(new FileInputStream(versionPropsFile))
23 |
24 | def majorVersion = 3
25 | def minorVersion = 1
26 | def patch = 44
27 |
28 | def verString = majorVersion + '.' + minorVersion + '.' + patch
29 |
30 | task printVersion {
31 | println verString
32 | }
33 |
34 | android {
35 | compileSdkVersion 31
36 | buildToolsVersion "30.0.3"
37 | ndkVersion "21.1.6352462"
38 |
39 | versionProps['VERSION_PATCH'] = patch.toString()
40 | versionProps.store(versionPropsFile.newWriter(), null)
41 |
42 | compileOptions {
43 | sourceCompatibility = 1.8
44 | targetCompatibility = 1.8
45 | kotlinOptions {
46 | allWarningsAsErrors = false
47 | }
48 | }
49 |
50 | defaultConfig {
51 | minSdkVersion 21
52 | targetSdkVersion 31
53 | }
54 |
55 | lintOptions {
56 | htmlReport false
57 | abortOnError false
58 | warningsAsErrors false
59 | disable "HardwareIds", "MissingPermission"
60 | }
61 |
62 | buildTypes {
63 | release {
64 | minifyEnabled false
65 | }
66 | debug {
67 | minifyEnabled false
68 | }
69 | }
70 |
71 | packagingOptions {
72 | exclude 'META-INF/build.kotlin_module'
73 | exclude 'META-INF/atomicfu.kotlin_module'
74 | exclude 'META-INF/library_release.kotlin_module'
75 | exclude 'META-INF/sdk-ble-android'
76 | exclude 'META-INF/sdk-base-android'
77 | exclude 'META-INF/sdk-core-kotlin'
78 | }
79 |
80 | }
81 |
82 | publishing {
83 | publications {
84 | Production(MavenPublication) {
85 | artifact("$buildDir/outputs/aar/xyo-android-library-release.aar")
86 | groupId 'network.xyo'
87 | artifactId 'sdk-xyo-android'
88 | version verString
89 |
90 | //The publication doesn't know about our dependencies, so we have to manually add them to the pom
91 | pom.withXml {
92 | def dependenciesNode = asNode().getAt('dependencies')[0] ?: asNode().appendNode('dependencies')
93 | //Iterate over the compile dependencies (we don't want the test ones), adding a node for each
94 | configurations.implementation.allDependencies.each {
95 | if (it.name != 'unspecified') {
96 | def dependencyNode = dependenciesNode.appendNode('dependency')
97 | dependencyNode.appendNode('groupId', it.group)
98 | dependencyNode.appendNode('artifactId', it.name)
99 | dependencyNode.appendNode('version', it.version)
100 | }
101 | }
102 | }
103 | }
104 | }
105 | }
106 |
107 | dependencies {
108 | implementation fileTree(dir: 'libs', include: ['*.jar'])
109 | implementation "org.jetbrains.kotlin:kotlin-stdlib:1.6.0"
110 | implementation "org.jetbrains.kotlin:kotlin-reflect:1.6.0"
111 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
112 | implementation 'androidx.appcompat:appcompat:1.4.0'
113 | implementation 'androidx.core:core-ktx:1.7.0'
114 | api 'com.github.xyoraclenetwork:sdk-ble-android:4.1.3'
115 | api 'com.github.xyoraclenetwork:sdk-core-kotlin:3.1.4'
116 | implementation 'com.snappydb:snappydb-lib:0.5.2'
117 | }
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoTcpIpClient.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import android.net.Uri
3 | import java.io.IOException
4 | import java.net.Socket
5 | import kotlinx.coroutines.GlobalScope
6 | import kotlinx.coroutines.launch
7 | import kotlinx.coroutines.sync.Mutex
8 | import network.xyo.sdkcorekotlin.boundWitness.XyoBoundWitness
9 | import network.xyo.sdkcorekotlin.network.XyoNetworkHandler
10 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalog
11 | import network.xyo.sdkcorekotlin.network.tcp.XyoTcpPipe
12 | import network.xyo.sdkcorekotlin.node.XyoNodeListener
13 | import network.xyo.sdkcorekotlin.node.XyoRelayNode
14 | import java.nio.ByteBuffer
15 |
16 | @kotlin.ExperimentalUnsignedTypes
17 | class XyoTcpIpClient(
18 | relayNode: XyoRelayNode,
19 | procedureCatalog: XyoProcedureCatalog,
20 | override var autoBridge: Boolean,
21 | override var acceptBridging: Boolean,
22 | autoBoundWitness: Boolean
23 | ) : XyoClient(relayNode, procedureCatalog, autoBoundWitness) {
24 |
25 | init {
26 | relayNode.addListener("XyoTcpIpClient", object : XyoNodeListener() {
27 | override fun onBoundWitnessEndSuccess(boundWitness: XyoBoundWitness) {
28 | super.onBoundWitnessEndSuccess(boundWitness)
29 | if (autoBridge && ByteBuffer.wrap((relayNode.stateRepository.getIndex()?.valueCopy)).int % 5 == 0) {
30 | GlobalScope.launch {
31 | this@XyoTcpIpClient.bridge()
32 | }
33 | }
34 | }
35 | })
36 | }
37 |
38 | private val bridgeMutex = Mutex()
39 |
40 | suspend fun bridge(): String? {
41 | var errorMessage: String? = null
42 | var networkErrorMessage: String? = null
43 | if (bridgeMutex.tryLock()) {
44 | log.info("bridge - started: [${knownBridges?.size}]")
45 | knownBridges?.let { knownBridges ->
46 | if (knownBridges.isEmpty()) {
47 | log.info("No known bridges, skipping bridging!")
48 | errorMessage = "No Known Bridges"
49 | } else {
50 | var bw: XyoBoundWitness? = null
51 | knownBridges.forEach { bridge ->
52 | log.info("Trying to bridge: $bridge")
53 | boundWitnessStarted(bridge)
54 | try {
55 | if (bw == null) {
56 | val uri = Uri.parse(bridge)
57 |
58 | log.info("Trying to bridge [info]: ${uri.host}:${uri.port}")
59 |
60 | val socket = Socket(uri.host, uri.port)
61 | val pipe = XyoTcpPipe(socket, null)
62 | val handler = XyoNetworkHandler(pipe)
63 |
64 | log.info("Starting Bridge BoundWitness")
65 | relayNode.addListener("XyoTcpIpClient-bridge", object : XyoNodeListener() {
66 | override fun onBoundWitnessEndFailure(error: Exception?) {
67 | errorMessage = error?.message ?: error?.toString() ?: "Unknown Error"
68 | }
69 | })
70 | bw = relayNode.boundWitness(handler, procedureCatalog)
71 | relayNode.removeListener("XyoTcpIpClient-bridge")
72 | pipe.close()
73 | log.info("Bridge Result: $bw")
74 | }
75 | } catch (e: IOException) {
76 | log.info("Bridging Excepted $e")
77 | networkErrorMessage = e.message ?: e.toString()
78 | }
79 | boundWitnessCompleted(bridge, bw, errorMessage ?: networkErrorMessage)
80 | }
81 | }
82 | }
83 | bridgeMutex.unlock()
84 | }
85 | return errorMessage ?: networkErrorMessage
86 | }
87 |
88 | fun startBridge() {
89 |
90 | }
91 |
92 | override var scan: Boolean
93 | get() { return false }
94 | set(_) { }
95 | }
96 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/advertiser/XyoBluetoothAdvertiser.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth.advertiser
2 |
3 | import android.bluetooth.BluetoothAdapter
4 | import android.bluetooth.le.AdvertiseData
5 | import android.bluetooth.le.AdvertiseSettings
6 | import android.os.ParcelUuid
7 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResult
8 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResultErrorCode
9 | import network.xyo.ble.generic.gatt.server.XYBluetoothAdvertiser
10 | import network.xyo.ble.generic.gatt.server.XYIBeaconAdvertiseDataCreator
11 | import network.xyo.sdk.bluetooth.XyoUuids
12 | import java.nio.ByteBuffer
13 |
14 | /**
15 | * A class for managing XYO advertising.
16 | *
17 | * @property major The device major to advertise
18 | * @property minor The device minor to advertise
19 | * @param advertiser The XY advertiser to advertise with.
20 | */
21 | @kotlin.ExperimentalUnsignedTypes
22 | class XyoBluetoothAdvertiser(
23 | private val major: UShort,
24 | private val minor: UShort,
25 | private val advertiser: XYBluetoothAdvertiser
26 | ) {
27 |
28 | private var includeName = false
29 |
30 | /**
31 | * Start a advertisement cycle
32 | */
33 | fun configureAdvertiser() {
34 | includeName = BluetoothAdapter.getDefaultAdapter().setName("Xyo")
35 | if (advertiser.isMultiAdvertisementSupported) {
36 | configureAdverserMulti()
37 | return
38 | }
39 | configureAdvertiserSingle()
40 | }
41 |
42 | // private fun getAdvertiseUuid (uuid: UUID, major: ByteArray, minor: ByteArray): UUID {
43 | // val uuidString = uuid.toString().dropLast(8)
44 | // val majorString = major.toHexString().drop(2)
45 | // val minorString = minor.toHexString().drop(2)
46 | //
47 | // return UUID.fromString( minorString + majorString + uuidString)
48 | // }
49 |
50 | private fun configureAdverserMulti() {
51 | val encodeMajor = ByteBuffer.allocate(2).putShort(major.toShort()).array()
52 | val encodedMinor = ByteBuffer.allocate(2).putShort(minor.toShort()).array()
53 | val advertiseData = XYIBeaconAdvertiseDataCreator.create(
54 | encodeMajor,
55 | encodedMinor,
56 | XyoUuids.XYO_SERVICE,
57 | APPLE_MANUFACTURER_ID,
58 | false
59 | ).build()
60 |
61 | val responseData = AdvertiseData.Builder()
62 | .setIncludeDeviceName(includeName)
63 | .addServiceUuid(ParcelUuid(
64 | XyoUuids.XYO_SERVICE
65 | ))
66 | .build()
67 |
68 | advertiser.advertisingData = advertiseData
69 | advertiser.advertisingResponse = responseData
70 | advertiser.changeContactable(true)
71 | advertiser.changeAdvertisingMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
72 | advertiser.changeAdvertisingTxLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
73 | }
74 |
75 | private fun configureAdvertiserSingle() {
76 | val advertiseData = AdvertiseData.Builder()
77 | .addServiceUuid(ParcelUuid(XyoUuids.XYO_SERVICE))
78 | .setIncludeDeviceName(includeName)
79 | .build()
80 |
81 | advertiser.advertisingData = advertiseData
82 | advertiser.changeContactable(true)
83 | advertiser.changeAdvertisingMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
84 | advertiser.changeAdvertisingTxLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
85 | }
86 |
87 | /**
88 | * Stop the current advertisement cycle
89 | */
90 | fun stopAdvertiser() {
91 | advertiser.stopAdvertising()
92 | _started = false
93 | }
94 |
95 | suspend fun startAdvertiser(): XYBluetoothResult {
96 | val result = advertiser.startAdvertising()
97 | _started = result.error == XYBluetoothResultErrorCode.None
98 | return result
99 | }
100 |
101 | var _started: Boolean = false
102 | val started: Boolean
103 | get() { return _started }
104 |
105 | companion object {
106 | const val APPLE_MANUFACTURER_ID = 76
107 | }
108 | }
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/java/network/xyo/sdk/sample/ui/ble_server/BleServerFragment.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.sample.ui.ble_server
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import kotlinx.coroutines.InternalCoroutinesApi
9 | import network.xyo.sdk.XyoBleNetwork
10 | import network.xyo.sdk.XyoBoundWitnessTarget
11 | import network.xyo.sdk.XyoSdk
12 | import network.xyo.sdk.sample.databinding.FragmentBleServerBinding
13 | import network.xyo.sdk.sample.ui
14 | import network.xyo.sdkcorekotlin.boundWitness.XyoBoundWitness
15 |
16 | @InternalCoroutinesApi
17 | @kotlin.ExperimentalUnsignedTypes
18 | class BleServerFragment : Fragment() {
19 |
20 | var statusText = ""
21 | private var _binding: FragmentBleServerBinding? = null
22 | private val binding get() = _binding!!
23 |
24 | fun addStatus(status: String) {
25 | statusText = "${statusText}\r\n$status"
26 | binding.textBleServer.let {
27 | ui {
28 | it.text = statusText
29 | }
30 | }
31 | }
32 |
33 | override fun onCreateView(
34 | inflater: LayoutInflater,
35 | container: ViewGroup?,
36 | savedInstanceState: Bundle?
37 | ): View? {
38 | _binding = FragmentBleServerBinding.inflate(inflater, container, false)
39 | val view = binding.root
40 | return view
41 | }
42 |
43 | fun updateUI() {
44 | ui {
45 | (XyoSdk.nodes[0].networks["ble"] as? XyoBleNetwork)?.let { network ->
46 | binding.acceptBridging.isChecked = network.server.acceptBridging
47 | binding.acceptBridging.setOnCheckedChangeListener { _, isChecked ->
48 | network.server.acceptBridging = isChecked
49 | }
50 |
51 | binding.autoBridge.isChecked = network.server.autoBridge
52 | binding.autoBridge.setOnCheckedChangeListener { _, isChecked ->
53 | network.server.autoBridge = isChecked
54 | }
55 |
56 | binding.listen.isChecked = network.server.listen
57 | binding.listen.setOnCheckedChangeListener { _, isChecked ->
58 | network.server.listen = isChecked
59 | }
60 | }
61 | }
62 | }
63 |
64 | override fun onResume() {
65 | updateUI()
66 | super.onResume()
67 | }
68 |
69 | override fun onDestroyView() {
70 | super.onDestroyView()
71 | _binding = null
72 | (XyoSdk.nodes[0].networks["ble"] as? XyoBleNetwork)?.server?.listeners?.remove("sample")
73 | }
74 |
75 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
76 | super.onViewCreated(view, savedInstanceState)
77 |
78 | (XyoSdk.nodes[0].networks["ble"] as? XyoBleNetwork)?.let { network ->
79 | ui {
80 | network.server.listeners["sample"] = object : XyoBoundWitnessTarget.Listener() {
81 | override fun boundWitnessStarted(source: Any?, target: XyoBoundWitnessTarget) {
82 | super.boundWitnessStarted(source, target)
83 | addStatus("Bound Witness Started [${source?.javaClass?.name}]")
84 | }
85 |
86 | override fun boundWitnessCompleted(source: Any?, target: XyoBoundWitnessTarget, boundWitness: XyoBoundWitness?, error:String?) {
87 | super.boundWitnessCompleted(source, target, boundWitness, error)
88 | val index = target.relayNode.originState.index.valueCopy.toList().toString()
89 | if (error == null) {
90 | addStatus("Bound Witness Completed [$index] [${boundWitness?.completed}]")
91 | } else {
92 | addStatus("Bound Witness Failed [$error]")
93 | }
94 | addStatus("- - - - - -")
95 | }
96 | }
97 | updateUI()
98 | binding.textBleServer.text = ""
99 | binding.publicKey.text = network.server.publicKey
100 | }
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/java/network/xyo/sdk/sample/ui/tcpip_client/TcpIpClientFragment.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.sample.ui.tcpip_client
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import network.xyo.sdk.XyoBoundWitnessTarget
9 | import network.xyo.sdk.XyoSdk
10 | import network.xyo.sdk.XyoTcpIpNetwork
11 | import network.xyo.sdk.sample.databinding.FragmentTcpipClientBinding
12 | import network.xyo.sdk.sample.ui
13 | import network.xyo.sdkcorekotlin.boundWitness.XyoBoundWitness
14 |
15 | @kotlin.ExperimentalUnsignedTypes
16 | class TcpIpClientFragment : Fragment() {
17 |
18 | private var _binding: FragmentTcpipClientBinding? = null
19 | private val binding get() = _binding!!
20 |
21 | override fun onCreateView(
22 | inflater: LayoutInflater,
23 | container: ViewGroup?,
24 | savedInstanceState: Bundle?
25 | ): View? {
26 | _binding = FragmentTcpipClientBinding.inflate(inflater, container, false)
27 | val view = binding.root
28 | return view
29 | }
30 |
31 | fun addStatus(status: String) {
32 | ui {
33 | val sb = StringBuilder()
34 | sb.append(binding.textTcpipClient.text)
35 | sb.append("\r\n")
36 | sb.append(status)
37 | binding.textTcpipClient.text = sb.toString()
38 | }
39 | }
40 |
41 | fun updateUI() {
42 | ui {
43 | (XyoSdk.nodes[0].networks["tcpip"] as? XyoTcpIpNetwork)?.let { network ->
44 | binding.acceptBridging.isChecked = network.client.acceptBridging
45 | binding.acceptBridging.setOnCheckedChangeListener { _, isChecked ->
46 | network.client.acceptBridging = isChecked
47 | }
48 |
49 | binding.autoBoundWitness.isChecked = network.client.autoBoundWitness
50 | binding.autoBoundWitness.setOnCheckedChangeListener { _, isChecked ->
51 | network.client.autoBoundWitness = isChecked
52 | }
53 |
54 | binding.autoBridge.isChecked = network.client.autoBridge
55 | binding.autoBridge.setOnCheckedChangeListener { _, isChecked ->
56 | network.client.autoBridge = isChecked
57 | }
58 |
59 | binding.scan.isChecked = network.client.scan
60 | binding.scan.setOnCheckedChangeListener { _, isChecked ->
61 | network.client.scan = isChecked
62 | }
63 | }
64 | }
65 | }
66 |
67 | override fun onResume() {
68 | updateUI()
69 | super.onResume()
70 | }
71 |
72 | override fun onDestroyView() {
73 | super.onDestroyView()
74 | _binding = null
75 | (XyoSdk.nodes[0].networks["tcpip"] as? XyoTcpIpNetwork)?.client?.listeners?.remove("sample")
76 | }
77 |
78 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
79 | super.onViewCreated(view, savedInstanceState)
80 |
81 | (XyoSdk.nodes[0].networks["tcpip"] as? XyoTcpIpNetwork)?.let { network ->
82 |
83 | network.client.listeners["sample"] = object : XyoBoundWitnessTarget.Listener() {
84 | override fun boundWitnessStarted(source: Any?, target: XyoBoundWitnessTarget) {
85 | super.boundWitnessStarted(source, target)
86 | addStatus("Bound Witness Started [${source?.javaClass?.name}]")
87 | }
88 |
89 | override fun boundWitnessCompleted(source: Any?, target: XyoBoundWitnessTarget, boundWitness: XyoBoundWitness?, error:String?) {
90 | super.boundWitnessCompleted(source, target, boundWitness, error)
91 | val index = target.relayNode.originState.index.valueCopy.toList().toString()
92 | if (error == null) {
93 | addStatus("Bound Witness Completed $index [${boundWitness?.completed}]")
94 | } else {
95 | addStatus("Bound Witness Failed [$error]")
96 | }
97 | addStatus("- - - - - -")
98 | }
99 |
100 | }
101 |
102 | ui {
103 | binding.textTcpipClient.text = ""
104 | binding.publicKey.text = network.client.publicKey
105 | }
106 | }
107 | }
108 | }
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/client/XyoBluetoothClientPipe.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth.client
2 |
3 | import android.bluetooth.BluetoothGatt
4 | import android.util.Log
5 | import kotlinx.coroutines.*
6 | import network.xyo.ble.generic.devices.XYBluetoothDevice
7 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResult
8 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResultErrorCode
9 | import network.xyo.ble.generic.listeners.XYBluetoothDeviceListener
10 | import network.xyo.sdk.bluetooth.XyoUuids
11 | import network.xyo.sdkcorekotlin.network.XyoAdvertisePacket
12 | import network.xyo.sdkcorekotlin.network.XyoNetworkPipe
13 | import network.xyo.sdkcorekotlin.schemas.XyoSchemas
14 | import network.xyo.sdkobjectmodelkotlin.structure.XyoObjectStructure
15 | import kotlin.coroutines.suspendCoroutine
16 | import network.xyo.sdkobjectmodelkotlin.toHexString
17 | import kotlin.coroutines.resume
18 |
19 | @kotlin.ExperimentalUnsignedTypes
20 | class XyoBluetoothClientPipe(val client: XyoBluetoothClient) : XyoNetworkPipe {
21 |
22 | override val initiationData: XyoAdvertisePacket? = null
23 |
24 | /**
25 | * Closes the pipe between parties. In this case, disconnects from the device and closes the GATT. This should
26 | * be called after the pipe is finished being used.
27 | */
28 | override suspend fun close(): Boolean {
29 | Log.i(TAG, "close: started")
30 | client.disconnect()
31 | return true
32 | }
33 |
34 | override fun getNetworkHeuristics(): Array {
35 | Log.i(TAG, "getNetworkHeuristics: started")
36 | val toReturn = ArrayList()
37 |
38 | val rssi = client.rssi
39 |
40 | if (rssi != null) {
41 | val encodedRssi = XyoObjectStructure.newInstance(XyoSchemas.RSSI, byteArrayOf(rssi.toByte()))
42 | toReturn.add(encodedRssi)
43 | }
44 |
45 | val pwr = XyoObjectStructure.newInstance(XyoSchemas.BLE_POWER_LVL, byteArrayOf(client.power))
46 | toReturn.add(pwr)
47 |
48 | return toReturn.toTypedArray()
49 | }
50 |
51 | fun wrapAsync() = GlobalScope.async {
52 | return@async client.readIncoming()
53 | }
54 |
55 | /**
56 | * Sends data and waits for a response if the waitForResponse flag is set to true.
57 | * NOTE: The send and recieve are abstracted away from the caller, thus the exact bytes transferred is unclear.
58 | *
59 | * @param data Data to send to the other end of the pipe.
60 | * @param waitForResponse If this flag is set, this function will wait for a response. If not, will return
61 | * null.
62 | * @return A deferred ByteArray of the response of the server. If waitForResponse is null, will return null.
63 | */
64 | override suspend fun send(data: ByteArray, waitForResponse: Boolean): ByteArray? {
65 | Log.i(TAG, "send: started")
66 | return suspendCoroutine { cont ->
67 | val disconnectKey = this.toString() + Math.random().toString()
68 |
69 | val sendAndReceive = GlobalScope.async {
70 | Log.i(TAG, "send: sendAndReceive: started")
71 | val job = wrapAsync()
72 | val packetError = client.chunkSend(data, XyoUuids.XYO_PIPE, XyoUuids.XYO_SERVICE, 4)
73 |
74 | Log.i(TAG, "Sent entire packet to the server.")
75 | if (packetError == XYBluetoothResultErrorCode.None) {
76 | Log.i(TAG, "Sent entire packet to the server (good).")
77 | var valueIn: ByteArray? = null
78 |
79 | if (waitForResponse) {
80 | valueIn = job.await()
81 | }
82 |
83 | Log.i(TAG, "Have read entire server response packet. ${valueIn?.toHexString()}")
84 | client.removeListener(disconnectKey)
85 | cont.resume(valueIn)
86 | } else {
87 | client.log.info("Error sending entire packet to the server. ${packetError.name}")
88 | client.removeListener(disconnectKey)
89 | cont.resume(null)
90 | }
91 | }
92 |
93 | // add the disconnect listener
94 | client.log.info("Adding disconnect listener.")
95 | client.addListener(disconnectKey, object : XYBluetoothDeviceListener() {
96 | override fun connectionStateChanged(device: XYBluetoothDevice, newState: Int) {
97 | Log.i(TAG, "send: connectionStateChanged: $newState")
98 | when (newState) {
99 |
100 | BluetoothGatt.STATE_DISCONNECTED -> {
101 | client.log.info("Someone disconnected.")
102 |
103 | if (cont.context.isActive) {
104 | client.log.info("Context is still active.")
105 | client.removeListener(disconnectKey)
106 |
107 | client.log.info("Canceling send and receive.")
108 |
109 | cont.resume(null)
110 | cont.context.cancel()
111 | sendAndReceive.cancel()
112 | }
113 | }
114 | }
115 | }
116 | })
117 | }
118 | }
119 |
120 | companion object {
121 | const val TAG = "XyoBluetoothClientPipe"
122 | }
123 | }
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/java/network/xyo/sdk/sample/ui/ble_client/BleClientFragment.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.sample.ui.ble_client
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.Fragment
9 |
10 | import kotlinx.coroutines.GlobalScope
11 | import kotlinx.coroutines.InternalCoroutinesApi
12 | import kotlinx.coroutines.delay
13 | import kotlinx.coroutines.launch
14 | import network.xyo.sdk.sample.R
15 | import network.xyo.sdk.XyoBleNetwork
16 | import network.xyo.sdk.XyoBoundWitnessTarget
17 | import network.xyo.sdk.XyoSdk
18 | import network.xyo.sdk.sample.databinding.FragmentBleClientBinding
19 | import network.xyo.sdk.sample.ui
20 |
21 | import network.xyo.sdkcorekotlin.boundWitness.XyoBoundWitness
22 |
23 | @InternalCoroutinesApi
24 | @kotlin.ExperimentalUnsignedTypes
25 | class BleClientFragment : Fragment() {
26 |
27 | var deviceCount = 0
28 | var xyoDeviceCount = 0
29 | var nearbyXyoDeviceCount = 0
30 |
31 | var autoUpdateUi = false
32 | var statusText = ""
33 |
34 | private var _binding: FragmentBleClientBinding? = null
35 | private val binding get() = _binding!!
36 |
37 | override fun onCreateView(
38 | inflater: LayoutInflater,
39 | container: ViewGroup?,
40 | savedInstanceState: Bundle?
41 | ): View? {
42 | _binding = FragmentBleClientBinding.inflate(inflater, container, false)
43 | val view = binding.root
44 | return view
45 | }
46 |
47 | override fun onDestroyView() {
48 | super.onDestroyView()
49 | _binding = null
50 | (XyoSdk.nodes[0].networks["ble"] as? XyoBleNetwork)?.client?.listeners?.remove("sample")
51 | }
52 |
53 | override fun onAttach(context: Context) {
54 | super.onAttach(context)
55 | autoUpdateUi = true
56 | GlobalScope.launch {
57 | while(autoUpdateUi) {
58 | this@BleClientFragment.updateUI()
59 | delay(1000)
60 | }
61 | }
62 | }
63 |
64 | override fun onDetach() {
65 | autoUpdateUi = false
66 | super.onDetach()
67 | }
68 |
69 | fun addStatus(status: String) {
70 | statusText = "${statusText}\r\n$status"
71 | ui {
72 | binding.textBleClient.text = statusText
73 | }
74 | }
75 |
76 | @InternalCoroutinesApi
77 | private fun updateUI() {
78 | ui {
79 | (XyoSdk.nodes[0].networks["ble"] as? XyoBleNetwork)?.let { network ->
80 | binding.acceptBridging.isChecked = network.client.acceptBridging
81 | binding.acceptBridging?.setOnCheckedChangeListener { _, isChecked ->
82 | network.client.acceptBridging = isChecked
83 | }
84 |
85 | binding.autoBoundWitness?.isChecked = network.client.autoBoundWitness
86 | binding.autoBoundWitness?.setOnCheckedChangeListener { _, isChecked ->
87 | network.client.autoBoundWitness = isChecked
88 | }
89 |
90 | binding.autoBridge?.isChecked = network.client.autoBridge
91 | binding.autoBridge?.setOnCheckedChangeListener { _, isChecked ->
92 | network.client.autoBridge = isChecked
93 | }
94 |
95 | binding.scan?.isChecked = network.client.scan
96 | binding.scan?.setOnCheckedChangeListener { _, isChecked ->
97 | network.client.scan = isChecked
98 | }
99 |
100 | deviceCount = network.client.deviceCount
101 | xyoDeviceCount = network.client.xyoDeviceCount
102 | nearbyXyoDeviceCount = network.client.nearbyXyoDeviceCount
103 | }
104 | binding.detectedDevices?.text = deviceCount.toString()
105 | binding.detectedXyoDevices?.text = xyoDeviceCount.toString()
106 | binding.nearbyXyoDevices?.text = nearbyXyoDeviceCount.toString()
107 | }
108 | }
109 |
110 | override fun onResume() {
111 | updateUI()
112 | super.onResume()
113 | }
114 |
115 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
116 | super.onViewCreated(view, savedInstanceState)
117 |
118 | (XyoSdk.nodes[0].networks["ble"] as? XyoBleNetwork)?.let { network ->
119 |
120 | network.client.listeners["sample"] = object : XyoBoundWitnessTarget.Listener() {
121 | override fun boundWitnessStarted(source: Any?, target: XyoBoundWitnessTarget) {
122 | super.boundWitnessStarted(source, target)
123 | addStatus("Bound Witness Started [${source?.javaClass?.name}]")
124 | }
125 |
126 | override fun boundWitnessCompleted(source: Any?, target: XyoBoundWitnessTarget, boundWitness: XyoBoundWitness?, error:String?) {
127 | super.boundWitnessCompleted(source, target, boundWitness, error)
128 | val index = target.relayNode.originState.index.valueCopy.toList().toString()
129 | if (error == null) {
130 | addStatus("Bound Witness Completed $index [${boundWitness?.completed}]")
131 | } else {
132 | addStatus("Bound Witness Failed [$error]")
133 | }
134 | addStatus("- - - - - -")
135 | }
136 | }
137 |
138 | ui {
139 | binding.textBleClient.text = ""
140 | binding.publicKey.text = network.client.publicKey
141 | }
142 | }
143 | }
144 | }
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/Base58.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 |
3 | /**
4 | * Base58 is a way to encode addresses (or arbitrary data) as alphanumeric strings.
5 | * Compared to base64, this encoding eliminates ambiguities created by O0Il and potential splits from punctuation
6 | *
7 | * The basic idea of the encoding is to treat the data bytes as a large number represented using
8 | * base-256 digits, convert the number to be represented using base-58 digits, preserve the exact
9 | * number of leading zeros (which are otherwise lost during the mathematical operations on the
10 | * numbers), and finally represent the resulting base-58 digits as alphanumeric ASCII characters.
11 | *
12 | * This is the Kotlin implementation of base58 - it is based implementation of base58 in java
13 | * in bitcoinj (https://bitcoinj.github.io) - thanks to Google Inc. and Andreas Schildbach
14 | *
15 | */
16 |
17 | import java.util.*
18 |
19 | private const val ENCODED_ZERO = '1'
20 | private const val CHECKSUM_SIZE = 4
21 |
22 | private const val alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
23 | private val alphabetIndices by lazy {
24 | IntArray(128) { alphabet.indexOf(it.toChar()) }
25 | }
26 |
27 | /**
28 | * Encodes the bytes as a base58 string (no checksum is appended).
29 | *
30 | * @return the base58-encoded string
31 | */
32 | @kotlin.ExperimentalUnsignedTypes
33 | fun ByteArray.toBase58String(): String {
34 |
35 | val input = copyOf(size) // since we modify it in-place
36 | if (input.isEmpty()) {
37 | return ""
38 | }
39 | // Count leading zeros.
40 | var zeros = 0
41 | while (zeros < input.size && input[zeros].toInt() == 0) {
42 | ++zeros
43 | }
44 | // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters)
45 | val encoded = CharArray(input.size * 2) // upper bound
46 | var outputStart = encoded.size
47 | var inputStart = zeros
48 | while (inputStart < input.size) {
49 | encoded[--outputStart] = alphabet[divmod(input, inputStart.toUInt(), 256.toUInt(), 58.toUInt()).toInt()]
50 | if (input[inputStart].toInt() == 0) {
51 | ++inputStart // optimization - skip leading zeros
52 | }
53 | }
54 | // Preserve exactly as many leading encoded zeros in output as there were leading zeros in data.
55 | while (outputStart < encoded.size && encoded[outputStart] == ENCODED_ZERO) {
56 | ++outputStart
57 | }
58 | while (--zeros >= 0) {
59 | encoded[--outputStart] = ENCODED_ZERO
60 | }
61 | // Return encoded string (including encoded leading zeros).
62 | return String(encoded, outputStart, encoded.size - outputStart)
63 | }
64 |
65 | /**
66 | * Decodes the base58 string into a [ByteArray]
67 | *
68 | * @return the decoded data bytes
69 | * @throws NumberFormatException if the string is not a valid base58 string
70 | */
71 | @kotlin.ExperimentalUnsignedTypes
72 | @Throws(NumberFormatException::class)
73 | fun String.fromBase58(): ByteArray {
74 | if (isEmpty()) {
75 | return ByteArray(0)
76 | }
77 | // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits).
78 | val input58 = ByteArray(length)
79 | for (i in 0 until length) {
80 | val c = this[i]
81 | val digit = if (c.toInt() < 128) alphabetIndices[c.toInt()] else -1
82 | if (digit < 0) {
83 | throw NumberFormatException("Illegal character $c at position $i")
84 | }
85 | input58[i] = digit.toByte()
86 | }
87 | // Count leading zeros.
88 | var zeros = 0
89 | while (zeros < input58.size && input58[zeros].toInt() == 0) {
90 | ++zeros
91 | }
92 | // Convert base-58 digits to base-256 digits.
93 | val decoded = ByteArray(length)
94 | var outputStart = decoded.size
95 | var inputStart = zeros
96 | while (inputStart < input58.size) {
97 | decoded[--outputStart] = divmod(input58, inputStart.toUInt(), 58.toUInt(), 256.toUInt()).toByte()
98 | if (input58[inputStart].toInt() == 0) {
99 | ++inputStart // optimization - skip leading zeros
100 | }
101 | }
102 | // Ignore extra leading zeroes that were added during the calculation.
103 | while (outputStart < decoded.size && decoded[outputStart].toInt() == 0) {
104 | ++outputStart
105 | }
106 | // Return decoded data (including original number of leading zeros).
107 | return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.size)
108 | }
109 |
110 | /**
111 | * Divides a number, represented as an array of bytes each containing a single digit
112 | * in the specified base, by the given divisor. The given number is modified in-place
113 | * to contain the quotient, and the return value is the remainder.
114 | *
115 | * @param number the number to divide
116 | * @param firstDigit the index within the array of the first non-zero digit
117 | * (this is used for optimization by skipping the leading zeros)
118 | * @param base the base in which the number's digits are represented (up to 256)
119 | * @param divisor the number to divide by (up to 256)
120 | * @return the remainder of the division operation
121 | */
122 | @kotlin.ExperimentalUnsignedTypes
123 | private fun divmod(number: ByteArray, firstDigit: UInt, base: UInt, divisor: UInt): UInt {
124 | // this is just long division which accounts for the base of the input digits
125 | var remainder = 0.toUInt()
126 | for (i in firstDigit until number.size.toUInt()) {
127 | val digit = number[i.toInt()].toUByte()
128 | val temp = remainder * base + digit
129 | number[i.toInt()] = (temp / divisor).toByte()
130 | remainder = temp % divisor
131 | }
132 | return remainder
133 | }
134 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/xyo-android-sample/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/res/layout/fragment_ble_client.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
20 |
21 |
28 |
29 |
35 |
36 |
42 |
43 |
49 |
50 |
56 |
57 |
63 |
64 |
68 |
74 |
81 |
82 |
86 |
92 |
99 |
100 |
104 |
110 |
117 |
118 |
119 |
120 |
129 |
142 |
143 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoBleClient.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("SpellCheckingInspection")
2 |
3 | package network.xyo.sdk
4 | import android.content.Context
5 | import android.util.Log
6 | import java.util.*
7 | import kotlinx.coroutines.GlobalScope
8 | import kotlinx.coroutines.launch
9 | import kotlinx.coroutines.sync.Mutex
10 | import network.xyo.ble.generic.devices.XYBluetoothDevice
11 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResult
12 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResultErrorCode
13 | import network.xyo.ble.generic.scanner.XYSmartScanListener
14 | import network.xyo.ble.generic.scanner.XYSmartScanModern
15 | import network.xyo.sdk.bluetooth.client.*
16 | import network.xyo.sdkcorekotlin.boundWitness.XyoBoundWitness
17 | import network.xyo.sdkcorekotlin.crypto.signing.ecdsa.secp256k.XyoSha256WithSecp256K
18 | import network.xyo.sdkcorekotlin.network.XyoNetworkHandler
19 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalog
20 | import network.xyo.sdkcorekotlin.node.XyoNodeListener
21 | import network.xyo.sdkcorekotlin.node.XyoRelayNode
22 |
23 | @kotlin.ExperimentalUnsignedTypes
24 | class XyoBleClient(
25 | context: Context,
26 | relayNode: XyoRelayNode,
27 | procedureCatalog: XyoProcedureCatalog,
28 | autoBridge: Boolean,
29 | acceptBridging: Boolean,
30 | autoBoundWitness: Boolean,
31 | scan: Boolean
32 | ) : XyoClient(relayNode, procedureCatalog, autoBoundWitness) {
33 |
34 | override var autoBridge: Boolean = false
35 | override var acceptBridging: Boolean = false
36 |
37 | override var deviceCount = 0
38 | override var xyoDeviceCount = 0
39 | override var nearbyXyoDeviceCount = 0
40 |
41 | var supportBridgeX = false
42 | var supportSentinelX = false
43 | var minimumRssi = -70
44 |
45 | val minBWTimeGap = 10 * 1000
46 |
47 | var lastBoundWitnessTime = Date().time - minBWTimeGap // ten seconds ago
48 |
49 | private var scanner: XYSmartScanModern
50 |
51 | private val boundWitnessMutex = Mutex()
52 |
53 | override var scan: Boolean
54 | get() {
55 | // val tag = "TAG "
56 | // Log.i(tag, "Does this scan??")
57 | return scanner.started()
58 | }
59 | set(value) {
60 | GlobalScope.launch {
61 | if (value && !scanner.started()) {
62 | scanner.start()
63 | } else if (!value && scanner.started()) {
64 | scanner.stop()
65 | }
66 | }
67 | }
68 |
69 | private val scannerListener = object : XYSmartScanListener() {
70 | // override fun onStart
71 | override fun entered(device: XYBluetoothDevice) {
72 | val tag = "TAG "
73 | super.entered(device)
74 | deviceCount++
75 | Log.i(tag, "Xyo Device Entered: ${device.id}")
76 | xyoDeviceCount++
77 | }
78 |
79 | override fun exited(device: XYBluetoothDevice) {
80 | val tag = "TAG "
81 | super.exited(device)
82 | deviceCount--
83 | Log.i(tag, "Xyo Device Exited: ${device.id}")
84 | xyoDeviceCount--
85 | }
86 | override fun detected(device: XYBluetoothDevice) {
87 | super.detected(device)
88 | if (this@XyoBleClient.autoBoundWitness) {
89 | if (Date().time - lastBoundWitnessTime > minBWTimeGap) {
90 | (device as? XyoBluetoothClient)?.let { client ->
91 | device.rssi?.let { rssi ->
92 | if (rssi < minimumRssi) {
93 | log.info("Rssi too low: $rssi")
94 | return
95 | }
96 | (client as? XyoBridgeX)?.let {
97 | if (!supportBridgeX) {
98 | log.info("BridgeX not Supported: $rssi")
99 | return
100 | }
101 | }
102 | (client as? XyoSentinelX)?.let {
103 | if (!supportSentinelX) {
104 | log.info("SentinelX not Supported: $rssi")
105 | return
106 | }
107 | }
108 | nearbyXyoDeviceCount++
109 | GlobalScope.launch {
110 | tryBoundWitnessWithDevice(client)
111 | }
112 | }
113 | }
114 | }
115 | }
116 | }
117 | }
118 |
119 | suspend fun tryBoundWitnessWithDevice(device: XyoBluetoothClient) {
120 | if (boundWitnessMutex.tryLock()) {
121 | boundWitnessStarted(device)
122 |
123 | var errorMessage: String? = null
124 |
125 | val result = device.connection {
126 | val pipe = device.createPipe()
127 |
128 | if (pipe != null) {
129 | val handler = XyoNetworkHandler(pipe)
130 |
131 | relayNode.addListener("tryBoundWitnessWithDevice", object : XyoNodeListener() {
132 | override fun onBoundWitnessEndFailure(error: Exception?) {
133 | errorMessage = error?.message ?: error?.toString() ?: "Unknown Error"
134 | }
135 | })
136 |
137 | val bw = relayNode.boundWitness(handler, procedureCatalog)
138 |
139 | relayNode.removeListener("tryBoundWitnessWithDevice")
140 |
141 | return@connection XYBluetoothResult(bw)
142 | }
143 |
144 | return@connection XYBluetoothResult(null)
145 | }
146 | val errorCode: String? =
147 | if (result.error != XYBluetoothResultErrorCode.None) {
148 | result.error.toString()
149 | } else {
150 | errorMessage
151 | }
152 | boundWitnessCompleted(device, result.value, errorCode)
153 | lastBoundWitnessTime = Date().time
154 | boundWitnessMutex.unlock()
155 | }
156 | }
157 |
158 | init {
159 | this.autoBridge = autoBridge
160 | this.acceptBridging = acceptBridging
161 | XyoBluetoothClient.enable(true)
162 | XyoBridgeX.enable(true)
163 | XyoSentinelX.enable(true)
164 | XyoIosAppX.enable(true)
165 | XyoAndroidAppX.enable(true)
166 | XyoSha256WithSecp256K.enable()
167 | this.scanner = XYSmartScanModern(context)
168 | this.scanner.addListener("xyo_client", this.scannerListener)
169 | this.scan = scan
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/xyo-android-sample/src/main/java/network/xyo/sdk/sample/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.sample
2 |
3 | import android.Manifest
4 | import android.content.Context
5 | import android.content.pm.PackageManager
6 | import android.location.LocationManager
7 | import android.util.Log
8 | import android.os.Bundle
9 | import com.google.android.material.bottomnavigation.BottomNavigationView
10 | import androidx.appcompat.app.AppCompatActivity
11 | import androidx.core.app.ActivityCompat
12 | import androidx.core.content.ContextCompat
13 | import androidx.navigation.findNavController
14 | import androidx.navigation.ui.AppBarConfiguration
15 | import androidx.navigation.ui.setupActionBarWithNavController
16 | import androidx.navigation.ui.setupWithNavController
17 | import kotlinx.coroutines.GlobalScope
18 | import kotlinx.coroutines.InternalCoroutinesApi
19 | import kotlinx.coroutines.launch
20 | import network.xyo.sdk.*
21 | import network.xyo.sdkcorekotlin.heuristics.XyoHeuristicGetter
22 | import network.xyo.sdkobjectmodelkotlin.structure.XyoObjectStructure
23 | import java.nio.ByteBuffer
24 | import network.xyo.sdkcorekotlin.schemas.*
25 | import network.xyo.sdkobjectmodelkotlin.structure.XyoIterableStructure
26 |
27 | @InternalCoroutinesApi
28 | @kotlin.ExperimentalUnsignedTypes
29 | class MainActivity : AppCompatActivity() {
30 | lateinit var node: XyoNode
31 |
32 | override fun onCreate(savedInstanceState: Bundle?) {
33 | super.onCreate(savedInstanceState)
34 |
35 | checkPermissions()
36 | }
37 |
38 | private fun checkPermissions () {
39 | // check if we already have the permission
40 | if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
41 | == PackageManager.PERMISSION_GRANTED) {
42 |
43 | // if we do, continue the setup process
44 | setupNodeAndUI()
45 | return
46 | }
47 |
48 | // if we do not, request the permission
49 | ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)
50 | }
51 |
52 |
53 | // this gets called when the user takes action on a permission request
54 | override fun onRequestPermissionsResult(
55 | requestCode: Int,
56 | permissions: Array,
57 | grantResults: IntArray
58 | ) {
59 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
60 |
61 | if (requestCode != 1) {
62 | return
63 | }
64 |
65 | // recheck the permissions
66 | checkPermissions()
67 | }
68 |
69 | private fun setupNodeAndUI () = GlobalScope.launch {
70 | // initializeXyoSimpleWithGps()
71 | // initializeXyoSimple()
72 | // initializeXyoBleClientOnly()
73 | // initializeXyoBleServerOnly()
74 | initializeXyoBleOnly()
75 | ui {
76 |
77 | setContentView(R.layout.activity_main)
78 | val navView: BottomNavigationView = findViewById(R.id.nav_view)
79 |
80 | val navController = findNavController(R.id.nav_host_fragment)
81 | // Passing each menu ID as a set of Ids because each
82 | // menu should be considered as top level destinations.
83 | val appBarConfiguration = AppBarConfiguration(
84 | setOf(
85 | R.id.navigation_ble_client, R.id.navigation_ble_server, R.id.navigation_tcpip_client, R.id.navigation_tcpip_server
86 | )
87 | )
88 | setupActionBarWithNavController(navController, appBarConfiguration)
89 | navView.setupWithNavController(navController)
90 | }
91 | }
92 |
93 | private suspend fun initializeXyoSimple() {
94 | val builder = XyoNodeBuilder()
95 | node = builder.build(this)
96 | }
97 |
98 | private suspend fun initializeXyoSimpleWithGps() {
99 | val builder = XyoNodeBuilder()
100 | node = builder.build(this)
101 | (node.networks["ble"] as? XyoBleNetwork)?.client?.relayNode?.addHeuristic(
102 | "GPS",
103 | object: XyoHeuristicGetter {
104 | override fun getHeuristic(): XyoObjectStructure? {
105 | val locationManager = applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
106 |
107 | if (ContextCompat.checkSelfPermission(applicationContext, android.Manifest.permission.ACCESS_FINE_LOCATION)
108 | == PackageManager.PERMISSION_GRANTED) {
109 | val lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
110 |
111 | if (lastLocation != null) {
112 | val encodedLat = ByteBuffer.allocate(8).putDouble(lastLocation.latitude).array()
113 | val encodedLng = ByteBuffer.allocate(8).putDouble(lastLocation.longitude).array()
114 | val lat = XyoObjectStructure.newInstance(XyoSchemas.LAT, encodedLat)
115 | val lng = XyoObjectStructure.newInstance(XyoSchemas.LNG, encodedLng)
116 |
117 | return XyoIterableStructure.createUntypedIterableObject(XyoSchemas.GPS, arrayOf(lat, lng))
118 | }
119 | }
120 | return null
121 | }
122 | }
123 | )
124 | }
125 |
126 | private suspend fun initializeXyoBleClientOnly() {
127 | val builder = XyoNodeBuilder()
128 | val tag = "TAG: "
129 | node = builder.build(this)
130 | (node.networks["ble"] as? XyoBleNetwork)?.let { network ->
131 | Log.i(tag, "Ble? Hello? ")
132 | network.client.autoBridge = true
133 | network.client.autoBoundWitness = true
134 | network.client.scan = true
135 | Log.i(tag, "Client Scan? $network.client.scan ")
136 | network.server.autoBridge = false
137 | network.server.listen = false
138 | }
139 | (node.networks["tcpip"] as? XyoTcpIpNetwork)?.let { network ->
140 | network.client.autoBridge = false
141 | network.client.autoBoundWitness = false
142 | network.server.autoBridge = false
143 | network.server.listen = false
144 | }
145 | }
146 |
147 | private suspend fun initializeXyoBleServerOnly() {
148 | val builder = XyoNodeBuilder()
149 | node = builder.build(this)
150 | (node.networks["ble"] as? XyoBleNetwork)?.let { network ->
151 | network.client.autoBridge = false
152 | network.client.autoBoundWitness = false
153 | network.client.scan = false
154 | network.server.autoBridge = true
155 | network.server.listen = true
156 | }
157 | (node.networks["tcpip"] as? XyoTcpIpNetwork)?.let { network ->
158 | network.client.autoBridge = false
159 | network.client.autoBoundWitness = false
160 | network.server.autoBridge = false
161 | network.server.listen = false
162 | }
163 | }
164 |
165 | private suspend fun initializeXyoBleOnly() {
166 | val builder = XyoNodeBuilder()
167 | node = builder.build(this)
168 | (node.networks["tcpip"] as? XyoTcpIpNetwork)?.let { network ->
169 | network.client.autoBridge = false
170 | network.client.autoBoundWitness = false
171 | network.server.autoBridge = false
172 | network.server.listen = false
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/client/XyoSentinelX.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth.client
2 |
3 | import android.bluetooth.BluetoothDevice
4 | import android.bluetooth.BluetoothGattCharacteristic
5 | import android.content.Context
6 | import android.os.Build
7 | import kotlinx.coroutines.*
8 | import network.xyo.ble.devices.xy.XY4BluetoothDevice
9 | import network.xyo.ble.devices.xy.XYFinderBluetoothDevice
10 | import network.xyo.ble.devices.xy.XYFinderBluetoothDeviceStayAwake
11 | import network.xyo.ble.firmware.XYBluetoothDeviceUpdate
12 | import network.xyo.ble.firmware.XYOtaFile
13 | import network.xyo.ble.firmware.XYOtaUpdate
14 | import network.xyo.ble.firmware.XYOtaUpdateListener
15 | import network.xyo.ble.generic.devices.XYBluetoothDevice
16 | import network.xyo.ble.generic.devices.XYCreator
17 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResult
18 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResultErrorCode
19 | import network.xyo.ble.generic.scanner.XYScanResult
20 | import network.xyo.ble.generic.services.standard.BatteryService
21 | import network.xyo.ble.generic.services.standard.DeviceInformationService
22 | import network.xyo.ble.services.dialog.SpotaService
23 | import network.xyo.ble.services.xy.PrimaryService
24 | import network.xyo.sdk.bluetooth.XyoUuids
25 | import java.io.ByteArrayInputStream
26 | import java.nio.ByteBuffer
27 | import java.util.*
28 | import java.util.concurrent.ConcurrentHashMap
29 | import kotlin.experimental.and
30 |
31 | @kotlin.ExperimentalUnsignedTypes
32 | open class XyoSentinelX : XyoBluetoothClient {
33 |
34 | constructor(context: Context, scanResult: XYScanResult, hash: String) : super(context, scanResult, hash)
35 |
36 | constructor(context: Context, scanResult: XYScanResult, hash: String, transport: Int) : super(context, scanResult, hash, transport)
37 |
38 | private val sentinelListeners = HashMap()
39 | private var lastButtonPressTime: Long = 0
40 |
41 | //Keep as public
42 | val batteryService = BatteryService(this)
43 | val primary = PrimaryService(this)
44 | val deviceInformationService = DeviceInformationService(this)
45 | val spotaService = SpotaService(this)
46 |
47 | fun addButtonListener(key: String, listener: Listener) {
48 | sentinelListeners[key] = listener
49 | }
50 |
51 | fun removeButtonListener(key: String) {
52 | sentinelListeners.remove(key)
53 | }
54 |
55 | fun isClaimed(): Boolean {
56 | val iBeaconData = scanResult?.scanRecord?.getManufacturerSpecificData(0x4c) ?: return true
57 |
58 | if (iBeaconData.size == 23) {
59 | val flags = iBeaconData[21]
60 | return flags and 1.toByte() != 0.toByte()
61 | }
62 |
63 | return true
64 | }
65 |
66 | private fun isButtonPressed(scanResult: XYScanResult): Boolean {
67 | val iBeaconData = scanResult.scanRecord?.getManufacturerSpecificData(0x4c) ?: return true
68 |
69 | if (iBeaconData.size == 23) {
70 | val flags = iBeaconData[21]
71 | return flags and 2.toByte() != 0.toByte()
72 | }
73 |
74 | return false
75 | }
76 |
77 | override fun onDetect(scanResult: XYScanResult?) {
78 | if (scanResult != null && isButtonPressed(scanResult) && lastButtonPressTime < System.currentTimeMillis() - 11_000) {
79 | // button of sentinel x is pressed
80 | lastButtonPressTime = System.currentTimeMillis()
81 | // TODO - added delay to allow listener attachment before calling it. onButtonPressed needs to be separate.
82 | CoroutineScope(Dispatchers.IO).launch {
83 | delay(1000)
84 | for ((_, l) in sentinelListeners) {
85 | l.onButtonPressed()
86 | }
87 | }
88 |
89 | return
90 | }
91 |
92 | return
93 | }
94 |
95 | /**
96 | * Changes the password on the remote device if the current password is correct.
97 | * @param password The password of the device now.
98 | * @param newPassword The password to change on the remote device.
99 | * @return An XYBluetoothError if there was an issue writing the packet.
100 | */
101 | suspend fun changePassword(password: ByteArray, newPassword: ByteArray): XYBluetoothResultErrorCode {
102 | val encoded = ByteBuffer.allocate(2 + password.size + newPassword.size)
103 | .put((password.size + 1).toByte())
104 | .put(password)
105 | .put((newPassword.size + 1).toByte())
106 | .put(newPassword)
107 | .array()
108 |
109 | return chunkSend(encoded, XyoUuids.XYO_PASSWORD, XyoUuids.XYO_SERVICE, 1)
110 | }
111 |
112 | /**
113 | * Changes the bound witness data on the remote device
114 | * @param boundWitnessData The data to include in tche remote devices bound witness.
115 | * @param password The password of the device to so it can write the boundWitnessData
116 | * @return An XYBluetoothError if there was an issue writing the packet.
117 | */
118 | suspend fun changeBoundWitnessData(password: ByteArray, boundWitnessData: ByteArray): XYBluetoothResultErrorCode {
119 | val encoded = ByteBuffer.allocate(3 + password.size + boundWitnessData.size)
120 | .put((password.size + 1).toByte())
121 | .put(password)
122 | .putShort((boundWitnessData.size + 2).toShort())
123 | .put(boundWitnessData)
124 | .array()
125 |
126 | return chunkSend(encoded, XyoUuids.XYO_CHANGE_BW_DATA, XyoUuids.XYO_SERVICE, 4)
127 | }
128 |
129 | suspend fun getBoundWitnessData(): XYBluetoothResult {
130 | return findAndReadCharacteristicBytes(XyoUuids.XYO_SERVICE, XyoUuids.XYO_CHANGE_BW_DATA)
131 | }
132 |
133 | /**
134 | * Reset the device.
135 | * @param password The password of the device to so it can write the boundWitnessData
136 | * @return An XYBluetoothError if there was an issue writing the packet.
137 | */
138 | suspend fun resetDevice(password: ByteArray): XYBluetoothResult {
139 | val msg = ByteBuffer.allocate(password.size + 2)
140 | .put((password.size + 2).toByte())
141 | .put((password.size + 1).toByte())
142 | .put(password)
143 | .array()
144 |
145 | return findAndWriteCharacteristic(
146 | XyoUuids.XYO_SERVICE,
147 | XyoUuids.XYO_RESET_DEVICE,
148 | msg,
149 | BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
150 | }
151 |
152 | /**
153 | * Lock the device.
154 | */
155 | suspend fun lock() = connection {
156 | return@connection primary.lock.set(XY4BluetoothDevice.DefaultLockCode)
157 | }
158 |
159 | /**
160 | * Unlock the device
161 | */
162 | suspend fun unlock() = connection {
163 | return@connection primary.unlock.set(XY4BluetoothDevice.DefaultLockCode)
164 | }
165 |
166 | suspend fun stayAwake() = connection {
167 | return@connection primary.stayAwake.set(XYFinderBluetoothDeviceStayAwake.On.state)
168 | }
169 |
170 | suspend fun fallAsleep() = connection {
171 | return@connection primary.stayAwake.set(XYFinderBluetoothDeviceStayAwake.Off.state)
172 | }
173 |
174 | suspend fun batteryLevel() = connection {
175 | return@connection batteryService.level.get()
176 | }
177 |
178 | /**
179 | * Firmware Update
180 | * @param fileByteArray the firmware as a ByteArray
181 | * @param listener listener for progress, failed, completed
182 | */
183 | fun updateFirmware(fileByteArray: ByteArrayInputStream, listener: XYOtaUpdateListener) {
184 | val otaFile = fileByteArray.let { XYOtaFile.getByStream(it) }
185 | val updater = XYBluetoothDeviceUpdate(spotaService, this, otaFile)
186 | updater.addListener("SentinelXDevice", listener)
187 | updater.start()
188 | }
189 |
190 | companion object : XYCreator() {
191 |
192 | open class Listener {
193 | open fun onButtonPressed() {}
194 | }
195 |
196 | fun enable(enable: Boolean) {
197 | if (enable) {
198 | xyoManufactureIdToCreator[XyoBluetoothClientDeviceType.SentinelX.raw] = this
199 | } else {
200 | xyoManufactureIdToCreator.remove(XyoBluetoothClientDeviceType.SentinelX.raw)
201 | }
202 | }
203 |
204 | override fun getDevicesFromScanResult(
205 | context: Context,
206 | scanResult: XYScanResult,
207 | globalDevices: ConcurrentHashMap,
208 | foundDevices: HashMap
210 | ) {
211 | val hash = hashFromScanResult(scanResult)
212 | val existingDevice = globalDevices[hash]
213 | if (existingDevice != null) {
214 | existingDevice.rssi = scanResult.rssi
215 | existingDevice.updateBluetoothDevice(scanResult.device)
216 | } else {
217 | val createdDevice = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
218 | XyoSentinelX(context, scanResult, hash, BluetoothDevice.TRANSPORT_LE)
219 | } else {
220 | XyoSentinelX(context, scanResult, hash)
221 | }
222 | foundDevices[hash] = createdDevice
223 | globalDevices[hash] = createdDevice
224 | }
225 | }
226 | }
227 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [logo]: https://cdn.xy.company/img/brand/XYO_full_colored.png
2 |
3 | [![logo]](https://xyo.network)
4 |
5 | # sdk-xyo-android
6 |
7 | [](https://github.com/XYOracleNetwork/sdk-xyo-android/actions?query=workflow%3ACI+branch%3Adevelop) [](https://github.com/XYOracleNetwork/sdk-xyo-android/actions?query=workflow%3ARelease+branch%3Amaster) [ ](https://bintray.com/xyoraclenetwork/xyo/sdk-xyo-android/_latestVersion) [](https://www.codacy.com?utm_source=github.com&utm_medium=referral&utm_content=XYOracleNetwork/sdk-xyo-android&utm_campaign=Badge_Grade) [](https://bettercodehub.com/) [](https://codeclimate.com/github/XYOracleNetwork/sdk-xyo-android/maintainability) [](https://snyk.io/test/github/XYOracleNetwork/sdk-xyo-android?targetFile=xyo-android-library/build.gradle)
8 |
9 | > The XYO Foundation provides this source code available in our efforts to advance the understanding of the XYO Procotol and its possible uses. We continue to maintain this software in the interest of developer education. Usage of this source code is not intended for production.
10 |
11 | ## Table of Contents
12 |
13 | - [Title](#sdk-xyo-android)
14 | - [Description](#description)
15 | - [Gradle Build](#gradle-build)
16 | - [Maven Build](#maven-build)
17 | - [Examples](#examples)
18 | - [Usage](#usage)
19 | - [Architecture](#architecture)
20 | - [Maintainers](#maintainers)
21 | - [Contributing](#contributing)
22 | - [License](#license)
23 | - [Credits](#credits)
24 |
25 | ## Description
26 |
27 | A high-level SDK for interacting with the XYO network.
28 | Including BLE, TCP/IP, Bound Witnessing, and Bridging. Use this instead of `sdk-core-kotlin` for integration with your app project.
29 |
30 | > As you are developing on the SDK, be sure to keep an eye out for updates, as they may include core and ble updates
31 |
32 | ## Gradle Build
33 |
34 | ```gradle
35 | compile 'network.xyo:sdk-xyo-android:3.1.38'
36 | ```
37 |
38 | ## Maven Build
39 |
40 | ```maven
41 |
42 | network.xyo
43 | sdk-xyo-android
44 | 3.1.38
45 | pom
46 |
47 |
48 | ```
49 |
50 | ## Permissions and Set Up
51 |
52 | When using Android Studio and an Android device to test BLE Client and Server be sure to enable permissions and add an initialization function in your `MainActivity`
53 |
54 | `AndroidManifest.xml`
55 |
56 | ```xml
57 |
58 |
59 |
60 | ```
61 |
62 | `MainActivity.kt`
63 |
64 | ```kotlin
65 | GlobalScope.launch {
66 | // place initialization function here for node build needed
67 | // you can look at the sample app for code example
68 | }
69 | ```
70 |
71 | ## Examples
72 |
73 | Copy this code to test. Look below for specific usage.
74 |
75 | One line is all it takes to start your node
76 |
77 | ```kotlin
78 | val node = XyoNodeBuilder().build(context)
79 | ```
80 |
81 | For a more complex test, create a listener callback.
82 |
83 | ``` kotlin
84 | // callback for node events
85 | val listener = object : XyoBoundWitnessTarget.Listener() {
86 | override fun boundWitnessCompleted(boundWitness: XyoBoundWitness?, error: String?) {
87 | super.boundWitnessCompleted(boundWitness, error)
88 |
89 | println("New bound witness!")
90 | }
91 |
92 | override fun boundWitnessStarted() {
93 | super.boundWitnessStarted()
94 |
95 | println("Bound witness started!")
96 |
97 | }
98 | }
99 | ```
100 | You can also configure to your specific roles.
101 |
102 | ```kotlin
103 | // build and configure the node
104 | val builder = XyoNodeBuilder()
105 | builder.setListener(listener)
106 |
107 | // create the node
108 | val context = getContextSomehow()
109 | val node = builder.build(context)
110 |
111 | // configure tcp
112 | val tcpNetwork = node.networks["tcpip"] ?: return
113 | tcpNetwork.client.autoBridge = true
114 | tcpNetwork.client.autoBoundWitness = true
115 | tcpNetwork.client.scan = false
116 | tcpNetwork.client.knownBridges = ["public key of bridge", "public key of bridge"]
117 |
118 | // configure ble
119 | val bleNetwork = node.networks["ble"] ?: return
120 | bleNetwork.client.autoBridge = true
121 | bleNetwork.client.autoBoundWitness = true
122 | bleNetwork.client.scan = false
123 | ```
124 |
125 | You can also use a heuristic getter, here is an example to get GPS.
126 |
127 | ```kotlin
128 | (node.networks["ble"] as? XyoBleNetwork)?.client?.relayNode?.addHeuristic(
129 | "GPS",
130 | object: XyoHeuristicGetter {
131 | override fun getHeuristic(): XyoObjectStructure? {
132 | val locationManager = applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
133 |
134 | if (ContextCompat.checkSelfPermission(applicationContext, android.Manifest.permission.ACCESS_FINE_LOCATION)
135 | == PackageManager.PERMISSION_GRANTED) {
136 | val lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
137 |
138 | if (lastLocation != null) {
139 | val encodedLat = ByteBuffer.allocate(8).putDouble(lastLocation.latitude).array()
140 | val encodedLng = ByteBuffer.allocate(8).putDouble(lastLocation.longitude).array()
141 | val lat = XyoObjectStructure.newInstance(XyoSchemas.LAT, encodedLat)
142 | val lng = XyoObjectStructure.newInstance(XyoSchemas.LNG, encodedLng)
143 |
144 | return XyoIterableStructure.createUntypedIterableObject(XyoSchemas.GPS, arrayOf(lat, lng))
145 | }
146 | }
147 | return null
148 | }
149 | }
150 | )
151 | ```
152 |
153 | ## Usage
154 |
155 | Build an XYO Node
156 |
157 | ```kotlin
158 | val builder = XYONodeBuilder()
159 | ```
160 |
161 | After calling the node builder, you can start the build
162 |
163 | ```kotlin
164 | val node = XyoNode()
165 |
166 | node = builder.build(this)
167 | ```
168 |
169 | Once you have a build, you have access to properties to help you shape your node and what you want out of it.
170 |
171 | ```kotlin
172 | node.networks["this can be "ble" or "tcpip""]
173 | ```
174 |
175 | After choosing the network, you have these properties available
176 |
177 | Client
178 |
179 | ```kotlin
180 | // select the network
181 | val network = node.networks["network"]
182 |
183 | // a flag to tell the client to automatically bridge
184 | network.client.autoBridge
185 |
186 | // a flag to tell the client to automatically bound witness
187 | network.client.autoBoundWitness
188 |
189 | // a flag to tell the client to automatically scan
190 | network.client.scan
191 | ```
192 |
193 | Server
194 |
195 | ```kotlin
196 | // select the network
197 | val network = node.networks["network"]
198 |
199 | // a flag to tell the server to automatically bridge
200 | network.server.autoBridge
201 |
202 | // a flag to tell the client to automatically listen for bridging
203 | network.server.listen
204 | ```
205 |
206 | These will allow your app to actively seek devices to bound witness with and bridge from the client to the server.
207 |
208 | You can also get payload data from the bound witness
209 |
210 | ```kotlin
211 | node.listener.getPayloadData(target: XyoBoundWitnessTarget)
212 | ```
213 |
214 | This will return a byteArray.
215 |
216 | There are other properties from the client and server which you can find in the source code as well as a reference guide that we have prepared.
217 |
218 |
219 | ## Architecture
220 |
221 | This sdk is built on a client/server to ensure ease of understanding during development. (The client takes on "central" role, and the server the "peripheral"). This allows us to define roles with simplicity.
222 |
223 | > SDK-XYO-ANDROID TREE
224 |
225 | - XyoSDK
226 | - mutableList ``
227 | - `XyoNode(storage, networks)`
228 | - `listeners`
229 | - `boundWitnessTarget`
230 | - XyoClient, XyoServer
231 | - Ble
232 | - `context`
233 | - `relayNode`
234 | - `procedureCatalog`
235 | - `autoBridge`
236 | - `acceptBridging`
237 | - `autoBoundWitness`
238 | - `scan`
239 |
240 | - TcpIp
241 | - `relayNode`
242 | - `procedureCatalog`
243 | - `autoBridge`
244 | - `acceptBridging`
245 | - `autoBoundWitness`
246 |
247 | ## Sample App
248 |
249 | Please refer to the [xyo-android-sample](/xyo-android-sample/src/main/java/network/xyo/sdk/sample/MainActivity.kt) for an exmple implementation for bound witness and bridging.
250 |
251 | ### Install
252 |
253 | To use the sample app to measure functionality
254 |
255 | - Launch [Android Studio](https://developer.android.com/studio/install)
256 | - Click on `Open an existing Android Studio Project`
257 | - Navigate to `/xyo-android-sample` in your file explorer
258 |
259 | Once you open the sample in Android Studio it will execute the build.
260 |
261 | You can then run the app in a simulator of your choice or an Android device.
262 |
263 | This sample app includes client bridging and bound witnessing with a BLE server listener.
264 |
265 | ## Maintainers
266 |
267 | - Arie Trouw
268 |
269 | ## Contributing
270 |
271 | Please note that any contributions must clear the `release` branch.
272 |
273 | ## License
274 |
275 | See the [LICENSE](LICENSE) file for license details.
276 |
277 | ## Credits
278 |
279 | Made with 🔥and ❄️ by [XYO](https://www.xyo.network)
280 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/XyoNodeBuilder.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk
2 | import android.content.Context
3 | import kotlinx.coroutines.InternalCoroutinesApi
4 | import java.lang.Exception
5 | import java.nio.ByteBuffer
6 | import network.xyo.base.XYBase
7 | import network.xyo.sdkcorekotlin.crypto.signing.XyoSigner
8 | import network.xyo.sdkcorekotlin.crypto.signing.ecdsa.secp256k.XyoSha256WithSecp256K
9 | import network.xyo.sdkcorekotlin.hashing.XyoBasicHashBase
10 | import network.xyo.sdkcorekotlin.hashing.XyoHash
11 | import network.xyo.sdkcorekotlin.heuristics.XyoUnixTime
12 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalog
13 | import network.xyo.sdkcorekotlin.network.XyoProcedureCatalogFlags
14 | import network.xyo.sdkcorekotlin.node.XyoRelayNode
15 | import network.xyo.sdkcorekotlin.persist.XyoKeyValueStore
16 | import network.xyo.sdkcorekotlin.persist.repositories.XyoStorageBridgeQueueRepository
17 | import network.xyo.sdkcorekotlin.persist.repositories.XyoStorageOriginBlockRepository
18 | import network.xyo.sdkcorekotlin.persist.repositories.XyoStorageOriginStateRepository
19 | import network.xyo.sdkcorekotlin.schemas.XyoSchemas
20 |
21 | @kotlin.ExperimentalUnsignedTypes
22 | class XyoNodeBuilder : XYBase() {
23 | private var networks = mutableMapOf()
24 | private var storage: XyoKeyValueStore? = null
25 | private var listener: XyoBoundWitnessTarget.Listener? = null
26 |
27 | private var relayNode: XyoRelayNode? = null
28 | private var procedureCatalog: XyoProcedureCatalog? = null
29 | private var blockRepository: XyoStorageOriginBlockRepository? = null
30 | private var stateRepository: XyoStorageOriginStateRepository? = null
31 | private var bridgeQueueRepository: XyoStorageBridgeQueueRepository? = null
32 | private var hashingProvider: XyoHash.XyoHashProvider? = null
33 | private var knownBridges = mutableListOf()
34 |
35 | fun addNetwork(name: String, network: XyoNetwork) {
36 | networks[name] = network
37 | }
38 |
39 | fun setStorage(storage: XyoKeyValueStore) {
40 | this.storage = storage
41 | }
42 |
43 | fun setListener(listener: XyoBoundWitnessTarget.Listener) {
44 | this.listener = listener
45 | }
46 |
47 | @InternalCoroutinesApi
48 | suspend fun build(context: Context): XyoNode {
49 | if (XyoSdk.nodes.isNotEmpty()) {
50 | throw Exception()
51 | }
52 |
53 | if (storage == null) {
54 | log.info("No storage specified, using default")
55 | setDefaultStorage(context)
56 | }
57 |
58 | if (hashingProvider == null) {
59 | log.info("No hashingProvider specified, using default")
60 | setDefaultHashingProvider()
61 | }
62 |
63 | if (blockRepository == null) {
64 | log.info("No blockRepository specified, using default")
65 | setDefaultBlockRepository()
66 | }
67 |
68 | if (stateRepository == null) {
69 | log.info("No stateRepository specified, using default")
70 | setDefaultStateRepository()
71 | }
72 |
73 | if (bridgeQueueRepository == null) {
74 | log.info("No bridgeQueueRepository specified, using default")
75 | setDefaultBridgeQueueRepository()
76 | }
77 |
78 | if (procedureCatalog == null) {
79 | log.info("No procedureCatalog specified, using default")
80 | setDefaultProcedureCatalog()
81 | }
82 |
83 | if (relayNode == null) {
84 | log.info("No relayNode specified, using default")
85 | setDefaultRelayNode()
86 | }
87 |
88 | if (networks.isEmpty()) {
89 | log.info("No networks specified, using default")
90 | setDefaultNetworks(context)
91 | }
92 |
93 | val node = XyoNode(networks)
94 | XyoSdk.nodes.add(node)
95 |
96 | restoreAndInitBlockStorage()
97 |
98 | listener?.let {
99 | node.setAllListeners("default", it)
100 | }
101 |
102 | return node
103 | }
104 |
105 | private fun setDefaultProcedureCatalog() {
106 | /*procedureCatalog = object : XyoProcedureCatalog {
107 | override fun canDo(byteArray: ByteArray): Boolean {
108 | if (true) {
109 | return true
110 | }
111 |
112 | return ByteBuffer.wrap(byteArray).int and 1 != 0
113 | }
114 |
115 | override fun choose(byteArray: ByteArray): ByteArray {
116 | return byteArrayOf(0x00, 0x00, 0x00, 0x01)
117 | }
118 |
119 | override fun getEncodedCanDo(): ByteArray {
120 | if (true) {
121 | return byteArrayOf(0x00, 0x00, 0x00, 0xff.toByte())
122 | }
123 |
124 | return byteArrayOf(0x00, 0x00, 0x00, 0x01)
125 | }
126 | } */
127 | procedureCatalog = object : XyoProcedureCatalog {
128 | val canDoByte = XyoProcedureCatalogFlags.BOUND_WITNESS or XyoProcedureCatalogFlags.GIVE_ORIGIN_CHAIN or XyoProcedureCatalogFlags.TAKE_ORIGIN_CHAIN
129 |
130 | override fun canDo(byteArray: ByteArray): Boolean {
131 | if (byteArray.isEmpty()) {
132 | return false
133 | }
134 |
135 | return byteArray.last().toInt() and canDoByte != 0
136 | }
137 |
138 | override fun choose(byteArray: ByteArray): ByteArray {
139 | if (byteArray.isEmpty()) {
140 | return byteArrayOf(XyoProcedureCatalogFlags.BOUND_WITNESS.toByte())
141 | }
142 |
143 | val interestedIn = byteArray.last().toInt()
144 |
145 | if (interestedIn and XyoProcedureCatalogFlags.GIVE_ORIGIN_CHAIN != 0) {
146 | return byteArrayOf(XyoProcedureCatalogFlags.TAKE_ORIGIN_CHAIN.toByte())
147 | }
148 |
149 | if (interestedIn and XyoProcedureCatalogFlags.TAKE_ORIGIN_CHAIN != 0) {
150 | return byteArrayOf(XyoProcedureCatalogFlags.GIVE_ORIGIN_CHAIN.toByte())
151 | }
152 |
153 | return byteArrayOf(XyoProcedureCatalogFlags.BOUND_WITNESS.toByte())
154 | }
155 |
156 | override fun getEncodedCanDo(): ByteArray {
157 |
158 | return byteArrayOf(0x00, 0x00, 0x00, canDoByte.toByte())
159 | }
160 | }
161 | }
162 |
163 | private fun setDefaultBlockRepository() {
164 | storage?.let { storage ->
165 | hashingProvider?.let { hashingProvider ->
166 | blockRepository = XyoStorageOriginBlockRepository(storage, hashingProvider)
167 | return
168 | }
169 | log.error("Missing hashingProvider", true)
170 | return
171 | }
172 | log.error("Missing storage", true)
173 | }
174 |
175 | private fun setDefaultStateRepository() {
176 | storage?.let { storage ->
177 | stateRepository = XyoStorageOriginStateRepository(storage)
178 | return
179 | }
180 | log.error("Missing storage", true)
181 | }
182 |
183 | private fun setDefaultBridgeQueueRepository() {
184 | storage?.let { storage ->
185 | bridgeQueueRepository = XyoStorageBridgeQueueRepository(storage)
186 | return
187 | }
188 | log.error("Missing storage", true)
189 | }
190 |
191 | private fun setDefaultHashingProvider() {
192 | hashingProvider = XyoBasicHashBase.createHashType(XyoSchemas.SHA_256, "SHA-256")
193 | }
194 |
195 | private fun setDefaultRelayNode() {
196 | blockRepository?.let { blockRepository ->
197 | stateRepository?.let { stateRepository ->
198 | bridgeQueueRepository?.let { bridgeQueueRepository ->
199 | hashingProvider?.let { hashingProvider ->
200 | relayNode = XyoRelayNode(
201 | blockRepository,
202 | stateRepository,
203 | bridgeQueueRepository,
204 | hashingProvider
205 | )
206 | return
207 | }
208 | log.error("Missing hashingProvider", true)
209 | return
210 | }
211 | log.error("Missing bridgeQueueRepository", true)
212 | return
213 | }
214 | log.error("Missing stateRepository", true)
215 | return
216 | }
217 | log.error("Missing blockRepository", true)
218 | }
219 |
220 | private suspend fun getSigner(): XyoSigner {
221 | storage!!.let { storage ->
222 | val currentSigner = storage.read(SIGNER_KEY)
223 |
224 | if (currentSigner == null) {
225 | val newSigner = XyoSha256WithSecp256K.newInstance()
226 | storage.write(SIGNER_KEY, newSigner.privateKey.bytesCopy)
227 | return newSigner
228 | }
229 |
230 | return XyoSha256WithSecp256K.newInstance(currentSigner)
231 | }
232 | }
233 |
234 | private suspend fun restoreAndInitBlockStorage() {
235 | relayNode!!.let { relayNode ->
236 | relayNode.originBlocksToBridge.removeWeight = 2
237 | relayNode.originBlocksToBridge.sendLimit = 38
238 | relayNode.addHeuristic("TIME", XyoUnixTime.getter)
239 |
240 | val currentSigner = getSigner()
241 |
242 | stateRepository!!.restore(arrayListOf(currentSigner))
243 | bridgeQueueRepository!!.restore()
244 |
245 | if (ByteBuffer.wrap((relayNode.originState.index.valueCopy)).int == 0) {
246 | relayNode.selfSignOriginChain()
247 | }
248 | }
249 | }
250 |
251 | @InternalCoroutinesApi
252 | private fun setDefaultNetworks(context: Context) {
253 | relayNode?.let { relayNode ->
254 | procedureCatalog?.let { procedureCatalog ->
255 | addNetwork("ble", XyoBleNetwork(context, relayNode, procedureCatalog))
256 | addNetwork("tcpip", XyoTcpIpNetwork(relayNode, procedureCatalog))
257 | return
258 | }
259 | log.error("Missing procedureCatalog", true)
260 | return
261 | }
262 | log.error("Missing relayNode", true)
263 | }
264 |
265 | private fun setDefaultStorage(context: Context) {
266 | setStorage(XyoSnappyDBStorageProvider(context))
267 | }
268 |
269 | companion object {
270 | private val SIGNER_KEY = "SIGNER_KEY".toByteArray()
271 | }
272 | }
273 |
--------------------------------------------------------------------------------
/xyo-android-library/src/main/java/network/xyo/sdk/bluetooth/client/XyoBluetoothClient.kt:
--------------------------------------------------------------------------------
1 | package network.xyo.sdk.bluetooth.client
2 |
3 | import android.annotation.SuppressLint
4 | import android.bluetooth.BluetoothDevice
5 | import android.bluetooth.BluetoothGatt
6 | import android.bluetooth.BluetoothGattCharacteristic
7 | import android.content.Context
8 | import android.os.Build
9 | import android.util.Log
10 | import kotlinx.coroutines.*
11 | import network.xyo.ble.devices.apple.XYAppleBluetoothDevice
12 | import network.xyo.ble.devices.apple.XYIBeaconBluetoothDevice
13 | import network.xyo.ble.generic.devices.XYBluetoothDevice
14 | import network.xyo.ble.generic.devices.XYCreator
15 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothGattCallback
16 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResult
17 | import network.xyo.ble.generic.gatt.peripheral.XYBluetoothResultErrorCode
18 | import network.xyo.ble.generic.scanner.XYScanResult
19 | import network.xyo.sdk.bluetooth.XyoUuids
20 | import network.xyo.sdk.bluetooth.packet.XyoBluetoothIncomingPacket
21 | import network.xyo.sdk.bluetooth.packet.XyoBluetoothOutgoingPacket
22 | import network.xyo.sdkcorekotlin.network.XyoNetworkPipe
23 | import java.nio.ByteBuffer
24 | import java.util.*
25 | import java.util.concurrent.ConcurrentHashMap
26 | import kotlin.collections.HashMap
27 | import kotlin.coroutines.resume
28 | import kotlin.coroutines.suspendCoroutine
29 | import kotlin.experimental.and
30 |
31 | enum class XyoBluetoothClientDeviceType(val raw: Byte){
32 | SentinelX(0x01),
33 | IosAppX(0x02),
34 | BridgeX(0x03),
35 | AndroidAppX(0x04),
36 | }
37 |
38 | /**
39 | * A Bluetooth client that can create a XyoNetworkPipe. This pipe can be used with the sdk-core-kotlin to talk to
40 | * other XYO enabled devices.
41 | *
42 | * @property context Context of the device
43 | * @property device The android bluetooth device
44 | * @property hash The unique hash of the device
45 | */
46 | @kotlin.ExperimentalUnsignedTypes
47 | open class XyoBluetoothClient : XYIBeaconBluetoothDevice {
48 |
49 | constructor(context: Context, scanResult: XYScanResult, hash: String) : super(context, scanResult, hash)
50 |
51 | constructor(context: Context, scanResult: XYScanResult, hash: String, transport: Int) : super(context, scanResult, hash, transport)
52 |
53 | /**
54 | * The standard size of the MTU of the connection. This value is used when chunking large amounts of data.
55 | */
56 | private var mtu = DEFAULT_MTU
57 |
58 | /**
59 | * creates a XyoNetworkPipe with THIS bluetooth device.
60 | * @return A Deferred XyoNetworkPipe if successful, null if not.
61 | */
62 | suspend fun createPipe(): XyoNetworkPipe? {
63 | findAndWriteCharacteristicNotify(XyoUuids.XYO_SERVICE, XyoUuids.XYO_PIPE, true)
64 |
65 | val requestMtu = requestMtu(MAX_MTU)
66 |
67 | mtu = (requestMtu.value ?: mtu) - 3
68 |
69 | return XyoBluetoothClientPipe(this)
70 | }
71 |
72 | /**
73 | * Get the public Key
74 | */
75 | suspend fun getPublicKey(): XYBluetoothResult {
76 | return findAndReadCharacteristicBytes(XyoUuids.XYO_SERVICE, XyoUuids.XYO_PUBLIC_KEY)
77 | }
78 |
79 | /**
80 | * Preforms a chunk send
81 | *
82 | * @param outgoingPacket The packet to send to the server. This value will be chunked accordingly, if larger than
83 | * the MTU of the connection.
84 | * @param characteristic The characteristic UUID to write to.
85 | * @param service The service UUID to write to.
86 | * @param sizeOfSize size of the packet header size to send
87 | * @return An XYBluetoothError if there was an issue writing the packet.
88 | */
89 | suspend fun chunkSend(outgoingPacket: ByteArray, characteristic: UUID, service: UUID, sizeOfSize: Int): XYBluetoothResultErrorCode {
90 | Log.i(TAG, "chunkSend: started")
91 | val chunkedOutgoingPacket = XyoBluetoothOutgoingPacket(mtu, outgoingPacket, sizeOfSize)
92 |
93 | var errorCode = XYBluetoothResultErrorCode.None
94 |
95 | connection {
96 | while (chunkedOutgoingPacket.canSendNext && errorCode == XYBluetoothResultErrorCode.None) {
97 | val result = findAndWriteCharacteristic(
98 | service,
99 | characteristic,
100 | chunkedOutgoingPacket.getNext(),
101 | BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
102 | )
103 | delay(500)
104 | if (result.error != XYBluetoothResultErrorCode.None) {
105 | errorCode = result.error
106 | }
107 | }
108 | return@connection XYBluetoothResult(true)
109 | }
110 | return errorCode
111 | }
112 |
113 |
114 | /**
115 | * Listens for notifications to read incoming packets. This must be invoked before notifications
116 | * are sent. Timeout of the first notification is defined with FIRST_NOTIFY_TIMEOUT, in
117 | * milliseconds and the notification timeout delta is defined as NOTIFY_TIMEOUT in milliseconds.
118 | *
119 | * @return A deferred ByteArray of the value read. If there was an error or timeout, will return null.
120 | */
121 | suspend fun readIncoming(): ByteArray? {
122 | Log.i(TAG, "readIncoming: started")
123 | return suspendCoroutine { cont ->
124 | val key = this.toString() + Math.random().toString()
125 |
126 | centralCallback.addListener(key, object : XYBluetoothGattCallback() {
127 | var numberOfPackets = 0
128 | var hasResumed = false
129 |
130 | var timeoutJob: Job = GlobalScope.launch {
131 | delay(FIRST_NOTIFY_TIMEOUT.toLong())
132 | if (isActive) {
133 | Log.e(TAG, "readIncoming: timeout")
134 | hasResumed = true
135 | centralCallback.removeListener(key)
136 | cont.resume(null)
137 | }
138 | }
139 |
140 | var incomingPacket: XyoBluetoothIncomingPacket? = null
141 |
142 | override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?) {
143 | Log.i(TAG, "readIncoming: onCharacteristicChanged")
144 | super.onCharacteristicChanged(gatt, characteristic)
145 | val value = characteristic?.value
146 |
147 | if (characteristic?.uuid == XyoUuids.XYO_PIPE && !hasResumed) {
148 |
149 | if (numberOfPackets == 0 && value != null) {
150 | incomingPacket = XyoBluetoothIncomingPacket(value)
151 | } else if (value != null) {
152 | incomingPacket?.addPacket(value)
153 | }
154 |
155 | if (incomingPacket?.done == true) {
156 | hasResumed = true
157 | centralCallback.removeListener(key)
158 | timeoutJob.cancel()
159 | cont.resume(incomingPacket?.getCurrentBuffer())
160 | } else {
161 | timeoutJob.cancel()
162 | timeoutJob = GlobalScope.launch {
163 | delay(NOTIFY_TIMEOUT.toLong())
164 | hasResumed = true
165 | centralCallback.removeListener(key)
166 | cont.resume(null)
167 | }
168 | }
169 |
170 | numberOfPackets++
171 | }
172 | }
173 | })
174 | }
175 | }
176 |
177 |
178 | companion object : XYCreator() {
179 | const val TAG = "XyoBluetoothClient"
180 | const val FIRST_NOTIFY_TIMEOUT = 12_000
181 | const val NOTIFY_TIMEOUT = 10_000
182 | const val MAX_MTU = 512
183 | const val DEFAULT_MTU = 22
184 |
185 | const val DEVICE_TYPE_MASK = 0x3f.toByte()
186 |
187 | @SuppressLint("UseSparseArrays") //SparseArrays cannot use Byte as key
188 | val xyoManufactureIdToCreator = HashMap()
189 |
190 | /**
191 | * Enable this device to be created on scan.
192 | *
193 | * @param enable Weather or not to enable the device.
194 | */
195 | fun enable(enable: Boolean) {
196 | if (enable) {
197 | serviceToCreator[XyoUuids.XYO_SERVICE] = this
198 | uuidToCreator[XyoUuids.XYO_SERVICE] = this
199 | XYBluetoothGattCallback.blockNotificationCallback = true
200 | XYIBeaconBluetoothDevice.enable(true)
201 | } else {
202 | serviceToCreator.remove(XyoUuids.XYO_SERVICE)
203 | uuidToCreator.remove(XyoUuids.XYO_SERVICE)
204 | XYIBeaconBluetoothDevice.enable(false)
205 | XYBluetoothGattCallback.blockNotificationCallback = false
206 | }
207 | }
208 |
209 | private fun majorFromScanResult(scanResult: XYScanResult): UShort? {
210 | val bytes = scanResult.scanRecord?.getManufacturerSpecificData(XYAppleBluetoothDevice.MANUFACTURER_ID)
211 | return if (bytes != null) {
212 | val buffer = ByteBuffer.wrap(bytes)
213 | buffer.getShort(18).toUShort()
214 | } else {
215 | null
216 | }
217 | }
218 |
219 | private fun minorFromScanResult(scanResult: XYScanResult): UShort? {
220 | val bytes = scanResult.scanRecord?.getManufacturerSpecificData(XYAppleBluetoothDevice.MANUFACTURER_ID)
221 | return if (bytes != null) {
222 | val buffer = ByteBuffer.wrap(bytes)
223 | buffer.getShort(20).toUShort()
224 | } else {
225 | null
226 | }
227 | }
228 |
229 | internal fun hashFromScanResult(scanResult: XYScanResult): String {
230 | val uuid = iBeaconUuidFromScanResult(scanResult)
231 | val major = majorFromScanResult(scanResult)
232 | val minor = minorFromScanResult(scanResult)
233 |
234 | return "$uuid:$major:$minor"
235 | }
236 |
237 | override fun getDevicesFromScanResult(
238 | context: Context,
239 | scanResult: XYScanResult,
240 | globalDevices: ConcurrentHashMap,
241 | foundDevices: HashMap
243 | ) {
244 | val hash = hashFromScanResult(scanResult)
245 |
246 | val existingDevice = globalDevices[hash]
247 | if (existingDevice != null) {
248 | existingDevice.rssi = scanResult.rssi
249 | existingDevice.updateBluetoothDevice(scanResult.device)
250 | } else {
251 | log.info("Device Creating: $hash")
252 | val ad = scanResult.scanRecord?.getManufacturerSpecificData(0x4c)
253 |
254 | if (ad?.size == 23) {
255 | val id = ad[19]
256 |
257 | // masks the byte with 00111111 - AT: Is this a Check if is Xyo Enabled Device?
258 | if (xyoManufactureIdToCreator.containsKey(id and DEVICE_TYPE_MASK)) {
259 | xyoManufactureIdToCreator[id and DEVICE_TYPE_MASK]?.getDevicesFromScanResult(context, scanResult, globalDevices, foundDevices)
260 | return
261 | } else {
262 | log.info("Not an Xyo Device - Not Creating: $hash")
263 |
264 | val createdDevice = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
265 | XyoBluetoothClient(context, scanResult, hash, BluetoothDevice.TRANSPORT_LE)
266 | } else {
267 | XyoBluetoothClient(context, scanResult, hash)
268 | }
269 |
270 | foundDevices[hash] = createdDevice
271 | globalDevices[hash] = createdDevice
272 | }
273 | }
274 | }
275 | }
276 | }
277 | }
278 |
--------------------------------------------------------------------------------