From 95917b013eb309cef2fc470e1bba77e2ce4f91ff Mon Sep 17 00:00:00 2001 From: kangping Date: Tue, 8 Dec 2020 02:14:17 +0800 Subject: [PATCH] [android-release/0.0.1] initial Commissioner Android App (#158) This commit adds the initial OT Commissioner Android App: - Commission new Joiner Device with Thread 1.1 MeshCoP - Scan Thread Network by mDNS - Scan Joiner QR code - do 1.1 MeshCoP - setup GitHub Action builds - nightly build with testing (only database tests for this initial PR) - release build - prettify Java code For testing, we didn't include integration tests against OTBR. Because Android emulator doesn't support multicast (link) and it is also not feasible to test QR code scanning given the emulator has no camera. --- .github/workflows/android-app-build.yml | 57 +++ .github/workflows/android-app-release.yml | 85 ++++ .gitignore | 5 +- android/BUILDING.md | 24 ++ android/README.md | 7 + android/build-commissioner-libs.sh | 83 ++++ android/openthread_commissioner/.gitignore | 15 + .../openthread_commissioner/app/.gitignore | 1 + .../openthread_commissioner/app/build.gradle | 88 +++++ .../app/proguard-rules.pro | 21 + .../app/src/main/AndroidManifest.xml | 23 ++ .../commissioner/app/MainActivity.java | 157 ++++++++ .../res/drawable-anydpi/ic_openthread.xml | 21 + .../main/res/drawable-hdpi/ic_openthread.png | Bin 0 -> 273 bytes .../main/res/drawable-mdpi/ic_openthread.png | Bin 0 -> 188 bytes .../main/res/drawable-xhdpi/ic_openthread.png | Bin 0 -> 357 bytes .../res/drawable-xxhdpi/ic_openthread.png | Bin 0 -> 598 bytes .../app/src/main/res/drawable/ic_launcher.png | Bin 0 -> 2269 bytes .../app/src/main/res/layout/activity_main.xml | 49 +++ .../app/src/main/res/menu/menu_main.xml | 10 + .../app/src/main/res/values-night/themes.xml | 18 + .../app/src/main/res/values/colors.xml | 10 + .../app/src/main/res/values/strings.xml | 5 + .../app/src/main/res/values/themes.xml | 27 ++ android/openthread_commissioner/build.gradle | 61 +++ .../openthread_commissioner/gradle.properties | 19 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + android/openthread_commissioner/gradlew | 172 +++++++++ android/openthread_commissioner/gradlew.bat | 84 ++++ .../service/.gitignore | 3 + .../service/build.gradle | 81 ++++ .../service/consumer-rules.pro | 0 .../service/proguard-rules.pro | 21 + .../service/BorderAgentDatabaseTest.java | 67 ++++ .../service/src/main/AndroidManifest.xml | 20 + .../commissioner/service/BorderAgentDao.java | 61 +++ .../service/BorderAgentDatabase.java | 89 +++++ .../service/BorderAgentDiscoverer.java | 238 ++++++++++++ .../commissioner/service/BorderAgentInfo.java | 98 +++++ .../service/BorderAgentRecord.java | 98 +++++ .../service/CameraSourceView.java | 148 +++++++ .../service/CommissionerActivity.java | 126 ++++++ .../service/CommissionerServiceApp.java | 51 +++ .../service/CommissionerUtils.java | 79 ++++ .../commissioner/service/Constants.java | 41 ++ .../FetchCredentialDialogFragment.java | 143 +++++++ .../service/FragmentCallback.java | 40 ++ .../InputNetworkPasswordDialogFragment.java | 80 ++++ .../service/JoinerDeviceInfo.java | 82 ++++ .../commissioner/service/MeshcopFragment.java | 208 ++++++++++ .../service/NativeCommissionerLogger.java | 42 ++ .../commissioner/service/NetworkAdapter.java | 125 ++++++ .../service/NetworkCredential.java | 33 ++ .../service/ScanQrCodeFragment.java | 239 ++++++++++++ .../service/SelectNetworkFragment.java | 236 ++++++++++++ .../service/ThreadCommissionerException.java | 57 +++ .../service/ThreadCommissionerService.java | 61 +++ .../ThreadCommissionerServiceFactory.java | 35 ++ .../ThreadCommissionerServiceImpl.java | 364 ++++++++++++++++++ .../service/ThreadNetworkCredential.java | 78 ++++ .../service/ThreadNetworkInfo.java | 88 +++++ .../service/ThreadNetworkInfoHolder.java | 83 ++++ .../src/main/res/drawable/ic_done_icon.xml | 9 + .../src/main/res/drawable/ic_error_icon.xml | 9 + .../res/drawable/ic_network_selected_icon.xml | 9 + .../res/drawable/ic_wireless_network_icon.xml | 9 + .../main/res/layout/activity_commissioner.xml | 9 + .../fragment_fetch_credential_dialog.xml | 15 + .../src/main/res/layout/fragment_meshcop.xml | 77 ++++ .../fragment_network_password_dialog.xml | 17 + .../main/res/layout/fragment_scan_qrcode.xml | 51 +++ .../res/layout/fragment_select_network.xml | 85 ++++ .../src/main/res/layout/network_list_item.xml | 24 ++ .../service/src/main/res/values/colors.xml | 6 + .../service/src/main/res/values/strings.xml | 22 ++ .../service/src/main/res/values/styles.xml | 13 + .../openthread_commissioner/settings.gradle | 3 + tests/integration/bootstrap.sh | 4 +- 79 files changed, 4619 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/android-app-build.yml create mode 100644 .github/workflows/android-app-release.yml create mode 100644 android/BUILDING.md create mode 100644 android/README.md create mode 100755 android/build-commissioner-libs.sh create mode 100644 android/openthread_commissioner/.gitignore create mode 100644 android/openthread_commissioner/app/.gitignore create mode 100644 android/openthread_commissioner/app/build.gradle create mode 100644 android/openthread_commissioner/app/proguard-rules.pro create mode 100644 android/openthread_commissioner/app/src/main/AndroidManifest.xml create mode 100644 android/openthread_commissioner/app/src/main/java/io/openthread/commissioner/app/MainActivity.java create mode 100644 android/openthread_commissioner/app/src/main/res/drawable-anydpi/ic_openthread.xml create mode 100644 android/openthread_commissioner/app/src/main/res/drawable-hdpi/ic_openthread.png create mode 100644 android/openthread_commissioner/app/src/main/res/drawable-mdpi/ic_openthread.png create mode 100644 android/openthread_commissioner/app/src/main/res/drawable-xhdpi/ic_openthread.png create mode 100644 android/openthread_commissioner/app/src/main/res/drawable-xxhdpi/ic_openthread.png create mode 100644 android/openthread_commissioner/app/src/main/res/drawable/ic_launcher.png create mode 100644 android/openthread_commissioner/app/src/main/res/layout/activity_main.xml create mode 100644 android/openthread_commissioner/app/src/main/res/menu/menu_main.xml create mode 100644 android/openthread_commissioner/app/src/main/res/values-night/themes.xml create mode 100644 android/openthread_commissioner/app/src/main/res/values/colors.xml create mode 100644 android/openthread_commissioner/app/src/main/res/values/strings.xml create mode 100644 android/openthread_commissioner/app/src/main/res/values/themes.xml create mode 100644 android/openthread_commissioner/build.gradle create mode 100644 android/openthread_commissioner/gradle.properties create mode 100644 android/openthread_commissioner/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/openthread_commissioner/gradle/wrapper/gradle-wrapper.properties create mode 100755 android/openthread_commissioner/gradlew create mode 100644 android/openthread_commissioner/gradlew.bat create mode 100644 android/openthread_commissioner/service/.gitignore create mode 100644 android/openthread_commissioner/service/build.gradle create mode 100644 android/openthread_commissioner/service/consumer-rules.pro create mode 100644 android/openthread_commissioner/service/proguard-rules.pro create mode 100644 android/openthread_commissioner/service/src/androidTest/java/io/openthread/commissioner/service/BorderAgentDatabaseTest.java create mode 100644 android/openthread_commissioner/service/src/main/AndroidManifest.xml create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/BorderAgentDao.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/BorderAgentDatabase.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/BorderAgentDiscoverer.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/BorderAgentInfo.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/BorderAgentRecord.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/CameraSourceView.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/CommissionerActivity.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/CommissionerServiceApp.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/CommissionerUtils.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/Constants.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/FetchCredentialDialogFragment.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/FragmentCallback.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/InputNetworkPasswordDialogFragment.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/JoinerDeviceInfo.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/MeshcopFragment.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/NativeCommissionerLogger.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/NetworkAdapter.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/NetworkCredential.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/ScanQrCodeFragment.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/SelectNetworkFragment.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/ThreadCommissionerException.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/ThreadCommissionerService.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/ThreadCommissionerServiceFactory.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/ThreadCommissionerServiceImpl.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/ThreadNetworkCredential.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/ThreadNetworkInfo.java create mode 100644 android/openthread_commissioner/service/src/main/java/io/openthread/commissioner/service/ThreadNetworkInfoHolder.java create mode 100644 android/openthread_commissioner/service/src/main/res/drawable/ic_done_icon.xml create mode 100644 android/openthread_commissioner/service/src/main/res/drawable/ic_error_icon.xml create mode 100644 android/openthread_commissioner/service/src/main/res/drawable/ic_network_selected_icon.xml create mode 100644 android/openthread_commissioner/service/src/main/res/drawable/ic_wireless_network_icon.xml create mode 100644 android/openthread_commissioner/service/src/main/res/layout/activity_commissioner.xml create mode 100644 android/openthread_commissioner/service/src/main/res/layout/fragment_fetch_credential_dialog.xml create mode 100644 android/openthread_commissioner/service/src/main/res/layout/fragment_meshcop.xml create mode 100644 android/openthread_commissioner/service/src/main/res/layout/fragment_network_password_dialog.xml create mode 100644 android/openthread_commissioner/service/src/main/res/layout/fragment_scan_qrcode.xml create mode 100644 android/openthread_commissioner/service/src/main/res/layout/fragment_select_network.xml create mode 100644 android/openthread_commissioner/service/src/main/res/layout/network_list_item.xml create mode 100644 android/openthread_commissioner/service/src/main/res/values/colors.xml create mode 100644 android/openthread_commissioner/service/src/main/res/values/strings.xml create mode 100644 android/openthread_commissioner/service/src/main/res/values/styles.xml create mode 100644 android/openthread_commissioner/settings.gradle diff --git a/.github/workflows/android-app-build.yml b/.github/workflows/android-app-build.yml new file mode 100644 index 000000000..23e4c7921 --- /dev/null +++ b/.github/workflows/android-app-build.yml @@ -0,0 +1,57 @@ +# +# Copyright (c) 2020, The OpenThread Commissioner Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +name: Android App Build + +on: [push, pull_request] + +jobs: + nightly-build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + android-api: [24] + android-abi: [x86, x86_64] + os: [macos-10.15, ubuntu-20.04] + steps: + - uses: actions/checkout@v2 + - name: Bootstrap + run: | + script/bootstrap.sh + - name: Build + run: | + cd android + ANDROID_ABI=${{ matrix.android-abi }} ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle ./build-commissioner-libs.sh + cd openthread_commissioner + ./gradlew build + - name: Tests + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.android-api }} + arch: ${{ matrix.android-abi }} + script: "cd android/openthread_commissioner && ./gradlew connectedAndroidTest" diff --git a/.github/workflows/android-app-release.yml b/.github/workflows/android-app-release.yml new file mode 100644 index 000000000..7982698b3 --- /dev/null +++ b/.github/workflows/android-app-release.yml @@ -0,0 +1,85 @@ +# +# Copyright (c) 2020, The OpenThread Commissioner Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# This file runs Android App release build and publish +# releases automatically. +# + +name: Android App Release + +on: + pull_request: + types: [assigned, opened, synchronize, reopened, closed] + +jobs: + build-release: + runs-on: ubuntu-20.04 + # We run this job only when the PR branch starts with 'android-release/'. + if: startsWith(github.head_ref, 'android-release/') + steps: + - uses: actions/checkout@v2 + - name: Check Release Version + id: check_release_version + run: | + # The PR branch should has the format 'android-release/'. + # should equal to versionName defined in the gradle file. + branch_version="${GITHUB_HEAD_REF##*/}" + gradle_version="$(cd android/openthread_commissioner && ./gradlew -q printVersionName)" + + if [ $branch_version != $gradle_version ]; then + echo "branch name should be android-release/${gradle_version}!" + exit 1 + fi + + echo "::set-output name=release_version::${gradle_version}" + echo "::set-output name=pr_merged::${{ github.event.pull_request.merged }}" + - name: Bootstrap + run: | + script/bootstrap.sh + - name: Build + run: | + cd android + ANDROID_ABI=armeabi-v7a ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle ./build-commissioner-libs.sh + ANDROID_ABI=arm64-v8a ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle ./build-commissioner-libs.sh + cd openthread_commissioner + ./gradlew assembleDebug + + cp app/build/outputs/apk/debug/app-debug.apk \ + ot-commissioner-app-debug-${{ steps.check_release_version.outputs.release_version }}.apk + - name: Create New Release + # We create the release only when this PR is merged. + # For some reason, github.event.pull_request.merged + # doesn't work at here. + if: ${{ steps.check_release_version.outputs.pr_merged == 'true' }} + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "${{ steps.check_release_version.outputs.release_version }}" + prerelease: false + title: "OT Commissioner App ${{ steps.check_release_version.outputs.release_version }}" + files: | + ./android/openthread_commissioner/ot-commissioner-app-debug-*.apk diff --git a/.gitignore b/.gitignore index 6ea0c74d0..c1df65e0d 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,7 @@ libtool Makefile.in # build -._build +.build* build cmake-build-* @@ -63,6 +63,3 @@ doc __pycache__/ venv/ *.py[cod] - -# Java (autogenerated) -**/io/openthread/commissioner diff --git a/android/BUILDING.md b/android/BUILDING.md new file mode 100644 index 000000000..dec26ec90 --- /dev/null +++ b/android/BUILDING.md @@ -0,0 +1,24 @@ +# Build OpenThread Commissioner App + +This document describes how to build the OT Commissioner Android App. + +## Prerequisites + +- macOS or Linux computer. +- Install latest Android Studio from [here](https://developer.android.com/studio). + +## Build shared library + +The Commissioner Android App is built on top of the native OT Commissioner library. Run the `build-commissioner-libs.sh` script in this directory to build the required libraries: + +```shell +ANDROID_ABI=arm64-v8a ANDROID_NDK_HOME=$HOME/Library/Android/sdk/ndk/21.3.6528147 ./build-commissioner-libs.sh +``` + +This script creates a build directory (`.build-${ANDROID_ABI}`) in the current directory and copies generated libraries into target sub-folders in `openthread_commissioner`. + +> Note: you need to set env `ANDROID_ABI` to the ABI of your phone. This value can be got with command `adb shell getprop ro.product.cpu.abi` after connecting the phone to your computer. + +## Build the App with Android Studio + +Connect the phone to your computer and open the `openthread_commissioner` directory with Android Studio. Click the **Gradle Sync** and **Run** buttons to run the Commissioner App on your phone. diff --git a/android/README.md b/android/README.md new file mode 100644 index 000000000..a1cd0c77e --- /dev/null +++ b/android/README.md @@ -0,0 +1,7 @@ +# OpenThread Commissioner Android App + +The OpenThread (OT) Commissioner Android App commissions a new Thread 1.1 Device onto an existing Thread Network. + +## Build + +See [BUILDING.md](./BUILDING.md). diff --git a/android/build-commissioner-libs.sh b/android/build-commissioner-libs.sh new file mode 100755 index 000000000..1054904d8 --- /dev/null +++ b/android/build-commissioner-libs.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# +# Copyright (c) 2020, The OpenThread Commissioner Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +readonly CUR_DIR="$(dirname "$(realpath -s "$0")")" + +set -e + +if [ -z "${ANDROID_ABI}" ]; then + echo "ANDROID_ABI not set! Candidates: armeabi-v7a, arm64-v8a, x86 and x86_64" + exit 1 +fi + +if [ -z "${ANDROID_NDK_HOME}" ]; then + echo "ANDROID_NDK_HOME not set! Please set it to your Android NDK location!" + exit 1 +fi + +cd "${CUR_DIR}" + +readonly BUILD_DIR=".build-$ANDROID_ABI" + +mkdir -p "$BUILD_DIR" && cd "$BUILD_DIR" +cmake -GNinja \ + -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME"/build/cmake/android.toolchain.cmake \ + -DANDROID_ABI="$ANDROID_ABI" \ + -DANDROID_ARM_NEON=ON \ + -DANDROID_NATIVE_API_LEVEL=21 \ + -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_CXX_STANDARD=11 \ + -DCMAKE_CXX_STANDARD_REQUIRED=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DOT_COMM_ANDROID=ON \ + -DOT_COMM_JAVA_BINDING=ON \ + -DOT_COMM_APP=OFF \ + -DOT_COMM_TEST=OFF \ + -DOT_COMM_CCM=OFF \ + ../.. + +ninja commissioner-java +cd ../ + +rm -rf "$BUILD_DIR"/libs && mkdir -p "$BUILD_DIR"/libs + +## Create JAR library +javac -source 8 -target 8 "$BUILD_DIR"/src/java/io/openthread/commissioner/*.java + +cd "$BUILD_DIR"/src/java +find ./io/openthread/commissioner -name "*.class" | xargs jar cvf ../../libs/libotcommissioner.jar +cd ../../../ + +## Copy shared native libraries +cp "$BUILD_DIR"/src/java/libcommissioner-java.so "$BUILD_DIR"/libs + +mkdir -p openthread_commissioner/service/libs +mkdir -p openthread_commissioner/service/src/main/jniLibs/"${ANDROID_ABI}" +cp "$BUILD_DIR"/libs/libotcommissioner.jar openthread_commissioner/service/libs +cp "$BUILD_DIR"/libs/*.so openthread_commissioner/service/src/main/jniLibs/"${ANDROID_ABI}" diff --git a/android/openthread_commissioner/.gitignore b/android/openthread_commissioner/.gitignore new file mode 100644 index 000000000..aa724b770 --- /dev/null +++ b/android/openthread_commissioner/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/android/openthread_commissioner/app/.gitignore b/android/openthread_commissioner/app/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/android/openthread_commissioner/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/android/openthread_commissioner/app/build.gradle b/android/openthread_commissioner/app/build.gradle new file mode 100644 index 000000000..743a828ad --- /dev/null +++ b/android/openthread_commissioner/app/build.gradle @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020, The OpenThread Commissioner Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +plugins { + id 'com.android.application' +} + +def getGitHash = { -> + def stdout = new ByteArrayOutputStream() + exec { + commandLine 'git', 'rev-parse', '--short', 'HEAD' + standardOutput = stdout + } + return stdout.toString().trim() +} + +// Print App version. +task printVersionName { + doLast { + println android.defaultConfig.versionName + } +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.1" + ndkVersion "21.3.6528147" + + defaultConfig { + applicationId "io.openthread.commissioner.app" + minSdkVersion 24 + targetSdkVersion 30 + versionCode 1 + versionName "0.0.1" + + buildConfigField "String", "GIT_HASH", "\"${getGitHash()}\"" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation project(':service') + + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.1' + implementation 'androidx.constraintlayout:constraintlayout:2.0.1' + testImplementation 'junit:junit:4.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' +} diff --git a/android/openthread_commissioner/app/proguard-rules.pro b/android/openthread_commissioner/app/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/android/openthread_commissioner/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/android/openthread_commissioner/app/src/main/AndroidManifest.xml b/android/openthread_commissioner/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..99bdb4637 --- /dev/null +++ b/android/openthread_commissioner/app/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/android/openthread_commissioner/app/src/main/java/io/openthread/commissioner/app/MainActivity.java b/android/openthread_commissioner/app/src/main/java/io/openthread/commissioner/app/MainActivity.java new file mode 100644 index 000000000..22df9feca --- /dev/null +++ b/android/openthread_commissioner/app/src/main/java/io/openthread/commissioner/app/MainActivity.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2020, The OpenThread Commissioner Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package io.openthread.commissioner.app; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.TextView; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import io.openthread.commissioner.service.FragmentCallback; +import io.openthread.commissioner.service.JoinerDeviceInfo; +import io.openthread.commissioner.service.MeshcopFragment; +import io.openthread.commissioner.service.ScanQrCodeFragment; +import io.openthread.commissioner.service.SelectNetworkFragment; +import io.openthread.commissioner.service.ThreadNetworkInfoHolder; + +public class MainActivity extends AppCompatActivity implements FragmentCallback { + + private static final String TAG = MainActivity.class.getSimpleName(); + + @Nullable private ThreadNetworkInfoHolder selectedNetwork; + + @Nullable private byte[] pskc; + + @Nullable private JoinerDeviceInfo joinerDeviceInfo; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + setSupportActionBar(findViewById(R.id.toolbar)); + + TextView versionName = findViewById(R.id.version_name); + versionName.setText(BuildConfig.VERSION_NAME); + TextView gitHash = findViewById(R.id.git_hash); + gitHash.setText(BuildConfig.GIT_HASH); + + if (savedInstanceState == null) { + showFragment(new SelectNetworkFragment(this, null), false); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // TODO(wgtdkp): + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + } + + private void showFragment(Fragment fragment, boolean addToBackStack) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName()); + if (addToBackStack) { + transaction.addToBackStack(null); + } + transaction.commit(); + } + + private void clearFragmentsInBackStack() { + getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); + } + + private void finishCommissioning(int result) { + // TODO(wgtdkp): show a dialog to the user. + selectedNetwork = null; + pskc = null; + joinerDeviceInfo = null; + clearFragmentsInBackStack(); + } + + @Override + public void onNetworkSelected( + @Nullable ThreadNetworkInfoHolder networkInfoHolder, @Nullable byte[] pskc) { + if (networkInfoHolder == null || pskc == null) { + Log.e(TAG, "failed to get selected network or PSKc!"); + finishCommissioning(Activity.RESULT_CANCELED); + return; + } + + this.selectedNetwork = networkInfoHolder; + this.pskc = pskc; + showFragment(new ScanQrCodeFragment(this), true); + } + + @Override + public void onJoinerInfoReceived(@Nullable JoinerDeviceInfo joinerDeviceInfo) { + if (joinerDeviceInfo == null) { + // TODO(wgtdkp): show dialog + Log.e(TAG, "failed to get Joiner Device info!"); + finishCommissioning(Activity.RESULT_CANCELED); + return; + } + + if (this.joinerDeviceInfo == null) { + this.joinerDeviceInfo = joinerDeviceInfo; + showFragment(new MeshcopFragment(this, selectedNetwork, pskc, joinerDeviceInfo), true); + } + } + + @Override + public void onMeshcopResult(int result) { + finishCommissioning(result); + } +} diff --git a/android/openthread_commissioner/app/src/main/res/drawable-anydpi/ic_openthread.xml b/android/openthread_commissioner/app/src/main/res/drawable-anydpi/ic_openthread.xml new file mode 100644 index 000000000..d87fb30e2 --- /dev/null +++ b/android/openthread_commissioner/app/src/main/res/drawable-anydpi/ic_openthread.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/android/openthread_commissioner/app/src/main/res/drawable-hdpi/ic_openthread.png b/android/openthread_commissioner/app/src/main/res/drawable-hdpi/ic_openthread.png new file mode 100644 index 0000000000000000000000000000000000000000..fa35747699da86b02d5c7a071d5100e95daa307d GIT binary patch literal 273 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 iDD@jL{3iY%Y5>1D<6LL0Pgu{q0H(%PM)g + + + + + + + + + + + + + diff --git a/android/openthread_commissioner/app/src/main/res/menu/menu_main.xml b/android/openthread_commissioner/app/src/main/res/menu/menu_main.xml new file mode 100644 index 000000000..4fb3a1419 --- /dev/null +++ b/android/openthread_commissioner/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,10 @@ + + + diff --git a/android/openthread_commissioner/app/src/main/res/values-night/themes.xml b/android/openthread_commissioner/app/src/main/res/values-night/themes.xml new file mode 100644 index 000000000..1fb93738c --- /dev/null +++ b/android/openthread_commissioner/app/src/main/res/values-night/themes.xml @@ -0,0 +1,18 @@ + + + + diff --git a/android/openthread_commissioner/app/src/main/res/values/colors.xml b/android/openthread_commissioner/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..758655a2e --- /dev/null +++ b/android/openthread_commissioner/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + diff --git a/android/openthread_commissioner/app/src/main/res/values/strings.xml b/android/openthread_commissioner/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..1e0341e7e --- /dev/null +++ b/android/openthread_commissioner/app/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + OpenThread Commissioner + Settings + Commission New Device + diff --git a/android/openthread_commissioner/app/src/main/res/values/themes.xml b/android/openthread_commissioner/app/src/main/res/values/themes.xml new file mode 100644 index 000000000..176d03a3e --- /dev/null +++ b/android/openthread_commissioner/app/src/main/res/values/themes.xml @@ -0,0 +1,27 @@ + + + + + + + + +