diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 76d1937260e09b05de78e1935de5c831923719b4..0000000000000000000000000000000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,213 +0,0 @@ -version: 2.1 - -## -## REFERENCES -################################################################ -references: - - # - # CONFIGURATION - # - workspace: &workspace - ~/code - - environment_config: &environment_config - working_directory: *workspace - docker: - - image: circleci/android:api-29 - environment: - JVM_OPTS: -Xmx2048m - GRADLE_OPTS: -Xmx1536m -XX:+HeapDumpOnOutOfMemoryError -Dorg.gradle.caching=true -Dorg.gradle.configureondemand=true -Dkotlin.compiler.execution.strategy=in-process -Dkotlin.incremental=false - - # - # CACHE CONTROL - # - generate_dependency_hashfile: &generate_dependency_hashfile - run: - name: Generate Dependency Hashfile - command: ./buildsystem/generate_dependency_hashfile.sh ./ ./dependency_hashfile.tmp - - print_dependency_hashfile: &print_dependency_hashfile - run: - name: Print Dependency Hashfile Contents - command: cat ./dependency_hashfile.tmp - - cache_key: &cache_key - key: cache-v1-{{ checksum "dependency_hashfile.tmp" }} - - cache_key_fallback: &cache_key_fallback - key: cache-v1- - - restore_cache: &restore_cache - restore_cache: - <<: *cache_key - <<: *cache_key_fallback - - save_cache: &save_cache - save_cache: - <<: *cache_key - paths: - - ~/.gradle - - ~/.m2 - - # - # DOWNLOADING DEPENDENCIES - # - download_android_dependencies: &download_android_dependencies - run: - name: Download Dependencies - command: ./gradlew dependencies androidDependencies - - # - # RUNNING UNIT TESTS - # - run_android_lint: &run_android_lint - run: - name: Running lint - command: ./gradlew lintDebug - - run_unit_tests: &run_unit_tests - run: - name: Running unit tests - command: ./gradlew testDebug library:testDebug - - produce_code_coverage: &produce_code_coverage - run: - name: Producing Code Coverage - command: ./gradlew jacocoTestReport - - print_code_coverage: &print_code_coverage - run: - name: Printing code coverage - command: ./print_codecov.sh - - # - # BUILDING DEBUG APKS (FOR FIREBASE) - # - build_debug_apks: &build_debug_apks - run: - name: Building Debug APKs - command: | - ./gradlew assembleDebug - - # - # BUILDING RELEASE APKS - # - build_release_apks: &build_release_apks - run: - name: Building Release APKs - command: | - ./gradlew assembleRelease - - setup_environment: &setup_environment - run: - name: Setup environment - command: | - echo "$ENCODED_KEYSTORE" | base64 -di >> "${HOME}"/keystore.jks - echo "$ENCODED_GPLAY_DEPLOY_KEY" | base64 -di --decode >> "${HOME}"/deployment_private_key.json - echo "export ANDROID_KEYSTORE=${HOME}/keystore.jks" >> "$BASH_ENV" - echo "export GPLAY_DEPLOY_KEY=${HOME}/deployment_private_key.json" >> "$BASH_ENV" - -## -## COMMANDS -################################################################ -commands: - upload_test_results_and_artifacts: - description: "Upload the test reports and artifacts for a given module" - parameters: - module_name: - type: string - default: "[MISSING]" - steps: - - store_artifacts: - path: << parameters.module_name >>/build/reports - destination: reports-<< parameters.module_name >> - - store_test_results: - path: << parameters.module_name >>/build/test-results - - upload_apks_to_ci: - description: "Upload the APKs generated for a given module" - parameters: - module_name: - type: string - default: "[MISSING]" - steps: - - store_artifacts: - path: << parameters.module_name >>/build/outputs/apk - destination: apks-<< parameters.module_name >> - - print_apk_info: - description: "Collect information of compiled APKs" - parameters: - module_name: - type: string - default: "[MISSING]" - steps: - - run: - name: "Collect information of compiled APKs" - when: on_success - command: ./apkdetails.sh -i << parameters.module_name >>/build/outputs/apk/ -r -h -v - - setup_dependency_cache: - description: "Restore (if present) populate and save (if needed) the dependency cache" - steps: - - *generate_dependency_hashfile - - *print_dependency_hashfile - - *restore_cache - - *download_android_dependencies - - deploy_to_google_play: - description: "Deploy APKs to Google Play" - parameters: - only_for_branch: - type: string - default: "[MISSING]" - steps: - - deploy: - name: "Deploy to Google Play" - when: on_success - command: | - if [ "${CIRCLE_BRANCH}" == "<< parameters.only_for_branch >>" ]; then - ./gradlew sample_app:publish - else - echo "Branch is not '<< parameters.only_for_branch >>'. Skipping deployment." - fi - -## -## JOBS -################################################################ -jobs: - local_test: - <<: *environment_config - steps: - - checkout - - *setup_environment - - setup_dependency_cache - - *run_unit_tests - - *produce_code_coverage # Produce code coverage needs to be straight after running unit tests - - *print_code_coverage - - *run_android_lint - - *build_debug_apks - - *build_release_apks - - *save_cache # Moved after test runs to capture dep downloads from the previous step - - print_apk_info: - module_name: "sample_app" - - persist_to_workspace: - root: *workspace - paths: - - sample_app/build/ - - library/build/ - - upload_test_results_and_artifacts: - module_name: "sample_app" - - upload_test_results_and_artifacts: - module_name: "library" - - upload_apks_to_ci: - module_name: "sample_app" - - deploy_to_google_play: - only_for_branch: "master" - -workflows: - version: 2.1 - build-and-deploy: - jobs: - - local_test \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 681f41ae2aee4749eb4ddda94f8c6a76c825c825..0000000000000000000000000000000000000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - -
- - - - xmlns:android - - ^$ - - - -
-
- - - - xmlns:.* - - ^$ - - - BY_NAME - -
-
- - - - .*:id - - http://schemas.android.com/apk/res/android - - - -
-
- - - - .*:name - - http://schemas.android.com/apk/res/android - - - -
-
- - - - name - - ^$ - - - -
-
- - - - style - - ^$ - - - -
-
- - - - .* - - ^$ - - - BY_NAME - -
-
- - - - .* - - http://schemas.android.com/apk/res/android - - - ANDROID_ATTRIBUTE_ORDER - -
-
- - - - .* - - .* - - - BY_NAME - -
-
-
-
-
-
\ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 96cc43efa6a0885098044e976cd780bb42c68a70..0000000000000000000000000000000000000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e7bedf3377d40335424fd605124d4761390218bb..0000000000000000000000000000000000000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 0771dc417799689da7e0c241960f74b114ba38d4..0000000000000000000000000000000000000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index ecd817f69ff383170542f381bec27e883f43acf3..0000000000000000000000000000000000000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index cb4f9e0f04e412155622cdb9053d94279b0e7993..0000000000000000000000000000000000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - Android - - - Android > Lint > Correctness - - - Android > Lint > Performance - - - CorrectnessLintAndroid - - - General - - - LintAndroid - - - - - Android - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 4cbfae796640074ce2b503e0c889703e9b342a1f..0000000000000000000000000000000000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460d8b38ac04e3a3224d7c79ef719b1991a9..0000000000000000000000000000000000000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f4cb416c083d265558da75d457237d671..0000000000000000000000000000000000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Bluetooth-LE-Library---Android.iml b/Bluetooth-LE-Library---Android.iml deleted file mode 100644 index dcc509866aea4ca418caaaa53ecafd316a2c0422..0000000000000000000000000000000000000000 --- a/Bluetooth-LE-Library---Android.iml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index e03ed2f425f5fcff4fb057ef03b87bceceb851bb..ae607f42d0b7f6756d0fb4e81a3618018a61881a 100644 --- a/README.md +++ b/README.md @@ -1,173 +1,126 @@ -# Bluetooth LE Library for Android - -This library allows for easy access to a Bluetooth LE device's Advertisement Records. -It also offers: - -* A simple running average RSSI reading keeping. -* For iBeacons: Manufacturer data record parser. -* For iBeacons: Distance indicators (Near, Far, Immediate, Unknown). -* For iBeacons: A decently inaccurate (due to real world issues) distance approximation. -* All the new object types are Parcelable. - -This will only work on devices with Android 4.3 (API Level 18) and above. - - -Get it on Google Play - - -## Including the Library in Your Project - -This project is available as an artifact for use with Gradle. To use that, add the following blocks to your build.gradle file: -```groovy - repositories { - maven { - url "https://dl.bintray.com/alt236/maven" - } - } - - dependencies { - compile 'uk.co.alt236:bluetooth-le-library-android:1.0.0' - } +# Bluetooth_LE_Library + +**本项目基于开源项目Bluetooth_LE_Library 进行openharmony化的移植和开发,可以通过项目标签以及github地址( https://github.com/alt236/Bluetooth-LE-Library---Android )追踪到原项目版本** + +#### 项目介绍 +- 项目名称:该库可轻松访问Bluetooth LE设备的AdRecord和RSSI值。 +- 所属系列:openharmony的第三方组件适配移植 +- 功能:该库可轻松访问Bluetooth LE设备的AdRecord和RSSI值。它为iBeacons提供了其他功能。 +- 基线版本: Bluetooth_LE_Library Tags 1.1.1 +- 项目移植状态:主功能完成 +- 调用差异:无 +- 开发版本:sdk5,DevEco Studio2.1 beta4 +- 原项目Doc地址:https://github.com/alt236/Bluetooth-LE-Library---Android + +#### 效果演示 + + + + + + +#### 安装教程 + +1.在项目根目录下的build.gradle文件中, + ``` + allprojects { + repositories { + maven { + url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' + } + } + } ``` -If you *really* need a Jar file, fork the project and execute `./gradlew clean build generateRelease` at the root of the project. -This will create a zip file under `/library/build/` the Jar can be found inside. - -## Using the Library -In the `onLeScan()` method of your `BluetoothAdapter.LeScanCallback()` create a new BluetoothLeDevice with the given information. - -For example: - -```java - private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { - - @Override - public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { - - final BluetoothLeDevice deviceLe = new BluetoothLeDevice(device, rssi, scanRecord, System.currentTimeMillis()); - - runOnUiThread(new Runnable() { - @Override - public void run() { - mDeviceStore.addDevice(deviceLe); - mLeDeviceListAdapter.replaceData(mDeviceStore.getDeviceList()); - } - - }); - } - }; +2.在entry模块的build.gradle文件中, + ``` + dependencies { + implementation('com.gitee.chinasoft_ohos:Bluetooth_LE_Library:0.0.1-SNAPSHOT') + ...... + } ``` -### Device Properties +#### 使用说明 -Once you have created a device, you can access the following methods: +1、您将需要以下权限来访问蓝牙硬件: -* `getAddress()` : Gets the MAC Address of the device -* `getAdRecordStore()`: Gives access to a device's Ad Records -* `getDevice()`: Gives access to the standard BluetoothDevice object -* `getFirstRssi()`: Retrieves the RSSI value which was used when the object was created -* `getFirstTimestamp()` Retrieves the timestamp (in millis) which was used when the object was created -* `getRssi()` Gets the current RSSI measurement (see note below). -* `getScanRecord()` Retrieves the RAW scan record array -* `getTimestamp()` Gets the timestamp of the last RSSI measurement -* `getRunningAverageRssi()` Retrieves the internally calculated running average RSSI value (see note below). +* `ohos.permission.USE_BLUETOOTH` +* `ohos.permission.LOCATION` +* `ohos.permission.DISCOVER_BLUETOOTH` +2、在MainAbilitySlice中获取蓝牙本机管理对象,并设置回调监听。 +``` +// 获取蓝牙本机管理对象 +BluetoothHost bluetoothHost = BluetoothHost.getDefaultHost(this); +bluetoothHost.enableBt(); +ScanCallback centralManagerCallback = new ScanCallback(); +BleCentralManager centralManager = new BleCentralManager(this, centralManagerCallback); +``` +3、开始扫描蓝牙设备,并在回调里面获取扫描到蓝牙数据 +``` +List filters = new ArrayList<>(); +centralManager.startScan(filters); + +public class ScanCallback implements BleCentralManagerCallback { + List results = new ArrayList<>(); + + @Override + public void scanResultEvent(BleScanResult resultCode) { + // 对扫描结果进行处理 + results.add(resultCode); + int rssi = resultCode.getRssi(); + byte[] scanRecord = resultCode.getRawData(); + BluetoothLeDevice deviceLe = new BluetoothLeDevice(resultCode, + rssi, scanRecord, System.currentTimeMillis()); + getUITaskDispatcher().asyncDispatch(() -> { + for (int index = 0; index < listDevice.size(); index++) { + if (deviceLe.getAddress().equals(listDevice.get(index).getAddress())) { + BluetoothLeDevice item = listDevice.get(index); + item.updateRssiReading(System.currentTimeMillis(), rssi); + return; + } + } + }); + } + + @Override + public void scanFailedEvent(int resultCode) { + } + + @Override + public void groupScanResultsEvent(List list) { + } + } +``` -**Note:** The Running Average RSSI is not updated automatically (i.e. the library does not monitor on its own in the background). To add another measurement, you need to call `updateRssiReading(long timestamp, int rssiReading)`. +支持功能: +``` +* getAddress() : Gets the MAC Address of the device +* getAdRecordStore(): Gives access to a device's Ad Records +* getDevice(): Gives access to the standard BluetoothDevice object +* getFirstRssi(): Retrieves the RSSI value which was used when the object was created +* getFirstTimestamp() : Retrieves the timestamp (in millis) which was used when the object was created +* getRssi() : Gets the current RSSI measurement (see note below). +* getScanRecord() : Retrieves the RAW scan record array +* getTimestamp() : Gets the timestamp of the last RSSI measurement +* getRunningAverageRssi() : Retrieves the internally calculated running average RSSI value (see note below). +``` +#### 测试信息 -### Accessing the Advertisement (Ad) Records +CodeCheck代码测试无异常 -Once you've created a BluetoothLe device, you can access the AdRecord store via the `leDevice.getAdRecordStore()`. Once you have the AdRecordStore you can use the following methods: +CloudTest代码测试无异常 -* `getRecord(int recordNo)`: Gets the AdRecord object corresponding to the recordNumber. -* `getRecordDataAsString(int recordNo)` : Gets the AdRecord contents as a String (expect non printable characters in most cases). -* `isRecordPresent(int recordNo)`: Checks to see if a record exists. +火绒安全病毒安全检测通过 -**Note:** Record numbers are declared in the Bluetooth 4 spec which can be found [here](https://developer.bluetooth.org/TechnologyOverview/Pages/core-specification.aspx). -They are also declared as constants in `AdRecord.java`. +当前版本demo功能与原组件基本无差异 -### Fun with iBeacons -You can check if a device is an iBeacon by using `BeaconUtils.getBeaconType(BluetootLeDevice device)`. Once you have confirmed that it is, you can create a new IBeaconDevice via the IBeaconDevice constructor. +#### 版本迭代 -Example Flow: -```java - final BluetoothLeDevice device = ... // A generic BLE device +- 0.0.1-SNAPSHOT - if (BeaconUtils.getBeaconType(device) == BeaconType.IBEACON) { - final IBeaconDevice iBeacon = new IBeaconDevice(device); - // DO STUFF - } +#### 版权和许可信息 ``` - -An IBeaconDevice extends BluetoothLeDevice, so you still have access to the same methods as before. In addition you can do the following: - -* `getAccuracy()`: Gets the estimated Accuracy of the reading in meters based on a simple running average calculation -* `getCalibratedTxPower()`: Gets the calibrated TX power of the iBeacon device as reported -* `getCompanyIdentifier()`: Gets the iBeacon company identifier (this should always be 0x004C for Apple) -* `getDistanceDescriptor()`: Gets the estimated Distance descriptor (an enum) -* `getIBeaconData()`: Gets the raw IBeaconManufacturerData object. -* `getUUID()`: Gets the device's UUID -* `getMajor()`: Gets the device's Major value -* `getMinor()`: Gets the device's Minor value - - -### Lookup Functions -You can also lookup values and convert them to human friendly strings: -* `BluetoothClassResolver.resolveDeviceClass(int btClass)`: Will try to resolve a Blueotooth Device class -* `CompanyIdentifierResolver.getCompanyName(int companyId, String fallback)`: Will try to resolve a Company identifier to the company name -* `GattAttributeResolver.getAttributeName(String uuid, String fallback)`: Will try to convert a UUID to its name. - -**Note:** The data can be found as ODS (Open Office Spreadsheets) in the documents folder. - -## Library Changelog -* v0.0.1 - * First public release -* v0.0.2: - * Attempting to create an iBeaconDevice from a device which is not an iBeacon will now throw an IllegalArgumentException exception. - * Fixed a ConcurrentModificationException on getRunningAverageRssi() - * Added some Estimote UUIDs -* v1.0.0: - * Migrated project to Android Studio/ gradle - * Note that the API has slightly changed in this version. - * We now use the more generic `BeaconUtils.getBeaconType()` method instead of `IBeaconUtils.isThisAnIBeacon()` - * Fix for [issue 5](https://github.com/alt236/Bluetooth-LE-Library---Android/issues/5) - * Fix for [issue 9](https://github.com/alt236/Bluetooth-LE-Library---Android/issues/9) - -## Sample Application Changelog -* v0.0.1 - * First public release -* v0.0.2: - * Can now export scanned devices as a CSV file. -* v0.0.3: - * UI Refresh. -* v1.0.0: - * Migrated project to Android Studio/ gradle - * Using version v1.0.0 of the library project -* v1.1.0: - * App refactor and materialisation. - * Added runtime permissions. -* v1.1.1: - * Fix for [issue 23](https://github.com/alt236/Bluetooth-LE-Library---Android/issues/23) - -## Permission Explanation -You will need the following permissions to access the Bluetooth Hardware - -* `android.permission.BLUETOOTH` -* `android.permission.BLUETOOTH_ADMIN` - -In addition one of the following is needed from API 23 and above to scan for BT LE devices: -* `android.permission.ACCESS_COARSE_LOCATION` -* `android.permission.ACCESS_FINE_LOCATION ` - -## TODO - -* Tidy up Javadoc. There is quite a lot of it that is template -* Add parsers for common Ad Records. - -## Links -* Github: [https://github.com/alt236/Bluetooth-LE-Library---Android]() - -## Credits Author: [Alexandros Schillings](https://github.com/alt236). * The Accuracy calculation algorithm was taken from: http://stackoverflow.com/questions/20416218/understanding-ibeacon-distancing @@ -180,3 +133,4 @@ All logos are the property of their respective owners. The code in this project is licensed under the Apache Software License 2.0. Copyright (c) 2014-2017 Alexandros Schillings. +``` \ No newline at end of file diff --git a/apkdetails.sh b/apkdetails.sh deleted file mode 100755 index 4645013fcc81f373999bb26767badec91f18a04d..0000000000000000000000000000000000000000 --- a/apkdetails.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -set -e -set -o xtrace -java -jar ./buildsystem/apkdetails/apkdetails-1.2.2.jar "$@" -set +o xtrace \ No newline at end of file diff --git a/bluetooth-le-library.iml b/bluetooth-le-library.iml deleted file mode 100644 index 1bf2c6ac737c0715231052ac01adad006bd6872e..0000000000000000000000000000000000000000 --- a/bluetooth-le-library.iml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build.gradle b/build.gradle index d571eb4c9b77766cd1ae8e7597113b592cac92d3..e83d0d6537187b1b6e318c86d0e7c3760f71c6e7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,43 +1,40 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. +apply plugin: 'com.huawei.ohos.app' -buildscript { - apply from: "${project.rootDir}/buildconstants/android-sdk-versions.gradle" - apply from: "${project.rootDir}/buildconstants/dependency-versions.gradle" +ohos { + compileSdkVersion 5 + defaultConfig { + compatibleSdkVersion 5 + } +} +buildscript { repositories { - google() + maven { + url 'https://repo.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } jcenter() } - dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' - classpath 'com.novoda:bintray-release:0.9.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.huawei.ohos:hap:2.4.2.7' + classpath 'com.huawei.ohos:decctest:1.0.0.6' } } allprojects { repositories { - google() - mavenCentral() - jcenter() - } - - tasks.withType(Test) { - testLogging { - exceptionFormat "full" - showCauses true - showExceptions true - showStackTraces true - showStandardStreams true - events = ["passed", "skipped", "failed"] - - // This line is left here to make test debugging easier - // events = ["passed", "skipped", "failed", "standardError", "standardOut"] + maven { + url 'https://repo.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' } + maven { + url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' + } + jcenter() } - - apply from: "${project.rootDir}/buildconstants/android-sdk-versions.gradle" - apply from: "${project.rootDir}/buildconstants/dependency-versions.gradle" - apply from: "${project.rootDir}/buildsystem/common-methods.gradle" } diff --git a/buildconstants/android-sdk-versions.gradle b/buildconstants/android-sdk-versions.gradle deleted file mode 100644 index 7fcddc7f3141d01e29e02f67ad4cebabb2df9a6a..0000000000000000000000000000000000000000 --- a/buildconstants/android-sdk-versions.gradle +++ /dev/null @@ -1,7 +0,0 @@ -ext { - min_sdk_version = 18 - target_sdk_version = 29 - compile_sdk_version = 29 - build_tools_version = "28.0.3" - kotlin_version = '1.3.60' -} \ No newline at end of file diff --git a/buildconstants/dependency-versions.gradle b/buildconstants/dependency-versions.gradle deleted file mode 100644 index 30ec5e5bdbcc8124fc772c19073157890f2fca4b..0000000000000000000000000000000000000000 --- a/buildconstants/dependency-versions.gradle +++ /dev/null @@ -1,33 +0,0 @@ -ext { - // androidx libraries - androidx_appcompat_version = "1.1.0" - androidx_annotation_version = "1.1.0" - androidx_espresso_core_version = "3.1.0" - androidx_legacy_v4 = "1.0.0" - androidx_lifecycle_version = "2.1.0" - androidx_room_version = "2.2.0" - androidx_core_ktx = "1.1.0" - androidx_preferences = "1.1.0" - - androidx_contraint_layout_version = "1.1.3" - androidx_recyclerview = "1.1.0" - - androidx_test_core_version = "1.1.0" - androidx_test_ext_junit_version = "1.0.0" - androidx_test_runner_version = "1.1.1" - androidx_test_rules_version = "1.1.1" - - // testing - mockito_version = "2.13.0" - mockito_kotlin_version = "2.1.0" - junit_version = "4.12" - - // code quality - jacoco_version = "0.8.5" - - // Other - osmdroid_version = "6.1.2" - material_intro_screen_version = "0.0.6" - butterknife_version = "10.2.1" - grant_permissions_version = "1.1.2" -} diff --git a/buildsystem/android-defaults.gradle b/buildsystem/android-defaults.gradle deleted file mode 100644 index 2158b83619e287cb6a4a714bca46289107a05b6a..0000000000000000000000000000000000000000 --- a/buildsystem/android-defaults.gradle +++ /dev/null @@ -1,119 +0,0 @@ -// The versions are defined in "${project.rootDir}/buildconstants/android-sdk-versions.gradle" -apply plugin: 'jacoco' - -jacoco { - toolVersion = jacoco_version -} - -android { - compileSdkVersion compile_sdk_version - buildToolsVersion build_tools_version - - defaultConfig { - minSdkVersion min_sdk_version - targetSdkVersion target_sdk_version - - multiDexEnabled false - multiDexKeepProguard file("${project.rootDir}/buildsystem/multidex/multidex.pro") - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles 'consumer-rules.pro' - } - - buildTypes { - release { - minifyEnabled true - testCoverageEnabled false - - // PROGUARD - def proguardRuleFiles = collectCommonProguardRules() - proguardRuleFiles.add(0, getDefaultProguardFile('proguard-android.txt')) - proguardRuleFiles.add(1, 'proguard-rules.pro') - logger.warn("Common proguard files: $proguardRuleFiles") - - proguardFiles proguardRuleFiles.toArray() - } - - debug { - minifyEnabled false - testCoverageEnabled true - - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - lintOptions { - lintConfig file("${project.rootDir}/buildsystem/codequality/lint.xml") - } - - testOptions { - execution 'ANDROIDX_TEST_ORCHESTRATOR' - animationsDisabled = true - - unitTests { - //returnDefaultValues = true - includeAndroidResources = true - } - } - - aaptOptions { - noCompress 'zip' - } -} - -task jacocoTestReport2(type: JacocoReport) { - - reports { - xml.enabled = true - html.enabled = true - } - - def fileFilter = ['jdk.internal.*', - 'android/**/*.*', - '**/R.class', - '**/R$*.class', - '**/BuildConfig.*', - '**/Manifest*.*', - '**/*Test*.*', - '**/*$ViewInjector*.*', - '**/*$ViewBinder*.*', - // DAGGER 2 - '**/*_*Factory*.*', - '**/*_MembersInjector*.*', - '**/*_MembersInjector.class', - '**/*Module*.*', - '**/Dagger*Component$Builder.class', - '**/Dagger*Component*.*'] - - def mainSrc = "$project.projectDir/src/main/java" - - def javaClasses = fileTree( - dir: "$buildDir/intermediates/classes/", - excludes: fileFilter - ) - - def kotlinClasses = fileTree( - dir: "$buildDir/tmp/kotlin-classes/", - excludes: fileFilter - ) - - classDirectories = files([ javaClasses ], [ kotlinClasses ]) - sourceDirectories = files(mainSrc) - executionData = fileTree(dir: "$buildDir", - includes: ['jacoco/*.exec', 'connected/*.ec'] - ) - - reports { - xml.enabled = true - html.enabled = true - } -} - -tasks.withType(Test) { - jacoco.includeNoLocationClasses = false -} diff --git a/buildsystem/apkdetails/apkdetails-1.2.2.jar b/buildsystem/apkdetails/apkdetails-1.2.2.jar deleted file mode 100644 index d8a2af5b7b1621380ff2f34731ad2abee556a6f7..0000000000000000000000000000000000000000 Binary files a/buildsystem/apkdetails/apkdetails-1.2.2.jar and /dev/null differ diff --git a/buildsystem/codequality/lint.xml b/buildsystem/codequality/lint.xml deleted file mode 100644 index ddbbe9219170895865dc137b1c865da77bad5205..0000000000000000000000000000000000000000 --- a/buildsystem/codequality/lint.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/buildsystem/common-methods.gradle b/buildsystem/common-methods.gradle deleted file mode 100644 index 9d1ab3f4262fa92d8ccac1c170368de2e9d2dbb5..0000000000000000000000000000000000000000 --- a/buildsystem/common-methods.gradle +++ /dev/null @@ -1,93 +0,0 @@ -def execGitHashShort() { - return cleanString('git rev-parse --short HEAD'.execute().text) -} - -def execGitHash() { - return cleanString('git rev-parse HEAD'.execute().text) -} - -def execGitBranch() { - return cleanString('git show -s --pretty=%d HEAD'.execute().text) -} - -def execGitCommitDate() { - final String cmd = "git show -s --format=%ci " + this.execGitHash() - return cleanString(cmd.execute().text) -} - -def execGitLog(int items) { - final String cmd = "git log -n " + items + " --abbrev-commit --pretty=oneline" - return cmd.execute().text -} - -def getBuildNumber() { - def buildNumberVariable = "CIRCLE_BUILD_NUM" - def buildNumberValue = System.getenv(buildNumberVariable) - - if (buildNumberValue != null && !buildNumberValue.isEmpty()) { - return buildNumberValue as Integer - } else { - return 1 - } -} - -def isRunningOnCi() { - def envVariable = "CI" - return Boolean.parseBoolean(System.getenv(envVariable) ?: "false") -} - -def cleanString(final String text) { - return text.trim().replaceAll('/', '_').replaceAll('-', '_') -} - -def quoteString(final String str) { - final String quote = "\"" - - if (str.length() > 0) { - if (str.startsWith(quote) && str.endsWith(quote)) { - return str - } else { - return quote + str + quote - } - } else { - return quote + quote - } -} - - - -def getLastGitCommitMessage() { - return 'git log -1 --pretty=%B'.execute().text.trim() -} - -def collectCommonProguardRules() { - def proguardFileDirectory = "${project.rootDir}/buildsystem/proguard-rules/" - - def proguardFileSet = fileTree(proguardFileDirectory).filter { it.isFile() }.files - if(proguardFileSet.isEmpty()) { - throw new IllegalStateException("No proguard rules found in $proguardFileDirectory") - } - - return proguardFileSet.toList().sort() -} - - -def getPropertySafe(prop, fallback) { - rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback -} - -ext { - execGitHashShort = this.&execGitHashShort - execGitHash = this.&execGitHash - execGitBranch = this.&execGitBranch - execGitCommitDate = this.&execGitCommitDate - execGitLog = this.&execGitLog - isRunningOnCi = this.&isRunningOnCi - getLibVersions = this.&getLibVersions - quoteString = this."eString - getBuildNumber = this.&getBuildNumber - getLastGitCommitMessage = this.&getLastGitCommitMessage - shouldSkipFlakyRobolectricTests = this.&shouldSkipFlakyRobolectricTests - collectCommonProguardRules = this.&collectCommonProguardRules - getPropertySafe = this.&getPropertySafe -} \ No newline at end of file diff --git a/buildsystem/generate_dependency_hashfile.sh b/buildsystem/generate_dependency_hashfile.sh deleted file mode 100755 index 57f5db9079d6fbd1a222543c38e8ae7e2b61c8c5..0000000000000000000000000000000000000000 --- a/buildsystem/generate_dependency_hashfile.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -# -# This script scans the directory passed as the first parameter and hashes all gradlefiles it finds/ -# It then outputs the hashes into the file passed as the second parameter -# - -SOURCE_DIR=$1 -HASH_FILE=$2 -REGEX_PATTERN_GRADLE="*\.gradle" -REGEX_PATTERN_ROBOLECTRIC="*robolectric.properties" - -find ${SOURCE_DIR} -type f \( -iname "$REGEX_PATTERN_GRADLE" -o -iname "$REGEX_PATTERN_ROBOLECTRIC" \) -exec md5sum {} \; | sort -k2 -b > ${HASH_FILE} - diff --git a/buildsystem/jacocoxml/jacocoxmlparser-1.0.0.jar b/buildsystem/jacocoxml/jacocoxmlparser-1.0.0.jar deleted file mode 100644 index b553f20b3a19f5cb4acca7e0193695172b40ee36..0000000000000000000000000000000000000000 Binary files a/buildsystem/jacocoxml/jacocoxmlparser-1.0.0.jar and /dev/null differ diff --git a/buildsystem/multidex/multidex.pro b/buildsystem/multidex/multidex.pro deleted file mode 100644 index a6aa9480d616f3a972852293344c76607249f47c..0000000000000000000000000000000000000000 --- a/buildsystem/multidex/multidex.pro +++ /dev/null @@ -1,2 +0,0 @@ --keep class **Test { *; } --keep class **Module { *; } \ No newline at end of file diff --git a/buildsystem/proguard-rules/dagger2-rules.pro b/buildsystem/proguard-rules/dagger2-rules.pro deleted file mode 100644 index e5480eea7d2f75c424f61ed0258171dae7b5cdb6..0000000000000000000000000000000000000000 --- a/buildsystem/proguard-rules/dagger2-rules.pro +++ /dev/null @@ -1 +0,0 @@ --dontwarn com.google.errorprone.annotations.** \ No newline at end of file diff --git a/buildsystem/proguard-rules/gson-rules.pro b/buildsystem/proguard-rules/gson-rules.pro deleted file mode 100644 index c50ceb0c606e8374862959da5fbcb158eb3205f6..0000000000000000000000000000000000000000 --- a/buildsystem/proguard-rules/gson-rules.pro +++ /dev/null @@ -1,24 +0,0 @@ -# Source: https://github.com/google/gson/blob/master/examples/android-proguard-example/proguard.cfg - -##---------------Begin: proguard configuration for Gson ---------- -# Gson uses generic type information stored in a class file when working with fields. Proguard -# removes such information by default, so configure it to keep all of it. --keepattributes Signature - -# For using GSON @Expose annotation --keepattributes *Annotation* - -# Gson specific classes --dontwarn sun.misc.** -#-keep class com.google.gson.stream.** { *; } - -# Application classes that will be serialized/deserialized over Gson --keep class com.google.gson.examples.android.model.** { *; } - -# Prevent proguard from stripping interface information from TypeAdapterFactory, -# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) --keep class * implements com.google.gson.TypeAdapterFactory --keep class * implements com.google.gson.JsonSerializer --keep class * implements com.google.gson.JsonDeserializer - -##---------------End: proguard configuration for Gson ---------- \ No newline at end of file diff --git a/buildsystem/proguard-rules/kotlin-rules.pro b/buildsystem/proguard-rules/kotlin-rules.pro deleted file mode 100644 index d595cdc4cf1042e03777e7e0abbcd1f1303901a1..0000000000000000000000000000000000000000 --- a/buildsystem/proguard-rules/kotlin-rules.pro +++ /dev/null @@ -1,2 +0,0 @@ --keep class kotlin.reflect.jvm.internal.** { *; } --keep class kotlin.Metadata { *; } \ No newline at end of file diff --git a/buildsystem/signing_keys/debug.keystore b/buildsystem/signing_keys/debug.keystore deleted file mode 100644 index 3a4172f54d5e27ebaa5490f7215d54b0b64d5d8c..0000000000000000000000000000000000000000 Binary files a/buildsystem/signing_keys/debug.keystore and /dev/null differ diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 23d13c5452061fd73289a0856cb3fa0bf28ead1f..0000000000000000000000000000000000000000 --- a/circle.yml +++ /dev/null @@ -1,7 +0,0 @@ - test: - override: - - (echo "Running JUnit tests!") - - ./gradlew test -PdisablePreDex - post: - - mkdir -p $CIRCLE_TEST_REPORTS/junit/ - - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \; diff --git a/dist/BluetoothLeLibrary-0.0.1-javadoc.jar b/dist/BluetoothLeLibrary-0.0.1-javadoc.jar deleted file mode 100644 index e0e1a58af61a65749b46abb7740368f515ced890..0000000000000000000000000000000000000000 Binary files a/dist/BluetoothLeLibrary-0.0.1-javadoc.jar and /dev/null differ diff --git a/dist/BluetoothLeLibrary-0.0.1.jar b/dist/BluetoothLeLibrary-0.0.1.jar deleted file mode 100644 index d660473ddb33b87024d71547b3e57581a5ba3fcc..0000000000000000000000000000000000000000 Binary files a/dist/BluetoothLeLibrary-0.0.1.jar and /dev/null differ diff --git a/documents/Bluetooth_UUIDs.ods b/documents/Bluetooth_UUIDs.ods deleted file mode 100644 index 1ff67fa3564449ad83033d3ec0ccf9cf81738c25..0000000000000000000000000000000000000000 Binary files a/documents/Bluetooth_UUIDs.ods and /dev/null differ diff --git a/documents/COMPANY_IDENTIFIERS.ods b/documents/COMPANY_IDENTIFIERS.ods deleted file mode 100644 index ce7f86cdb294a51b28a636647863654ca02e190e..0000000000000000000000000000000000000000 Binary files a/documents/COMPANY_IDENTIFIERS.ods and /dev/null differ diff --git a/sample_app/.gitignore b/entry/.gitignore similarity index 100% rename from sample_app/.gitignore rename to entry/.gitignore diff --git a/entry/build.gradle b/entry/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..5a04d972dc55df408b95c75947b3ebccf52f9ecf --- /dev/null +++ b/entry/build.gradle @@ -0,0 +1,26 @@ +apply plugin: 'com.huawei.ohos.hap' +apply plugin: 'com.huawei.ohos.decctest' +ohos { + compileSdkVersion 5 + defaultConfig { + compatibleSdkVersion 5 + } + buildTypes { + release { + proguardOpt { + proguardEnabled false + rulesFiles 'proguard-rules.pro' + } + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.har']) + testImplementation 'junit:junit:4.13' + ohosTestImplementation 'com.huawei.ohos.testkit:runner:1.0.0.100' + compile project(":library") +} +decc { + supportType = ['html','xml'] +} diff --git a/entry/proguard-rules.pro b/entry/proguard-rules.pro new file mode 100644 index 0000000000000000000000000000000000000000..f7666e47561d514b2a76d5a7dfbb43ede86da92a --- /dev/null +++ b/entry/proguard-rules.pro @@ -0,0 +1 @@ +# config module specific ProGuard rules here. \ No newline at end of file diff --git a/entry/src/main/config.json b/entry/src/main/config.json new file mode 100644 index 0000000000000000000000000000000000000000..4a70fa9d2f1b2ae7270dba8d133564e8d175ff93 --- /dev/null +++ b/entry/src/main/config.json @@ -0,0 +1,109 @@ +{ + "app": { + "bundleName": "me.jagar.chatvoiceplayerlibrary", + "vendor": "co", + "version": { + "code": 1, + "name": "1.0" + }, + "apiVersion": { + "compatible": 5, + "target": 5 + } + }, + "deviceConfig": {}, + "module": { + "package": "uk.co.alt236.btlescan", + "name": ".MyApplication", + "deviceType": [ + "phone" + ], + "reqPermissions": [ + { + "name": "ohos.permission.USE_BLUETOOTH", + "reason": "reason", + "usedScene": { + "ability": [ + "uk.co.alt236.btlescan" + ], + "when": "always" + } + }, + { + "name": "ohos.permission.LOCATION", + "reason": "reason", + "usedScene": { + "ability": [ + "uk.co.alt236.btlescan" + ], + "when": "always" + } + }, + { + "name": "ohos.permission.DISCOVER_BLUETOOTH", + "reason": "reason", + "usedScene": { + "ability": [ + "uk.co.alt236.btlescan" + ], + "when": "always" + } + }, + { + "name": "ohos.permission.COMMONEVENT_STICKY", + "reason": "reason", + "usedScene": { + "ability": [ + "uk.co.alt236.btlescan" + ], + "when": "always" + } + }, + { + "name": "ohos.abilitydemo.permission.PROVIDER", + "reason": "reason", + "usedScene": { + "ability": [ + "uk.co.alt236.btlescan" + ], + "when": "always" + } + } + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry" + }, + "abilities": [ + { + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + "orientation": "unspecified", + "name": "uk.co.alt236.btlescan.MainAbility", + "icon": "$media:icon", + "description": "$string:mainability_description", + "label": "$string:app_name", + "type": "page", + "launchType": "standard", + "metaData": { + "customizeData": [ + { + "extra": "", + "name": "hwc-theme", + "value": "androidhwext:style/Theme.Emui.Light.NoTitleBar" + } + ] + } + } + ] + } +} \ No newline at end of file diff --git a/entry/src/main/java/uk/co/alt236/btlescan/MainAbility.java b/entry/src/main/java/uk/co/alt236/btlescan/MainAbility.java new file mode 100644 index 0000000000000000000000000000000000000000..063ce966f8e9dd192291985dc5721ed702e7184c --- /dev/null +++ b/entry/src/main/java/uk/co/alt236/btlescan/MainAbility.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain an copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package uk.co.alt236.btlescan; + +import ohos.aafwk.ability.Ability; +import ohos.aafwk.content.Intent; +import uk.co.alt236.btlescan.slice.MainAbilitySlice; + +/** + * MainAbility + * + * @author ljx + * @since 2021-04-29 + */ +public class MainAbility extends Ability { + @Override + public void onStart(Intent intent) { + super.onStart(intent); + super.setMainRoute(MainAbilitySlice.class.getName()); + + requestPermissionsFromUser(new String[] {"ohos.permission.USE_BLUETOOTH", + "ohos.permission.LOCATION", + "ohos.permission.DISCOVER_BLUETOOTH" }, 1); + } +} \ No newline at end of file diff --git a/entry/src/main/java/uk/co/alt236/btlescan/MyApplication.java b/entry/src/main/java/uk/co/alt236/btlescan/MyApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..e6b8a883c4bf9f72b721563ce78df8664a373431 --- /dev/null +++ b/entry/src/main/java/uk/co/alt236/btlescan/MyApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain an copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package uk.co.alt236.btlescan; + +import ohos.aafwk.ability.AbilityPackage; + +/** + * MyApplication + * + * @author MyApplication + * @since 2021-04-29 + */ +public class MyApplication extends AbilityPackage { + @Override + public void onInitialize() { + super.onInitialize(); + } +} diff --git a/entry/src/main/java/uk/co/alt236/btlescan/adapter/ConnectItemProvider.java b/entry/src/main/java/uk/co/alt236/btlescan/adapter/ConnectItemProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..1844755a7cfad669d26712f122e075047e36db78 --- /dev/null +++ b/entry/src/main/java/uk/co/alt236/btlescan/adapter/ConnectItemProvider.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain an copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package uk.co.alt236.btlescan.adapter; + +import ohos.aafwk.ability.AbilitySlice; +import ohos.agp.components.BaseItemProvider; +import ohos.agp.components.Component; +import ohos.agp.components.ComponentContainer; +import ohos.agp.components.DirectionalLayout; +import ohos.agp.components.Image; +import ohos.agp.components.LayoutScatter; +import ohos.agp.components.ListContainer; +import ohos.agp.components.Text; +import ohos.bluetooth.ble.GattCharacteristic; +import ohos.bluetooth.ble.GattService; +import uk.co.alt236.bluetoothlelib.resolvers.GattAttributeResolver; +import uk.co.alt236.btlescan.ResourceTable; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * ConnectItemProvider + * + * @author ljx + * @since 2021-04-29 + */ +public class ConnectItemProvider extends BaseItemProvider { + private List list; + private AbilitySlice slice; + + /** + * ConnectItemProvider + * + * @param list + * @param slice + */ + + public ConnectItemProvider(List list, AbilitySlice slice) { + this.list = list; + this.slice = slice; + } + + /** + * setData + * + * @param listData + */ + public void setData(List listData) { + this.list = listData; + notifyDataChanged(); + } + + @Override + public int getCount() { + return list == null ? 0 : list.size(); + } + + @Override + public Object getItem(int position) { + if (list != null && position >= 0 && position < list.size()) { + return list.get(position); + } + return Optional.empty(); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public Component getComponent(int position, Component convertComponent, ComponentContainer componentContainer) { + final Component cpt; + if (convertComponent == null) { + cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_connect, null, false); + } else { + cpt = convertComponent; + } + GattService service = list.get(position); + Text name = (Text) cpt.findComponentById(ResourceTable.Id_tv_name); + Text mac = (Text) cpt.findComponentById(ResourceTable.Id_tv_uuid); + Image arrow = (Image) cpt.findComponentById(ResourceTable.Id_iv_arrow); + ListContainer listContainer = (ListContainer) cpt.findComponentById(ResourceTable.Id_item_list); + + DirectionalLayout itemView = (DirectionalLayout) cpt.findComponentById(ResourceTable.Id_root_item_view); + itemView.setClickedListener(v -> { + if (listContainer.getVisibility() == Component.HIDE) { + listContainer.setVisibility(Component.VISIBLE); + arrow.setImageAndDecodeBounds(ResourceTable.Media_arrow2); + } else { + listContainer.setVisibility(Component.HIDE); + arrow.setImageAndDecodeBounds(ResourceTable.Media_arrow); + } + notifyDataChanged(); + }); + UUID uuid = service.getUuid(); + String attributeName = GattAttributeResolver.getAttributeName(uuid.toString(), "Unknown Service"); + name.setText(attributeName); + mac.setText(uuid.toString()); + + List characteristics = service.getCharacteristics(); + ItemItemProvider itemProvider = new ItemItemProvider(characteristics); + listContainer.setItemProvider(itemProvider); + return cpt; + } + + /** + * ItemItemProvider + * + * @author ljx + * @since 2021-04-14 + */ + public class ItemItemProvider extends BaseItemProvider { + private List list; + + /** + * ItemItemProvider + * + * @param list + */ + public ItemItemProvider(List list) { + this.list = list; + } + + @Override + public int getCount() { + return list == null ? 0 : list.size(); + } + + @Override + public Object getItem(int position) { + if (list != null && position >= 0 && position < list.size()) { + return list.get(position); + } + return Optional.empty(); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public Component getComponent(int position, Component convertComponent, ComponentContainer componentContainer) { + final Component cpt; + if (convertComponent == null) { + cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_item_connect, null, false); + } else { + cpt = convertComponent; + } + GattCharacteristic service = list.get(position); + + Text name = (Text) cpt.findComponentById(ResourceTable.Id_tv_name); + Text mac = (Text) cpt.findComponentById(ResourceTable.Id_tv_uuid); + UUID uuid = service.getUuid(); + String attributeName = GattAttributeResolver.getAttributeName(uuid.toString(), "Unknown characteristic"); + name.setText(attributeName); + mac.setText(uuid.toString()); + return cpt; + } + } +} \ No newline at end of file diff --git a/entry/src/main/java/uk/co/alt236/btlescan/adapter/DeviceItemProvider.java b/entry/src/main/java/uk/co/alt236/btlescan/adapter/DeviceItemProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..d3af5727936d9366649569e632b18b976e3d0fb9 --- /dev/null +++ b/entry/src/main/java/uk/co/alt236/btlescan/adapter/DeviceItemProvider.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain an copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package uk.co.alt236.btlescan.adapter; + +import ohos.aafwk.ability.AbilitySlice; +import ohos.agp.components.BaseItemProvider; +import ohos.agp.components.Component; +import ohos.agp.components.ComponentContainer; +import ohos.agp.components.LayoutScatter; +import ohos.agp.components.Text; +import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; +import uk.co.alt236.btlescan.ResourceTable; + +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Optional; + +/** + * ConnectItemProvider + * + * @author ljx + * @since 2021-04-29 + */ +public class DeviceItemProvider extends BaseItemProvider { + private List list; + private AbilitySlice slice; + + /** + * DeviceItemProvider + * + * @param listData + * @param abilitySlice + */ + public DeviceItemProvider(List listData, AbilitySlice abilitySlice) { + this.list = listData; + this.slice = abilitySlice; + } + + /** + * setData + * + * @param listData + */ + public void setData(List listData) { + this.list = listData; + notifyDataChanged(); + } + + @Override + public int getCount() { + return list == null ? 0 : list.size(); + } + + @Override + public Object getItem(int position) { + if (list != null && position >= 0 && position < list.size()) { + return list.get(position); + } + return Optional.empty(); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public Component getComponent(int position, Component convertComponent, ComponentContainer componentContainer) { + final Component cpt; + if (convertComponent == null) { + cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_sample, null, false); + } else { + cpt = convertComponent; + } + BluetoothLeDevice sampleItem = list.get(position); + Text name = (Text) cpt.findComponentById(ResourceTable.Id_item_name); + Text mac = (Text) cpt.findComponentById(ResourceTable.Id_mac); + Text date = (Text) cpt.findComponentById(ResourceTable.Id_date); + name.setText(sampleItem.getName()); + mac.setText(sampleItem.getAddress()); + Text rssi = (Text) cpt.findComponentById(ResourceTable.Id_rssi); + long timestamp = sampleItem.getTimestamp(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String sd = sdf.format(timestamp); + date.setText(sd); + rssi.setText(sampleItem.getRssi() + "db / " + sampleItem.getFirstRssi() + "db"); + return cpt; + } +} \ No newline at end of file diff --git a/entry/src/main/java/uk/co/alt236/btlescan/slice/DeviceAbilitySlice.java b/entry/src/main/java/uk/co/alt236/btlescan/slice/DeviceAbilitySlice.java new file mode 100644 index 0000000000000000000000000000000000000000..1cb0e2003faba8ca42a441e0ddc4960bf7f50838 --- /dev/null +++ b/entry/src/main/java/uk/co/alt236/btlescan/slice/DeviceAbilitySlice.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain an copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package uk.co.alt236.btlescan.slice; + +import ohos.aafwk.ability.AbilitySlice; +import ohos.aafwk.content.Intent; +import ohos.agp.components.Button; +import ohos.agp.components.Text; +import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; +import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; +import uk.co.alt236.bluetoothlelib.util.AdRecordUtils; +import uk.co.alt236.bluetoothlelib.util.ByteUtils; +import uk.co.alt236.btlescan.ResourceTable; + +import java.text.SimpleDateFormat; +import java.util.Collection; + +/** + * DeviceAbilitySlice + * + * @author ljx + * @since 2021-04-29 + */ +public class DeviceAbilitySlice extends AbilitySlice { + private static final String DB = "db"; + private BluetoothLeDevice device; + + @Override + public void onStart(Intent intent) { + super.onStart(intent); + super.setUIContent(ResourceTable.Layout_ability_device); + device = intent.getSequenceableParam("device"); + initView(); + initListener(); + } + + private void initView() { + Text titlebar = (Text) findComponentById(ResourceTable.Id_tv_title); + titlebar.setText(device.getName()); + Text deviceName = (Text) findComponentById(ResourceTable.Id_device_name); + Text deviceAddress = (Text) findComponentById(ResourceTable.Id_device_address); + Text deviceClass = (Text) findComponentById(ResourceTable.Id_device_class); + deviceName.setText("Device Name:" + device.getName()); + deviceAddress.setText("Device address:" + device.getAddress()); + deviceClass.setText("Device Class:"); + Text majorClass = (Text) findComponentById(ResourceTable.Id_device_major); + Text services = (Text) findComponentById(ResourceTable.Id_device_services); + Text state = (Text) findComponentById(ResourceTable.Id_device_state); + majorClass.setText("Major Class:"); + services.setText("Services:"); + state.setText("Bonding State:"); + Text firstTime = (Text) findComponentById(ResourceTable.Id_rssi_first_time); + Text rssi = (Text) findComponentById(ResourceTable.Id_rssi_first); + Text lastTime = (Text) findComponentById(ResourceTable.Id_rssi_last_time); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS zzz"); + String sd = sdf.format(device.getFirstTimestamp()); + firstTime.setText("First timestamp:" + sd); + rssi.setText("First RSSI:" + device.getFirstRssi() + DB); + String lastsd = sdf.format(device.getTimestamp()); + lastTime.setText("Last timestamp:" + lastsd); + Text lastRssi = (Text) findComponentById(ResourceTable.Id_rssi_last); + lastRssi.setText("Last RSSI:" + device.getRssi() + DB); + Text average = (Text) findComponentById(ResourceTable.Id_rssi_average); + average.setText("Running Average RSSI:" + device.getRunningAverageRssi() + DB); + Text scanRecord = (Text) findComponentById(ResourceTable.Id_scan_record); + scanRecord.setText(ByteUtils.byteArrayToHexString(device.getScanRecord())); + Text adtitle = (Text) findComponentById(ResourceTable.Id_title); + Text asstring = (Text) findComponentById(ResourceTable.Id_as_string); + Text asarray = (Text) findComponentById(ResourceTable.Id_as_array); + Collection adRecords = device.getAdRecordStore().getRecordsAsCollection(); + if (adRecords.size() > 0) { + for (AdRecord record : adRecords) { + if (record != null) { + String title = "#" + record.getType() + " " + record.getHumanReadableType(); + adtitle.setText(title); + asstring.setText("As String:" + AdRecordUtils.getRecordDataAsString(record)); + asarray.setText("As Array:" + ByteUtils.byteArrayToHexString(record.getData())); + } + } + } + } + + private void initListener() { + Button connect = (Button) findComponentById(ResourceTable.Id_connect); + connect.setClickedListener(v -> { + Intent in = new Intent(); + in.setParam("device", device); + present(new DeviceControlAbilitySlice(), in); + }); + } + + @Override + public void onActive() { + super.onActive(); + } + + @Override + public void onForeground(Intent intent) { + super.onForeground(intent); + } +} \ No newline at end of file diff --git a/entry/src/main/java/uk/co/alt236/btlescan/slice/DeviceControlAbilitySlice.java b/entry/src/main/java/uk/co/alt236/btlescan/slice/DeviceControlAbilitySlice.java new file mode 100644 index 0000000000000000000000000000000000000000..b76c71646dca2af48e436aff838b6fbfe875f701 --- /dev/null +++ b/entry/src/main/java/uk/co/alt236/btlescan/slice/DeviceControlAbilitySlice.java @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain an copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package uk.co.alt236.btlescan.slice; + +import ohos.aafwk.ability.AbilitySlice; +import ohos.aafwk.content.Intent; +import ohos.agp.components.Button; +import ohos.agp.components.Component; +import ohos.agp.components.ListContainer; +import ohos.agp.components.RoundProgressBar; +import ohos.agp.components.Text; +import ohos.bluetooth.ProfileBase; +import ohos.bluetooth.ble.BlePeripheralCallback; +import ohos.bluetooth.ble.BlePeripheralDevice; +import ohos.bluetooth.ble.BleScanResult; +import ohos.bluetooth.ble.GattCharacteristic; +import ohos.bluetooth.ble.GattDescriptor; +import ohos.bluetooth.ble.GattService; +import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; +import uk.co.alt236.bluetoothlelib.resolvers.GattAttributeResolver; +import uk.co.alt236.bluetoothlelib.util.ByteUtils; +import uk.co.alt236.btlescan.ResourceTable; +import uk.co.alt236.btlescan.adapter.ConnectItemProvider; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +/** + * DeviceAbilitySlice + * + * @author ljx + * @since 2021-04-29 + */ +public class DeviceControlAbilitySlice extends AbilitySlice { + private static final String MDISCONNECTSTR = "DISCONNECT"; + private static final String MCONNECTINGSTR = "connecting"; + private static final String MCONNECTSTR = "CONNECT"; + private static final String MUNKNOWNSTR = "unknown"; + private static final String MUUIDSTR = "UUID:"; + private static final String MDESCSTR = "Desc:"; + private static final String ASSTRING = "As String:"; + private static final String ASARRAY = "As Array:"; + private static final int INCREASEROTATE = 20; + private static final int MAXROTATE = 360; + private static final int DELAYTIME = 100; + private boolean isFirst = true; + private BlePeripheralDevice peripheraDevice; + private Text mState; + private RoundProgressBar mProgressBar; + private Button mConnect; + private int rotate = 0; + private boolean isRotation = true; + private Text mUuid; + private Text mDesc; + private Text mStr; + private Text mArr; + private ListContainer listContainer; + + @Override + public void onStart(Intent intent) { + super.onStart(intent); + super.setUIContent(ResourceTable.Layout_ability_device_control); + initView(); + BluetoothLeDevice device = intent.getSequenceableParam("device"); + Text titlebar = (Text) findComponentById(ResourceTable.Id_tv_title); + titlebar.setText(device.getName()); + Text address = (Text) findComponentById(ResourceTable.Id_address); + address.setText(device.getAddress()); + BleScanResult bleDevice = device.getDevice(); + peripheraDevice = bleDevice.getPeripheralDevice(); + + // 与外围设备建立连接,允许自动回连,连接会触发connectionStateChangedEvent回调 + MyBlePeripheralCallback callback = new MyBlePeripheralCallback(); + peripheraDevice.connect(true, callback); + + // 连接后读取外围设备RSSI值,获取后触发readRemoteRssiEvent()回调 + peripheraDevice.readRemoteRssiValue(); + mState.setText(MCONNECTINGSTR); + mConnect.setText(MDISCONNECTSTR); + startRotate(); + mConnect.setClickedListener(v -> { + String text = mConnect.getText(); + if (MCONNECTSTR.equals(text)) { + startRotate(); + mState.setText(MCONNECTINGSTR); + mConnect.setText(MDISCONNECTSTR); + peripheraDevice.connect(true, callback); + peripheraDevice.readRemoteRssiValue(); + } else { + stopRotate(); + peripheraDevice.disconnect(); + mState.setText("disconnect"); + mConnect.setText(MCONNECTSTR); + } + }); + } + + private void initView() { + mState = (Text) findComponentById(ResourceTable.Id_ble_state); + mProgressBar = (RoundProgressBar) findComponentById(ResourceTable.Id_round_progress_bar); + mConnect = (Button) findComponentById(ResourceTable.Id_disconnect); + listContainer = (ListContainer) findComponentById(ResourceTable.Id_listcontainer); + mUuid = (Text) findComponentById(ResourceTable.Id_uuid); + mDesc = (Text) findComponentById(ResourceTable.Id_desc); + mStr = (Text) findComponentById(ResourceTable.Id_as_string); + mArr = (Text) findComponentById(ResourceTable.Id_as_array); + } + + /** + * 实现外围设备操作回调 + * + * @author ljx + * @since 2021-04-29 + */ + private class MyBlePeripheralCallback extends BlePeripheralCallback { + @Override + public void connectionStateChangeEvent(int connectionState) { + super.connectionStateChangeEvent(connectionState); + if (connectionState == ProfileBase.STATE_CONNECTED) { + peripheraDevice.discoverServices(); // 连接成功获取外围设备的Service + getUITaskDispatcher().asyncDispatch(() -> { + mState.setText("connected"); + mConnect.setText(MDISCONNECTSTR); + stopRotate(); + listContainer.setVisibility(Component.VISIBLE); + }); + } + if (connectionState == ProfileBase.STATE_CONNECTING) { + // 连接中 + getUITaskDispatcher().asyncDispatch(() -> { + mState.setText(MCONNECTINGSTR); + mConnect.setText(MDISCONNECTSTR); + startRotate(); + listContainer.setVisibility(Component.INVISIBLE); + }); + } + if (connectionState == ProfileBase.STATE_DISCONNECTED) { + // 连接断开 + getUITaskDispatcher().asyncDispatch(() -> { + mState.setText("disconnected"); + mConnect.setText(MCONNECTSTR); + stopRotate(); + listContainer.setVisibility(Component.INVISIBLE); + }); + } + } + + @Override + public void servicesDiscoveredEvent(int status) { + // 获取外围设备Service的回调 + if (status == BlePeripheralDevice.OPERATION_SUCC) { + if (isFirst) { + // 获取Service成功后获服务列表 + List services = peripheraDevice.getServices(); + ConnectItemProvider provider = new ConnectItemProvider(services, DeviceControlAbilitySlice.this); + getUITaskDispatcher().asyncDispatch(new Runnable() { + @Override + public void run() { + isFirst = false; + listContainer.setItemProvider(provider); + } + }); + } + } + } + + @Override + public void characteristicChangedEvent(GattCharacteristic charecteristic) { + // 外围设备主动向中心设备发送特征值通知时触发回调 + // 根据通知的charecteristic获取特征值携带的数据 + UUID uuid = charecteristic.getUuid(); + byte[] value = charecteristic.getValue(); + String attribute = GattAttributeResolver.getAttributeName(uuid.toString(), MUNKNOWNSTR); + getUITaskDispatcher().asyncDispatch(new Runnable() { + @Override + public void run() { + mUuid.setText(MUUIDSTR + uuid.toString()); + mDesc.setText(MDESCSTR + attribute); + mStr.setText(ASSTRING + Arrays.toString(value)); + mArr.setText(ASARRAY + ByteUtils.byteArrayToHexString(value)); + } + }); + } + + @Override + public void characteristicWriteEvent(GattCharacteristic charecteristic, int ret) { + if (ret == BlePeripheralDevice.OPERATION_SUCC) { + // 向外围设备写特征值数据成功后的操作 + UUID uuid = charecteristic.getUuid(); + byte[] value = charecteristic.getValue(); + String attribute = GattAttributeResolver.getAttributeName(uuid.toString(), MUNKNOWNSTR); + getUITaskDispatcher().asyncDispatch(new Runnable() { + @Override + public void run() { + mUuid.setText(MUUIDSTR + uuid.toString()); + mDesc.setText(MDESCSTR + attribute); + mStr.setText(ASSTRING + Arrays.toString(value)); + mArr.setText(ASARRAY + ByteUtils.byteArrayToHexString(value)); + } + }); + } + } + + @Override + public void characteristicReadEvent(GattCharacteristic charecteristic, int ret) { + if (ret == BlePeripheralDevice.OPERATION_SUCC) { + // 向外围设备写特征值数据成功后的操作 + UUID uuid = charecteristic.getUuid(); + byte[] value = charecteristic.getValue(); + String attribute = GattAttributeResolver.getAttributeName(uuid.toString(), MUNKNOWNSTR); + getUITaskDispatcher().asyncDispatch(new Runnable() { + @Override + public void run() { + mUuid.setText(MUUIDSTR + uuid.toString()); + mDesc.setText(MDESCSTR + attribute); + mStr.setText(ASSTRING + Arrays.toString(value)); + mArr.setText(ASARRAY + ByteUtils.byteArrayToHexString(value)); + } + }); + } + } + + @Override + public void descriptorReadEvent(GattDescriptor descriptor, int ret) { + // 向外围设备读描述值数据成功后的操作 + } + + @Override + public void descriptorWriteEvent(GattDescriptor descriptor, int ret) { + // 向外围设备写描述值数据成功后的操作 + } + + @Override + public void readRemoteRssiEvent(int rssi, int ret) { + // 读取外围设备RSSI值成功后的操作,对端RSSI值为rssi + } + } + + private void startRotate() { + isRotation = true; + mProgressBar.setVisibility(Component.VISIBLE); + getUITaskDispatcher().delayDispatch(() -> { + rotate = rotate + INCREASEROTATE; + if (rotate == MAXROTATE) { + rotate = 0; + } + if (isRotation && mProgressBar != null && mProgressBar.getVisibility() == Component.VISIBLE) { + mProgressBar.setRotation(rotate); + startRotate(); + } + }, DELAYTIME); + } + + private void stopRotate() { + isRotation = false; + mProgressBar.setVisibility(Component.INVISIBLE); + } + + @Override + protected void onStop() { + super.onStop(); + stopRotate(); + peripheraDevice.disconnect(); + } + + @Override + public void onActive() { + super.onActive(); + } + + @Override + public void onForeground(Intent intent) { + super.onForeground(intent); + } +} \ No newline at end of file diff --git a/entry/src/main/java/uk/co/alt236/btlescan/slice/MainAbilitySlice.java b/entry/src/main/java/uk/co/alt236/btlescan/slice/MainAbilitySlice.java new file mode 100644 index 0000000000000000000000000000000000000000..a05c4870cca80553836f52fa3574eff73437cb54 --- /dev/null +++ b/entry/src/main/java/uk/co/alt236/btlescan/slice/MainAbilitySlice.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain an copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package uk.co.alt236.btlescan.slice; + +import ohos.aafwk.ability.AbilitySlice; +import ohos.aafwk.content.Intent; +import ohos.aafwk.content.Operation; +import ohos.agp.components.Button; +import ohos.agp.components.Component; +import ohos.agp.components.DirectionalLayout; +import ohos.agp.components.LayoutScatter; +import ohos.agp.components.ListContainer; +import ohos.agp.components.RoundProgressBar; +import ohos.agp.components.Text; +import ohos.agp.utils.LayoutAlignment; +import ohos.agp.window.dialog.CommonDialog; +import ohos.agp.window.service.DisplayAttributes; +import ohos.agp.window.service.DisplayManager; +import ohos.app.Context; +import ohos.bluetooth.BluetoothHost; +import ohos.bluetooth.ble.BleCentralManager; +import ohos.bluetooth.ble.BleCentralManagerCallback; +import ohos.bluetooth.ble.BleScanFilter; +import ohos.bluetooth.ble.BleScanResult; +import ohos.utils.IntentConstants; +import ohos.utils.net.Uri; +import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; +import uk.co.alt236.btlescan.ResourceTable; +import uk.co.alt236.btlescan.adapter.DeviceItemProvider; + +import java.util.ArrayList; +import java.util.List; + +/** + * DeviceAbilitySlice + * + * @author ljx + * @since 2021-04-29 + */ +public class MainAbilitySlice extends AbilitySlice { + private static final int WIDTH = 340; + private static final int RADIUS = 15; + private BluetoothHost bluetoothHost; + private ScanCallback centralManagerCallback; + private BleCentralManager centralManager; + private ListContainer listContainer; + private RoundProgressBar mProgressBar; + private int rotate = 0; + private boolean isRotation = true; + private Button scan; + private DeviceItemProvider sampleItemProvider; + private List listDevice; + private Text itemNum; + private Text tvState; + private final int increaseRotate = 20; + private final int maxRotate = 360; + private final int delayTime = 100; + + @Override + public void onStart(Intent intent) { + super.onStart(intent); + super.setUIContent(ResourceTable.Layout_ability_main); + + // 获取蓝牙本机管理对象 + bluetoothHost = BluetoothHost.getDefaultHost(this); + bluetoothHost.enableBt(); + centralManagerCallback = new ScanCallback(); + centralManager = new BleCentralManager(this, centralManagerCallback); + mProgressBar = (RoundProgressBar) findComponentById(ResourceTable.Id_round_progress_bar); + scan = (Button) findComponentById(ResourceTable.Id_scan); + listContainer = (ListContainer) findComponentById(ResourceTable.Id_listcontainer); + tvState = (Text) findComponentById(ResourceTable.Id_ble_state); + itemNum = (Text) findComponentById(ResourceTable.Id_item_num); + Text about = (Text) findComponentById(ResourceTable.Id_tv_about); + about.setClickedListener(v -> { + about(); + }); + init(); + } + + /** + * 初始化 + */ + public void init() { + scan.setClickedListener(v -> { + int state = bluetoothHost.getBtState(); + if (state == BluetoothHost.STATE_ON) { + tvState.setText("on"); + if ("scan".equals(scan.getText())) { + scan.setText("stop"); + startRotate(); + List filters = new ArrayList<>(); + centralManager.startScan(filters); + } else { + scan.setText("scan"); + stopRotate(); + centralManager.stopScan(); + } + } else { + bluetoothHost.enableBt(); + } + }); + if (bluetoothHost.getBtState() == BluetoothHost.STATE_ON) { + tvState.setText("on"); + } else { + tvState.setText("off"); + } + + listDevice = new ArrayList<>(); + sampleItemProvider = new DeviceItemProvider(listDevice, this); + listContainer.setItemProvider(sampleItemProvider); + listContainer.setItemClickedListener((container, component, position, id) -> { + BluetoothLeDevice deviceItem = (BluetoothLeDevice) listContainer.getItemProvider().getItem(position); + + Intent intent = new Intent(); + intent.setParam("device", deviceItem); + present(new DeviceAbilitySlice(), intent); + }); + } + + private void about() { + DirectionalLayout directionalLayout = (DirectionalLayout) LayoutScatter.getInstance(this) + .parse(ResourceTable.Layout_dialog_picker, null, false); + CommonDialog dialog = new CommonDialog(this); + dialog.setContentCustomComponent(directionalLayout); + dialog.setAutoClosable(true); + dialog.setSize(vp2px(this, WIDTH), DirectionalLayout.LayoutConfig.MATCH_CONTENT); + dialog.setAlignment(LayoutAlignment.CENTER); + dialog.setCornerRadius(vp2px(this, RADIUS)); + dialog.show(); + Text confirm = (Text) directionalLayout.findComponentById(ResourceTable.Id_tv_confirm); + confirm.setClickedListener(v -> { + dialog.destroy(); + }); + Text tvUrl = (Text) directionalLayout.findComponentById(ResourceTable.Id_tv_url); + tvUrl.setClickedListener(v -> { + initWeb(); + dialog.destroy(); + }); + } + + private void initWeb() { + Intent intents = new Intent(); + Operation operation = new Intent.OperationBuilder() + .withUri(Uri.parse("https://github.com/alt236/Bluetooth-LE-Library---Android")) + .withAction(IntentConstants.ACTION_SEARCH) + .build(); + intents.setOperation(operation); + startAbility(intents); + } + + /** + * vp转像素 + */ + private int vp2px(Context context, float vp) { + DisplayAttributes attributes = DisplayManager.getInstance().getDefaultDisplay(context).get().getAttributes(); + return (int) (attributes.densityPixels * vp); + } + + private void startRotate() { + isRotation = true; + mProgressBar.setVisibility(Component.VISIBLE); + getUITaskDispatcher().delayDispatch(() -> { + rotate = rotate + increaseRotate; + if (rotate == maxRotate) { + rotate = 0; + } + if (isRotation && mProgressBar != null && mProgressBar.getVisibility() == Component.VISIBLE) { + mProgressBar.setRotation(rotate); + startRotate(); + } + }, delayTime); + } + + private void stopRotate() { + isRotation = false; + mProgressBar.setVisibility(Component.INVISIBLE); + } + + /** + * ScanCallback + * + * @author ljx + * @since 2021-04-29 + */ + public class ScanCallback implements BleCentralManagerCallback { + List results = new ArrayList<>(); + + @Override + public void scanResultEvent(BleScanResult resultCode) { + // 对扫描结果进行处理 + results.add(resultCode); + int rssi = resultCode.getRssi(); + byte[] scanRecord = resultCode.getRawData(); + BluetoothLeDevice deviceLe = new BluetoothLeDevice(resultCode, + rssi, scanRecord, System.currentTimeMillis()); + getUITaskDispatcher().asyncDispatch(() -> { + for (int index = 0; index < listDevice.size(); index++) { + if (deviceLe.getAddress().equals(listDevice.get(index).getAddress())) { + BluetoothLeDevice item = listDevice.get(index); + item.updateRssiReading(System.currentTimeMillis(), rssi); + return; + } + } + listDevice.add(deviceLe); + sampleItemProvider.setData(listDevice); + itemNum.setText("Items:" + listDevice.size()); + }); + } + + @Override + public void scanFailedEvent(int resultCode) { + } + + @Override + public void groupScanResultsEvent(List list) { + } + } + + @Override + public void onActive() { + super.onActive(); + } + + @Override + public void onForeground(Intent intent) { + super.onForeground(intent); + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..743400fe0880475dd69f12f249d8b9a87fc53243 --- /dev/null +++ b/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "app_name", + "value": "Bluetooth_LE_Library" + }, + { + "name": "mainability_description", + "value": "Java_Phone_Empty Feature Ability" + }, + { + "name": "HelloWorld", + "value": "Hello World" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/graphic/background_ability_main.xml b/entry/src/main/resources/base/graphic/background_ability_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..c0c0a3df480fa387a452b9c40ca191cc918a3fc0 --- /dev/null +++ b/entry/src/main/resources/base/graphic/background_ability_main.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/layout/ability_device.xml b/entry/src/main/resources/base/layout/ability_device.xml new file mode 100644 index 0000000000000000000000000000000000000000..90a8a95f04252f13512593b03cd36927aa39fc82 --- /dev/null +++ b/entry/src/main/resources/base/layout/ability_device.xml @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + +