diff --git a/.gitignore b/.gitignore index 47ab45a..663ac4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ -/bin -/gen -local.properties -.idea/ -lint.xml -/.apt_generated +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +documents/.~lock.Bluetooth_UUIDs.ods# \ No newline at end of file diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..55fafa8 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +bluetooth-le-library \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..9a8b7e5 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..f841ede --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..198ad64 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..3b31283 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..c1b4dde --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Android API 18 Platform + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..c7e5960 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Run_Android_Tests.xml b/.idea/runConfigurations/Run_Android_Tests.xml new file mode 100644 index 0000000..1f32fb7 --- /dev/null +++ b/.idea/runConfigurations/Run_Android_Tests.xml @@ -0,0 +1,27 @@ + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Run_JUnit_Tests.xml b/.idea/runConfigurations/Run_JUnit_Tests.xml new file mode 100644 index 0000000..09ab733 --- /dev/null +++ b/.idea/runConfigurations/Run_JUnit_Tests.xml @@ -0,0 +1,23 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Run_Sample_App.xml b/.idea/runConfigurations/Run_Sample_App.xml new file mode 100644 index 0000000..8873b95 --- /dev/null +++ b/.idea/runConfigurations/Run_Sample_App.xml @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/LICENSE-2.0.html b/LICENSE-2.0.html new file mode 100644 index 0000000..2320883 --- /dev/null +++ b/LICENSE-2.0.html @@ -0,0 +1,235 @@ + + + + + + + + Apache License, Version 2.0 - The Apache Software Foundation + + +

+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/ +

+ +

+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +

+ +

1. Definitions.

+ +

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

+ +

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

+ +

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

+ +

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

+ +

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

+ +

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

+ +

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

+ +

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

+ +

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

+ +

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

+ +

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

+ +

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

+ +

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

+
    +
  1. You must give any other recipients of the Work or + Derivative Works a copy of this License; and +

  2. + +
  3. You must cause any modified files to carry prominent notices + stating that You changed the files; and +

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

  6. + +
  7. If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. +
  8. +
+You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +

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

+ +

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

+ +

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

+ +

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

+ +

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

+ +

+ END OF TERMS AND CONDITIONS +

+ + \ No newline at end of file diff --git a/README.md b/README.md index f7c0f09..8efe6ac 100644 --- a/README.md +++ b/README.md @@ -9,49 +9,51 @@ It also offers: * For iBeacons: A decently inaccurate (due to real world issues) distance approximation. * All the new object types are Parcelable. -This will only work on Android 4.3 (API Level 18). +This will only work on devices with Android 4.3 (API Level 18) and above. Sample app available on the [Play Store](https://play.google.com/store/apps/details?id=uk.co.alt236.btlescan) ## Including the Library in Your Project -There are two ways to use this library: -* Download a copy of the Bluetooth LE Library project and reference it in your project. -* Create a Jar file (see Jarification below) and add it into 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: +``` + repositories { + maven { + url "https://dl.bintray.com/alt236/maven" + } + } - -### Jarification - -Type `ant jar` at the root of the Library Project to produce a Jar file. - -The library jar along with it's javadoc jar will be found in the `dist` directory inside the library project. - -You will need to provide your own `local.properties` inside the library project. + dependencies { + compile 'uk.co.alt236:bluetooth-le-library-android:1.0.0' + } +``` +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: -
-   private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
-   
-        @Override
+```
+	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());
 				}
-				
+
 			});
 		}
 	};
-
+``` ### Device Properties @@ -83,7 +85,17 @@ Once you've created a BluetoothLe device, you can access the AdRecord store via They are also declared as constants in `AdRecord.java`. ### Fun with iBeacons -You can check if a device is an iBeacon by using `IBeaconUtils.isThisAnIBeacon(BluetootLeDevice device)`. Once you have confirmed that it is, you can create a new IBeaconDevice via the IBeaconDevice constructor. +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: +``` + final BluetoothLeDevice device = ... // A generic BLE device + + 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: @@ -112,6 +124,12 @@ You can also lookup values and convert them to human friendly strings: * 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 @@ -120,7 +138,10 @@ You can also lookup values and convert them to human friendly strings: * 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 + ## Permission Explanation You will need the following permissions to access the Bluetooth Hardware diff --git a/bluetooth-le-library.iml b/bluetooth-le-library.iml new file mode 100644 index 0000000..cf2c73f --- /dev/null +++ b/bluetooth-le-library.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..327b2ba --- /dev/null +++ b/build.gradle @@ -0,0 +1,21 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +ext.compileSdkVersion = 22 +ext.buildToolsVersion = "22.0.1" +ext.minSdkVersion = 18 +ext.targetSdkVersion = 22 + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.2.3' + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/dist/BluetoothLeLibrary-0.0.1-javadoc.jar b/dist/BluetoothLeLibrary-0.0.1-javadoc.jar new file mode 100644 index 0000000..e0e1a58 Binary files /dev/null and b/dist/BluetoothLeLibrary-0.0.1-javadoc.jar differ diff --git a/dist/BluetoothLeLibrary-0.0.1.jar b/dist/BluetoothLeLibrary-0.0.1.jar new file mode 100644 index 0000000..d660473 Binary files /dev/null and b/dist/BluetoothLeLibrary-0.0.1.jar differ diff --git a/documents/Bluetooth_UUIDs.ods b/documents/Bluetooth_UUIDs.ods index 8658094..1ff67fa 100644 Binary files a/documents/Bluetooth_UUIDs.ods and b/documents/Bluetooth_UUIDs.ods differ diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8c0fb64 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..0c71e76 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/library/.classpath b/library/.classpath deleted file mode 100644 index 7bc01d9..0000000 --- a/library/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/library/.gitignore b/library/.gitignore index 7211978..796b96d 100644 --- a/library/.gitignore +++ b/library/.gitignore @@ -1,7 +1 @@ -/bin -/gen -local.properties -.idea/ -lint.xml -/.apt_generated -/dist +/build diff --git a/library/.project b/library/.project deleted file mode 100644 index db0e780..0000000 --- a/library/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - Bluetooth LE Library - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - diff --git a/library/.settings/org.eclipse.jdt.core.prefs b/library/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index b080d2d..0000000 --- a/library/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/library/AndroidManifest.xml b/library/AndroidManifest.xml deleted file mode 100644 index a6ace79..0000000 --- a/library/AndroidManifest.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/library/build.gradle b/library/build.gradle new file mode 100644 index 0000000..6c85d48 --- /dev/null +++ b/library/build.gradle @@ -0,0 +1,53 @@ +apply plugin: 'com.android.library' + +final int versionMajor = 1 +final int versionMinor = 0 +final int versionPatch = 0 +final int androidVersionCode = 5 + +final int targetSdk = rootProject.targetSdkVersion; +final int minSdkRed = rootProject.minSdkVersion; +final String semanticVersion = "${versionMajor}.${versionMinor}.${versionPatch}" + + +dependencies { + testCompile 'junit:junit:4.12' + testCompile "org.mockito:mockito-core:1.9.5" + compile fileTree(include: ['*.jar'], dir: 'libs') +} + +ext { + PUBLISH_GROUP_ID = 'uk.co.alt236' + PUBLISH_ARTIFACT_ID = 'bluetooth-le-library-android' + PUBLISH_VERSION = "${semanticVersion}" +} + +android { + compileSdkVersion rootProject.compileSdkVersion + buildToolsVersion rootProject.buildToolsVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + defaultConfig { + minSdkVersion minSdkRed + targetSdkVersion targetSdk + versionCode androidVersionCode + versionName semanticVersion + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + productFlavors { + } +} + +// Script from https://github.com/blundell/release-android-library +// execute: ./gradlew clean build generateRelease +apply from: 'https://raw.githubusercontent.com/ArthurHub/release-android-library/master/android-release-jar.gradle' \ No newline at end of file diff --git a/library/build.xml b/library/build.xml deleted file mode 100644 index 4aa9f98..0000000 --- a/library/build.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/library/library.iml b/library/library.iml new file mode 100644 index 0000000..7656b88 --- /dev/null +++ b/library/library.iml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/libs/android-support-v4.jar b/library/libs/android-support-v4.jar deleted file mode 100644 index 96644ed..0000000 Binary files a/library/libs/android-support-v4.jar and /dev/null differ diff --git a/library/proguard-project.txt b/library/proguard-rules.pro similarity index 62% rename from library/proguard-project.txt rename to library/proguard-rules.pro index f2fe155..f8c620f 100644 --- a/library/proguard-project.txt +++ b/library/proguard-rules.pro @@ -1,11 +1,8 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. +# in /home/alex/Dev/android-sdk-linux/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html diff --git a/library/project.properties b/library/project.properties deleted file mode 100644 index 91d2b02..0000000 --- a/library/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-19 -android.library=true diff --git a/library/res/drawable-hdpi/ic_launcher.png b/library/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 7fe8ce8..0000000 Binary files a/library/res/drawable-hdpi/ic_launcher.png and /dev/null differ diff --git a/library/res/drawable-mdpi/ic_launcher.png b/library/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 81ca4ca..0000000 Binary files a/library/res/drawable-mdpi/ic_launcher.png and /dev/null differ diff --git a/library/res/drawable-xhdpi/ic_launcher.png b/library/res/drawable-xhdpi/ic_launcher.png deleted file mode 100644 index 8e4b1c6..0000000 Binary files a/library/res/drawable-xhdpi/ic_launcher.png and /dev/null differ diff --git a/library/res/drawable-xxhdpi/ic_launcher.png b/library/res/drawable-xxhdpi/ic_launcher.png deleted file mode 100644 index ed2d393..0000000 Binary files a/library/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/library/res/values-v11/styles.xml b/library/res/values-v11/styles.xml deleted file mode 100644 index 3c02242..0000000 --- a/library/res/values-v11/styles.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/library/res/values-v14/styles.xml b/library/res/values-v14/styles.xml deleted file mode 100644 index a91fd03..0000000 --- a/library/res/values-v14/styles.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/library/res/values/strings.xml b/library/res/values/strings.xml deleted file mode 100644 index 666f295..0000000 --- a/library/res/values/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - Bluetooth LE Library - - diff --git a/library/res/values/styles.xml b/library/res/values/styles.xml deleted file mode 100644 index 6ce89c7..0000000 --- a/library/res/values/styles.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8aa616d --- /dev/null +++ b/library/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/BluetoothLeDevice.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/BluetoothLeDevice.java new file mode 100644 index 0000000..ef3c8b4 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/BluetoothLeDevice.java @@ -0,0 +1,422 @@ +package uk.co.alt236.bluetoothlelib.device; + +import android.bluetooth.BluetoothDevice; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecordStore; +import uk.co.alt236.bluetoothlelib.resolvers.BluetoothClassResolver; +import uk.co.alt236.bluetoothlelib.util.AdRecordUtils; +import uk.co.alt236.bluetoothlelib.util.ByteUtils; +import uk.co.alt236.bluetoothlelib.util.LimitedLinkHashMap; + +// TODO: Auto-generated Javadoc + +/** + * This is a wrapper around the default BluetoothDevice object + * As BluetoothDevice is final it cannot be extended, so to get it you + * need to call {@link #getDevice()} method. + * + * @author Alexandros Schillings + */ +public class BluetoothLeDevice implements Parcelable { + /** + * The Constant CREATOR. + */ + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public BluetoothLeDevice createFromParcel(final Parcel in) { + return new BluetoothLeDevice(in); + } + + public BluetoothLeDevice[] newArray(final int size) { + return new BluetoothLeDevice[size]; + } + }; + protected static final int MAX_RSSI_LOG_SIZE = 10; + private static final String PARCEL_EXTRA_BLUETOOTH_DEVICE = "bluetooth_device"; + private static final String PARCEL_EXTRA_CURRENT_RSSI = "current_rssi"; + private static final String PARCEL_EXTRA_CURRENT_TIMESTAMP = "current_timestamp"; + private static final String PARCEL_EXTRA_DEVICE_RSSI_LOG = "device_rssi_log"; + private static final String PARCEL_EXTRA_DEVICE_SCANRECORD = "device_scanrecord"; + private static final String PARCEL_EXTRA_DEVICE_SCANRECORD_STORE = "device_scanrecord_store"; + private static final String PARCEL_EXTRA_FIRST_RSSI = "device_first_rssi"; + private static final String PARCEL_EXTRA_FIRST_TIMESTAMP = "first_timestamp"; + private static final long LOG_INVALIDATION_THRESHOLD = 10 * 1000; + private final AdRecordStore mRecordStore; + private final BluetoothDevice mDevice; + private final Map mRssiLog; + private final byte[] mScanRecord; + private final int mFirstRssi; + private final long mFirstTimestamp; + private int mCurrentRssi; + private long mCurrentTimestamp; + private transient Set mServiceSet; + + /** + * Instantiates a new Bluetooth LE device. + * + * @param device a standard android Bluetooth device + * @param rssi the RSSI value of the Bluetooth device + * @param scanRecord the scan record of the device + * @param timestamp the timestamp of the RSSI reading + */ + public BluetoothLeDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord, final long timestamp) { + mDevice = device; + mFirstRssi = rssi; + mFirstTimestamp = timestamp; + mRecordStore = new AdRecordStore(AdRecordUtils.parseScanRecordAsSparseArray(scanRecord)); + mScanRecord = scanRecord; + mRssiLog = new LimitedLinkHashMap<>(MAX_RSSI_LOG_SIZE); + updateRssiReading(timestamp, rssi); + } + + /** + * Instantiates a new Bluetooth LE device. + * + * @param device the device + */ + public BluetoothLeDevice(final BluetoothLeDevice device) { + mCurrentRssi = device.getRssi(); + mCurrentTimestamp = device.getTimestamp(); + mDevice = device.getDevice(); + mFirstRssi = device.getFirstRssi(); + mFirstTimestamp = device.getFirstTimestamp(); + mRecordStore = new AdRecordStore( + AdRecordUtils.parseScanRecordAsSparseArray(device.getScanRecord())); + mRssiLog = device.getRssiLog(); + mScanRecord = device.getScanRecord(); + } + + /** + * Instantiates a new bluetooth le device. + * + * @param in the in + */ + @SuppressWarnings("unchecked") + protected BluetoothLeDevice(final Parcel in) { + final Bundle b = in.readBundle(getClass().getClassLoader()); + + mCurrentRssi = b.getInt(PARCEL_EXTRA_CURRENT_RSSI, 0); + mCurrentTimestamp = b.getLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, 0); + mDevice = b.getParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE); + mFirstRssi = b.getInt(PARCEL_EXTRA_FIRST_RSSI, 0); + mFirstTimestamp = b.getLong(PARCEL_EXTRA_FIRST_TIMESTAMP, 0); + mRecordStore = b.getParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE); + mRssiLog = (Map) b.getSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG); + mScanRecord = b.getByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD); + } + + /** + * Adds the to rssi log. + * + * @param timestamp the timestamp + * @param rssiReading the rssi reading + */ + private void addToRssiLog(final long timestamp, final int rssiReading) { + synchronized (mRssiLog) { + if (timestamp - mCurrentTimestamp > LOG_INVALIDATION_THRESHOLD) { + mRssiLog.clear(); + } + + mCurrentRssi = rssiReading; + mCurrentTimestamp = timestamp; + mRssiLog.put(timestamp, rssiReading); + } + } + + /* (non-Javadoc) + * @see android.os.Parcelable#describeContents() + */ + @Override + public int describeContents() { + return 0; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final BluetoothLeDevice other = (BluetoothLeDevice) obj; + if (mCurrentRssi != other.mCurrentRssi) + return false; + if (mCurrentTimestamp != other.mCurrentTimestamp) + return false; + if (mDevice == null) { + if (other.mDevice != null) + return false; + } else if (!mDevice.equals(other.mDevice)) + return false; + if (mFirstRssi != other.mFirstRssi) + return false; + if (mFirstTimestamp != other.mFirstTimestamp) + return false; + if (mRecordStore == null) { + if (other.mRecordStore != null) + return false; + } else if (!mRecordStore.equals(other.mRecordStore)) + return false; + if (mRssiLog == null) { + if (other.mRssiLog != null) + return false; + } else if (!mRssiLog.equals(other.mRssiLog)) + return false; + if (!Arrays.equals(mScanRecord, other.mScanRecord)) + return false; + return true; + } + + /** + * Gets the ad record store. + * + * @return the ad record store + */ + public AdRecordStore getAdRecordStore() { + return mRecordStore; + } + + /** + * Gets the address. + * + * @return the address + */ + public String getAddress() { + return mDevice.getAddress(); + } + + /** + * Gets the bluetooth device bond state. + * + * @return the bluetooth device bond state + */ + public String getBluetoothDeviceBondState() { + return resolveBondingState(mDevice.getBondState()); + } + + /** + * Gets the bluetooth device class name. + * + * @return the bluetooth device class name + */ + public String getBluetoothDeviceClassName() { + return BluetoothClassResolver.resolveDeviceClass(mDevice.getBluetoothClass().getDeviceClass()); + } + + public Set getBluetoothDeviceKnownSupportedServices() { + if (mServiceSet == null) { + synchronized (this) { + if (mServiceSet == null) { + final Set serviceSet = new HashSet<>(); + for (final BluetoothService service : BluetoothService.values()) { + + if (mDevice.getBluetoothClass().hasService(service.getAndroidConstant())) { + serviceSet.add(service); + } + } + mServiceSet = Collections.unmodifiableSet(serviceSet); + } + } + } + + return mServiceSet; + } + + /** + * Gets the bluetooth device major class name. + * + * @return the bluetooth device major class name + */ + public String getBluetoothDeviceMajorClassName() { + return BluetoothClassResolver.resolveMajorDeviceClass(mDevice.getBluetoothClass().getMajorDeviceClass()); + } + + /** + * Gets the device. + * + * @return the device + */ + public BluetoothDevice getDevice() { + return mDevice; + } + + /** + * Gets the first rssi. + * + * @return the first rssi + */ + public int getFirstRssi() { + return mFirstRssi; + } + + /** + * Gets the first timestamp. + * + * @return the first timestamp + */ + public long getFirstTimestamp() { + return mFirstTimestamp; + } + + /** + * Gets the name. + * + * @return the name + */ + public String getName() { + return mDevice.getName(); + } + + /** + * Gets the rssi. + * + * @return the rssi + */ + public int getRssi() { + return mCurrentRssi; + } + + /** + * Gets the rssi log. + * + * @return the rssi log + */ + protected Map getRssiLog() { + synchronized (mRssiLog) { + return mRssiLog; + } + } + + /** + * Gets the running average rssi. + * + * @return the running average rssi + */ + public double getRunningAverageRssi() { + int sum = 0; + int count = 0; + + synchronized (mRssiLog) { + + for (final Long aLong : mRssiLog.keySet()) { + count++; + sum += mRssiLog.get(aLong); + } + } + + if (count > 0) { + return sum / count; + } else { + return 0; + } + + } + + /** + * Gets the scan record. + * + * @return the scan record + */ + public byte[] getScanRecord() { + return mScanRecord; + } + + /** + * Gets the timestamp. + * + * @return the timestamp + */ + public long getTimestamp() { + return mCurrentTimestamp; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mCurrentRssi; + result = prime * result + (int) (mCurrentTimestamp ^ (mCurrentTimestamp >>> 32)); + result = prime * result + ((mDevice == null) ? 0 : mDevice.hashCode()); + result = prime * result + mFirstRssi; + result = prime * result + (int) (mFirstTimestamp ^ (mFirstTimestamp >>> 32)); + result = prime * result + ((mRecordStore == null) ? 0 : mRecordStore.hashCode()); + result = prime * result + ((mRssiLog == null) ? 0 : mRssiLog.hashCode()); + result = prime * result + Arrays.hashCode(mScanRecord); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "BluetoothLeDevice [mDevice=" + mDevice + ", mRssi=" + mFirstRssi + ", mScanRecord=" + ByteUtils.byteArrayToHexString(mScanRecord) + ", mRecordStore=" + mRecordStore + ", getBluetoothDeviceBondState()=" + getBluetoothDeviceBondState() + ", getBluetoothDeviceClassName()=" + getBluetoothDeviceClassName() + "]"; + } + + /** + * Update rssi reading. + * + * @param timestamp the timestamp + * @param rssiReading the rssi reading + */ + public void updateRssiReading(final long timestamp, final int rssiReading) { + addToRssiLog(timestamp, rssiReading); + } + + /* (non-Javadoc) + * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) + */ + @Override + public void writeToParcel(final Parcel parcel, final int arg1) { + final Bundle b = new Bundle(getClass().getClassLoader()); + + b.putByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD, mScanRecord); + + b.putInt(PARCEL_EXTRA_FIRST_RSSI, mFirstRssi); + b.putInt(PARCEL_EXTRA_CURRENT_RSSI, mCurrentRssi); + + b.putLong(PARCEL_EXTRA_FIRST_TIMESTAMP, mFirstTimestamp); + b.putLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, mCurrentTimestamp); + + b.putParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE, mDevice); + b.putParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE, mRecordStore); + b.putSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG, (Serializable) mRssiLog); + + parcel.writeBundle(b); + } + + /** + * Resolve bonding state. + * + * @param bondState the bond state + * @return the string + */ + private static String resolveBondingState(final int bondState) { + switch (bondState) { + case BluetoothDevice.BOND_BONDED: + return "Paired"; + case BluetoothDevice.BOND_BONDING: + return "Pairing"; + case BluetoothDevice.BOND_NONE: + return "Unbonded"; + default: + return "Unknown"; + } + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/BluetoothService.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/BluetoothService.java new file mode 100644 index 0000000..c4ec277 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/BluetoothService.java @@ -0,0 +1,28 @@ +package uk.co.alt236.bluetoothlelib.device; + +import android.bluetooth.BluetoothClass; + +/** + * + */ +public enum BluetoothService { + AUDIO(BluetoothClass.Service.AUDIO), + CAPTURE(BluetoothClass.Service.CAPTURE), + INFORMATION(BluetoothClass.Service.INFORMATION), + LIMITED_DISCOVERABILITY(BluetoothClass.Service.LIMITED_DISCOVERABILITY), + NETWORKING(BluetoothClass.Service.NETWORKING), + OBJECT_TRANSFER(BluetoothClass.Service.OBJECT_TRANSFER), + POSITIONING(BluetoothClass.Service.POSITIONING), + RENDER(BluetoothClass.Service.RENDER), + TELEPHONY(BluetoothClass.Service.TELEPHONY); + + private final int mAndroidConstant; + + BluetoothService(final int androidCode){ + mAndroidConstant = androidCode; + } + + public int getAndroidConstant(){ + return mAndroidConstant; + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecord.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecord.java new file mode 100644 index 0000000..d043da1 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecord.java @@ -0,0 +1,227 @@ +package uk.co.alt236.bluetoothlelib.device.adrecord; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** + * Created by Dave Smith + * Double Encore, Inc. + *

+ * Expanded by Alexandros Schillings + */ +public final class AdRecord implements Parcelable { + // 02 # Number of bytes that follow in first AD structure + // 01 # Flags AD type + // 1A # Flags value 0x1A = 000011010 + // bit 0 (OFF) LE Limited Discoverable Mode + // bit 1 (ON) LE General Discoverable Mode + // bit 2 (OFF) BR/EDR Not Supported + // bit 3 (ON) Simultaneous LE and BR/EDR to Same Device Capable (controller) + // bit 4 (ON) Simultaneous LE and BR/EDR to Same Device Capable (Host) + // 1A # Number of bytes that follow in second (and last) AD structure + // FF # Manufacturer specific data AD type + // 4C 00 # Company identifier code (0x004C == Apple) + // 02 # Byte 0 of iBeacon advertisement indicator + // 15 # Byte 1 of iBeacon advertisement indicator + // e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 # iBeacon proximity uuid + // 00 00 # major + // 00 00 # minor + // c5 # The 2's complement of the calibrated Tx Power + + + /** + * General FLAGS + *

+ * Description: Flags + *

+ * Information: + * Bit 0: LE Limited Discoverable Mode + * Bit 1: LE General Discoverable Mode + * Bit 2: BR/EDR Not Supported (i.e. bit 37 of LMP Extended Feature bits Page 0) + * Bit 3: Simultaneous LE and BR/EDR to Same Device Capable (Controller) (i.e. bit 49 of LMP Extended Feature bits Page 0) + * Bit 4: Simultaneous LE and BR/EDR to Same Device Capable (Host) (i.e. bit 66 of LMP Extended Feature bits Page 1) + * Bits 5-7 Reserved + */ + public static final int TYPE_FLAGS = 0x01; + // SERVICE + public static final int TYPE_UUID16_INC = 0x02; + public static final int TYPE_UUID16 = 0x03; + public static final int TYPE_UUID32_INC = 0x04; + public static final int TYPE_UUID32 = 0x05; + public static final int TYPE_UUID128_INC = 0x06; + public static final int TYPE_UUID128 = 0x07; + // Local name + public static final int TYPE_LOCAL_NAME_SHORT = 0x08; + public static final int TYPE_LOCAL_NAME_COMPLETE = 0x09; + // TX Power Level + public static final int TYPE_TX_POWER_LEVEL = 0x0A; + // SIMPLE PAIRING OPTIONAL OOB TAGS + public static final int TYPE_DEVICE_CLASS = 0x0D; + public static final int TYPE_SIMPLE_PAIRING_HASH_C = 0x0E; + public static final int TYPE_SIMPLE_PAIRING_RANDOMIZER_R = 0x0F; + // SECURITY MANAGER TK VALUE + public static final int TYPE_TK_VALUE = 0x10; + /* SECURITY MANAGER OOB FLAGS + * + * Description: Flag (1 octet) + * + * Information: + * Bit 0: OOB Flags Field: (0 = OOB data not present, 1 = OOB data present) + * Bit 1: LE supported (Host) (i.e. bit 65 of LMP Extended Feature bits Page 1 + * Bit 2: Simultaneous LE and BR/EDR to Same Device Capable (Host) (i.e. bit 66 of LMP Extended Feature bits Page 1) + * Bit 3: Address type (0 = Public Address, 1 = Random Address) + * Bits 4-7 Reserved + */ + public static final int TYPE_SECURITY_MANAGER_OOB_FLAGS = 0x11; + /* SLAVE CONNECTION INTERVAL RANGE + * + * Description: Slave Connection Interval Range + * + * Information: + * The first 2 octets defines the minimum value for the connection interval in the following manner: + * connInterval min = Conn_Interval_Min * 1.25 ms + * Conn_Interval_Min range: 0x0006 to 0x0C80 + * Value of 0xFFFF indicates no specific minimum. + * Values outside the range are reserved. (excluding 0xFFFF) + * + * The second 2 octets defines the maximum value for the connection interval in the following manner: + * connInterval max = Conn_Interval_Max * 1.25 ms + * Conn_Interval_Max range: 0x0006 to 0x0C80 + * Conn_Interval_Max shall be equal to or greater + * than the Conn_Interval_Min. + * Value of 0xFFFF indicates no specific maximum. + * Values outside the range are reserved (excluding 0xFFFF) + */ + public static final int TYPE_CONNECTION_INTERVAL_RANGE = 0x12; + // SERVICE SOLICITATION + public static final int TYPE_SERVICE_UUIDS_LIST_16BIT = 0x14; + public static final int TYPE_SERVICE_UUIDS_LIST_128BIT = 0x15; + /* SERVICE DATA + * + * Description: Service Data (2 or more octets) + * Information: The first 2 octets contain the 16 bit Service UUID followed by additional service data + */ + public static final int TYPE_SERVICE_DATA = 0x16; + /* MANUFACTURER SPECIFIC DATA + * + * Description: Manufacturer Specific Data (2 or more octets) + * Information: The first 2 octets contain the Company Identifier Code followed by additional manufacturer specific data + */ + public static final int TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public AdRecord createFromParcel(final Parcel in) { + return new AdRecord(in); + } + + public AdRecord[] newArray(final int size) { + return new AdRecord[size]; + } + }; + private static final String PARCEL_RECORD_DATA = "record_data"; + private static final String PARCEL_RECORD_TYPE = "record_type"; + private static final String PARCEL_RECORD_LENGTH = "record_length"; + /* Model Object Definition */ + private final int mLength; + private final int mType; + private final byte[] mData; + + public AdRecord(final int length, final int type, final byte[] data) { + mLength = length; + mType = type; + mData = data; + } + + public AdRecord(final Parcel in) { + final Bundle b = in.readBundle(getClass().getClassLoader()); + mLength = b.getInt(PARCEL_RECORD_LENGTH); + mType = b.getInt(PARCEL_RECORD_TYPE); + mData = b.getByteArray(PARCEL_RECORD_DATA); + } + + @Override + public int describeContents() { + return 0; + } + + public byte[] getData() { + return mData; + } + + public String getHumanReadableType() { + return getHumanReadableAdType(mType); + } + + public int getLength() { + return mLength; + } + + public int getType() { + return mType; + } + + @Override + public String toString() { + return "AdRecord [mLength=" + mLength + ", mType=" + mType + ", mData=" + Arrays.toString(mData) + ", getHumanReadableType()=" + getHumanReadableType() + "]"; + } + + @Override + public void writeToParcel(final Parcel parcel, final int arg1) { + final Bundle b = new Bundle(getClass().getClassLoader()); + + b.putInt(PARCEL_RECORD_LENGTH, mLength); + b.putInt(PARCEL_RECORD_TYPE, mType); + b.putByteArray(PARCEL_RECORD_DATA, mData); + + parcel.writeBundle(b); + } + + private static String getHumanReadableAdType(final int type) { + switch (type) { + case TYPE_CONNECTION_INTERVAL_RANGE: + return "Slave Connection Interval Range"; + case TYPE_DEVICE_CLASS: + return "Class of device"; + case TYPE_FLAGS: + return "Flags"; + case TYPE_MANUFACTURER_SPECIFIC_DATA: + return "Manufacturer Specific Data"; + case TYPE_LOCAL_NAME_COMPLETE: + return "Name (Complete)"; + case TYPE_LOCAL_NAME_SHORT: + return "Name (Short)"; + case TYPE_SECURITY_MANAGER_OOB_FLAGS: + return "Security Manager OOB Flags"; + case TYPE_SERVICE_UUIDS_LIST_128BIT: + return "Service UUIDs (128bit)"; + case TYPE_SERVICE_UUIDS_LIST_16BIT: + return "Service UUIDs (16bit)"; + case TYPE_SERVICE_DATA: + return "Service Data"; + case TYPE_SIMPLE_PAIRING_HASH_C: + return "Simple Pairing Hash C"; + case TYPE_SIMPLE_PAIRING_RANDOMIZER_R: + return "Simple Pairing Randomizer R"; + case TYPE_TK_VALUE: + return "TK Value"; + case TYPE_TX_POWER_LEVEL: + return "Transmission Power Level"; + case TYPE_UUID128: + return "Complete list of 128-bit UUIDs available"; + case TYPE_UUID128_INC: + return "More 128-bit UUIDs available"; + case TYPE_UUID16: + return "Complete list of 16-bit UUIDs available"; + case TYPE_UUID16_INC: + return "More 16-bit UUIDs available"; + case TYPE_UUID32: + return "Complete list of 32-bit UUIDs available"; + case TYPE_UUID32_INC: + return "More 32-bit UUIDs available"; + default: + return "Unknown AdRecord Structure: " + type; + } + } +} \ No newline at end of file diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecordStore.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecordStore.java new file mode 100644 index 0000000..a0993bd --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecordStore.java @@ -0,0 +1,158 @@ +package uk.co.alt236.bluetoothlelib.device.adrecord; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.SparseArray; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import uk.co.alt236.bluetoothlelib.util.AdRecordUtils; + +/** + * The Class AdRecordStore. + */ +public class AdRecordStore implements Parcelable { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public AdRecordStore createFromParcel(final Parcel in) { + return new AdRecordStore(in); + } + + public AdRecordStore[] newArray(final int size) { + return new AdRecordStore[size]; + } + }; + private final SparseArray mAdRecords; + private final String mLocalNameComplete; + private final String mLocalNameShort; + + public AdRecordStore(final Parcel in) { + final Bundle b = in.readBundle(getClass().getClassLoader()); + mAdRecords = b.getSparseParcelableArray("records_array"); + mLocalNameComplete = b.getString("local_name_complete"); + mLocalNameShort = b.getString("local_name_short"); + } + + /** + * Instantiates a new Bluetooth LE device Ad Record Store. + * + * @param adRecords the ad records + */ + public AdRecordStore(final SparseArray adRecords) { + mAdRecords = adRecords; + + mLocalNameComplete = AdRecordUtils.getRecordDataAsString( + mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_COMPLETE)); + + mLocalNameShort = AdRecordUtils.getRecordDataAsString( + mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_SHORT)); + + } + + /* (non-Javadoc) + * @see android.os.Parcelable#describeContents() + */ + @Override + public int describeContents() { + return 0; + } + + /** + * Gets the short local device name. + * + * @return the local name complete + */ + public String getLocalNameComplete() { + return mLocalNameComplete; + } + + /** + * Gets the complete local device name. + * + * @return the local name short + */ + public String getLocalNameShort() { + return mLocalNameShort; + } + + /** + * retrieves an individual record. + * + * @param record the record + * @return the record + */ + public AdRecord getRecord(final int record) { + return mAdRecords.get(record); + } + + /** + * Gets the record data as string. + * + * @param record the record + * @return the record data as string + */ + public String getRecordDataAsString(final int record) { + return AdRecordUtils.getRecordDataAsString( + mAdRecords.get(record)); + } + + /** + * Gets the record as collection. + * + * @return the records as collection + */ + public Collection getRecordsAsCollection() { + return Collections.unmodifiableCollection(asList(mAdRecords)); + } + + /** + * Checks if is record present. + * + * @param record the record + * @return true, if is record present + */ + public boolean isRecordPresent(final int record) { + return mAdRecords.indexOfKey(record) >= 0; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "AdRecordStore [mLocalNameComplete=" + mLocalNameComplete + ", mLocalNameShort=" + mLocalNameShort + "]"; + } + + /* (non-Javadoc) + * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) + */ + @Override + public void writeToParcel(final Parcel parcel, final int arg1) { + final Bundle b = new Bundle(); + b.putString("local_name_complete", mLocalNameComplete); + b.putString("local_name_short", mLocalNameShort); + b.putSparseParcelableArray("records_array", mAdRecords); + + parcel.writeBundle(b); + } + + /** + * As list. + * + * @param the generic type + * @param sparseArray the sparse array + * @return the collection + */ + public static Collection asList(final SparseArray sparseArray) { + if (sparseArray == null) return null; + + final Collection arrayList = new ArrayList<>(sparseArray.size()); + for (int i = 0; i < sparseArray.size(); i++) { + arrayList.add(sparseArray.valueAt(i)); + } + + return arrayList; + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconDevice.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconDevice.java new file mode 100644 index 0000000..f4d682c --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconDevice.java @@ -0,0 +1,8 @@ +package uk.co.alt236.bluetoothlelib.device.beacon; + +/** + * + */ +public interface BeaconDevice { + BeaconType getBeaconType(); +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconManufacturerData.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconManufacturerData.java new file mode 100644 index 0000000..ebabbce --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconManufacturerData.java @@ -0,0 +1,31 @@ +package uk.co.alt236.bluetoothlelib.device.beacon; + +import java.util.Arrays; + +/** + * + */ +public abstract class BeaconManufacturerData { + private final BeaconType mBeaconType; + private final byte[] mData; + + protected BeaconManufacturerData(final BeaconType expectedType, final byte[] data){ + if (BeaconUtils.getBeaconType(data) != expectedType) { + throw new IllegalArgumentException( + "Manufacturer record '" + + Arrays.toString(data) + + "' is not from a " + expectedType); + } + + this.mData = data; + this.mBeaconType = expectedType; + } + + public BeaconType getBeaconType(){ + return mBeaconType; + } + + public byte[] getData(){ + return mData; + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconType.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconType.java new file mode 100644 index 0000000..7179248 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconType.java @@ -0,0 +1,9 @@ +package uk.co.alt236.bluetoothlelib.device.beacon; + +/** + * + */ +public enum BeaconType { + NOT_A_BEACON, + IBEACON, +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconUtils.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconUtils.java new file mode 100644 index 0000000..294f906 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconUtils.java @@ -0,0 +1,58 @@ +package uk.co.alt236.bluetoothlelib.device.beacon; + +import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; +import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; +import uk.co.alt236.bluetoothlelib.device.beacon.ibeacon.IBeaconConstants; +import uk.co.alt236.bluetoothlelib.util.ByteUtils; + +/** + * + */ +public final class BeaconUtils { + + private BeaconUtils(){ + // TO AVOID INSTANTIATION + } + + /** + * Ascertains whether a Manufacturer Data byte array belongs to a known Beacon type; + * + * @param manufacturerData a Bluetooth LE device's raw manufacturerData. + * @return the {@link BeaconType} + */ + public static BeaconType getBeaconType(final byte[] manufacturerData) { + if (manufacturerData == null || manufacturerData.length == 0) { + return BeaconType.NOT_A_BEACON; + } + + if(isIBeacon(manufacturerData)){ + return BeaconType.IBEACON; + } else { + return BeaconType.NOT_A_BEACON; + } + } + + /** + * Ascertains whether a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} is an iBeacon; + * + * @param device a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} device. + * @return the {@link BeaconType} + */ + public static BeaconType getBeaconType(final BluetoothLeDevice device) { + final int key = AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA; + return getBeaconType(device.getAdRecordStore().getRecordDataAsString(key).getBytes()); + } + + private static boolean isIBeacon(final byte[] manufacturerData){ + // An iBeacon record must be at least 25 chars long + if (!(manufacturerData.length >= 25)) { + return false; + } + + if (ByteUtils.doesArrayBeginWith(manufacturerData, IBeaconConstants.MANUFACTURER_DATA_IBEACON_PREFIX)) { + return true; + } + + return false; + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconConstants.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconConstants.java new file mode 100644 index 0000000..6c1914f --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconConstants.java @@ -0,0 +1,9 @@ +package uk.co.alt236.bluetoothlelib.device.beacon.ibeacon; + +/** + * + */ +public class IBeaconConstants { + public static final byte[] MANUFACTURER_DATA_IBEACON_PREFIX = {0x4C, 0x00, 0x02, 0x15}; + +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconDevice.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconDevice.java new file mode 100644 index 0000000..4b65b45 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconDevice.java @@ -0,0 +1,142 @@ +package uk.co.alt236.bluetoothlelib.device.beacon.ibeacon; + +import android.bluetooth.BluetoothDevice; +import android.os.Parcel; + +import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconDevice; +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconType; +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconUtils; + +public class IBeaconDevice extends BluetoothLeDevice implements BeaconDevice{ + + /** + * The m iBeacon data. + */ + private final IBeaconManufacturerData mIBeaconData; + + /** + * Instantiates a new iBeacon device. + * + * @param device the device + * @param rssi the RSSI value + * @param scanRecord the scanRecord + * @throws IllegalArgumentException if the passed device is not an iBeacon + */ + public IBeaconDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord) { + super(device, rssi, scanRecord, 0); + mIBeaconData = new IBeaconManufacturerData(this); + } + + /** + * Instantiates a new iBeacon device. + * + * @param device the device + * @param rssi the RSSI value of the RSSI measurement + * @param scanRecord the scan record + * @param timestamp the timestamp of the RSSI measurement + * @throws IllegalArgumentException if the passed device is not an iBeacon + */ + public IBeaconDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord, final long timestamp) { + super(device, rssi, scanRecord, timestamp); + mIBeaconData = new IBeaconManufacturerData(this); + } + + /** + * Will try to convert a {@link BluetoothLeDevice} into an + * iBeacon Device. + * + * @param device the device + * @throws IllegalArgumentException if the passed device is not an iBeacon + */ + public IBeaconDevice(final BluetoothLeDevice device) { + super(device); + mIBeaconData = new IBeaconManufacturerData(this); + } + + private IBeaconDevice(final Parcel in) { + super(in); + mIBeaconData = new IBeaconManufacturerData(this); + } + + /** + * Gets the estimated Accuracy of the reading in meters based on + * a simple running average of the last {@link #MAX_RSSI_LOG_SIZE} + * samples. + * + * @return the accuracy in meters + */ + public double getAccuracy() { + return IBeaconUtils.calculateAccuracy( + getCalibratedTxPower(), + getRunningAverageRssi()); + } + + @Override + public BeaconType getBeaconType() { + return BeaconType.IBEACON; + } + + /** + * Gets the calibrated TX power of the iBeacon device as reported. + * + * @return the calibrated TX power + */ + public int getCalibratedTxPower() { + return getIBeaconData().getCalibratedTxPower(); + } + + /** + * Gets the iBeacon company identifier. + * + * @return the company identifier + */ + public int getCompanyIdentifier() { + return getIBeaconData().getCompanyIdentifier(); + } + + /** + * Gets the estimated Distance descriptor. + * + * @return the distance descriptor + */ + public IBeaconDistanceDescriptor getDistanceDescriptor() { + return IBeaconUtils.getDistanceDescriptor(getAccuracy()); + } + + /** + * Gets the iBeacon manufacturing data. + * + * @return the iBeacon data + */ + public IBeaconManufacturerData getIBeaconData() { + return mIBeaconData; + } + + /** + * Gets the iBeacon Major value. + * + * @return the Major value + */ + public int getMajor() { + return getIBeaconData().getMajor(); + } + + /** + * Gets the iBeacon Minor value. + * + * @return the Minor value + */ + public int getMinor() { + return getIBeaconData().getMinor(); + } + + /** + * Gets the iBeacon UUID. + * + * @return the UUID + */ + public String getUUID() { + return getIBeaconData().getUUID(); + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconDistanceDescriptor.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconDistanceDescriptor.java new file mode 100644 index 0000000..413e63e --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconDistanceDescriptor.java @@ -0,0 +1,8 @@ +package uk.co.alt236.bluetoothlelib.device.beacon.ibeacon; + +public enum IBeaconDistanceDescriptor { + IMMEDIATE, + NEAR, + FAR, + UNKNOWN, +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconManufacturerData.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconManufacturerData.java new file mode 100644 index 0000000..4c7d706 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconManufacturerData.java @@ -0,0 +1,133 @@ +package uk.co.alt236.bluetoothlelib.device.beacon.ibeacon; + +import java.util.Arrays; + +import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; +import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconManufacturerData; +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconType; +import uk.co.alt236.bluetoothlelib.util.ByteUtils; + +/** + * Parses the Manufactured Data field of an iBeacon + *

+ * The parsing is based on the following schema: + *

+ * Byte|Value
+ * -------------------------------------------------
+ * 0	4C - Byte 1 (LSB) of Company identifier code
+ * 1	00 - Byte 0 (MSB) of Company identifier code (0x004C == Apple)
+ * 2	02 - Byte 0 of iBeacon advertisement indicator
+ * 3	15 - Byte 1 of iBeacon advertisement indicator
+ * 4	e2 |\
+ * 5	c5 |\\
+ * 6	6d |#\\
+ * 7	b5 |##\\
+ * 8	df |###\\
+ * 9	fb |####\\
+ * 10	48 |#####\\
+ * 11	d2 |#####|| iBeacon
+ * 12	b0 |#####|| Proximity UUID
+ * 13	60 |#####//
+ * 14	d0 |####//
+ * 15	f5 |###//
+ * 16	a7 |##//
+ * 17	10 |#//
+ * 18	96 |//
+ * 19	e0 |/
+ * 20	00 - major
+ * 21	00
+ * 22	00 - minor
+ * 23	00
+ * 24	c5 - The 2's complement of the calibrated Tx Power
+ * 
+ * @author Alexandros Schillings + */ + +public final class IBeaconManufacturerData extends BeaconManufacturerData{ + private final int mCalibratedTxPower; + private final int mCompanyIdentidier; + private final int mIBeaconAdvertisment; + private final int mMajor; + private final int mMinor; + private final String mUUID; + + /** + * Instantiates a new iBeacon manufacturer data object. + * + * @param device a {@link BluetoothLeDevice} + * @throws IllegalArgumentException if the data is not from an iBeacon. + */ + public IBeaconManufacturerData(final BluetoothLeDevice device) { + this(device.getAdRecordStore().getRecord(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getData()); + } + + /** + * Instantiates a new iBeacon manufacturer data object. + * + * @param manufacturerData the {@link AdRecord#TYPE_MANUFACTURER_SPECIFIC_DATA} data array + * @throws IllegalArgumentException if the data is not from an iBeacon. + */ + public IBeaconManufacturerData(final byte[] manufacturerData) { + super(BeaconType.IBEACON, manufacturerData); + + final byte[] intArray = Arrays.copyOfRange(manufacturerData, 0, 2); + ByteUtils.invertArray(intArray); + + mCompanyIdentidier = ByteUtils.getIntFrom2ByteArray(intArray); + mIBeaconAdvertisment = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(manufacturerData, 2, 4)); + mUUID = IBeaconUtils.calculateUuidString(Arrays.copyOfRange(manufacturerData, 4, 20)); + mMajor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(manufacturerData, 20, 22)); + mMinor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(manufacturerData, 22, 24)); + mCalibratedTxPower = manufacturerData[24]; + } + + /** + * Gets the calibrated TX power of the iBeacon device as reported. + * + * @return the calibrated TX power + */ + public int getCalibratedTxPower() { + return mCalibratedTxPower; + } + + /** + * Gets the iBeacon company identifier. + * + * @return the company identifier + */ + public int getCompanyIdentifier() { + return mCompanyIdentidier; + } + + public int getIBeaconAdvertisement() { + return mIBeaconAdvertisment; + } + + /** + * Gets the iBeacon Major value. + * + * @return the Major value + */ + public int getMajor() { + return mMajor; + } + + /** + * Gets the iBeacon Minor value. + * + * @return the Minor value + */ + public int getMinor() { + return mMinor; + } + + /** + * Gets the iBeacon UUID. + * + * @return the UUID + */ + public String getUUID() { + return mUUID; + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconUtils.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconUtils.java new file mode 100644 index 0000000..1d8d552 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconUtils.java @@ -0,0 +1,79 @@ +package uk.co.alt236.bluetoothlelib.device.beacon.ibeacon; + +import uk.co.alt236.bluetoothlelib.util.ByteUtils; + +final class IBeaconUtils { + private static final double DISTANCE_THRESHOLD_WTF = 0.0; + private static final double DISTANCE_THRESHOLD_IMMEDIATE = 0.5; + private static final double DISTANCE_THRESHOLD_NEAR = 3.0; + + private IBeaconUtils(){ + // TO AVOID INSTANTIATION + } + + /** + * Calculates the accuracy of an RSSI reading. + *

+ * The code was taken from + * + * @param txPower the calibrated TX power of an iBeacon + * @param rssi the RSSI value of the iBeacon + * @return the calculated Accuracy + */ + public static double calculateAccuracy(final int txPower, final double rssi) { + if (rssi == 0) { + return -1.0; // if we cannot determine accuracy, return -1. + } + + final double ratio = rssi * 1.0 / txPower; + if (ratio < 1.0) { + return Math.pow(ratio, 10); + } else { + return (0.89976) * Math.pow(ratio, 7.7095) + 0.111; + } + } + + public static String calculateUuidString(final byte[] uuid) { + final StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < uuid.length; i++) { + if (i == 4) { + sb.append('-'); + } + if (i == 6) { + sb.append('-'); + } + if (i == 8) { + sb.append('-'); + } + if (i == 10) { + sb.append('-'); + } + + final int intFromByte = ByteUtils.getIntFromByte(uuid[i]); + if(intFromByte <= 0xF){ + sb.append('0'); + } + sb.append(Integer.toHexString(intFromByte)); + } + + + return sb.toString(); + } + + public static IBeaconDistanceDescriptor getDistanceDescriptor(final double accuracy) { + if (accuracy < DISTANCE_THRESHOLD_WTF) { + return IBeaconDistanceDescriptor.UNKNOWN; + } + + if (accuracy < DISTANCE_THRESHOLD_IMMEDIATE) { + return IBeaconDistanceDescriptor.IMMEDIATE; + } + + if (accuracy < DISTANCE_THRESHOLD_NEAR) { + return IBeaconDistanceDescriptor.NEAR; + } + + return IBeaconDistanceDescriptor.FAR; + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/resolvers/BluetoothClassResolver.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/resolvers/BluetoothClassResolver.java new file mode 100644 index 0000000..9697ee9 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/resolvers/BluetoothClassResolver.java @@ -0,0 +1,142 @@ +package uk.co.alt236.bluetoothlelib.resolvers; + +import android.bluetooth.BluetoothClass; + +public class BluetoothClassResolver { + + public static String resolveDeviceClass(final int btClass) { + switch (btClass) { + case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER: + return "A/V, Camcorder"; + case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: + return "A/V, Car Audio"; + case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: + return "A/V, Handsfree"; + case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: + return "A/V, Headphones"; + case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: + return "A/V, HiFi Audio"; + case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: + return "A/V, Loudspeaker"; + case BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE: + return "A/V, Microphone"; + case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: + return "A/V, Portable Audio"; + case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX: + return "A/V, Set Top Box"; + case BluetoothClass.Device.AUDIO_VIDEO_UNCATEGORIZED: + return "A/V, Uncategorized"; + case BluetoothClass.Device.AUDIO_VIDEO_VCR: + return "A/V, VCR"; + case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA: + return "A/V, Video Camera"; + case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING: + return "A/V, Video Conferencing"; + case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER: + return "A/V, Video Display and Loudspeaker"; + case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_GAMING_TOY: + return "A/V, Video Gaming Toy"; + case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_MONITOR: + return "A/V, Video Monitor"; + case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: + return "A/V, Video Wearable Headset"; + case BluetoothClass.Device.COMPUTER_DESKTOP: + return "Computer, Desktop"; + case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA: + return "Computer, Handheld PC/PDA"; + case BluetoothClass.Device.COMPUTER_LAPTOP: + return "Computer, Laptop"; + case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA: + return "Computer, Palm Size PC/PDA"; + case BluetoothClass.Device.COMPUTER_SERVER: + return "Computer, Server"; + case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: + return "Computer, Uncategorized"; + case BluetoothClass.Device.COMPUTER_WEARABLE: + return "Computer, Wearable"; + case BluetoothClass.Device.HEALTH_BLOOD_PRESSURE: + return "Health, Blood Pressure"; + case BluetoothClass.Device.HEALTH_DATA_DISPLAY: + return "Health, Data Display"; + case BluetoothClass.Device.HEALTH_GLUCOSE: + return "Health, Glucose"; + case BluetoothClass.Device.HEALTH_PULSE_OXIMETER: + return "Health, Pulse Oximeter"; + case BluetoothClass.Device.HEALTH_PULSE_RATE: + return "Health, Pulse Rate"; + case BluetoothClass.Device.HEALTH_THERMOMETER: + return "Health, Thermometer"; + case BluetoothClass.Device.HEALTH_UNCATEGORIZED: + return "Health, Uncategorized"; + case BluetoothClass.Device.HEALTH_WEIGHING: + return "Health, Weighting"; + case BluetoothClass.Device.PHONE_CELLULAR: + return "Phone, Cellular"; + case BluetoothClass.Device.PHONE_CORDLESS: + return "Phone, Cordless"; + case BluetoothClass.Device.PHONE_ISDN: + return "Phone, ISDN"; + case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY: + return "Phone, Modem or Gateway"; + case BluetoothClass.Device.PHONE_SMART: + return "Phone, Smart"; + case BluetoothClass.Device.PHONE_UNCATEGORIZED: + return "Phone, Uncategorized"; + case BluetoothClass.Device.TOY_CONTROLLER: + return "Toy, Controller"; + case BluetoothClass.Device.TOY_DOLL_ACTION_FIGURE: + return "Toy, Doll/Action Figure"; + case BluetoothClass.Device.TOY_GAME: + return "Toy, Game"; + case BluetoothClass.Device.TOY_ROBOT: + return "Toy, Robot"; + case BluetoothClass.Device.TOY_UNCATEGORIZED: + return "Toy, Uncategorized"; + case BluetoothClass.Device.TOY_VEHICLE: + return "Toy, Vehicle"; + case BluetoothClass.Device.WEARABLE_GLASSES: + return "Wearable, Glasses"; + case BluetoothClass.Device.WEARABLE_HELMET: + return "Wearable, Helmet"; + case BluetoothClass.Device.WEARABLE_JACKET: + return "Wearable, Jacket"; + case BluetoothClass.Device.WEARABLE_PAGER: + return "Wearable, Pager"; + case BluetoothClass.Device.WEARABLE_UNCATEGORIZED: + return "Wearable, Uncategorized"; + case BluetoothClass.Device.WEARABLE_WRIST_WATCH: + return "Wearable, Wrist Watch"; + default: + return "Unknown, Unknown (class=" + btClass + ")"; + } + } + + public static String resolveMajorDeviceClass(final int majorBtClass) { + switch (majorBtClass) { + case BluetoothClass.Device.Major.AUDIO_VIDEO: + return "Audio/ Video"; + case BluetoothClass.Device.Major.COMPUTER: + return "Computer"; + case BluetoothClass.Device.Major.HEALTH: + return "Health"; + case BluetoothClass.Device.Major.IMAGING: + return "Imaging"; + case BluetoothClass.Device.Major.MISC: + return "Misc"; + case BluetoothClass.Device.Major.NETWORKING: + return "Networking"; + case BluetoothClass.Device.Major.PERIPHERAL: + return "Peripheral"; + case BluetoothClass.Device.Major.PHONE: + return "Phone"; + case BluetoothClass.Device.Major.TOY: + return "Toy"; + case BluetoothClass.Device.Major.UNCATEGORIZED: + return "Uncategorized"; + case BluetoothClass.Device.Major.WEARABLE: + return "Wearable"; + default: + return "Unknown (" +majorBtClass+ ")"; + } + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/resolvers/CompanyIdentifierResolver.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/resolvers/CompanyIdentifierResolver.java new file mode 100644 index 0000000..2a7271f --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/resolvers/CompanyIdentifierResolver.java @@ -0,0 +1,646 @@ +package uk.co.alt236.bluetoothlelib.resolvers; + +import android.util.SparseArray; + +public class CompanyIdentifierResolver { + public static final int ERICSSON_TECHNOLOGY_LICENSING = 0x0000; + public static final int NOKIA_MOBILE_PHONES = 0x0001; + public static final int INTEL_CORP = 0x0002; + public static final int IBM_CORP = 0x0003; + public static final int TOSHIBA_CORP = 0x0004; + public static final int THREE_COM = 0x0005; + public static final int MICROSOFT = 0x0006; + public static final int LUCENT = 0x0007; + public static final int MOTOROLA = 0x0008; + public static final int INFINEON_TECHNOLOGIES_AG = 0x0009; + public static final int CAMBRIDGE_SILICON_RADIO = 0x000A; + public static final int SILICON_WAVE = 0x000B; + public static final int DIGIANSWER_A_S = 0x000C; + public static final int TEXAS_INSTRUMENTS_INC = 0x000D; + public static final int CEVA_INC_FORMERLY_PARTHUS_TECHNOLOGIES_INC = 0x000E; + public static final int BROADCOM_CORPORATION = 0x000F; + public static final int MITEL_SEMICONDUCTOR = 0x0010; + public static final int WIDCOMM_INC = 0x0011; + public static final int ZEEVO_INC = 0x0012; + public static final int ATMEL_CORPORATION = 0x0013; + public static final int MITSUBISHI_ELECTRIC_CORPORATION = 0x0014; + public static final int RTX_TELECOM_A_S = 0x0015; + public static final int KC_TECHNOLOGY_INC = 0x0016; + public static final int NEWLOGIC = 0x0017; + public static final int TRANSILICA_INC = 0x0018; + public static final int ROHDE_SCHWARZ_GMBH_CO_KG = 0x0019; + public static final int TTPCOM_LIMITED = 0x001A; + public static final int SIGNIA_TECHNOLOGIES_INC = 0x001B; + public static final int CONEXANT_SYSTEMS_INC = 0x001C; + public static final int QUALCOMM = 0x001D; + public static final int INVENTEL = 0x001E; + public static final int AVM_BERLIN = 0x001F; + public static final int BANDSPEED_INC = 0x0020; + public static final int MANSELLA_LTD = 0x0021; + public static final int NEC_CORPORATION = 0x0022; + public static final int WAVEPLUS_TECHNOLOGY_CO_LTD = 0x0023; + public static final int ALCATEL = 0x0024; + public static final int PHILIPS_SEMICONDUCTORS = 0x0025; + public static final int C_TECHNOLOGIES = 0x0026; + public static final int OPEN_INTERFACE = 0x0027; + public static final int R_F_MICRO_DEVICES = 0x0028; + public static final int HITACHI_LTD = 0x0029; + public static final int SYMBOL_TECHNOLOGIES_INC = 0x002A; + public static final int TENOVIS = 0x002B; + public static final int MACRONIX_INTERNATIONAL_CO_LTD = 0x002C; + public static final int GCT_SEMICONDUCTOR = 0x002D; + public static final int NORWOOD_SYSTEMS = 0x002E; + public static final int MEWTEL_TECHNOLOGY_INC = 0x002F; + public static final int ST_MICROELECTRONICS = 0x0030; + public static final int SYNOPSIS = 0x0031; + public static final int REDM_COMMUNICATIONS_LTD = 0x0032; + public static final int COMMIL_LTD = 0x0033; + public static final int COMPUTER_ACCESS_TECHNOLOGY_CORPORATION_CATC = 0x0034; + public static final int ECLIPSE_HQ_ESPANA_SL = 0x0035; + public static final int RENESAS_TECHNOLOGY_CORP = 0x0036; + public static final int MOBILIAN_CORPORATION = 0x0037; + public static final int TERAX = 0x0038; + public static final int INTEGRATED_SYSTEM_SOLUTION_CORP = 0x0039; + public static final int MATSUSHITA_ELECTRIC_INDUSTRIAL_CO_LTD = 0x003A; + public static final int GENNUM_CORPORATION = 0x003B; + public static final int RESEARCH_IN_MOTION = 0x003C; + public static final int IPEXTREME_INC = 0x003D; + public static final int SYSTEMS_AND_CHIPS_INC = 0x003E; + public static final int BLUETOOTH_SIG_INC = 0x003F; + public static final int SEIKO_EPSON_CORPORATION = 0x0040; + public static final int INTEGRATED_SILICON_SOLUTION_TAIWAN_INC = 0x0041; + public static final int CONWISE_TECHNOLOGY_CORPORATION_LTD = 0x0042; + public static final int PARROT_SA = 0x0043; + public static final int SOCKET_MOBILE = 0x0044; + public static final int ATHEROS_COMMUNICATIONS_INC = 0x0045; + public static final int MEDIATEK_INC = 0x0046; + public static final int BLUEGIGA = 0x0047; + public static final int MARVELL_TECHNOLOGY_GROUP_LTD = 0x0048; + public static final int THREE_DSP_CORPORATION = 0x0049; + public static final int ACCEL_SEMICONDUCTOR_LTD = 0x004A; + public static final int CONTINENTAL_AUTOMOTIVE_SYSTEMS = 0x004B; + public static final int APPLE_INC = 0x004C; + public static final int STACCATO_COMMUNICATIONS_INC = 0x004D; + public static final int AVAGO_TECHNOLOGIES = 0x004E; + public static final int APT_LICENSING_LTD = 0x004F; + public static final int SIRF_TECHNOLOGY = 0x0050; + public static final int TZERO_TECHNOLOGIES_INC = 0x0051; + public static final int JM_CORPORATION = 0x0052; + public static final int FREE2MOVE_AB = 0x0053; + public static final int THREE_DIJOY_CORPORATION = 0x0054; + public static final int PLANTRONICS_INC = 0x0055; + public static final int SONY_ERICSSON_MOBILE_COMMUNICATIONS = 0x0056; + public static final int HARMAN_INTERNATIONAL_INDUSTRIES_INC = 0x0057; + public static final int VIZIO_INC = 0x0058; + public static final int NORDIC_SEMICONDUCTOR_ASA = 0x0059; + public static final int EM_MICROELECTRONICMARIN_SA = 0x005A; + public static final int RALINK_TECHNOLOGY_CORPORATION = 0x005B; + public static final int BELKIN_INTERNATIONAL_INC = 0x005C; + public static final int REALTEK_SEMICONDUCTOR_CORPORATION = 0x005D; + public static final int STONESTREET_ONE_LLC = 0x005E; + public static final int WICENTRIC_INC = 0x005F; + public static final int RIVIERAWAVES_SAS = 0x0060; + public static final int RDA_MICROELECTRONICS = 0x0061; + public static final int GIBSON_GUITARS = 0x0062; + public static final int MICOMMAND_INC = 0x0063; + public static final int BAND_XI_INTERNATIONAL_LLC = 0x0064; + public static final int HEWLETTPACKARD_COMPANY = 0x0065; + public static final int NINE_SOLUTIONS_OY = 0x0066; + public static final int GN_NETCOM_A_S = 0x0067; + public static final int GENERAL_MOTORS = 0x0068; + public static final int AD_ENGINEERING_INC = 0x0069; + public static final int MINDTREE_LTD = 0x006A; + public static final int POLAR_ELECTRO_OY = 0x006B; + public static final int BEAUTIFUL_ENTERPRISE_CO_LTD = 0x006C; + public static final int BRIARTEK_INC = 0x006D; + public static final int SUMMIT_DATA_COMMUNICATIONS_INC = 0x006E; + public static final int SOUND_ID = 0x006F; + public static final int MONSTER_LLC = 0x0070; + public static final int CONNECTBLUE_AB = 0x0071; + public static final int SHANGHAI_SUPER_SMART_ELECTRONICS_CO_LTD = 0x0072; + public static final int GROUP_SENSE_LTD = 0x0073; + public static final int ZOMM_LLC = 0x0074; + public static final int SAMSUNG_ELECTRONICS_CO_LTD = 0x0075; + public static final int CREATIVE_TECHNOLOGY_LTD = 0x0076; + public static final int LAIRD_TECHNOLOGIES = 0x0077; + public static final int NIKE_INC = 0x0078; + public static final int LESSWIRE_AG = 0x0079; + public static final int MSTAR_SEMICONDUCTOR_INC = 0x007A; + public static final int HANLYNN_TECHNOLOGIES = 0x007B; + public static final int A_R_CAMBRIDGE = 0x007C; + public static final int SEERS_TECHNOLOGY_CO_LTD = 0x007D; + public static final int SPORTS_TRACKING_TECHNOLOGIES_LTD = 0x007E; + public static final int AUTONET_MOBILE = 0x007F; + public static final int DELORME_PUBLISHING_COMPANY_INC = 0x0080; + public static final int WUXI_VIMICRO = 0x0081; + public static final int SENNHEISER_COMMUNICATIONS_A_S = 0x0082; + public static final int TIMEKEEPING_SYSTEMS_INC = 0x0083; + public static final int LUDUS_HELSINKI_LTD = 0x0084; + public static final int BLUERADIOS_INC = 0x0085; + public static final int EQUINOX_AG = 0x0086; + public static final int GARMIN_INTERNATIONAL_INC = 0x0087; + public static final int ECOTEST = 0x0088; + public static final int GN_RESOUND_A_S = 0x0089; + public static final int JAWBONE = 0x008A; + public static final int TOPCORN_POSITIONING_SYSTEMS_LLC = 0x008B; + public static final int QUALCOMM_RETAIL_SOLUTIONS_INC_FORMERLY_QUALCOMM_LABS_INC = 0x008C; + public static final int ZSCAN_SOFTWARE = 0x008D; + public static final int QUINTIC_CORP = 0x008E; + public static final int STOLLMAN_EV_GMBH = 0x008F; + public static final int FUNAI_ELECTRIC_CO_LTD = 0x0090; + public static final int ADVANCED_PANMOBIL_SYSTEMS_GMBH_CO_KG = 0x0091; + public static final int THINKOPTICS_INC = 0x0092; + public static final int UNIVERSAL_ELECTRONICS_INC = 0x0093; + public static final int AIROHA_TECHNOLOGY_CORP = 0x0094; + public static final int NEC_LIGHTING_LTD = 0x0095; + public static final int ODM_TECHNOLOGY_INC = 0x0096; + public static final int CONNECTEDEVICE_LTD = 0x0097; + public static final int ZER01TV_GMBH = 0x0098; + public static final int ITECH_DYNAMIC_GLOBAL_DISTRIBUTION_LTD = 0x0099; + public static final int ALPWISE = 0x009A; + public static final int JIANGSU_TOPPOWER_AUTOMOTIVE_ELECTRONICS_CO_LTD = 0x009B; + public static final int COLORFY_INC = 0x009C; + public static final int GEOFORCE_INC = 0x009D; + public static final int BOSE_CORPORATION = 0x009E; + public static final int SUUNTO_OY = 0x009F; + public static final int KENSINGTON_COMPUTER_PRODUCTS_GROUP = 0x00A0; + public static final int SRMEDIZINELEKTRONIK = 0x00A1; + public static final int VERTU_CORPORATION_LIMITED = 0x00A2; + public static final int META_WATCH_LTD = 0x00A3; + public static final int LINAK_A_S = 0x00A4; + public static final int OTL_DYNAMICS_LLC = 0x00A5; + public static final int PANDA_OCEAN_INC = 0x00A6; + public static final int VISTEON_CORPORATION = 0x00A7; + public static final int ARP_DEVICES_LIMITED = 0x00A8; + public static final int MAGNETI_MARELLI_SPA = 0x00A9; + public static final int CAEN_RFID_SRL = 0x00AA; + public static final int INGENIEURSYSTEMGRUPPE_ZAHN_GMBH = 0x00AB; + public static final int GREEN_THROTTLE_GAMES = 0x00AC; + public static final int PETER_SYSTEMTECHNIK_GMBH = 0x00AD; + public static final int OMEGAWAVE_OY = 0x00AE; + public static final int CINETIX = 0x00AF; + public static final int PASSIF_SEMICONDUCTOR_CORP = 0x00B0; + public static final int SARIS_CYCLING_GROUP_INC = 0x00B1; + public static final int BEKEY_A_S = 0x00B2; + public static final int CLARINOX_TECHNOLOGIES_PTY_LTD = 0x00B3; + public static final int BDE_TECHNOLOGY_CO_LTD = 0x00B4; + public static final int SWIRL_NETWORKS = 0x00B5; + public static final int MESO_INTERNATIONAL = 0x00B6; + public static final int TRELAB_LTD = 0x00B7; + public static final int QUALCOMM_INNOVATION_CENTER_INC_QUIC = 0x00B8; + public static final int JOHNSON_CONTROLS_INC = 0x00B9; + public static final int STARKEY_LABORATORIES_INC = 0x00BA; + public static final int SPOWER_ELECTRONICS_LIMITED = 0x00BB; + public static final int ACE_SENSOR_INC = 0x00BC; + public static final int APLIX_CORPORATION = 0x00BD; + public static final int AAMP_OF_AMERICA = 0x00BE; + public static final int STALMART_TECHNOLOGY_LIMITED = 0x00BF; + public static final int AMICCOM_ELECTRONICS_CORPORATION = 0x00C0; + public static final int SHENZHEN_EXCELSECU_DATA_TECHNOLOGY_COLTD = 0x00C1; + public static final int GENEQ_INC = 0x00C2; + public static final int ADIDAS_AG = 0x00C3; + public static final int LG_ELECTRONICS = 0x00C4; + public static final int ONSET_COMPUTER_CORPORATION = 0x00C5; + public static final int SELFLY_BV = 0x00C6; + public static final int QUUPPA_OY = 0x00C7; + public static final int GELO_INC = 0x00C8; + public static final int EVLUMA = 0x00C9; + public static final int MC10 = 0x00CA; + public static final int BINAURIC_SE = 0x00CB; + public static final int BEATS_ELECTRONICS = 0x00CC; + public static final int MICROCHIP_TECHNOLOGY_INC = 0x00CD; + public static final int ELGATO_SYSTEMS_GMBH = 0x00CE; + public static final int ARCHOS_SA = 0x00CF; + public static final int DEXCOM_INC = 0x00D0; + public static final int POLAR_ELECTRO_EUROPE_BV = 0x00D1; + public static final int DIALOG_SEMICONDUCTOR_BV = 0x00D2; + public static final int TAIXINGBANG_TECHNOLOGY_HK_CO_LTD = 0x00D3; + public static final int KAWANTECH = 0x00D4; + public static final int AUSTCO_COMMUNICATION_SYSTEMS = 0x00D5; + public static final int TIMEX_GROUP_USA_INC = 0x00D6; + public static final int QUALCOMM_TECHNOLOGIES_INC = 0x00D7; + public static final int QUALCOMM_CONNECTED_EXPERIENCES_INC = 0x00D8; + public static final int VOYETRA_TURTLE_BEACH = 0x00D9; + public static final int TXTR_GMBH = 0x00DA; + public static final int BIOSENTRONICS = 0x00DB; + public static final int PROCTER_GAMBLE = 0x00DC; + public static final int HOSIDEN_CORPORATION = 0x00DD; + public static final int MUZIK_LLC = 0x00DE; + public static final int MISFIT_WEARABLES_CORP = 0x00DF; + public static final int GOOGLE = 0x00E0; + public static final int DANLERS_LTD = 0x00E1; + public static final int SEMILINK_INC = 0x00E2; + public static final int INMUSIC_BRANDS_INC = 0x00E3; + public static final int LS_RESEARCH_INC = 0x00E4; + public static final int EDEN_SOFTWARE_CONSULTANTS_LTD = 0x00E5; + public static final int FRESHTEMP = 0x00E6; + public static final int KS_TECHNOLOGIES = 0x00E7; + public static final int ACTS_TECHNOLOGIES = 0x00E8; + public static final int VTRACK_SYSTEMS = 0x00E9; + public static final int NIELSENKELLERMAN_COMPANY = 0x00EA; + public static final int SERVER_TECHNOLOGY_INC = 0x00EB; + public static final int BIORESEARCH_ASSOCIATES = 0x00EC; + public static final int JOLLY_LOGIC_LLC = 0x00ED; + public static final int ABOVE_AVERAGE_OUTCOMES_INC = 0x00EE; + public static final int BITSPLITTERS_GMBH = 0x00EF; + public static final int PAYPAL_INC = 0x00F0; + public static final int WITRON_TECHNOLOGY_LIMITED = 0x00F1; + public static final int MORSE_PROJECT_INC = 0x00F2; + public static final int KENT_DISPLAYS_INC = 0x00F3; + public static final int NAUTILUS_INC = 0x00F4; + public static final int SMARTIFIER_OY = 0x00F5; + public static final int ELCOMETER_LIMITED = 0x00F6; + public static final int VSN_TECHNOLOGIES_INC = 0x00F7; + public static final int ACEUNI_CORP_LTD = 0x00F8; + public static final int STICKNFIND = 0x00F9; + public static final int CRYSTAL_CODE_AB = 0x00FA; + public static final int KOUKAAM_AS = 0x00FB; + public static final int DELPHI_CORPORATION = 0x00FC; + public static final int VALENCETECH_LIMITED = 0x00FD; + public static final int RESERVED = 0x00FE; + public static final int TYPO_PRODUCTS_LLC = 0x00FF; + public static final int TOMTOM_INTERNATIONAL_BV = 0x0100; + public static final int FUGOO_INC = 0x0101; + public static final int KEISER_CORPORATION = 0x0102; + public static final int BANG_OLUFSEN_A_S = 0x0103; + public static final int PLUS_LOCATIONS_SYSTEMS_PTY_LTD = 0x0104; + public static final int UBIQUITOUS_COMPUTING_TECHNOLOGY_CORPORATION = 0x0105; + public static final int INNOVATIVE_YACHTTER_SOLUTIONS = 0x0106; + public static final int WILLIAM_DEMANT_HOLDING_A_S = 0x0107; + public static final int CHICONY_ELECTRONICS_CO_LTD = 0x0108; + public static final int ATUS_BV = 0x0109; + public static final int CODEGATE_LTD = 0x010A; + public static final int ERI_INC = 0x010B; + public static final int TRANSDUCERS_DIRECT_LLC = 0x010C; + public static final int FUJITSU_TEN_LIMITED = 0x010D; + public static final int AUDI_AG = 0x010E; + public static final int HISILICON_TECHNOLOGIES_CO_LTD = 0x010F; + public static final int NIPPON_SEIKI_CO_LTD = 0x0110; + public static final int STEELSERIES_APS = 0x0111; + public static final int VYZYBL_INC = 0x0112; + public static final int OPENBRAIN_TECHNOLOGIES_CO_LTD = 0x0113; + public static final int XENSR = 0x0114; + public static final int ESOLUTIONS = 0x0115; + public static final int ONE_OAK_TECHNOLOGIES = 0x0116; + public static final int WIMOTO_TECHNOLOGIES_INC = 0x0117; + public static final int RADIUS_NETWORKS_INC = 0x0118; + public static final int WIZE_TECHNOLOGY_CO_LTD = 0x0119; + public static final int QUALCOMM_LABS_INC = 0x011A; + public static final int ARUBA_NETWORKS = 0x011B; + public static final int BAIDU = 0x011C; + public static final int ARENDI_AG = 0x011D; + public static final int SKODA_AUTO_AS = 0x011E; + public static final int VOLKSWAGON_AG = 0x011F; + public static final int PORSCHE_AG = 0x0120; + public static final int SINO_WEALTH_ELECTRONIC_LTD = 0x0121; + public static final int AIRTURN_INC = 0x0122; + public static final int KINSA_INC = 0x0123; + public static final int HID_GLOBAL = 0x0124; + public static final int SEAT_ES = 0x0125; + public static final int PROMETHEAN_LTD = 0x0126; + public static final int SALUTICA_ALLIED_SOLUTIONS = 0x0127; + public static final int GPSI_GROUP_PTY_LTD = 0x0128; + public static final int NIMBLE_DEVICES_OY = 0x0129; + public static final int CHANGZHOU_YONGSE_INFOTECH_CO_LTD = 0x012A; + public static final int SPORTIQ = 0x012B; + public static final int TEMEC_INSTRUMENTS_BV = 0x012C; + public static final int SONY_CORPORATION = 0x012D; + public static final int ASSA_ABLOY = 0x012E; + public static final int CLARION_CO_LTD = 0x012F; + public static final int WAREHOUSE_INNOVATIONS = 0x0130; + public static final int CYPRESS_SEMICONDUCTOR_CORPORATION = 0x0131; + public static final int MADS_INC = 0x0132; + public static final int BLUE_MAESTRO_LIMITED = 0x0133; + public static final int RESOLUTION_PRODUCTS_INC = 0x0134; + public static final int AIREWEAR_LLC = 0x0135; + public static final int ETC_SP_ZOO = 0x0136; + public static final int PRESTIGIO_PLAZA_LTD = 0x0137; + + private static final SparseArray COMPANY_NAME_MAP = populateCompanyNameMap(); + + public static String getCompanyName(final int companyId, final String fallback) { + final String name = COMPANY_NAME_MAP.get(companyId); + return name == null ? fallback : name; + } + + private static SparseArray populateCompanyNameMap() { + final SparseArray map = new SparseArray<>(); + + map.put(ERICSSON_TECHNOLOGY_LICENSING, "Ericsson Technology Licensing"); + map.put(NOKIA_MOBILE_PHONES, "Nokia Mobile Phones"); + map.put(INTEL_CORP, "Intel Corp."); + map.put(IBM_CORP, "IBM Corp."); + map.put(TOSHIBA_CORP, "Toshiba Corp."); + map.put(THREE_COM, "3Com"); + map.put(MICROSOFT, "Microsoft"); + map.put(LUCENT, "Lucent"); + map.put(MOTOROLA, "Motorola"); + map.put(INFINEON_TECHNOLOGIES_AG, "Infineon Technologies AG"); + map.put(CAMBRIDGE_SILICON_RADIO, "Cambridge Silicon Radio"); + map.put(SILICON_WAVE, "Silicon Wave"); + map.put(DIGIANSWER_A_S, "Digianswer A/S"); + map.put(TEXAS_INSTRUMENTS_INC, "Texas Instruments Inc."); + map.put(CEVA_INC_FORMERLY_PARTHUS_TECHNOLOGIES_INC, "Ceva, Inc. (formerly Parthus Technologies, Inc.)"); + map.put(BROADCOM_CORPORATION, "Broadcom Corporation"); + map.put(MITEL_SEMICONDUCTOR, "Mitel Semiconductor"); + map.put(WIDCOMM_INC, "Widcomm, Inc"); + map.put(ZEEVO_INC, "Zeevo, Inc."); + map.put(ATMEL_CORPORATION, "Atmel Corporation"); + map.put(MITSUBISHI_ELECTRIC_CORPORATION, "Mitsubishi Electric Corporation"); + map.put(RTX_TELECOM_A_S, "RTX Telecom A/S"); + map.put(KC_TECHNOLOGY_INC, "KC Technology Inc."); + map.put(NEWLOGIC, "NewLogic"); + map.put(TRANSILICA_INC, "Transilica, Inc."); + map.put(ROHDE_SCHWARZ_GMBH_CO_KG, "Rohde & Schwarz GmbH & Co. KG"); + map.put(TTPCOM_LIMITED, "TTPCom Limited"); + map.put(SIGNIA_TECHNOLOGIES_INC, "Signia Technologies, Inc."); + map.put(CONEXANT_SYSTEMS_INC, "Conexant Systems Inc."); + map.put(QUALCOMM, "Qualcomm"); + map.put(INVENTEL, "Inventel"); + map.put(AVM_BERLIN, "AVM Berlin"); + map.put(BANDSPEED_INC, "BandSpeed, Inc."); + map.put(MANSELLA_LTD, "Mansella Ltd"); + map.put(NEC_CORPORATION, "NEC Corporation"); + map.put(WAVEPLUS_TECHNOLOGY_CO_LTD, "WavePlus Technology Co., Ltd."); + map.put(ALCATEL, "Alcatel"); + map.put(PHILIPS_SEMICONDUCTORS, "Philips Semiconductors"); + map.put(C_TECHNOLOGIES, "C Technologies"); + map.put(OPEN_INTERFACE, "Open Interface"); + map.put(R_F_MICRO_DEVICES, "R F Micro Devices"); + map.put(HITACHI_LTD, "Hitachi Ltd"); + map.put(SYMBOL_TECHNOLOGIES_INC, "Symbol Technologies, Inc."); + map.put(TENOVIS, "Tenovis"); + map.put(MACRONIX_INTERNATIONAL_CO_LTD, "Macronix International Co. Ltd."); + map.put(GCT_SEMICONDUCTOR, "GCT Semiconductor"); + map.put(NORWOOD_SYSTEMS, "Norwood Systems"); + map.put(MEWTEL_TECHNOLOGY_INC, "MewTel Technology Inc."); + map.put(ST_MICROELECTRONICS, "ST Microelectronics"); + map.put(SYNOPSIS, "Synopsis"); + map.put(REDM_COMMUNICATIONS_LTD, "Red-M (Communications) Ltd"); + map.put(COMMIL_LTD, "Commil Ltd"); + map.put(COMPUTER_ACCESS_TECHNOLOGY_CORPORATION_CATC, "Computer Access Technology Corporation (CATC)"); + map.put(ECLIPSE_HQ_ESPANA_SL, "Eclipse (HQ Espana) S.L."); + map.put(RENESAS_TECHNOLOGY_CORP, "Renesas Technology Corp."); + map.put(MOBILIAN_CORPORATION, "Mobilian Corporation"); + map.put(TERAX, "Terax"); + map.put(INTEGRATED_SYSTEM_SOLUTION_CORP, "Integrated System Solution Corp."); + map.put(MATSUSHITA_ELECTRIC_INDUSTRIAL_CO_LTD, "Matsushita Electric Industrial Co., Ltd."); + map.put(GENNUM_CORPORATION, "Gennum Corporation"); + map.put(RESEARCH_IN_MOTION, "Research In Motion"); + map.put(IPEXTREME_INC, "IPextreme, Inc."); + map.put(SYSTEMS_AND_CHIPS_INC, "Systems and Chips, Inc."); + map.put(BLUETOOTH_SIG_INC, "Bluetooth SIG, Inc."); + map.put(SEIKO_EPSON_CORPORATION, "Seiko Epson Corporation"); + map.put(INTEGRATED_SILICON_SOLUTION_TAIWAN_INC, "Integrated Silicon Solution Taiwan, Inc."); + map.put(CONWISE_TECHNOLOGY_CORPORATION_LTD, "CONWISE Technology Corporation Ltd"); + map.put(PARROT_SA, "PARROT SA"); + map.put(SOCKET_MOBILE, "Socket Mobile"); + map.put(ATHEROS_COMMUNICATIONS_INC, "Atheros Communications, Inc."); + map.put(MEDIATEK_INC, "MediaTek, Inc."); + map.put(BLUEGIGA, "Bluegiga"); + map.put(MARVELL_TECHNOLOGY_GROUP_LTD, "Marvell Technology Group Ltd."); + map.put(THREE_DSP_CORPORATION, "3DSP Corporation"); + map.put(ACCEL_SEMICONDUCTOR_LTD, "Accel Semiconductor Ltd."); + map.put(CONTINENTAL_AUTOMOTIVE_SYSTEMS, "Continental Automotive Systems"); + map.put(APPLE_INC, "Apple, Inc."); + map.put(STACCATO_COMMUNICATIONS_INC, "Staccato Communications, Inc."); + map.put(AVAGO_TECHNOLOGIES, "Avago Technologies"); + map.put(APT_LICENSING_LTD, "APT Licensing Ltd."); + map.put(SIRF_TECHNOLOGY, "SiRF Technology"); + map.put(TZERO_TECHNOLOGIES_INC, "Tzero Technologies, Inc."); + map.put(JM_CORPORATION, "J&M Corporation"); + map.put(FREE2MOVE_AB, "Free2move AB"); + map.put(THREE_DIJOY_CORPORATION, "3DiJoy Corporation"); + map.put(PLANTRONICS_INC, "Plantronics, Inc."); + map.put(SONY_ERICSSON_MOBILE_COMMUNICATIONS, "Sony Ericsson Mobile Communications"); + map.put(HARMAN_INTERNATIONAL_INDUSTRIES_INC, "Harman International Industries, Inc."); + map.put(VIZIO_INC, "Vizio, Inc."); + map.put(NORDIC_SEMICONDUCTOR_ASA, "Nordic Semiconductor ASA"); + map.put(EM_MICROELECTRONICMARIN_SA, "EM Microelectronic-Marin SA"); + map.put(RALINK_TECHNOLOGY_CORPORATION, "Ralink Technology Corporation"); + map.put(BELKIN_INTERNATIONAL_INC, "Belkin International, Inc."); + map.put(REALTEK_SEMICONDUCTOR_CORPORATION, "Realtek Semiconductor Corporation"); + map.put(STONESTREET_ONE_LLC, "Stonestreet One, LLC"); + map.put(WICENTRIC_INC, "Wicentric, Inc."); + map.put(RIVIERAWAVES_SAS, "RivieraWaves S.A.S"); + map.put(RDA_MICROELECTRONICS, "RDA Microelectronics"); + map.put(GIBSON_GUITARS, "Gibson Guitars"); + map.put(MICOMMAND_INC, "MiCommand Inc."); + map.put(BAND_XI_INTERNATIONAL_LLC, "Band XI International, LLC"); + map.put(HEWLETTPACKARD_COMPANY, "Hewlett-Packard Company"); + map.put(NINE_SOLUTIONS_OY, "9Solutions Oy"); + map.put(GN_NETCOM_A_S, "GN Netcom A/S"); + map.put(GENERAL_MOTORS, "General Motors"); + map.put(AD_ENGINEERING_INC, "A&D Engineering, Inc."); + map.put(MINDTREE_LTD, "MindTree Ltd."); + map.put(POLAR_ELECTRO_OY, "Polar Electro OY"); + map.put(BEAUTIFUL_ENTERPRISE_CO_LTD, "Beautiful Enterprise Co., Ltd."); + map.put(BRIARTEK_INC, "BriarTek, Inc."); + map.put(SUMMIT_DATA_COMMUNICATIONS_INC, "Summit Data Communications, Inc."); + map.put(SOUND_ID, "Sound ID"); + map.put(MONSTER_LLC, "Monster, LLC"); + map.put(CONNECTBLUE_AB, "connectBlue AB"); + map.put(SHANGHAI_SUPER_SMART_ELECTRONICS_CO_LTD, "ShangHai Super Smart Electronics Co. Ltd."); + map.put(GROUP_SENSE_LTD, "Group Sense Ltd."); + map.put(ZOMM_LLC, "Zomm, LLC"); + map.put(SAMSUNG_ELECTRONICS_CO_LTD, "Samsung Electronics Co. Ltd."); + map.put(CREATIVE_TECHNOLOGY_LTD, "Creative Technology Ltd."); + map.put(LAIRD_TECHNOLOGIES, "Laird Technologies"); + map.put(NIKE_INC, "Nike, Inc."); + map.put(LESSWIRE_AG, "lesswire AG"); + map.put(MSTAR_SEMICONDUCTOR_INC, "MStar Semiconductor, Inc."); + map.put(HANLYNN_TECHNOLOGIES, "Hanlynn Technologies"); + map.put(A_R_CAMBRIDGE, "A & R Cambridge"); + map.put(SEERS_TECHNOLOGY_CO_LTD, "Seers Technology Co. Ltd"); + map.put(SPORTS_TRACKING_TECHNOLOGIES_LTD, "Sports Tracking Technologies Ltd."); + map.put(AUTONET_MOBILE, "Autonet Mobile"); + map.put(DELORME_PUBLISHING_COMPANY_INC, "DeLorme Publishing Company, Inc."); + map.put(WUXI_VIMICRO, "WuXi Vimicro"); + map.put(SENNHEISER_COMMUNICATIONS_A_S, "Sennheiser Communications A/S"); + map.put(TIMEKEEPING_SYSTEMS_INC, "TimeKeeping Systems, Inc."); + map.put(LUDUS_HELSINKI_LTD, "Ludus Helsinki Ltd."); + map.put(BLUERADIOS_INC, "BlueRadios, Inc."); + map.put(EQUINOX_AG, "equinox AG"); + map.put(GARMIN_INTERNATIONAL_INC, "Garmin International, Inc."); + map.put(ECOTEST, "Ecotest"); + map.put(GN_RESOUND_A_S, "GN ReSound A/S"); + map.put(JAWBONE, "Jawbone"); + map.put(TOPCORN_POSITIONING_SYSTEMS_LLC, "Topcorn Positioning Systems, LLC"); + map.put(QUALCOMM_RETAIL_SOLUTIONS_INC_FORMERLY_QUALCOMM_LABS_INC, "Qualcomm Retail Solutions, Inc. (formerly Qualcomm Labs, Inc.)"); + map.put(ZSCAN_SOFTWARE, "Zscan Software"); + map.put(QUINTIC_CORP, "Quintic Corp."); + map.put(STOLLMAN_EV_GMBH, "Stollman E+V GmbH"); + map.put(FUNAI_ELECTRIC_CO_LTD, "Funai Electric Co., Ltd."); + map.put(ADVANCED_PANMOBIL_SYSTEMS_GMBH_CO_KG, "Advanced PANMOBIL Systems GmbH & Co. KG"); + map.put(THINKOPTICS_INC, "ThinkOptics, Inc."); + map.put(UNIVERSAL_ELECTRONICS_INC, "Universal Electronics, Inc."); + map.put(AIROHA_TECHNOLOGY_CORP, "Airoha Technology Corp."); + map.put(NEC_LIGHTING_LTD, "NEC Lighting, Ltd."); + map.put(ODM_TECHNOLOGY_INC, "ODM Technology, Inc."); + map.put(CONNECTEDEVICE_LTD, "ConnecteDevice Ltd."); + map.put(ZER01TV_GMBH, "zer01.tv GmbH"); + map.put(ITECH_DYNAMIC_GLOBAL_DISTRIBUTION_LTD, "i.Tech Dynamic Global Distribution Ltd."); + map.put(ALPWISE, "Alpwise"); + map.put(JIANGSU_TOPPOWER_AUTOMOTIVE_ELECTRONICS_CO_LTD, "Jiangsu Toppower Automotive Electronics Co., Ltd."); + map.put(COLORFY_INC, "Colorfy, Inc."); + map.put(GEOFORCE_INC, "Geoforce Inc."); + map.put(BOSE_CORPORATION, "Bose Corporation"); + map.put(SUUNTO_OY, "Suunto Oy"); + map.put(KENSINGTON_COMPUTER_PRODUCTS_GROUP, "Kensington Computer Products Group"); + map.put(SRMEDIZINELEKTRONIK, "SR-Medizinelektronik"); + map.put(VERTU_CORPORATION_LIMITED, "Vertu Corporation Limited"); + map.put(META_WATCH_LTD, "Meta Watch Ltd."); + map.put(LINAK_A_S, "LINAK A/S"); + map.put(OTL_DYNAMICS_LLC, "OTL Dynamics LLC"); + map.put(PANDA_OCEAN_INC, "Panda Ocean Inc."); + map.put(VISTEON_CORPORATION, "Visteon Corporation"); + map.put(ARP_DEVICES_LIMITED, "ARP Devices Limited"); + map.put(MAGNETI_MARELLI_SPA, "Magneti Marelli S.p.A"); + map.put(CAEN_RFID_SRL, "CAEN RFID srl"); + map.put(INGENIEURSYSTEMGRUPPE_ZAHN_GMBH, "Ingenieur-Systemgruppe Zahn GmbH"); + map.put(GREEN_THROTTLE_GAMES, "Green Throttle Games"); + map.put(PETER_SYSTEMTECHNIK_GMBH, "Peter Systemtechnik GmbH"); + map.put(OMEGAWAVE_OY, "Omegawave Oy"); + map.put(CINETIX, "Cinetix"); + map.put(PASSIF_SEMICONDUCTOR_CORP, "Passif Semiconductor Corp"); + map.put(SARIS_CYCLING_GROUP_INC, "Saris Cycling Group, Inc"); + map.put(BEKEY_A_S, "Bekey A/S"); + map.put(CLARINOX_TECHNOLOGIES_PTY_LTD, "Clarinox Technologies Pty. Ltd."); + map.put(BDE_TECHNOLOGY_CO_LTD, "BDE Technology Co., Ltd."); + map.put(SWIRL_NETWORKS, "Swirl Networks"); + map.put(MESO_INTERNATIONAL, "Meso international"); + map.put(TRELAB_LTD, "TreLab Ltd"); + map.put(QUALCOMM_INNOVATION_CENTER_INC_QUIC, "Qualcomm Innovation Center, Inc. (QuIC)"); + map.put(JOHNSON_CONTROLS_INC, "Johnson Controls, Inc."); + map.put(STARKEY_LABORATORIES_INC, "Starkey Laboratories Inc."); + map.put(SPOWER_ELECTRONICS_LIMITED, "S-Power Electronics Limited"); + map.put(ACE_SENSOR_INC, "Ace Sensor Inc"); + map.put(APLIX_CORPORATION, "Aplix Corporation"); + map.put(AAMP_OF_AMERICA, "AAMP of America"); + map.put(STALMART_TECHNOLOGY_LIMITED, "Stalmart Technology Limited"); + map.put(AMICCOM_ELECTRONICS_CORPORATION, "AMICCOM Electronics Corporation"); + map.put(SHENZHEN_EXCELSECU_DATA_TECHNOLOGY_COLTD, "Shenzhen Excelsecu Data Technology Co.,Ltd"); + map.put(GENEQ_INC, "Geneq Inc."); + map.put(ADIDAS_AG, "adidas AG"); + map.put(LG_ELECTRONICS, "LG Electronics"); + map.put(ONSET_COMPUTER_CORPORATION, "Onset Computer Corporation"); + map.put(SELFLY_BV, "Selfly BV"); + map.put(QUUPPA_OY, "Quuppa Oy."); + map.put(GELO_INC, "GeLo Inc"); + map.put(EVLUMA, "Evluma"); + map.put(MC10, "MC10"); + map.put(BINAURIC_SE, "Binauric SE"); + map.put(BEATS_ELECTRONICS, "Beats Electronics"); + map.put(MICROCHIP_TECHNOLOGY_INC, "Microchip Technology Inc."); + map.put(ELGATO_SYSTEMS_GMBH, "Elgato Systems GmbH"); + map.put(ARCHOS_SA, "ARCHOS SA"); + map.put(DEXCOM_INC, "Dexcom, Inc."); + map.put(POLAR_ELECTRO_EUROPE_BV, "Polar Electro Europe B.V."); + map.put(DIALOG_SEMICONDUCTOR_BV, "Dialog Semiconductor B.V."); + map.put(TAIXINGBANG_TECHNOLOGY_HK_CO_LTD, "Taixingbang Technology (HK) Co,. LTD."); + map.put(KAWANTECH, "Kawantech"); + map.put(AUSTCO_COMMUNICATION_SYSTEMS, "Austco Communication Systems"); + map.put(TIMEX_GROUP_USA_INC, "Timex Group USA, Inc."); + map.put(QUALCOMM_TECHNOLOGIES_INC, "Qualcomm Technologies, Inc."); + map.put(QUALCOMM_CONNECTED_EXPERIENCES_INC, "Qualcomm Connected Experiences, Inc."); + map.put(VOYETRA_TURTLE_BEACH, "Voyetra Turtle Beach"); + map.put(TXTR_GMBH, "txtr GmbH"); + map.put(BIOSENTRONICS, "Biosentronics"); + map.put(PROCTER_GAMBLE, "Procter & Gamble"); + map.put(HOSIDEN_CORPORATION, "Hosiden Corporation"); + map.put(MUZIK_LLC, "Muzik LLC"); + map.put(MISFIT_WEARABLES_CORP, "Misfit Wearables Corp"); + map.put(GOOGLE, "Google"); + map.put(DANLERS_LTD, "Danlers Ltd"); + map.put(SEMILINK_INC, "Semilink Inc"); + map.put(INMUSIC_BRANDS_INC, "inMusic Brands, Inc"); + map.put(LS_RESEARCH_INC, "L.S. Research Inc."); + map.put(EDEN_SOFTWARE_CONSULTANTS_LTD, "Eden Software Consultants Ltd."); + map.put(FRESHTEMP, "Freshtemp"); + map.put(KS_TECHNOLOGIES, "KS Technologies"); + map.put(ACTS_TECHNOLOGIES, "ACTS Technologies"); + map.put(VTRACK_SYSTEMS, "Vtrack Systems"); + map.put(NIELSENKELLERMAN_COMPANY, "Nielsen-Kellerman Company"); + map.put(SERVER_TECHNOLOGY_INC, "Server Technology, Inc."); + map.put(BIORESEARCH_ASSOCIATES, "BioResearch Associates"); + map.put(JOLLY_LOGIC_LLC, "Jolly Logic, LLC"); + map.put(ABOVE_AVERAGE_OUTCOMES_INC, "Above Average Outcomes, Inc."); + map.put(BITSPLITTERS_GMBH, "Bitsplitters GmbH"); + map.put(PAYPAL_INC, "PayPal, Inc."); + map.put(WITRON_TECHNOLOGY_LIMITED, "Witron Technology Limited"); + map.put(MORSE_PROJECT_INC, "Morse Project Inc."); + map.put(KENT_DISPLAYS_INC, "Kent Displays Inc."); + map.put(NAUTILUS_INC, "Nautilus Inc."); + map.put(SMARTIFIER_OY, "Smartifier Oy"); + map.put(ELCOMETER_LIMITED, "Elcometer Limited"); + map.put(VSN_TECHNOLOGIES_INC, "VSN Technologies Inc."); + map.put(ACEUNI_CORP_LTD, "AceUni Corp., Ltd."); + map.put(STICKNFIND, "StickNFind"); + map.put(CRYSTAL_CODE_AB, "Crystal Code AB"); + map.put(KOUKAAM_AS, "KOUKAAM a.s."); + map.put(DELPHI_CORPORATION, "Delphi Corporation"); + map.put(VALENCETECH_LIMITED, "ValenceTech Limited"); + map.put(RESERVED, "Reserved"); + map.put(TYPO_PRODUCTS_LLC, "Typo Products, LLC"); + map.put(TOMTOM_INTERNATIONAL_BV, "TomTom International BV"); + map.put(FUGOO_INC, "Fugoo, Inc"); + map.put(KEISER_CORPORATION, "Keiser Corporation"); + map.put(BANG_OLUFSEN_A_S, "Bang & Olufsen A/S"); + map.put(PLUS_LOCATIONS_SYSTEMS_PTY_LTD, "PLUS Locations Systems Pty Ltd"); + map.put(UBIQUITOUS_COMPUTING_TECHNOLOGY_CORPORATION, "Ubiquitous Computing Technology Corporation"); + map.put(INNOVATIVE_YACHTTER_SOLUTIONS, "Innovative Yachtter Solutions"); + map.put(WILLIAM_DEMANT_HOLDING_A_S, "William Demant Holding A/S"); + map.put(CHICONY_ELECTRONICS_CO_LTD, "Chicony Electronics Co., Ltd."); + map.put(ATUS_BV, "Atus BV"); + map.put(CODEGATE_LTD, "Codegate Ltd."); + map.put(ERI_INC, "ERi, Inc."); + map.put(TRANSDUCERS_DIRECT_LLC, "Transducers Direct, LLC"); + map.put(FUJITSU_TEN_LIMITED, "Fujitsu Ten Limited"); + map.put(AUDI_AG, "Audi AG"); + map.put(HISILICON_TECHNOLOGIES_CO_LTD, "HiSilicon Technologies Co., Ltd."); + map.put(NIPPON_SEIKI_CO_LTD, "Nippon Seiki Co., Ltd."); + map.put(STEELSERIES_APS, "Steelseries ApS"); + map.put(VYZYBL_INC, "vyzybl Inc."); + map.put(OPENBRAIN_TECHNOLOGIES_CO_LTD, "Openbrain Technologies, Co., Ltd."); + map.put(XENSR, "Xensr"); + map.put(ESOLUTIONS, "e.solutions"); + map.put(ONE_OAK_TECHNOLOGIES, "1OAK Technologies"); + map.put(WIMOTO_TECHNOLOGIES_INC, "Wimoto Technologies Inc"); + map.put(RADIUS_NETWORKS_INC, "Radius Networks, Inc."); + map.put(WIZE_TECHNOLOGY_CO_LTD, "Wize Technology Co., Ltd."); + map.put(QUALCOMM_LABS_INC, "Qualcomm Labs, Inc."); + map.put(ARUBA_NETWORKS, "Aruba Networks"); + map.put(BAIDU, "Baidu"); + map.put(ARENDI_AG, "Arendi AG"); + map.put(SKODA_AUTO_AS, "Skoda Auto a.s."); + map.put(VOLKSWAGON_AG, "Volkswagon AG"); + map.put(PORSCHE_AG, "Porsche AG"); + map.put(SINO_WEALTH_ELECTRONIC_LTD, "Sino Wealth Electronic Ltd."); + map.put(AIRTURN_INC, "AirTurn, Inc."); + map.put(KINSA_INC, "Kinsa, Inc."); + map.put(HID_GLOBAL, "HID Global"); + map.put(SEAT_ES, "SEAT es"); + map.put(PROMETHEAN_LTD, "Promethean Ltd."); + map.put(SALUTICA_ALLIED_SOLUTIONS, "Salutica Allied Solutions"); + map.put(GPSI_GROUP_PTY_LTD, "GPSI Group Pty Ltd"); + map.put(NIMBLE_DEVICES_OY, "Nimble Devices Oy"); + map.put(CHANGZHOU_YONGSE_INFOTECH_CO_LTD, "Changzhou Yongse Infotech Co., Ltd"); + map.put(SPORTIQ, "SportIQ"); + map.put(TEMEC_INSTRUMENTS_BV, "TEMEC Instruments B.V."); + map.put(SONY_CORPORATION, "Sony Corporation"); + map.put(ASSA_ABLOY, "ASSA ABLOY"); + map.put(CLARION_CO_LTD, "Clarion Co., Ltd."); + map.put(WAREHOUSE_INNOVATIONS, "Warehouse Innovations"); + map.put(CYPRESS_SEMICONDUCTOR_CORPORATION, "Cypress Semiconductor Corporation"); + map.put(MADS_INC, "MADS Inc"); + map.put(BLUE_MAESTRO_LIMITED, "Blue Maestro Limited"); + map.put(RESOLUTION_PRODUCTS_INC, "Resolution Products, Inc."); + map.put(AIREWEAR_LLC, "Airewear LLC"); + map.put(ETC_SP_ZOO, "ETC sp. z.o.o."); + map.put(PRESTIGIO_PLAZA_LTD, "Prestigio Plaza Ltd."); + + return map; + } + + +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/resolvers/GattAttributeResolver.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/resolvers/GattAttributeResolver.java new file mode 100644 index 0000000..429ffef --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/resolvers/GattAttributeResolver.java @@ -0,0 +1,380 @@ +package uk.co.alt236.bluetoothlelib.resolvers; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * The UUIDS have been collected from the following sources: + *

+ * - http://developer.nokia.com/community/wiki/Bluetooth_Services_for_Windows_Phone + * - The Bluez project + * + * @author Alexandros Schillings + */ +public class GattAttributeResolver { + public static final String BASE_GUID = "00000000-0000-1000-8000-00805f9b34fb"; + public static final String SERVICE_DISCOVERY_PROTOCOL_SDP = "00000001-0000-1000-8000-00805f9b34fb"; + public static final String USER_DATAGRAM_PROTOCOL_UDP = "00000002-0000-1000-8000-00805f9b34fb"; + public static final String RADIO_FREQUENCY_COMMUNICATION_PROTOCOL_RFCOMM = "00000003-0000-1000-8000-00805f9b34fb"; + public static final String TCP = "00000004-0000-1000-8000-00805f9b34fb"; + public static final String TCSBIN = "00000005-0000-1000-8000-00805f9b34fb"; + public static final String TCSAT = "00000006-0000-1000-8000-00805f9b34fb"; + public static final String OBJECT_EXCHANGE_PROTOCOL_OBEX = "00000008-0000-1000-8000-00805f9b34fb"; + public static final String IP = "00000009-0000-1000-8000-00805f9b34fb"; + public static final String FTP = "0000000a-0000-1000-8000-00805f9b34fb"; + public static final String HTTP = "0000000c-0000-1000-8000-00805f9b34fb"; + public static final String WSP = "0000000e-0000-1000-8000-00805f9b34fb"; + public static final String BNEP_SVC = "0000000f-0000-1000-8000-00805f9b34fb"; + public static final String UPNP_PROTOCOL = "00000010-0000-1000-8000-00805f9b34fb"; + public static final String HIDP = "00000011-0000-1000-8000-00805f9b34fb"; + public static final String HARDCOPY_CONTROL_CHANNEL_PROTOCOL = "00000012-0000-1000-8000-00805f9b34fb"; + public static final String HARDCOPY_DATA_CHANNEL_PROTOCOL = "00000014-0000-1000-8000-00805f9b34fb"; + public static final String HARDCOPY_NOTIFICATION_PROTOCOL = "00000016-0000-1000-8000-00805f9b34fb"; + public static final String VCTP_PROTOCOL = "00000017-0000-1000-8000-00805f9b34fb"; + public static final String VDTP_PROTOCOL = "00000019-0000-1000-8000-00805f9b34fb"; + public static final String CMPT_PROTOCOL = "0000001b-0000-1000-8000-00805f9b34fb"; + public static final String UDI_C_PLANE_PROTOCOL = "0000001d-0000-1000-8000-00805f9b34fb"; + public static final String MCAP_CONTROL_CHANNEL = "0000001e-0000-1000-8000-00805f9b34fb"; + public static final String MCAP_DATA_CHANNEL = "0000001f-0000-1000-8000-00805f9b34fb"; + public static final String L2CAP = "00000100-0000-1000-8000-00805f9b34fb"; + public static final String SERVICE_DISCOVERY_SERVER = "00001000-0000-1000-8000-00805f9b34fb"; + public static final String BROWSE_GROUP_DESCRIPTOR = "00001001-0000-1000-8000-00805f9b34fb"; + public static final String PUBLIC_BROWSE_GROUP = "00001002-0000-1000-8000-00805f9b34fb"; + public static final String SPP = "00001101-0000-1000-8000-00805f9b34fb"; + public static final String LAN_ACCESS_USING_PPP = "00001102-0000-1000-8000-00805f9b34fb"; + public static final String DUN_GW = "00001103-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_SYNC = "00001104-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_OBJECT_PUSH = "00001105-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_FILE_TRANSFER = "00001106-0000-1000-8000-00805f9b34fb"; + public static final String IRMC_SYNC_COMMAND = "00001107-0000-1000-8000-00805f9b34fb"; + public static final String HSP_HS = "00001108-0000-1000-8000-00805f9b34fb"; + public static final String CORDLESS_TELEPHONY = "00001109-0000-1000-8000-00805f9b34fb"; + public static final String AUDIO_SOURCE = "0000110a-0000-1000-8000-00805f9b34fb"; + public static final String AUDIO_SINK = "0000110b-0000-1000-8000-00805f9b34fb"; + public static final String AV_REMOTE_CONTROL_TARGET = "0000110c-0000-1000-8000-00805f9b34fb"; + public static final String ADVANCED_AUDIO = "0000110d-0000-1000-8000-00805f9b34fb"; + public static final String AVRCP_REMOTE = "0000110e-0000-1000-8000-00805f9b34fb"; + public static final String VIDEO_CONFERENCING = "0000110f-0000-1000-8000-00805f9b34fb"; + public static final String INTERCOM = "00001110-0000-1000-8000-00805f9b34fb"; + public static final String FAX = "00001111-0000-1000-8000-00805f9b34fb"; + public static final String HEADSET_PROFILE_HSP_AUDIO_GATEWAY = "00001112-0000-1000-8000-00805f9b34fb"; + public static final String WAP = "00001113-0000-1000-8000-00805f9b34fb"; + public static final String WAP_CLIENT = "00001114-0000-1000-8000-00805f9b34fb"; + public static final String PANU = "00001115-0000-1000-8000-00805f9b34fb"; + public static final String NAP = "00001116-0000-1000-8000-00805f9b34fb"; + public static final String GN = "00001117-0000-1000-8000-00805f9b34fb"; + public static final String DIRECT_PRINTING = "00001118-0000-1000-8000-00805f9b34fb"; + public static final String REFERENCE_PRINTING = "00001119-0000-1000-8000-00805f9b34fb"; + public static final String IMAGING = "0000111a-0000-1000-8000-00805f9b34fb"; + public static final String IMAGING_RESPONDER = "0000111b-0000-1000-8000-00805f9b34fb"; + public static final String IMAGING_AUTOMATIC_ARCHIVE = "0000111c-0000-1000-8000-00805f9b34fb"; + public static final String IMAGING_REFERENCE_OBJECTS = "0000111d-0000-1000-8000-00805f9b34fb"; + public static final String HANDS_FREE_PROFILE_HFP = "0000111e-0000-1000-8000-00805f9b34fb"; + public static final String HANDS_FREE_PROFILE_HFP_AUDIO_GATEWAY = "0000111f-0000-1000-8000-00805f9b34fb"; + public static final String DIRECT_PRINTING_REFERENCE_OBJECTS = "00001120-0000-1000-8000-00805f9b34fb"; + public static final String REFLECTED_UI = "00001121-0000-1000-8000-00805f9b34fb"; + public static final String BASIC_PRINTING = "00001122-0000-1000-8000-00805f9b34fb"; + public static final String PRINTING_STATUS = "00001123-0000-1000-8000-00805f9b34fb"; + public static final String HID = "00001124-0000-1000-8000-00805f9b34fb"; + public static final String HARDCOPY_CABLE_REPLACEMENT = "00001125-0000-1000-8000-00805f9b34fb"; + public static final String HCR_PRINT = "00001126-0000-1000-8000-00805f9b34fb"; + public static final String HCR_SCAN = "00001127-0000-1000-8000-00805f9b34fb"; + public static final String COMMON_ISDN_ACCESS = "00001128-0000-1000-8000-00805f9b34fb"; + public static final String VIDEO_CONFERENCING_GATEWAY = "00001129-0000-1000-8000-00805f9b34fb"; + public static final String UDIMT = "0000112a-0000-1000-8000-00805f9b34fb"; + public static final String UDITA = "0000112b-0000-1000-8000-00805f9b34fb"; + public static final String AUDIO_VIDEO = "0000112c-0000-1000-8000-00805f9b34fb"; + public static final String SIM_ACCESS = "0000112d-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_PCE = "0000112e-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_PSE = "0000112f-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_PBAP = "00001130-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_MAS = "00001132-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_MNS = "00001133-0000-1000-8000-00805f9b34fb"; + public static final String OBEX_MAP = "00001134-0000-1000-8000-00805f9b34fb"; + public static final String PNP = "00001200-0000-1000-8000-00805f9b34fb"; + public static final String GENERIC_NETWORKING = "00001201-0000-1000-8000-00805f9b34fb"; + public static final String GENERIC_FILE_TRANSFER = "00001202-0000-1000-8000-00805f9b34fb"; + public static final String GENERIC_AUDIO = "00001203-0000-1000-8000-00805f9b34fb"; + public static final String GENERIC_TELEPHONY = "00001204-0000-1000-8000-00805f9b34fb"; + public static final String UPNP = "00001205-0000-1000-8000-00805f9b34fb"; + public static final String UPNP_IP = "00001206-0000-1000-8000-00805f9b34fb"; + public static final String ESDP_UPNP_IP_PAN = "00001300-0000-1000-8000-00805f9b34fb"; + public static final String ESDP_UPNP_IP_LAP = "00001301-0000-1000-8000-00805f9b34fb"; + public static final String ESDP_UPNP_L2CAP = "00001302-0000-1000-8000-00805f9b34fb"; + public static final String VIDEO_DISTRIBUTION_PROFILE_VDP_SOURCE = "00001303-0000-1000-8000-00805f9b34fb"; + public static final String VIDEO_DISTRIBUTION_PROFILE_VDP_SINK = "00001304-0000-1000-8000-00805f9b34fb"; + public static final String VIDEO_DISTRIBUTION_PROFILE_VDP = "00001305-0000-1000-8000-00805f9b34fb"; + public static final String HEALTH_DEVICE_PROFILE_HDP = "00001400-0000-1000-8000-00805f9b34fb"; + public static final String HEALTH_DEVICE_PROFILE_HDP_SOURCE = "00001401-0000-1000-8000-00805f9b34fb"; + public static final String HEALTH_DEVICE_PROFILE_HDP_SINK = "00001402-0000-1000-8000-00805f9b34fb"; + public static final String GAP = "00001800-0000-1000-8000-00805f9b34fb"; + public static final String GATT = "00001801-0000-1000-8000-00805f9b34fb"; + public static final String IMMEDIATE_ALERT = "00001802-0000-1000-8000-00805f9b34fb"; + public static final String LINK_LOSS = "00001803-0000-1000-8000-00805f9b34fb"; + public static final String TX_POWER = "00001804-0000-1000-8000-00805f9b34fb"; + public static final String HEALTH_THERMOMETER = "00001809-0000-1000-8000-00805f9b34fb"; + public static final String DEVICE_INFORMATION = "0000180a-0000-1000-8000-00805f9b34fb"; + public static final String HEART_RATE = "0000180d-0000-1000-8000-00805f9b34fb"; + public static final String CYCLING_SC = "00001816-0000-1000-8000-00805f9b34fb"; + public static final String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"; + public static final String DEVICE_NAME = "00002a00-0000-1000-8000-00805f9b34fb"; + public static final String APPEARANCE = "00002a01-0000-1000-8000-00805f9b34fb"; + public static final String PERIPHERAL_PRIVACY_FLAG = "00002a02-0000-1000-8000-00805f9b34fb"; + public static final String RECONNECTION_ADDRESS = "00002a03-0000-1000-8000-00805f9b34fb"; + public static final String PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS = "00002a04-0000-1000-8000-00805f9b34fb"; + public static final String SERVICE_CHANGED = "00002a05-0000-1000-8000-00805f9b34fb"; + public static final String ALERT_LEVEL = "00002a06-0000-1000-8000-00805f9b34fb"; + public static final String TX_POWER_LEVEL = "00002a07-0000-1000-8000-00805f9b34fb"; + public static final String DATE_TIME = "00002a08-0000-1000-8000-00805f9b34fb"; + public static final String DAY_OF_WEEK = "00002a09-0000-1000-8000-00805f9b34fb"; + public static final String DAY_DATE_TIME = "00002a0a-0000-1000-8000-00805f9b34fb"; + public static final String EXACT_TIME_256 = "00002a0c-0000-1000-8000-00805f9b34fb"; + public static final String DST_OFFSET = "00002a0d-0000-1000-8000-00805f9b34fb"; + public static final String TIME_ZONE = "00002a0e-0000-1000-8000-00805f9b34fb"; + public static final String LOCAL_TIME_INFORMATION = "00002a0f-0000-1000-8000-00805f9b34fb"; + public static final String TIME_WITH_DST = "00002a11-0000-1000-8000-00805f9b34fb"; + public static final String TIME_ACCURACY = "00002a12-0000-1000-8000-00805f9b34fb"; + public static final String TIME_SOURCE = "00002a13-0000-1000-8000-00805f9b34fb"; + public static final String REFERENCE_TIME_INFORMATION = "00002a14-0000-1000-8000-00805f9b34fb"; + public static final String TIME_UPDATE_CONTROL_POINT = "00002a16-0000-1000-8000-00805f9b34fb"; + public static final String TIME_UPDATE_STATE = "00002a17-0000-1000-8000-00805f9b34fb"; + public static final String TEMPERATURE_MEASUREMENT = "00002a1c-0000-1000-8000-00805f9b34fb"; + public static final String TEMPERATURE_TYPE = "00002a1d-0000-1000-8000-00805f9b34fb"; + public static final String INTERMEDIATE_TEMPERATURE = "00002a1e-0000-1000-8000-00805f9b34fb"; + public static final String MEASUREMENT_INTERVAL = "00002a21-0000-1000-8000-00805f9b34fb"; + public static final String SYSTEM_ID = "00002a23-0000-1000-8000-00805f9b34fb"; + public static final String MODEL_NUMBER_STRING = "00002a24-0000-1000-8000-00805f9b34fb"; + public static final String SERIAL_NUMBER_STRING = "00002a25-0000-1000-8000-00805f9b34fb"; + public static final String FIRMWARE_REVISION_STRING = "00002a26-0000-1000-8000-00805f9b34fb"; + public static final String HARDWARE_REVISION_STRING = "00002a27-0000-1000-8000-00805f9b34fb"; + public static final String SOFTWARE_REVISION_STRING = "00002a28-0000-1000-8000-00805f9b34fb"; + public static final String MANUFACTURER_NAME_STRING = "00002a29-0000-1000-8000-00805f9b34fb"; + public static final String IEEE_1107320601_REGULATORY = "00002a2a-0000-1000-8000-00805f9b34fb"; + public static final String CURRENT_TIME = "00002a2b-0000-1000-8000-00805f9b34fb"; + public static final String BLOOD_PRESSURE_MEASUREMENT = "00002a35-0000-1000-8000-00805f9b34fb"; + public static final String INTERMEDIATE_CUFF_PRESSURE = "00002a36-0000-1000-8000-00805f9b34fb"; + public static final String HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb"; + public static final String BODY_SENSOR_LOCATION = "00002a38-0000-1000-8000-00805f9b34fb"; + public static final String HEART_RATE_CONTROL_POINT = "00002a39-0000-1000-8000-00805f9b34fb"; + public static final String ALERT_STATUS = "00002a3f-0000-1000-8000-00805f9b34fb"; + public static final String RINGER_CONTROL_POINT = "00002a40-0000-1000-8000-00805f9b34fb"; + public static final String RINGER_SETTING = "00002a41-0000-1000-8000-00805f9b34fb"; + public static final String ALERT_CATEGORY_ID_BIT_MASK = "00002a42-0000-1000-8000-00805f9b34fb"; + public static final String ALERT_CATEGORY_ID = "00002a43-0000-1000-8000-00805f9b34fb"; + public static final String ALERT_NOTIFICATION_CONTROL_POINT = "00002a44-0000-1000-8000-00805f9b34fb"; + public static final String UNREAD_ALERT_STATUS = "00002a45-0000-1000-8000-00805f9b34fb"; + public static final String NEW_ALERT = "00002a46-0000-1000-8000-00805f9b34fb"; + public static final String SUPPORTED_NEW_ALERT_CATEGORY = "00002a47-0000-1000-8000-00805f9b34fb"; + public static final String SUPPORTED_UNREAD_ALERT_CATEGORY = "00002a48-0000-1000-8000-00805f9b34fb"; + public static final String BLOOD_PRESSURE_FEATURE = "00002a49-0000-1000-8000-00805f9b34fb"; + public static final String PNPID = "00002a50-0000-1000-8000-00805f9b34fb"; + public static final String SC_CONTROL_POINT = "00002a55-0000-1000-8000-00805f9b34fb"; + public static final String CSC_MEASUREMENT = "00002a5b-0000-1000-8000-00805f9b34fb"; + public static final String CSC_FEATURE = "00002a5c-0000-1000-8000-00805f9b34fb"; + public static final String SENSOR_LOCATION = "00002a5d-0000-1000-8000-00805f9b34fb"; + public static final String ACTIVESYNC = "831c4071-7bc8-4a9c-a01c-15df25a4adbc"; + public static final String ESTIMOTE_SERVICE = "b9403000-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_UUID = "b9403003-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_MAJOR = "b9403001-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_MINOR = "b9403002-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_BATTERY = "b9403041-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_TEMPERATURE = "b9403021-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_POWER = "b9403011-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_ADVERTISING_INTERVAL = "b9403012-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_VERSION_SERVICE = "b9404000-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_SOFTWARE_VERSION = "b9404001-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_HARDWARE_VERSION = "b9404002-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_AUTHENTICATION_SERVICE = "b9402000-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_ADVERTISING_SEED = "b9402001-f5f8-466e-aff9-25556b57fe6d"; + public static final String ESTIMOTE_ADVERTISING_VECTOR = "b9402002-f5f8-466e-aff9-25556b57fe6d"; + + private final static Map sGattAttributesMap = populateGattAttributesMap(); + + public static String getAttributeName(final String uuid, final String fallback) { + final String name = sGattAttributesMap.get(uuid.toLowerCase(Locale.US)); + return name == null ? fallback : name; + } + + private static Map populateGattAttributesMap() { + final Map map = new HashMap<>(); + + map.put(BASE_GUID , "Base GUID"); + map.put(SERVICE_DISCOVERY_PROTOCOL_SDP , "Service Discovery Protocol (SDP)"); + map.put(USER_DATAGRAM_PROTOCOL_UDP , "User Datagram Protocol (UDP)"); + map.put(RADIO_FREQUENCY_COMMUNICATION_PROTOCOL_RFCOMM , "Radio Frequency Communication Protocol (RFCOMM)"); + map.put(TCP , "TCP"); + map.put(TCSBIN , "TCSBIN"); + map.put(TCSAT , "TCSAT"); + map.put(OBJECT_EXCHANGE_PROTOCOL_OBEX , "Object Exchange Protocol (OBEX)"); + map.put(IP , "IP"); + map.put(FTP , "FTP"); + map.put(HTTP , "HTTP"); + map.put(WSP , "WSP"); + map.put(BNEP_SVC , "BNEP_SVC"); + map.put(UPNP_PROTOCOL , "UPNP Protocol"); + map.put(HIDP , "HIDP"); + map.put(HARDCOPY_CONTROL_CHANNEL_PROTOCOL , "Hardcopy Control Channel Protocol"); + map.put(HARDCOPY_DATA_CHANNEL_PROTOCOL , "Hardcopy Data Channel Protocol"); + map.put(HARDCOPY_NOTIFICATION_PROTOCOL , "Hardcopy Notification Protocol"); + map.put(VCTP_PROTOCOL , "VCTP Protocol"); + map.put(VDTP_PROTOCOL , "VDTP Protocol"); + map.put(CMPT_PROTOCOL , "CMPT Protocol"); + map.put(UDI_C_PLANE_PROTOCOL , "UDI C Plane Protocol"); + map.put(MCAP_CONTROL_CHANNEL , "MCAP Control Channel"); + map.put(MCAP_DATA_CHANNEL , "MCAP Data Channel"); + map.put(L2CAP , "L2CAP"); + map.put(SERVICE_DISCOVERY_SERVER , "Service Discovery Server"); + map.put(BROWSE_GROUP_DESCRIPTOR , "Browse Group Descriptor"); + map.put(PUBLIC_BROWSE_GROUP , "Public Browse Group"); + map.put(SPP , "SPP"); + map.put(LAN_ACCESS_USING_PPP , "LAN Access Using PPP"); + map.put(DUN_GW , "DUN_GW"); + map.put(OBEX_SYNC , "OBEX_SYNC"); + map.put(OBEX_OBJECT_PUSH , "OBEX Object Push"); + map.put(OBEX_FILE_TRANSFER , "OBEX File Transfer"); + map.put(IRMC_SYNC_COMMAND , "IrMC Sync Command"); + map.put(HSP_HS , "HSP_HS"); + map.put(CORDLESS_TELEPHONY , "Cordless Telephony"); + map.put(AUDIO_SOURCE , "Audio Source"); + map.put(AUDIO_SINK , "Audio Sink"); + map.put(AV_REMOTE_CONTROL_TARGET , "AV Remote Control Target"); + map.put(ADVANCED_AUDIO , "ADVANCED_AUDIO"); + map.put(AVRCP_REMOTE , "AVRCP_REMOTE"); + map.put(VIDEO_CONFERENCING , "Video Conferencing"); + map.put(INTERCOM , "Intercom"); + map.put(FAX , "FAX"); + map.put(HEADSET_PROFILE_HSP_AUDIO_GATEWAY , "Headset Profile (HSP) - Audio Gateway"); + map.put(WAP , "WAP"); + map.put(WAP_CLIENT , "WAP Client"); + map.put(PANU , "PANU"); + map.put(NAP , "NAP"); + map.put(GN , "GN"); + map.put(DIRECT_PRINTING , "Direct Printing"); + map.put(REFERENCE_PRINTING , "Reference Printing"); + map.put(IMAGING , "Imaging"); + map.put(IMAGING_RESPONDER , "Imaging Responder"); + map.put(IMAGING_AUTOMATIC_ARCHIVE , "Imaging Automatic Archive"); + map.put(IMAGING_REFERENCE_OBJECTS , "Imaging Reference Objects"); + map.put(HANDS_FREE_PROFILE_HFP , "Hands Free Profile (HFP)"); + map.put(HANDS_FREE_PROFILE_HFP_AUDIO_GATEWAY , "Hands Free Profile (HFP) – Audio Gateway"); + map.put(DIRECT_PRINTING_REFERENCE_OBJECTS , "Direct Printing Reference Objects"); + map.put(REFLECTED_UI , "Reflected UI"); + map.put(BASIC_PRINTING , "Basic Printing"); + map.put(PRINTING_STATUS , "Printing Status"); + map.put(HID , "HID"); + map.put(HARDCOPY_CABLE_REPLACEMENT , "Hardcopy Cable Replacement"); + map.put(HCR_PRINT , "HCR Print"); + map.put(HCR_SCAN , "HCR Scan"); + map.put(COMMON_ISDN_ACCESS , "Common ISDN Access"); + map.put(VIDEO_CONFERENCING_GATEWAY , "Video Conferencing Gateway"); + map.put(UDIMT , "UDIMT"); + map.put(UDITA , "UDITA"); + map.put(AUDIO_VIDEO , "Audio Video"); + map.put(SIM_ACCESS , "SIM Access"); + map.put(OBEX_PCE , "OBEX PCE"); + map.put(OBEX_PSE , "OBEX PSE"); + map.put(OBEX_PBAP , "OBEX PBAP"); + map.put(OBEX_MAS , "OBEX MAS"); + map.put(OBEX_MNS , "OBEX MNS"); + map.put(OBEX_MAP , "OBEX MAP"); + map.put(PNP , "PNP"); + map.put(GENERIC_NETWORKING , "Generic Networking"); + map.put(GENERIC_FILE_TRANSFER , "Generic File Transfer"); + map.put(GENERIC_AUDIO , "Generic Audio"); + map.put(GENERIC_TELEPHONY , "Generic Telephony"); + map.put(UPNP , "UPNP"); + map.put(UPNP_IP , "UPNP IP"); + map.put(ESDP_UPNP_IP_PAN , "ESDP UPnP IP PAN"); + map.put(ESDP_UPNP_IP_LAP , "ESDP UPnP IP LAP"); + map.put(ESDP_UPNP_L2CAP , "ESDP Upnp L2CAP"); + map.put(VIDEO_DISTRIBUTION_PROFILE_VDP_SOURCE , "Video Distribution Profile (VDP) - Source"); + map.put(VIDEO_DISTRIBUTION_PROFILE_VDP_SINK , "Video Distribution Profile (VDP) - Sink"); + map.put(VIDEO_DISTRIBUTION_PROFILE_VDP , "Video Distribution Profile (VDP)"); + map.put(HEALTH_DEVICE_PROFILE_HDP , "Health Device Profile (HDP)"); + map.put(HEALTH_DEVICE_PROFILE_HDP_SOURCE , "Health Device Profile (HDP) - Source"); + map.put(HEALTH_DEVICE_PROFILE_HDP_SINK , "Health Device Profile (HDP) - Sink"); + map.put(GAP , "GAP"); + map.put(GATT , "GATT"); + map.put(IMMEDIATE_ALERT , "IMMEDIATE_ALERT"); + map.put(LINK_LOSS , "LINK_LOSS"); + map.put(TX_POWER , "TX_POWER"); + map.put(HEALTH_THERMOMETER , "Health Thermometer"); + map.put(DEVICE_INFORMATION , "Device Information"); + map.put(HEART_RATE , "HEART_RATE"); + map.put(CYCLING_SC , "CYCLING_SC"); + map.put(CLIENT_CHARACTERISTIC_CONFIG , "CLIENT_CHARACTERISTIC_CONFIG"); + map.put(DEVICE_NAME , "Device Name"); + map.put(APPEARANCE , "Appearance"); + map.put(PERIPHERAL_PRIVACY_FLAG , "Peripheral Privacy Flag"); + map.put(RECONNECTION_ADDRESS , "Reconnection Address"); + map.put(PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS , "Peripheral Preferred Connection Parameters"); + map.put(SERVICE_CHANGED , "Service Changed"); + map.put(ALERT_LEVEL , "Alert Level"); + map.put(TX_POWER_LEVEL , "Tx Power Level"); + map.put(DATE_TIME , "Date Time"); + map.put(DAY_OF_WEEK , "Day of Week"); + map.put(DAY_DATE_TIME , "Day Date Time"); + map.put(EXACT_TIME_256 , "Exact Time 256"); + map.put(DST_OFFSET , "DST Offset"); + map.put(TIME_ZONE , "Time Zone"); + map.put(LOCAL_TIME_INFORMATION , "Local Time Information"); + map.put(TIME_WITH_DST , "Time with DST"); + map.put(TIME_ACCURACY , "Time Accuracy"); + map.put(TIME_SOURCE , "Time Source"); + map.put(REFERENCE_TIME_INFORMATION , "Reference Time Information"); + map.put(TIME_UPDATE_CONTROL_POINT , "Time Update Control Point"); + map.put(TIME_UPDATE_STATE , "Time Update State"); + map.put(TEMPERATURE_MEASUREMENT , "Temperature Measurement"); + map.put(TEMPERATURE_TYPE , "Temperature Type"); + map.put(INTERMEDIATE_TEMPERATURE , "Intermediate Temperature"); + map.put(MEASUREMENT_INTERVAL , "Measurement Interval"); + map.put(SYSTEM_ID , "System ID"); + map.put(MODEL_NUMBER_STRING , "Model Number String"); + map.put(SERIAL_NUMBER_STRING , "Serial Number String"); + map.put(FIRMWARE_REVISION_STRING , "Firmware Revision String"); + map.put(HARDWARE_REVISION_STRING , "Hardware Revision String"); + map.put(SOFTWARE_REVISION_STRING , "Software Revision String"); + map.put(MANUFACTURER_NAME_STRING , "Manufacturer Name String"); + map.put(IEEE_1107320601_REGULATORY , "IEEE 11073-20601 Regulatory"); + map.put(CURRENT_TIME , "Current Time"); + map.put(BLOOD_PRESSURE_MEASUREMENT , "Blood Pressure Measurement"); + map.put(INTERMEDIATE_CUFF_PRESSURE , "Intermediate Cuff Pressure"); + map.put(HEART_RATE_MEASUREMENT , "Heart Rate Measurement"); + map.put(BODY_SENSOR_LOCATION , "Body Sensor Location"); + map.put(HEART_RATE_CONTROL_POINT , "Heart Rate Control Point"); + map.put(ALERT_STATUS , "Alert Status"); + map.put(RINGER_CONTROL_POINT , "Ringer Control Point"); + map.put(RINGER_SETTING , "Ringer Setting"); + map.put(ALERT_CATEGORY_ID_BIT_MASK , "Alert Category ID Bit Mask"); + map.put(ALERT_CATEGORY_ID , "Alert Category ID"); + map.put(ALERT_NOTIFICATION_CONTROL_POINT , "Alert Notification Control Point"); + map.put(UNREAD_ALERT_STATUS , "Unread Alert Status"); + map.put(NEW_ALERT , "New Alert"); + map.put(SUPPORTED_NEW_ALERT_CATEGORY , "Supported New Alert Category"); + map.put(SUPPORTED_UNREAD_ALERT_CATEGORY , "Supported Unread Alert Category"); + map.put(BLOOD_PRESSURE_FEATURE , "Blood Pressure Feature"); + map.put(PNPID , "PNPID"); + map.put(SC_CONTROL_POINT , "SC_CONTROL_POINT"); + map.put(CSC_MEASUREMENT , "CSC_MEASUREMENT"); + map.put(CSC_FEATURE , "CSC_FEATURE"); + map.put(SENSOR_LOCATION , "SENSOR_LOCATION"); + map.put(ACTIVESYNC , "ActiveSync"); + map.put(ESTIMOTE_SERVICE , "Estimote Service"); + map.put(ESTIMOTE_UUID , "Estimote UUID"); + map.put(ESTIMOTE_MAJOR , "Estimote Major"); + map.put(ESTIMOTE_MINOR , "Estimote Minor"); + map.put(ESTIMOTE_BATTERY , "Estimote Battery"); + map.put(ESTIMOTE_TEMPERATURE , "Estimote Temperature"); + map.put(ESTIMOTE_POWER , "Estimote Power"); + map.put(ESTIMOTE_ADVERTISING_INTERVAL , "Estimote Advertising Interval"); + map.put(ESTIMOTE_VERSION_SERVICE , "Estimote Version Service"); + map.put(ESTIMOTE_SOFTWARE_VERSION , "Estimote Software Version"); + map.put(ESTIMOTE_HARDWARE_VERSION , "Estimote Hardware Version"); + map.put(ESTIMOTE_AUTHENTICATION_SERVICE , "Estimote Authentication Service"); + map.put(ESTIMOTE_ADVERTISING_SEED , "Estimote Advertising Seed"); + map.put(ESTIMOTE_ADVERTISING_VECTOR , "Estimote Advertising Vector"); + + return map; + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/util/AdRecordUtils.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/util/AdRecordUtils.java new file mode 100644 index 0000000..c872f82 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/util/AdRecordUtils.java @@ -0,0 +1,131 @@ +package uk.co.alt236.bluetoothlelib.util; + +import android.annotation.SuppressLint; +import android.util.SparseArray; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; + +public final class AdRecordUtils { + + private AdRecordUtils(){ + // TO AVOID INSTANTIATION + } + + public static String getRecordDataAsString(final AdRecord nameRecord) { + if (nameRecord == null) { + return ""; + } + return new String(nameRecord.getData()); + } + + public static byte[] getServiceData(final AdRecord serviceData) { + if (serviceData == null) { + return null; + } + if (serviceData.getType() != AdRecord.TYPE_SERVICE_DATA) return null; + + final byte[] raw = serviceData.getData(); + //Chop out the uuid + return Arrays.copyOfRange(raw, 2, raw.length); + } + + public static int getServiceDataUuid(final AdRecord serviceData) { + if (serviceData == null) { + return -1; + } + if (serviceData.getType() != AdRecord.TYPE_SERVICE_DATA) return -1; + + final byte[] raw = serviceData.getData(); + //Find UUID data in byte array + int uuid = (raw[1] & 0xFF) << 8; + uuid += (raw[0] & 0xFF); + + return uuid; + } + + /* + * Read out all the AD structures from the raw scan record + */ + public static List parseScanRecordAsList(final byte[] scanRecord) { + final List records = new ArrayList<>(); + + int index = 0; + while (index < scanRecord.length) { + final int length = scanRecord[index++]; + //Done once we run out of records + if (length == 0) break; + + final int type = ByteUtils.getIntFromByte(scanRecord[index]); + + //Done if our record isn't a valid type + if (type == 0) break; + + final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length); + + records.add(new AdRecord(length, type, data)); + + //Advance + index += length; + } + + return Collections.unmodifiableList(records); + } + + @SuppressLint("UseSparseArrays") + public static Map parseScanRecordAsMap(final byte[] scanRecord) { + final Map records = new HashMap<>(); + + int index = 0; + while (index < scanRecord.length) { + final int length = scanRecord[index++]; + //Done once we run out of records + if (length == 0) break; + + final int type = ByteUtils.getIntFromByte(scanRecord[index]); + + //Done if our record isn't a valid type + if (type == 0) break; + + final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length); + + records.put(type, new AdRecord(length, type, data)); + + //Advance + index += length; + } + + return Collections.unmodifiableMap(records); + } + + public static SparseArray parseScanRecordAsSparseArray(final byte[] scanRecord) { + final SparseArray records = new SparseArray<>(); + + int index = 0; + while (index < scanRecord.length) { + final int length = scanRecord[index++]; + //Done once we run out of records + if (length == 0) break; + + final int type = ByteUtils.getIntFromByte(scanRecord[index]); + + //Done if our record isn't a valid type + if (type == 0) break; + + final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length); + + records.put(type, new AdRecord(length, type, data)); + + //Advance + index += length; + } + + return records; + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/util/ByteUtils.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/util/ByteUtils.java new file mode 100644 index 0000000..c13dbf9 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/util/ByteUtils.java @@ -0,0 +1,128 @@ +package uk.co.alt236.bluetoothlelib.util; + +import java.nio.ByteBuffer; + +public class ByteUtils { + + /** + * The Constant HEXES. + */ + private static final String HEXES = "0123456789ABCDEF"; + + private ByteUtils(){ + // TO AVOID INSTANTIATION + } + + /** + * Gets a pretty representation of a Byte Array as a HEX String. + *

+ * Sample output: [01, 30, FF, AA] + * + * @param array the array + * @return the string + */ + public static String byteArrayToHexString(final byte[] array) { + final StringBuilder sb = new StringBuilder(); + boolean firstEntry = true; + sb.append('['); + + for (final byte b : array) { + if (!firstEntry) { + sb.append(", "); + } + sb.append(HEXES.charAt((b & 0xF0) >> 4)); + sb.append(HEXES.charAt((b & 0x0F))); + firstEntry = false; + } + + sb.append(']'); + return sb.toString(); + } + + /** + * Checks to see if a byte array starts with another byte array. + * + * @param array the array + * @param prefix the prefix + * @return true, if successful + */ + public static boolean doesArrayBeginWith(final byte[] array, final byte[] prefix) { + if (array.length < prefix.length) { + return false; + } + + for (int i = 0; i < prefix.length; i++) { + if (array[i] != prefix[i]) { + return false; + } + } + + return true; + } + + /** + * Converts a byte array with a length of 2 into an int + * + * @param input the input + * @return the int from the array + */ + public static int getIntFrom2ByteArray(final byte[] input) { + final byte[] result = new byte[4]; + + result[0] = 0; + result[1] = 0; + result[2] = input[0]; + result[3] = input[1]; + + return ByteUtils.getIntFromByteArray(result); + } + + /** + * Converts a byte to an int, preserving the sign. + *

+ * For example, FF will be converted to 255 and not -1. + * + * @param bite the byte + * @return the int from byte + */ + public static int getIntFromByte(final byte bite) { + return bite & 0xFF; + } + + /** + * Converts a byte array to an int. + * + * @param bytes the bytes + * @return the int from byte array + */ + public static int getIntFromByteArray(final byte[] bytes) { + return ByteBuffer.wrap(bytes).getInt(); + } + + /** + * Converts a byte array to a long. + * + * @param bytes the bytes + * @return the long from byte array + */ + public static long getLongFromByteArray(final byte[] bytes) { + return ByteBuffer.wrap(bytes).getLong(); + } + + + /** + * Inverts an byte array in place. + * + * @param array the array + */ + public static void invertArray(final byte[] array) { + final int size = array.length; + byte temp; + + for (int i = 0; i < size / 2; i++) { + temp = array[i]; + array[i] = array[size - 1 - i]; + array[size - 1 - i] = temp; + } + } +} diff --git a/library/src/main/java/uk/co/alt236/bluetoothlelib/util/LimitedLinkHashMap.java b/library/src/main/java/uk/co/alt236/bluetoothlelib/util/LimitedLinkHashMap.java new file mode 100644 index 0000000..f7c4136 --- /dev/null +++ b/library/src/main/java/uk/co/alt236/bluetoothlelib/util/LimitedLinkHashMap.java @@ -0,0 +1,20 @@ +package uk.co.alt236.bluetoothlelib.util; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class LimitedLinkHashMap extends LinkedHashMap { + private static final long serialVersionUID = -5375660288461724925L; + + private final int mMaxSize; + + public LimitedLinkHashMap(final int maxSize) { + super(maxSize + 1, 1, false); + mMaxSize = maxSize; + } + + @Override + protected boolean removeEldestEntry(final Map.Entry eldest) { + return this.size() > mMaxSize; + } +} diff --git a/library/src/test/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconUtilsTest.java b/library/src/test/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconUtilsTest.java new file mode 100644 index 0000000..1de810e --- /dev/null +++ b/library/src/test/java/uk/co/alt236/bluetoothlelib/device/beacon/BeaconUtilsTest.java @@ -0,0 +1,25 @@ +package uk.co.alt236.bluetoothlelib.device.beacon; + +import junit.framework.TestCase; + +/** + * + */ +public class BeaconUtilsTest extends TestCase { + + public void testGetBeaconTypeIBeacon() throws Exception { + assertEquals(BeaconType.IBEACON, BeaconUtils.getBeaconType(new byte[]{ + 0x4C, 0x00, 0x02, 0x15, 0x00, // <- Magic iBeacon header + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 + })); + } + + public void testGetBeaconTypeInvalid() throws Exception { + assertEquals(BeaconType.NOT_A_BEACON, BeaconUtils.getBeaconType((byte[]) null)); + assertEquals(BeaconType.NOT_A_BEACON, BeaconUtils.getBeaconType(new byte[0])); + assertEquals(BeaconType.NOT_A_BEACON, BeaconUtils.getBeaconType(new byte[25])); + } +} \ No newline at end of file diff --git a/library/src/test/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconManufacturerDataTest.java b/library/src/test/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconManufacturerDataTest.java new file mode 100644 index 0000000..cef150b --- /dev/null +++ b/library/src/test/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconManufacturerDataTest.java @@ -0,0 +1,39 @@ +package uk.co.alt236.bluetoothlelib.device.beacon.ibeacon; + +import junit.framework.TestCase; + +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconManufacturerData; + +/** + * + */ +public class IBeaconManufacturerDataTest extends TestCase { + private static final byte[] NON_BEACON = + {2, 1, 26, 11, -1, 76, 0, 9, 6, 3, -32, -64, -88, + 1, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + public void testNonIBeaconData() throws Exception{ + try { + BeaconManufacturerData data = new IBeaconManufacturerData(NON_BEACON); + fail("Should have thrown an exception"); + } catch (final IllegalArgumentException e){ + // EXPECTED + } + + try { + BeaconManufacturerData data = new IBeaconManufacturerData((byte[]) null); + fail("Should have thrown an exception"); + } catch (final IllegalArgumentException e){ + // EXPECTED + } + + try { + BeaconManufacturerData data = new IBeaconManufacturerData(new byte[0]); + fail("Should have thrown an exception"); + } catch (final IllegalArgumentException e){ + // EXPECTED + } + } +} \ No newline at end of file diff --git a/library/src/test/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconUtilsTest.java b/library/src/test/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconUtilsTest.java new file mode 100644 index 0000000..7399ed0 --- /dev/null +++ b/library/src/test/java/uk/co/alt236/bluetoothlelib/device/beacon/ibeacon/IBeaconUtilsTest.java @@ -0,0 +1,32 @@ +package uk.co.alt236.bluetoothlelib.device.beacon.ibeacon; + +import junit.framework.TestCase; + +/** + * + */ +public class IBeaconUtilsTest extends TestCase { + + public void testCalculateUuidString() throws Exception { + assertEquals("00", IBeaconUtils.calculateUuidString(new byte[]{0})); + assertEquals("0a", IBeaconUtils.calculateUuidString(new byte[]{10})); + assertEquals("0f", IBeaconUtils.calculateUuidString(new byte[]{15})); + assertEquals("10", IBeaconUtils.calculateUuidString(new byte[]{16})); + assertEquals("7f", IBeaconUtils.calculateUuidString(new byte[]{127})); + assertEquals( + "00000000-0000-0000-0000-00", + IBeaconUtils.calculateUuidString(new byte[]{0,0,0,0,0,0,0,0,0,0,0})); + } + + public void testGetDistanceDescriptor() throws Exception { + assertEquals(IBeaconDistanceDescriptor.UNKNOWN, IBeaconUtils.getDistanceDescriptor(-1)); + + assertEquals(IBeaconDistanceDescriptor.IMMEDIATE, IBeaconUtils.getDistanceDescriptor(0)); + assertEquals(IBeaconDistanceDescriptor.IMMEDIATE, IBeaconUtils.getDistanceDescriptor(0.4)); + + assertEquals(IBeaconDistanceDescriptor.NEAR, IBeaconUtils.getDistanceDescriptor(0.5)); + assertEquals(IBeaconDistanceDescriptor.NEAR, IBeaconUtils.getDistanceDescriptor(2.9)); + + assertEquals(IBeaconDistanceDescriptor.FAR, IBeaconUtils.getDistanceDescriptor(3)); + } +} \ No newline at end of file diff --git a/library/src/test/java/uk/co/alt236/bluetoothlelib/resolvers/GattAttributeResolverTest.java b/library/src/test/java/uk/co/alt236/bluetoothlelib/resolvers/GattAttributeResolverTest.java new file mode 100644 index 0000000..6b482c9 --- /dev/null +++ b/library/src/test/java/uk/co/alt236/bluetoothlelib/resolvers/GattAttributeResolverTest.java @@ -0,0 +1,20 @@ +package uk.co.alt236.bluetoothlelib.resolvers; + +import junit.framework.TestCase; + +/** + * + */ +public class GattAttributeResolverTest extends TestCase { + private static final String UKNOWN = "unknown"; + + public void testGetAttributeName() throws Exception { + assertEquals(UKNOWN, GattAttributeResolver.getAttributeName("foo", UKNOWN)); + assertEquals("Estimote Advertising Vector", GattAttributeResolver.getAttributeName("b9402002-f5f8-466e-aff9-25556b57fe6d", UKNOWN)); + assertEquals("LINK_LOSS", GattAttributeResolver.getAttributeName("00001803-0000-1000-8000-00805f9b34fb", UKNOWN)); + assertEquals("Base GUID", GattAttributeResolver.getAttributeName("00000000-0000-1000-8000-00805f9b34fb", UKNOWN)); + assertEquals("PNPID", GattAttributeResolver.getAttributeName("00002a50-0000-1000-8000-00805f9b34fb", UKNOWN)); + assertEquals("HTTP", GattAttributeResolver.getAttributeName("0000000c-0000-1000-8000-00805f9b34fb", UKNOWN)); + + } +} \ No newline at end of file diff --git a/library/src/test/java/uk/co/alt236/bluetoothlelib/util/AdRecordUtilsTest.java b/library/src/test/java/uk/co/alt236/bluetoothlelib/util/AdRecordUtilsTest.java new file mode 100644 index 0000000..d528dd8 --- /dev/null +++ b/library/src/test/java/uk/co/alt236/bluetoothlelib/util/AdRecordUtilsTest.java @@ -0,0 +1,58 @@ +package uk.co.alt236.bluetoothlelib.util; + +import junit.framework.TestCase; + +import java.util.List; +import java.util.Map; + +import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; + +/** + * + */ +public class AdRecordUtilsTest extends TestCase { + private static final byte[] NON_IBEACON = + {2, 1, 26, 11, -1, 76, 0, 9, 6, 3, -32, -64, -88, + 1, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + public void testParseScanRecordAsList() throws Exception { + final List adRecords = AdRecordUtils.parseScanRecordAsList(NON_IBEACON); + assertNotNull(adRecords); + assertEquals(2, adRecords.size()); + + int type = AdRecord.TYPE_FLAGS; + assertEquals(type, adRecords.get(0).getType()); + assertEquals(2, adRecords.get(0).getLength()); + + type = AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA; + assertEquals(type, adRecords.get(1).getType()); + assertEquals(11, adRecords.get(1).getLength()); + } + + public void testParseScanRecordAsMap() throws Exception { + final Map adRecords = AdRecordUtils.parseScanRecordAsMap(NON_IBEACON); + assertNotNull(adRecords); + assertEquals(2, adRecords.size()); + + int type = AdRecord.TYPE_FLAGS; + assertEquals(type, adRecords.get(type).getType()); + assertEquals(2, adRecords.get(type).getLength()); + + type = AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA; + assertEquals(type, adRecords.get(type).getType()); + assertEquals(11, adRecords.get(type).getLength()); + } + + public void testParseScanRecordAsSparseArray() throws Exception { + // + // Cannot be tested here as it relies on Android code... + // + // final SparseArray adRecords = AdRecordUtils.parseScanRecordAsSparseArray(NON_IBEACON); + // assertNotNull(adRecords); + // assertEquals(2, adRecords.size()); + // assertEquals(AdRecord.TYPE_FLAGS, adRecords.get(AdRecord.TYPE_FLAGS).getType()); + // assertEquals(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA, adRecords.get(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getType()); + } +} \ No newline at end of file diff --git a/library/src/test/java/uk/co/alt236/bluetoothlelib/util/ByteUtilsTest.java b/library/src/test/java/uk/co/alt236/bluetoothlelib/util/ByteUtilsTest.java new file mode 100644 index 0000000..e0705e9 --- /dev/null +++ b/library/src/test/java/uk/co/alt236/bluetoothlelib/util/ByteUtilsTest.java @@ -0,0 +1,60 @@ +package uk.co.alt236.bluetoothlelib.util; + +import junit.framework.TestCase; + +/** + * + */ +public class ByteUtilsTest extends TestCase { + + public void testByteArrayToHexString() throws Exception { + assertEquals("[]", ByteUtils.byteArrayToHexString(new byte[0])); + + final byte[] one = {1, 10, 15, 127}; + assertEquals("[01, 0A, 0F, 7F]", ByteUtils.byteArrayToHexString(one)); + } + + public void testDoesArrayBeginWith() throws Exception { + + // If the prefix is longer than the array, + // we automatically fail + byte[] array = new byte[10]; + byte[] prefix = new byte[array.length * 2]; + assertFalse(ByteUtils.doesArrayBeginWith(array, prefix)); + + array = new byte[]{1, 2, 3}; + prefix = new byte[]{1, 3}; + assertFalse(ByteUtils.doesArrayBeginWith(array, prefix)); + + array = new byte[10]; + prefix = new byte[array.length]; + assertTrue(ByteUtils.doesArrayBeginWith(array, prefix)); + + array = new byte[]{1, 2, 3}; + prefix = new byte[]{1, 2}; + assertTrue(ByteUtils.doesArrayBeginWith(array, prefix)); + } + + public void testGetIntFromByte() throws Exception { + byte bite = 127; + int integer = ByteUtils.getIntFromByte(bite); + assertEquals(127, integer); + + bite = -1; + integer = ByteUtils.getIntFromByte(bite); + assertEquals(255, integer); + } + + public void testInvertArray() throws Exception { + final byte[] original = {1, 2 ,3 ,4}; + final byte[] out = new byte[original.length]; + + System.arraycopy( original, 0, out, 0, original.length); + ByteUtils.invertArray(out); + + assertEquals(original[0], out[3]); + assertEquals(original[1], out[2]); + assertEquals(original[2], out[1]); + assertEquals(original[3], out[0]); + } +} \ No newline at end of file diff --git a/library/src/uk/co/alt236/bluetoothlelib/device/BluetoothLeDevice.java b/library/src/uk/co/alt236/bluetoothlelib/device/BluetoothLeDevice.java deleted file mode 100644 index e8ed154..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/device/BluetoothLeDevice.java +++ /dev/null @@ -1,395 +0,0 @@ -package uk.co.alt236.bluetoothlelib.device; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.Iterator; -import java.util.Map; - -import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecordStore; -import uk.co.alt236.bluetoothlelib.resolvers.BluetoothClassResolver; -import uk.co.alt236.bluetoothlelib.util.AdRecordUtils; -import uk.co.alt236.bluetoothlelib.util.ByteUtils; -import uk.co.alt236.bluetoothlelib.util.LimitedLinkHashMap; -import android.bluetooth.BluetoothDevice; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; - -// TODO: Auto-generated Javadoc -/** - * This is a wrapper around the default BluetoothDevice object - * As BluetoothDevice is final it cannot be extended, so to get it you - * need to call {@link #getDevice()} method. - * - * @author Alexandros Schillings - */ -public class BluetoothLeDevice implements Parcelable{ - private static final String PARCEL_EXTRA_BLUETOOTH_DEVICE = "bluetooth_device"; - private static final String PARCEL_EXTRA_CURRENT_RSSI = "current_rssi"; - private static final String PARCEL_EXTRA_CURRENT_TIMESTAMP = "current_timestamp"; - private static final String PARCEL_EXTRA_DEVICE_RSSI_LOG = "device_rssi_log"; - private static final String PARCEL_EXTRA_DEVICE_SCANRECORD = "device_scanrecord"; - private static final String PARCEL_EXTRA_DEVICE_SCANRECORD_STORE = "device_scanrecord_store"; - private static final String PARCEL_EXTRA_FIRST_RSSI = "device_first_rssi"; - private static final String PARCEL_EXTRA_FIRST_TIMESTAMP = "first_timestamp"; - private static final long LOG_INVALIDATION_THRESHOLD = 10 * 1000; - protected static final int MAX_RSSI_LOG_SIZE = 10; - - private final AdRecordStore mRecordStore; - private final BluetoothDevice mDevice; - private final Map mRssiLog; - private final byte[] mScanRecord; - private final int mFirstRssi; - private final long mFirstTimestamp; - - private int mCurrentRssi; - private long mCurrentTimestamp; - - /** The Constant CREATOR. */ - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public BluetoothLeDevice createFromParcel(Parcel in) { - return new BluetoothLeDevice(in); - } - - public BluetoothLeDevice[] newArray(int size) { - return new BluetoothLeDevice[size]; - } - }; - - /** - * Instantiates a new Bluetooth LE device. - * - * @param device a standard android Bluetooth device - * @param rssi the RSSI value of the Bluetooth device - * @param scanRecord the scan record of the device - * @param timestamp the timestamp of the RSSI reading - */ - public BluetoothLeDevice(BluetoothDevice device, int rssi, byte[] scanRecord, long timestamp){ - mDevice = device; - mFirstRssi = rssi; - mFirstTimestamp = timestamp; - mRecordStore = new AdRecordStore(AdRecordUtils.parseScanRecordAsSparseArray(scanRecord)); - mScanRecord = scanRecord; - mRssiLog = new LimitedLinkHashMap(MAX_RSSI_LOG_SIZE); - updateRssiReading(timestamp, rssi); - } - - /** - * Instantiates a new Bluetooth LE device. - * - * @param device the device - */ - public BluetoothLeDevice(BluetoothLeDevice device) { - mCurrentRssi = device.getRssi(); - mCurrentTimestamp = device.getTimestamp(); - mDevice = device.getDevice(); - mFirstRssi = device.getFirstRssi(); - mFirstTimestamp = device.getFirstTimestamp(); - mRecordStore = new AdRecordStore( - AdRecordUtils.parseScanRecordAsSparseArray(device.getScanRecord())); - mRssiLog = device.getRssiLog(); - mScanRecord = device.getScanRecord(); - } - - /** - * Instantiates a new bluetooth le device. - * - * @param in the in - */ - @SuppressWarnings("unchecked") - protected BluetoothLeDevice(Parcel in) { - final Bundle b = in.readBundle(getClass().getClassLoader()); - - mCurrentRssi = b.getInt(PARCEL_EXTRA_CURRENT_RSSI, 0); - mCurrentTimestamp = b.getLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, 0); - mDevice = b.getParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE); - mFirstRssi = b.getInt(PARCEL_EXTRA_FIRST_RSSI, 0); - mFirstTimestamp = b.getLong(PARCEL_EXTRA_FIRST_TIMESTAMP, 0); - mRecordStore = b.getParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE); - mRssiLog = (Map) b.getSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG); - mScanRecord = b.getByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD); - } - - /** - * Adds the to rssi log. - * - * @param timestamp the timestamp - * @param rssiReading the rssi reading - */ - private void addToRssiLog(long timestamp, int rssiReading){ - synchronized (mRssiLog) { - if(timestamp - mCurrentTimestamp > LOG_INVALIDATION_THRESHOLD){ - mRssiLog.clear(); - } - - mCurrentRssi = rssiReading; - mCurrentTimestamp = timestamp; - mRssiLog.put(timestamp, rssiReading); - } - } - - /* (non-Javadoc) - * @see android.os.Parcelable#describeContents() - */ - @Override - public int describeContents() { - return 0; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - BluetoothLeDevice other = (BluetoothLeDevice) obj; - if (mCurrentRssi != other.mCurrentRssi) - return false; - if (mCurrentTimestamp != other.mCurrentTimestamp) - return false; - if (mDevice == null) { - if (other.mDevice != null) - return false; - } else if (!mDevice.equals(other.mDevice)) - return false; - if (mFirstRssi != other.mFirstRssi) - return false; - if (mFirstTimestamp != other.mFirstTimestamp) - return false; - if (mRecordStore == null) { - if (other.mRecordStore != null) - return false; - } else if (!mRecordStore.equals(other.mRecordStore)) - return false; - if (mRssiLog == null) { - if (other.mRssiLog != null) - return false; - } else if (!mRssiLog.equals(other.mRssiLog)) - return false; - if (!Arrays.equals(mScanRecord, other.mScanRecord)) - return false; - return true; - } - - /** - * Gets the address. - * - * @return the address - */ - public String getAddress(){ - return mDevice.getAddress(); - } - - /** - * Gets the ad record store. - * - * @return the ad record store - */ - public AdRecordStore getAdRecordStore(){ - return mRecordStore; - } - - /** - * Gets the bluetooth device bond state. - * - * @return the bluetooth device bond state - */ - public String getBluetoothDeviceBondState(){ - return resolveBondingState(mDevice.getBondState()); - } - - /** - * Gets the bluetooth device class name. - * - * @return the bluetooth device class name - */ - public String getBluetoothDeviceClassName(){ - return BluetoothClassResolver.resolveDeviceClass(mDevice.getBluetoothClass().getDeviceClass()); - } - - /** - * Gets the device. - * - * @return the device - */ - public BluetoothDevice getDevice() { - return mDevice; - } - - /** - * Gets the first rssi. - * - * @return the first rssi - */ - public int getFirstRssi(){ - return mFirstRssi; - } - - /** - * Gets the first timestamp. - * - * @return the first timestamp - */ - public long getFirstTimestamp(){ - return mFirstTimestamp; - } - - /** - * Gets the name. - * - * @return the name - */ - public String getName(){ - return mDevice.getName(); - } - - /** - * Gets the rssi. - * - * @return the rssi - */ - public int getRssi() { - return mCurrentRssi; - } - - /** - * Gets the rssi log. - * - * @return the rssi log - */ - protected Map getRssiLog() { - synchronized (mRssiLog) { - return mRssiLog; - } - } - - /** - * Gets the running average rssi. - * - * @return the running average rssi - */ - public double getRunningAverageRssi(){ - int sum = 0; - int count = 0; - - synchronized (mRssiLog) { - final Iterator it1 = mRssiLog.keySet().iterator(); - - while(it1.hasNext()){ - count ++; - sum += mRssiLog.get(it1.next()); - } - } - // for(final Map.Entry e : mRssiLog.entrySet()){ - // count ++; - // sum += e.getValue(); - // } - - if(count > 0){ - return sum/count; - } else { - return 0; - } - - } - - /** - * Gets the scan record. - * - * @return the scan record - */ - public byte[] getScanRecord() { - return mScanRecord; - } - - /** - * Gets the timestamp. - * - * @return the timestamp - */ - public long getTimestamp(){ - return mCurrentTimestamp; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + mCurrentRssi; - result = prime * result + (int) (mCurrentTimestamp ^ (mCurrentTimestamp >>> 32)); - result = prime * result + ((mDevice == null) ? 0 : mDevice.hashCode()); - result = prime * result + mFirstRssi; - result = prime * result + (int) (mFirstTimestamp ^ (mFirstTimestamp >>> 32)); - result = prime * result + ((mRecordStore == null) ? 0 : mRecordStore.hashCode()); - result = prime * result + ((mRssiLog == null) ? 0 : mRssiLog.hashCode()); - result = prime * result + Arrays.hashCode(mScanRecord); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "BluetoothLeDevice [mDevice=" + mDevice + ", mRssi=" + mFirstRssi + ", mScanRecord=" + ByteUtils.byteArrayToHexString(mScanRecord) + ", mRecordStore=" + mRecordStore + ", getBluetoothDeviceBondState()=" + getBluetoothDeviceBondState() + ", getBluetoothDeviceClassName()=" + getBluetoothDeviceClassName() + "]"; - } - - /** - * Update rssi reading. - * - * @param timestamp the timestamp - * @param rssiReading the rssi reading - */ - public void updateRssiReading(long timestamp, int rssiReading){ - addToRssiLog(timestamp, rssiReading); - } - - /* (non-Javadoc) - * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) - */ - @Override - public void writeToParcel(Parcel parcel, int arg1) { - final Bundle b = new Bundle(getClass().getClassLoader()); - - b.putByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD, mScanRecord); - - b.putInt(PARCEL_EXTRA_FIRST_RSSI, mFirstRssi); - b.putInt(PARCEL_EXTRA_CURRENT_RSSI, mCurrentRssi); - - b.putLong(PARCEL_EXTRA_FIRST_TIMESTAMP, mFirstTimestamp); - b.putLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, mCurrentTimestamp); - - b.putParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE, mDevice); - b.putParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE, mRecordStore); - b.putSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG, (Serializable) mRssiLog); - - parcel.writeBundle(b); - } - - /** - * Resolve bonding state. - * - * @param bondState the bond state - * @return the string - */ - private static String resolveBondingState(int bondState){ - switch (bondState){ - case BluetoothDevice.BOND_BONDED: - return "Paired"; - case BluetoothDevice.BOND_BONDING: - return "Pairing"; - case BluetoothDevice.BOND_NONE: - return "Unbonded"; - default: - return "Unknown"; - } - } -} diff --git a/library/src/uk/co/alt236/bluetoothlelib/device/IBeaconDevice.java b/library/src/uk/co/alt236/bluetoothlelib/device/IBeaconDevice.java deleted file mode 100644 index 9a4b90f..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/device/IBeaconDevice.java +++ /dev/null @@ -1,143 +0,0 @@ -package uk.co.alt236.bluetoothlelib.device; - -import uk.co.alt236.bluetoothlelib.device.mfdata.IBeaconManufacturerData; -import uk.co.alt236.bluetoothlelib.util.IBeaconUtils; -import uk.co.alt236.bluetoothlelib.util.IBeaconUtils.IBeaconDistanceDescriptor; -import android.bluetooth.BluetoothDevice; -import android.os.Parcel; - -public class IBeaconDevice extends BluetoothLeDevice{ - - /** The m iBeacon data. */ - private final IBeaconManufacturerData mIBeaconData; - - /** - * Instantiates a new iBeacon device. - * - * @param device the device - * @param rssi the RSSI value - * @param scanRecord the scanRecord - * @throws IllegalArguementException if the passed device is not an iBecon - */ - public IBeaconDevice(BluetoothDevice device, int rssi, byte[] scanRecord) { - super(device, rssi, scanRecord, 0); - validate(); - mIBeaconData = new IBeaconManufacturerData(this); - } - - /** - * Instantiates a new iBeacon device. - * - * @param device the device - * @param rssi the RSSI value of the RSSI measurement - * @param scanRecord the scan record - * @param timestamp the timestamp of the RSSI measurement - * @throws IllegalArguementException if the passed device is not an iBecon - */ - public IBeaconDevice(BluetoothDevice device, int rssi, byte[] scanRecord, long timestamp){ - super(device, rssi, scanRecord, timestamp); - validate(); - mIBeaconData = new IBeaconManufacturerData(this); - } - - /** - * Will try to convert a {@link BluetoothLeDevice} into an - * iBeacon Device. - * - * @param device the device - * @throws IllegalArguementException if the passed device is not an iBecon - */ - public IBeaconDevice(BluetoothLeDevice device){ - super(device); - validate(); - mIBeaconData = new IBeaconManufacturerData(this); - } - - private IBeaconDevice(Parcel in) { - super(in); - validate(); - mIBeaconData = new IBeaconManufacturerData(this); - } - - /** - * Gets the estimated Accuracy of the reading in meters based on - * a simple running average of the last {@link #MAX_RSSI_LOG_SIZE} - * samples. - * - * @return the accuracy in meters - */ - public double getAccuracy(){ - return IBeaconUtils.calculateAccuracy( - getCalibratedTxPower(), - getRunningAverageRssi()); - } - - /** - * Gets the calibrated TX power of the iBeacon device as reported. - * - * @return the calibrated TX power - */ - public int getCalibratedTxPower(){ - return getIBeaconData().getCalibratedTxPower(); - } - - /** - * Gets the iBeacon company identifier. - * - * @return the company identifier - */ - public int getCompanyIdentifier(){ - return getIBeaconData().getCompanyIdentifier(); - } - - /** - * Gets the estimated Distance descriptor. - * - * @return the distance descriptor - */ - public IBeaconDistanceDescriptor getDistanceDescriptor(){ - return IBeaconUtils.getDistanceDescriptor(getAccuracy()); - } - - /** - * Gets the iBeacon manufacturing data. - * - * @return the iBeacon data - */ - public IBeaconManufacturerData getIBeaconData(){ - return mIBeaconData; - } - - /** - * Gets the iBeacon Major value. - * - * @return the Major value - */ - public int getMajor(){ - return getIBeaconData().getMajor(); - } - - /** - * Gets the iBeacon Minor value. - * - * @return the Minor value - */ - public int getMinor(){ - return getIBeaconData().getMinor(); - } - - /** - * Gets the iBeacon UUID. - * - * @return the UUID - */ - public String getUUID(){ - return getIBeaconData().getUUID(); - } - - private void validate(){ - if(!IBeaconUtils.isThisAnIBeacon(this)){ - throw new IllegalArgumentException("Device " + getDevice() + " is not an iBeacon."); - } - } -} diff --git a/library/src/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecord.java b/library/src/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecord.java deleted file mode 100644 index ac8cc0c..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecord.java +++ /dev/null @@ -1,244 +0,0 @@ -package uk.co.alt236.bluetoothlelib.device.adrecord; -import java.util.Arrays; - -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Created by Dave Smith - * Double Encore, Inc. - * - * Expanded by Alexandros Schillings - */ -public final class AdRecord implements Parcelable{ - - // 02 # Number of bytes that follow in first AD structure - // 01 # Flags AD type - // 1A # Flags value 0x1A = 000011010 - // bit 0 (OFF) LE Limited Discoverable Mode - // bit 1 (ON) LE General Discoverable Mode - // bit 2 (OFF) BR/EDR Not Supported - // bit 3 (ON) Simultaneous LE and BR/EDR to Same Device Capable (controller) - // bit 4 (ON) Simultaneous LE and BR/EDR to Same Device Capable (Host) - // 1A # Number of bytes that follow in second (and last) AD structure - // FF # Manufacturer specific data AD type - // 4C 00 # Company identifier code (0x004C == Apple) - // 02 # Byte 0 of iBeacon advertisement indicator - // 15 # Byte 1 of iBeacon advertisement indicator - // e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 # iBeacon proximity uuid - // 00 00 # major - // 00 00 # minor - // c5 # The 2's complement of the calibrated Tx Power - - - private static final String PARCEL_RECORD_DATA = "record_data"; - private static final String PARCEL_RECORD_TYPE = "record_type"; - private static final String PARCEL_RECORD_LENGTH = "record_length"; - - /** - * General FLAGS - * - * Description: Flags - * - * Information: - * Bit 0: LE Limited Discoverable Mode - * Bit 1: LE General Discoverable Mode - * Bit 2: BR/EDR Not Supported (i.e. bit 37 of LMP Extended Feature bits Page 0) - * Bit 3: Simultaneous LE and BR/EDR to Same Device Capable (Controller) (i.e. bit 49 of LMP Extended Feature bits Page 0) - * Bit 4: Simultaneous LE and BR/EDR to Same Device Capable (Host) (i.e. bit 66 of LMP Extended Feature bits Page 1) - * Bits 5-7 Reserved - */ - public static final int TYPE_FLAGS = 0x01; - - // SERVICE - public static final int TYPE_UUID16_INC = 0x02; - public static final int TYPE_UUID16 = 0x03; - public static final int TYPE_UUID32_INC = 0x04; - public static final int TYPE_UUID32 = 0x05; - public static final int TYPE_UUID128_INC = 0x06; - public static final int TYPE_UUID128 = 0x07; - - // Local name - public static final int TYPE_LOCAL_NAME_SHORT = 0x08; - public static final int TYPE_LOCAL_NAME_COMPLETE = 0x09; - - // TX Power Level - public static final int TYPE_TX_POWER_LEVEL = 0x0A; - - // SIMPLE PAIRING OPTIONAL OOB TAGS - public static final int TYPE_DEVICE_CLASS = 0x0D; - public static final int TYPE_SIMPLE_PAIRING_HASH_C = 0x0E; - public static final int TYPE_SIMPLE_PAIRING_RANDOMIZER_R = 0x0F; - - // SECURITY MANAGER TK VALUE - public static final int TYPE_TK_VALUE = 0x10; - - - /* SECURITY MANAGER OOB FLAGS - * - * Description: Flag (1 octet) - * - * Information: - * Bit 0: OOB Flags Field: (0 = OOB data not present, 1 = OOB data present) - * Bit 1: LE supported (Host) (i.e. bit 65 of LMP Extended Feature bits Page 1 - * Bit 2: Simultaneous LE and BR/EDR to Same Device Capable (Host) (i.e. bit 66 of LMP Extended Feature bits Page 1) - * Bit 3: Address type (0 = Public Address, 1 = Random Address) - * Bits 4-7 Reserved - */ - public static final int TYPE_SECURITY_MANAGER_OOB_FLAGS = 0x11; - - - /* SLAVE CONNECTION INTERVAL RANGE - * - * Description: Slave Connection Interval Range - * - * Information: - * The first 2 octets defines the minimum value for the connection interval in the following manner: - * connInterval min = Conn_Interval_Min * 1.25 ms - * Conn_Interval_Min range: 0x0006 to 0x0C80 - * Value of 0xFFFF indicates no specific minimum. - * Values outside the range are reserved. (excluding 0xFFFF) - * - * The second 2 octets defines the maximum value for the connection interval in the following manner: - * connInterval max = Conn_Interval_Max * 1.25 ms - * Conn_Interval_Max range: 0x0006 to 0x0C80 - * Conn_Interval_Max shall be equal to or greater - * than the Conn_Interval_Min. - * Value of 0xFFFF indicates no specific maximum. - * Values outside the range are reserved (excluding 0xFFFF) - */ - public static final int TYPE_CONNECTION_INTERVAL_RANGE = 0x12; - - // SERVICE SOLICITATION - public static final int TYPE_SERVICE_UUIDS_LIST_16BIT = 0x14; - public static final int TYPE_SERVICE_UUIDS_LIST_128BIT = 0x15; - - /* SERVICE DATA - * - * Description: Service Data (2 or more octets) - * Information: The first 2 octets contain the 16 bit Service UUID followed by additional service data - */ - public static final int TYPE_SERVICE_DATA = 0x16; - - - /* MANUFACTURER SPECIFIC DATA - * - * Description: Manufacturer Specific Data (2 or more octets) - * Information: The first 2 octets contain the Company Identifier Code followed by additional manufacturer specific data - */ - public static final int TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; - - /* Model Object Definition */ - private final int mLength; - private final int mType; - private final byte[] mData; - - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public AdRecord createFromParcel(Parcel in) { - return new AdRecord(in); - } - - public AdRecord[] newArray(int size) { - return new AdRecord[size]; - } - }; - - public AdRecord(int length, int type, byte[] data) { - mLength = length; - mType = type; - mData = data; - } - - public AdRecord(Parcel in) { - final Bundle b = in.readBundle(getClass().getClassLoader()); - mLength = b.getInt(PARCEL_RECORD_LENGTH); - mType = b.getInt(PARCEL_RECORD_TYPE); - mData = b.getByteArray(PARCEL_RECORD_DATA); - } - - @Override - public int describeContents() { - return 0; - } - - public byte[] getData(){ - return mData; - } - - public String getHumanReadableType(){ - return getHumanReadableAdType(mType); - } - - public int getLength() { - return mLength; - } - - public int getType() { - return mType; - } - - @Override - public String toString() { - return "AdRecord [mLength=" + mLength + ", mType=" + mType + ", mData=" + Arrays.toString(mData) + ", getHumanReadableType()=" + getHumanReadableType() + "]"; - } - - @Override - public void writeToParcel(Parcel parcel, int arg1) { - final Bundle b = new Bundle(getClass().getClassLoader()); - - b.putInt(PARCEL_RECORD_LENGTH, mLength); - b.putInt(PARCEL_RECORD_TYPE, mType); - b.putByteArray(PARCEL_RECORD_DATA, mData); - - parcel.writeBundle(b); - } - - private static String getHumanReadableAdType(int type){ - switch(type){ - case TYPE_CONNECTION_INTERVAL_RANGE: - return "Slave Connection Interval Range"; - case TYPE_DEVICE_CLASS: - return "Class of device"; - case TYPE_FLAGS: - return "Flags"; - case TYPE_MANUFACTURER_SPECIFIC_DATA: - return "Manufacturer Specific Data"; - case TYPE_LOCAL_NAME_COMPLETE: - return "Name (Complete)"; - case TYPE_LOCAL_NAME_SHORT: - return "Name (Short)"; - case TYPE_SECURITY_MANAGER_OOB_FLAGS: - return "Security Manager OOB Flags"; - case TYPE_SERVICE_UUIDS_LIST_128BIT: - return "Service UUIDs (128bit)"; - case TYPE_SERVICE_UUIDS_LIST_16BIT: - return "Service UUIDs (16bit)"; - case TYPE_SERVICE_DATA: - return "Service Data"; - case TYPE_SIMPLE_PAIRING_HASH_C: - return "Simple Pairing Hash C"; - case TYPE_SIMPLE_PAIRING_RANDOMIZER_R: - return "Simple Pairing Randomizer R"; - case TYPE_TK_VALUE: - return "TK Value"; - case TYPE_TX_POWER_LEVEL: - return "Transmission Power Level"; - case TYPE_UUID128: - return "Complete list of 128-bit UUIDs available"; - case TYPE_UUID128_INC: - return "More 128-bit UUIDs available"; - case TYPE_UUID16: - return "Complete list of 16-bit UUIDs available"; - case TYPE_UUID16_INC: - return "More 16-bit UUIDs available"; - case TYPE_UUID32: - return "Complete list of 32-bit UUIDs available"; - case TYPE_UUID32_INC: - return "More 32-bit UUIDs available"; - default: - return "Unknown AdRecord Structure: " + type; - } - } -} \ No newline at end of file diff --git a/library/src/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecordStore.java b/library/src/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecordStore.java deleted file mode 100644 index 21b5984..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/device/adrecord/AdRecordStore.java +++ /dev/null @@ -1,158 +0,0 @@ -package uk.co.alt236.bluetoothlelib.device.adrecord; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; - -import uk.co.alt236.bluetoothlelib.util.AdRecordUtils; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.SparseArray; - -/** - * The Class AdRecordStore. - */ -public class AdRecordStore implements Parcelable{ - private final SparseArray mAdRecords; - private final String mLocalNameComplete; - private final String mLocalNameShort; - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public AdRecordStore createFromParcel(Parcel in) { - return new AdRecordStore(in); - } - - public AdRecordStore[] newArray(int size) { - return new AdRecordStore[size]; - } - }; - - public AdRecordStore(Parcel in) { - final Bundle b = in.readBundle(getClass().getClassLoader()); - mAdRecords = b.getSparseParcelableArray("records_array"); - mLocalNameComplete = b.getString("local_name_complete"); - mLocalNameShort = b.getString("local_name_short"); - } - - /** - * Instantiates a new Bluetooth LE device Ad Record Store. - * - * @param adRecords the ad records - */ - public AdRecordStore(final SparseArray adRecords){ - mAdRecords = adRecords; - - mLocalNameComplete = AdRecordUtils.getRecordDataAsString( - mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_COMPLETE)); - - mLocalNameShort = AdRecordUtils.getRecordDataAsString( - mAdRecords.get(AdRecord.TYPE_LOCAL_NAME_SHORT)); - - } - - /* (non-Javadoc) - * @see android.os.Parcelable#describeContents() - */ - @Override - public int describeContents() { - return 0; - } - - /** - * Gets the short local device name. - * - * @return the local name complete - */ - public String getLocalNameComplete() { - return mLocalNameComplete; - } - - /** - * Gets the complete local device name. - * - * @return the local name short - */ - public String getLocalNameShort() { - return mLocalNameShort; - } - - /** - * retrieves an individual record. - * - * @param record the record - * @return the record - */ - public AdRecord getRecord(int record){ - return mAdRecords.get(record); - } - - /** - * Gets the record data as string. - * - * @param record the record - * @return the record data as string - */ - public String getRecordDataAsString(int record){ - return AdRecordUtils.getRecordDataAsString( - mAdRecords.get(record)); - } - - /** - * Gets the record as collection. - * - * @return the records as collection - */ - public Collection getRecordsAsCollection() { - return Collections.unmodifiableCollection(asList(mAdRecords)); - } - - /** - * Checks if is record present. - * - * @param record the record - * @return true, if is record present - */ - public boolean isRecordPresent(int record){ - return mAdRecords.indexOfKey(record) >= 0; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "AdRecordStore [mLocalNameComplete=" + mLocalNameComplete + ", mLocalNameShort=" + mLocalNameShort + "]"; - } - - /* (non-Javadoc) - * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) - */ - @Override - public void writeToParcel(Parcel parcel, int arg1) { - final Bundle b = new Bundle(); - b.putString("local_name_complete", mLocalNameComplete); - b.putString("local_name_short", mLocalNameShort); - b.putSparseParcelableArray("records_array", mAdRecords); - - parcel.writeBundle(b); - } - - /** - * As list. - * - * @param the generic type - * @param sparseArray the sparse array - * @return the collection - */ - public static Collection asList(SparseArray sparseArray) { - if (sparseArray == null) return null; - - final Collection arrayList = new ArrayList(sparseArray.size()); - for (int i = 0; i < sparseArray.size(); i++){ - arrayList.add(sparseArray.valueAt(i)); - } - - return arrayList; - } -} diff --git a/library/src/uk/co/alt236/bluetoothlelib/device/mfdata/IBeaconManufacturerData.java b/library/src/uk/co/alt236/bluetoothlelib/device/mfdata/IBeaconManufacturerData.java deleted file mode 100644 index 07888d9..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/device/mfdata/IBeaconManufacturerData.java +++ /dev/null @@ -1,144 +0,0 @@ -package uk.co.alt236.bluetoothlelib.device.mfdata; - -import java.util.Arrays; - -import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; -import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; -import uk.co.alt236.bluetoothlelib.util.ByteUtils; - -/** - * Parses the Manufactured Data field of an iBeacon - * - * The parsing is based on the following schema: - * - *

- * 0 4C - Byte 1 (LSB) of Company identifier code - * 1 00 - Byte 0 (MSB) of Company identifier code (0x004C == Apple) - * 2 02 - Byte 0 of iBeacon advertisement indicator - * 3 15 - Byte 1 of iBeacon advertisement indicator - * 4 e2 |\ - * 5 c5 |\\ - * 6 6d |#\\ - * 7 b5 |##\\ - * 8 df |###\\ - * 9 fb |####\\ - * 10 48 |#####\\ - * 11 d2 |#####|| iBeacon proximity UUID - * 12 b0 |#####|| - * 13 60 |#####// - * 14 d0 |####// - * 15 f5 |###// - * 16 a7 |##// - * 17 10 |#// - * 18 96 |// - * 19 e0 |/ - * 20 00 - major - * 21 00 - * 22 00 - minor - * 23 00 - * 24 c5 - The 2's complement of the calibrated Tx Power - * - *

- * - * @author Alexandros Schillings - * - */ - -public final class IBeaconManufacturerData { - private final byte[] mData; - private final int mCalibratedTxPower; - private final int mCompanyIdentidier; - private final int mIBeaconAdvertisment; - private final int mMajor; - private final int mMinor; - private final String mUUID; - - public IBeaconManufacturerData(BluetoothLeDevice device){ - this(device.getAdRecordStore().getRecord(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getData()); - } - - /** - * Instantiates a new iBeacon manufacturer data object. - - * @param data the {@link #uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA} data array - * @throws IndexOutOfBoundsException if the data array is shorter than expected - */ - public IBeaconManufacturerData(byte[] data){ - mData = data; - - mCompanyIdentidier = ByteUtils.getIntFrom2ByteArray( - ByteUtils.invertArray(Arrays.copyOfRange(mData, 0, 2))); - - mIBeaconAdvertisment = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 2, 4)); - mUUID = calculateUUIDString(Arrays.copyOfRange(mData, 4, 20)); - mMajor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 20, 22)); - mMinor = ByteUtils.getIntFrom2ByteArray(Arrays.copyOfRange(mData, 22, 24)); - mCalibratedTxPower = data[24]; - } - - /** - * Gets the calibrated TX power of the iBeacon device as reported. - * - * @return the calibrated TX power - */ - public int getCalibratedTxPower(){ - return mCalibratedTxPower; - } - - /** - * Gets the iBeacon company identifier. - * - * @return the company identifier - */ - public int getCompanyIdentifier(){ - return mCompanyIdentidier; - } - - public int getIBeaconAdvertisement(){ - return mIBeaconAdvertisment; - } - - /** - * Gets the iBeacon Major value. - * - * @return the Major value - */ - public int getMajor(){ - return mMajor; - } - - /** - * Gets the iBeacon Minor value. - * - * @return the Minor value - */ - public int getMinor(){ - return mMinor; - } - - /** - * Gets the iBeacon UUID. - * - * @return the UUID - */ - public String getUUID(){ - return mUUID; - } - - private static String calculateUUIDString(final byte[] uuid){ - final StringBuffer sb = new StringBuffer(); - - for(int i = 0 ; i< uuid.length; i++){ - if(i == 4){sb.append('-');} - if(i == 6){sb.append('-');} - if(i == 8){sb.append('-');} - if(i == 10){sb.append('-');} - - sb.append( - Integer.toHexString(ByteUtils.getIntFromByte(uuid[i]))); - } - - - return sb.toString(); - } -} diff --git a/library/src/uk/co/alt236/bluetoothlelib/resolvers/BluetoothClassResolver.java b/library/src/uk/co/alt236/bluetoothlelib/resolvers/BluetoothClassResolver.java deleted file mode 100644 index 46a408d..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/resolvers/BluetoothClassResolver.java +++ /dev/null @@ -1,113 +0,0 @@ -package uk.co.alt236.bluetoothlelib.resolvers; - -import android.bluetooth.BluetoothClass; - -public class BluetoothClassResolver { - - public static String resolveDeviceClass(int btClass){ - switch (btClass){ - case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER: - return "A/V, Camcorder"; - case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: - return "A/V, Car Audio"; - case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: - return "A/V, Handsfree"; - case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: - return "A/V, Headphones"; - case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: - return "A/V, HiFi Audio"; - case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: - return "A/V, Loudspeaker"; - case BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE: - return "A/V, Microphone"; - case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: - return "A/V, Portable Audio"; - case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX: - return "A/V, Set Top Box"; - case BluetoothClass.Device.AUDIO_VIDEO_UNCATEGORIZED: - return "A/V, Uncategorized"; - case BluetoothClass.Device.AUDIO_VIDEO_VCR: - return "A/V, VCR"; - case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA: - return "A/V, Video Camera"; - case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING: - return "A/V, Video Conferencing"; - case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER: - return "A/V, Video Display and Loudspeaker"; - case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_GAMING_TOY: - return "A/V, Video Gaming Toy"; - case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_MONITOR: - return "A/V, Video Monitor"; - case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: - return "A/V, Video Wearable Headset"; - case BluetoothClass.Device.COMPUTER_DESKTOP: - return "Computer, Desktop"; - case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA: - return "Computer, Handheld PC/PDA"; - case BluetoothClass.Device.COMPUTER_LAPTOP: - return "Computer, Laptop"; - case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA: - return "Computer, Palm Size PC/PDA"; - case BluetoothClass.Device.COMPUTER_SERVER: - return "Computer, Server"; - case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: - return "Computer, Uncategorized"; - case BluetoothClass.Device.COMPUTER_WEARABLE: - return "Computer, Wearable"; - case BluetoothClass.Device.HEALTH_BLOOD_PRESSURE: - return "Health, Blood Pressure"; - case BluetoothClass.Device.HEALTH_DATA_DISPLAY: - return "Health, Data Display"; - case BluetoothClass.Device.HEALTH_GLUCOSE: - return "Health, Glucose"; - case BluetoothClass.Device.HEALTH_PULSE_OXIMETER : - return "Health, Pulse Oximeter"; - case BluetoothClass.Device.HEALTH_PULSE_RATE : - return "Health, Pulse Rate"; - case BluetoothClass.Device.HEALTH_THERMOMETER : - return "Health, Thermometer"; - case BluetoothClass.Device.HEALTH_UNCATEGORIZED : - return "Health, Uncategorized"; - case BluetoothClass.Device.HEALTH_WEIGHING: - return "Health, Weighting"; - case BluetoothClass.Device.PHONE_CELLULAR: - return "Phone, Cellular"; - case BluetoothClass.Device.PHONE_CORDLESS: - return "Phone, Cordless"; - case BluetoothClass.Device.PHONE_ISDN: - return "Phone, ISDN"; - case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY: - return "Phone, Modem or Gateway"; - case BluetoothClass.Device.PHONE_SMART: - return "Phone, Smart"; - case BluetoothClass.Device.PHONE_UNCATEGORIZED: - return "Phone, Uncategorized"; - case BluetoothClass.Device.TOY_CONTROLLER: - return "Toy, Controller"; - case BluetoothClass.Device.TOY_DOLL_ACTION_FIGURE: - return "Toy, Doll/Action Figure"; - case BluetoothClass.Device.TOY_GAME: - return "Toy, Game"; - case BluetoothClass.Device.TOY_ROBOT: - return "Toy, Robot"; - case BluetoothClass.Device.TOY_UNCATEGORIZED: - return "Toy, Uncategorized"; - case BluetoothClass.Device.TOY_VEHICLE: - return "Toy, Vehicle"; - case BluetoothClass.Device.WEARABLE_GLASSES: - return "Wearable, Glasses"; - case BluetoothClass.Device.WEARABLE_HELMET: - return "Wearable, Helmet"; - case BluetoothClass.Device.WEARABLE_JACKET: - return "Wearable, Jacket"; - case BluetoothClass.Device.WEARABLE_PAGER: - return "Wearable, Pager"; - case BluetoothClass.Device.WEARABLE_UNCATEGORIZED: - return "Wearable, Uncategorized"; - case BluetoothClass.Device.WEARABLE_WRIST_WATCH: - return "Wearable, Wrist Watch"; - default: - return "Unknown, Unknown (class=" + btClass +")"; - } - } -} diff --git a/library/src/uk/co/alt236/bluetoothlelib/resolvers/CompanyIdentifierResolver.java b/library/src/uk/co/alt236/bluetoothlelib/resolvers/CompanyIdentifierResolver.java deleted file mode 100644 index 231cfad..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/resolvers/CompanyIdentifierResolver.java +++ /dev/null @@ -1,646 +0,0 @@ -package uk.co.alt236.bluetoothlelib.resolvers; - -import android.util.SparseArray; - -public class CompanyIdentifierResolver { - public static final int ERICSSON_TECHNOLOGY_LICENSING = 0x0000; - public static final int NOKIA_MOBILE_PHONES = 0x0001; - public static final int INTEL_CORP = 0x0002; - public static final int IBM_CORP = 0x0003; - public static final int TOSHIBA_CORP = 0x0004; - public static final int THREE_COM = 0x0005; - public static final int MICROSOFT = 0x0006; - public static final int LUCENT = 0x0007; - public static final int MOTOROLA = 0x0008; - public static final int INFINEON_TECHNOLOGIES_AG = 0x0009; - public static final int CAMBRIDGE_SILICON_RADIO = 0x000A; - public static final int SILICON_WAVE = 0x000B; - public static final int DIGIANSWER_A_S = 0x000C; - public static final int TEXAS_INSTRUMENTS_INC = 0x000D; - public static final int CEVA_INC_FORMERLY_PARTHUS_TECHNOLOGIES_INC = 0x000E; - public static final int BROADCOM_CORPORATION = 0x000F; - public static final int MITEL_SEMICONDUCTOR = 0x0010; - public static final int WIDCOMM_INC = 0x0011; - public static final int ZEEVO_INC = 0x0012; - public static final int ATMEL_CORPORATION = 0x0013; - public static final int MITSUBISHI_ELECTRIC_CORPORATION = 0x0014; - public static final int RTX_TELECOM_A_S = 0x0015; - public static final int KC_TECHNOLOGY_INC = 0x0016; - public static final int NEWLOGIC = 0x0017; - public static final int TRANSILICA_INC = 0x0018; - public static final int ROHDE_SCHWARZ_GMBH_CO_KG = 0x0019; - public static final int TTPCOM_LIMITED = 0x001A; - public static final int SIGNIA_TECHNOLOGIES_INC = 0x001B; - public static final int CONEXANT_SYSTEMS_INC = 0x001C; - public static final int QUALCOMM = 0x001D; - public static final int INVENTEL = 0x001E; - public static final int AVM_BERLIN = 0x001F; - public static final int BANDSPEED_INC = 0x0020; - public static final int MANSELLA_LTD = 0x0021; - public static final int NEC_CORPORATION = 0x0022; - public static final int WAVEPLUS_TECHNOLOGY_CO_LTD = 0x0023; - public static final int ALCATEL = 0x0024; - public static final int PHILIPS_SEMICONDUCTORS = 0x0025; - public static final int C_TECHNOLOGIES = 0x0026; - public static final int OPEN_INTERFACE = 0x0027; - public static final int R_F_MICRO_DEVICES = 0x0028; - public static final int HITACHI_LTD = 0x0029; - public static final int SYMBOL_TECHNOLOGIES_INC = 0x002A; - public static final int TENOVIS = 0x002B; - public static final int MACRONIX_INTERNATIONAL_CO_LTD = 0x002C; - public static final int GCT_SEMICONDUCTOR = 0x002D; - public static final int NORWOOD_SYSTEMS = 0x002E; - public static final int MEWTEL_TECHNOLOGY_INC = 0x002F; - public static final int ST_MICROELECTRONICS = 0x0030; - public static final int SYNOPSIS = 0x0031; - public static final int REDM_COMMUNICATIONS_LTD = 0x0032; - public static final int COMMIL_LTD = 0x0033; - public static final int COMPUTER_ACCESS_TECHNOLOGY_CORPORATION_CATC = 0x0034; - public static final int ECLIPSE_HQ_ESPANA_SL = 0x0035; - public static final int RENESAS_TECHNOLOGY_CORP = 0x0036; - public static final int MOBILIAN_CORPORATION = 0x0037; - public static final int TERAX = 0x0038; - public static final int INTEGRATED_SYSTEM_SOLUTION_CORP = 0x0039; - public static final int MATSUSHITA_ELECTRIC_INDUSTRIAL_CO_LTD = 0x003A; - public static final int GENNUM_CORPORATION = 0x003B; - public static final int RESEARCH_IN_MOTION = 0x003C; - public static final int IPEXTREME_INC = 0x003D; - public static final int SYSTEMS_AND_CHIPS_INC = 0x003E; - public static final int BLUETOOTH_SIG_INC = 0x003F; - public static final int SEIKO_EPSON_CORPORATION = 0x0040; - public static final int INTEGRATED_SILICON_SOLUTION_TAIWAN_INC = 0x0041; - public static final int CONWISE_TECHNOLOGY_CORPORATION_LTD = 0x0042; - public static final int PARROT_SA = 0x0043; - public static final int SOCKET_MOBILE = 0x0044; - public static final int ATHEROS_COMMUNICATIONS_INC = 0x0045; - public static final int MEDIATEK_INC = 0x0046; - public static final int BLUEGIGA = 0x0047; - public static final int MARVELL_TECHNOLOGY_GROUP_LTD = 0x0048; - public static final int THREE_DSP_CORPORATION = 0x0049; - public static final int ACCEL_SEMICONDUCTOR_LTD = 0x004A; - public static final int CONTINENTAL_AUTOMOTIVE_SYSTEMS = 0x004B; - public static final int APPLE_INC = 0x004C; - public static final int STACCATO_COMMUNICATIONS_INC = 0x004D; - public static final int AVAGO_TECHNOLOGIES = 0x004E; - public static final int APT_LICENSING_LTD = 0x004F; - public static final int SIRF_TECHNOLOGY = 0x0050; - public static final int TZERO_TECHNOLOGIES_INC = 0x0051; - public static final int JM_CORPORATION = 0x0052; - public static final int FREE2MOVE_AB = 0x0053; - public static final int THREE_DIJOY_CORPORATION = 0x0054; - public static final int PLANTRONICS_INC = 0x0055; - public static final int SONY_ERICSSON_MOBILE_COMMUNICATIONS = 0x0056; - public static final int HARMAN_INTERNATIONAL_INDUSTRIES_INC = 0x0057; - public static final int VIZIO_INC = 0x0058; - public static final int NORDIC_SEMICONDUCTOR_ASA = 0x0059; - public static final int EM_MICROELECTRONICMARIN_SA = 0x005A; - public static final int RALINK_TECHNOLOGY_CORPORATION = 0x005B; - public static final int BELKIN_INTERNATIONAL_INC = 0x005C; - public static final int REALTEK_SEMICONDUCTOR_CORPORATION = 0x005D; - public static final int STONESTREET_ONE_LLC = 0x005E; - public static final int WICENTRIC_INC = 0x005F; - public static final int RIVIERAWAVES_SAS = 0x0060; - public static final int RDA_MICROELECTRONICS = 0x0061; - public static final int GIBSON_GUITARS = 0x0062; - public static final int MICOMMAND_INC = 0x0063; - public static final int BAND_XI_INTERNATIONAL_LLC = 0x0064; - public static final int HEWLETTPACKARD_COMPANY = 0x0065; - public static final int NINE_SOLUTIONS_OY = 0x0066; - public static final int GN_NETCOM_A_S = 0x0067; - public static final int GENERAL_MOTORS = 0x0068; - public static final int AD_ENGINEERING_INC = 0x0069; - public static final int MINDTREE_LTD = 0x006A; - public static final int POLAR_ELECTRO_OY = 0x006B; - public static final int BEAUTIFUL_ENTERPRISE_CO_LTD = 0x006C; - public static final int BRIARTEK_INC = 0x006D; - public static final int SUMMIT_DATA_COMMUNICATIONS_INC = 0x006E; - public static final int SOUND_ID = 0x006F; - public static final int MONSTER_LLC = 0x0070; - public static final int CONNECTBLUE_AB = 0x0071; - public static final int SHANGHAI_SUPER_SMART_ELECTRONICS_CO_LTD = 0x0072; - public static final int GROUP_SENSE_LTD = 0x0073; - public static final int ZOMM_LLC = 0x0074; - public static final int SAMSUNG_ELECTRONICS_CO_LTD = 0x0075; - public static final int CREATIVE_TECHNOLOGY_LTD = 0x0076; - public static final int LAIRD_TECHNOLOGIES = 0x0077; - public static final int NIKE_INC = 0x0078; - public static final int LESSWIRE_AG = 0x0079; - public static final int MSTAR_SEMICONDUCTOR_INC = 0x007A; - public static final int HANLYNN_TECHNOLOGIES = 0x007B; - public static final int A_R_CAMBRIDGE = 0x007C; - public static final int SEERS_TECHNOLOGY_CO_LTD = 0x007D; - public static final int SPORTS_TRACKING_TECHNOLOGIES_LTD = 0x007E; - public static final int AUTONET_MOBILE = 0x007F; - public static final int DELORME_PUBLISHING_COMPANY_INC = 0x0080; - public static final int WUXI_VIMICRO = 0x0081; - public static final int SENNHEISER_COMMUNICATIONS_A_S = 0x0082; - public static final int TIMEKEEPING_SYSTEMS_INC = 0x0083; - public static final int LUDUS_HELSINKI_LTD = 0x0084; - public static final int BLUERADIOS_INC = 0x0085; - public static final int EQUINOX_AG = 0x0086; - public static final int GARMIN_INTERNATIONAL_INC = 0x0087; - public static final int ECOTEST = 0x0088; - public static final int GN_RESOUND_A_S = 0x0089; - public static final int JAWBONE = 0x008A; - public static final int TOPCORN_POSITIONING_SYSTEMS_LLC = 0x008B; - public static final int QUALCOMM_RETAIL_SOLUTIONS_INC_FORMERLY_QUALCOMM_LABS_INC = 0x008C; - public static final int ZSCAN_SOFTWARE = 0x008D; - public static final int QUINTIC_CORP = 0x008E; - public static final int STOLLMAN_EV_GMBH = 0x008F; - public static final int FUNAI_ELECTRIC_CO_LTD = 0x0090; - public static final int ADVANCED_PANMOBIL_SYSTEMS_GMBH_CO_KG = 0x0091; - public static final int THINKOPTICS_INC = 0x0092; - public static final int UNIVERSAL_ELECTRONICS_INC = 0x0093; - public static final int AIROHA_TECHNOLOGY_CORP = 0x0094; - public static final int NEC_LIGHTING_LTD = 0x0095; - public static final int ODM_TECHNOLOGY_INC = 0x0096; - public static final int CONNECTEDEVICE_LTD = 0x0097; - public static final int ZER01TV_GMBH = 0x0098; - public static final int ITECH_DYNAMIC_GLOBAL_DISTRIBUTION_LTD = 0x0099; - public static final int ALPWISE = 0x009A; - public static final int JIANGSU_TOPPOWER_AUTOMOTIVE_ELECTRONICS_CO_LTD = 0x009B; - public static final int COLORFY_INC = 0x009C; - public static final int GEOFORCE_INC = 0x009D; - public static final int BOSE_CORPORATION = 0x009E; - public static final int SUUNTO_OY = 0x009F; - public static final int KENSINGTON_COMPUTER_PRODUCTS_GROUP = 0x00A0; - public static final int SRMEDIZINELEKTRONIK = 0x00A1; - public static final int VERTU_CORPORATION_LIMITED = 0x00A2; - public static final int META_WATCH_LTD = 0x00A3; - public static final int LINAK_A_S = 0x00A4; - public static final int OTL_DYNAMICS_LLC = 0x00A5; - public static final int PANDA_OCEAN_INC = 0x00A6; - public static final int VISTEON_CORPORATION = 0x00A7; - public static final int ARP_DEVICES_LIMITED = 0x00A8; - public static final int MAGNETI_MARELLI_SPA = 0x00A9; - public static final int CAEN_RFID_SRL = 0x00AA; - public static final int INGENIEURSYSTEMGRUPPE_ZAHN_GMBH = 0x00AB; - public static final int GREEN_THROTTLE_GAMES = 0x00AC; - public static final int PETER_SYSTEMTECHNIK_GMBH = 0x00AD; - public static final int OMEGAWAVE_OY = 0x00AE; - public static final int CINETIX = 0x00AF; - public static final int PASSIF_SEMICONDUCTOR_CORP = 0x00B0; - public static final int SARIS_CYCLING_GROUP_INC = 0x00B1; - public static final int BEKEY_A_S = 0x00B2; - public static final int CLARINOX_TECHNOLOGIES_PTY_LTD = 0x00B3; - public static final int BDE_TECHNOLOGY_CO_LTD = 0x00B4; - public static final int SWIRL_NETWORKS = 0x00B5; - public static final int MESO_INTERNATIONAL = 0x00B6; - public static final int TRELAB_LTD = 0x00B7; - public static final int QUALCOMM_INNOVATION_CENTER_INC_QUIC = 0x00B8; - public static final int JOHNSON_CONTROLS_INC = 0x00B9; - public static final int STARKEY_LABORATORIES_INC = 0x00BA; - public static final int SPOWER_ELECTRONICS_LIMITED = 0x00BB; - public static final int ACE_SENSOR_INC = 0x00BC; - public static final int APLIX_CORPORATION = 0x00BD; - public static final int AAMP_OF_AMERICA = 0x00BE; - public static final int STALMART_TECHNOLOGY_LIMITED = 0x00BF; - public static final int AMICCOM_ELECTRONICS_CORPORATION = 0x00C0; - public static final int SHENZHEN_EXCELSECU_DATA_TECHNOLOGY_COLTD = 0x00C1; - public static final int GENEQ_INC = 0x00C2; - public static final int ADIDAS_AG = 0x00C3; - public static final int LG_ELECTRONICS = 0x00C4; - public static final int ONSET_COMPUTER_CORPORATION = 0x00C5; - public static final int SELFLY_BV = 0x00C6; - public static final int QUUPPA_OY = 0x00C7; - public static final int GELO_INC = 0x00C8; - public static final int EVLUMA = 0x00C9; - public static final int MC10 = 0x00CA; - public static final int BINAURIC_SE = 0x00CB; - public static final int BEATS_ELECTRONICS = 0x00CC; - public static final int MICROCHIP_TECHNOLOGY_INC = 0x00CD; - public static final int ELGATO_SYSTEMS_GMBH = 0x00CE; - public static final int ARCHOS_SA = 0x00CF; - public static final int DEXCOM_INC = 0x00D0; - public static final int POLAR_ELECTRO_EUROPE_BV = 0x00D1; - public static final int DIALOG_SEMICONDUCTOR_BV = 0x00D2; - public static final int TAIXINGBANG_TECHNOLOGY_HK_CO_LTD = 0x00D3; - public static final int KAWANTECH = 0x00D4; - public static final int AUSTCO_COMMUNICATION_SYSTEMS = 0x00D5; - public static final int TIMEX_GROUP_USA_INC = 0x00D6; - public static final int QUALCOMM_TECHNOLOGIES_INC = 0x00D7; - public static final int QUALCOMM_CONNECTED_EXPERIENCES_INC = 0x00D8; - public static final int VOYETRA_TURTLE_BEACH = 0x00D9; - public static final int TXTR_GMBH = 0x00DA; - public static final int BIOSENTRONICS = 0x00DB; - public static final int PROCTER_GAMBLE = 0x00DC; - public static final int HOSIDEN_CORPORATION = 0x00DD; - public static final int MUZIK_LLC = 0x00DE; - public static final int MISFIT_WEARABLES_CORP = 0x00DF; - public static final int GOOGLE = 0x00E0; - public static final int DANLERS_LTD = 0x00E1; - public static final int SEMILINK_INC = 0x00E2; - public static final int INMUSIC_BRANDS_INC = 0x00E3; - public static final int LS_RESEARCH_INC = 0x00E4; - public static final int EDEN_SOFTWARE_CONSULTANTS_LTD = 0x00E5; - public static final int FRESHTEMP = 0x00E6; - public static final int KS_TECHNOLOGIES = 0x00E7; - public static final int ACTS_TECHNOLOGIES = 0x00E8; - public static final int VTRACK_SYSTEMS = 0x00E9; - public static final int NIELSENKELLERMAN_COMPANY = 0x00EA; - public static final int SERVER_TECHNOLOGY_INC = 0x00EB; - public static final int BIORESEARCH_ASSOCIATES = 0x00EC; - public static final int JOLLY_LOGIC_LLC = 0x00ED; - public static final int ABOVE_AVERAGE_OUTCOMES_INC = 0x00EE; - public static final int BITSPLITTERS_GMBH = 0x00EF; - public static final int PAYPAL_INC = 0x00F0; - public static final int WITRON_TECHNOLOGY_LIMITED = 0x00F1; - public static final int MORSE_PROJECT_INC = 0x00F2; - public static final int KENT_DISPLAYS_INC = 0x00F3; - public static final int NAUTILUS_INC = 0x00F4; - public static final int SMARTIFIER_OY = 0x00F5; - public static final int ELCOMETER_LIMITED = 0x00F6; - public static final int VSN_TECHNOLOGIES_INC = 0x00F7; - public static final int ACEUNI_CORP_LTD = 0x00F8; - public static final int STICKNFIND = 0x00F9; - public static final int CRYSTAL_CODE_AB = 0x00FA; - public static final int KOUKAAM_AS = 0x00FB; - public static final int DELPHI_CORPORATION = 0x00FC; - public static final int VALENCETECH_LIMITED = 0x00FD; - public static final int RESERVED = 0x00FE; - public static final int TYPO_PRODUCTS_LLC = 0x00FF; - public static final int TOMTOM_INTERNATIONAL_BV = 0x0100; - public static final int FUGOO_INC = 0x0101; - public static final int KEISER_CORPORATION = 0x0102; - public static final int BANG_OLUFSEN_A_S = 0x0103; - public static final int PLUS_LOCATIONS_SYSTEMS_PTY_LTD = 0x0104; - public static final int UBIQUITOUS_COMPUTING_TECHNOLOGY_CORPORATION = 0x0105; - public static final int INNOVATIVE_YACHTTER_SOLUTIONS = 0x0106; - public static final int WILLIAM_DEMANT_HOLDING_A_S = 0x0107; - public static final int CHICONY_ELECTRONICS_CO_LTD = 0x0108; - public static final int ATUS_BV = 0x0109; - public static final int CODEGATE_LTD = 0x010A; - public static final int ERI_INC = 0x010B; - public static final int TRANSDUCERS_DIRECT_LLC = 0x010C; - public static final int FUJITSU_TEN_LIMITED = 0x010D; - public static final int AUDI_AG = 0x010E; - public static final int HISILICON_TECHNOLOGIES_CO_LTD = 0x010F; - public static final int NIPPON_SEIKI_CO_LTD = 0x0110; - public static final int STEELSERIES_APS = 0x0111; - public static final int VYZYBL_INC = 0x0112; - public static final int OPENBRAIN_TECHNOLOGIES_CO_LTD = 0x0113; - public static final int XENSR = 0x0114; - public static final int ESOLUTIONS = 0x0115; - public static final int ONE_OAK_TECHNOLOGIES = 0x0116; - public static final int WIMOTO_TECHNOLOGIES_INC = 0x0117; - public static final int RADIUS_NETWORKS_INC = 0x0118; - public static final int WIZE_TECHNOLOGY_CO_LTD = 0x0119; - public static final int QUALCOMM_LABS_INC = 0x011A; - public static final int ARUBA_NETWORKS = 0x011B; - public static final int BAIDU = 0x011C; - public static final int ARENDI_AG = 0x011D; - public static final int SKODA_AUTO_AS = 0x011E; - public static final int VOLKSWAGON_AG = 0x011F; - public static final int PORSCHE_AG = 0x0120; - public static final int SINO_WEALTH_ELECTRONIC_LTD = 0x0121; - public static final int AIRTURN_INC = 0x0122; - public static final int KINSA_INC = 0x0123; - public static final int HID_GLOBAL = 0x0124; - public static final int SEAT_ES = 0x0125; - public static final int PROMETHEAN_LTD = 0x0126; - public static final int SALUTICA_ALLIED_SOLUTIONS = 0x0127; - public static final int GPSI_GROUP_PTY_LTD = 0x0128; - public static final int NIMBLE_DEVICES_OY = 0x0129; - public static final int CHANGZHOU_YONGSE_INFOTECH_CO_LTD = 0x012A; - public static final int SPORTIQ = 0x012B; - public static final int TEMEC_INSTRUMENTS_BV = 0x012C; - public static final int SONY_CORPORATION = 0x012D; - public static final int ASSA_ABLOY = 0x012E; - public static final int CLARION_CO_LTD = 0x012F; - public static final int WAREHOUSE_INNOVATIONS = 0x0130; - public static final int CYPRESS_SEMICONDUCTOR_CORPORATION = 0x0131; - public static final int MADS_INC = 0x0132; - public static final int BLUE_MAESTRO_LIMITED = 0x0133; - public static final int RESOLUTION_PRODUCTS_INC = 0x0134; - public static final int AIREWEAR_LLC = 0x0135; - public static final int ETC_SP_ZOO = 0x0136; - public static final int PRESTIGIO_PLAZA_LTD = 0x0137; - - private static final SparseArray COMPANY_NAME_MAP = populateCompanyNameMap(); - - public static String getCompanyName(int companyId, String fallback){ - final String name = COMPANY_NAME_MAP.get(companyId); - return name == null ? fallback : name; - } - - private static SparseArray populateCompanyNameMap() { - final SparseArray map = new SparseArray(); - - map.put(ERICSSON_TECHNOLOGY_LICENSING , "Ericsson Technology Licensing"); - map.put(NOKIA_MOBILE_PHONES , "Nokia Mobile Phones"); - map.put(INTEL_CORP , "Intel Corp."); - map.put(IBM_CORP , "IBM Corp."); - map.put(TOSHIBA_CORP , "Toshiba Corp."); - map.put(THREE_COM , "3Com"); - map.put(MICROSOFT , "Microsoft"); - map.put(LUCENT , "Lucent"); - map.put(MOTOROLA , "Motorola"); - map.put(INFINEON_TECHNOLOGIES_AG , "Infineon Technologies AG"); - map.put(CAMBRIDGE_SILICON_RADIO , "Cambridge Silicon Radio"); - map.put(SILICON_WAVE , "Silicon Wave"); - map.put(DIGIANSWER_A_S , "Digianswer A/S"); - map.put(TEXAS_INSTRUMENTS_INC , "Texas Instruments Inc."); - map.put(CEVA_INC_FORMERLY_PARTHUS_TECHNOLOGIES_INC , "Ceva, Inc. (formerly Parthus Technologies, Inc.)"); - map.put(BROADCOM_CORPORATION , "Broadcom Corporation"); - map.put(MITEL_SEMICONDUCTOR , "Mitel Semiconductor"); - map.put(WIDCOMM_INC , "Widcomm, Inc"); - map.put(ZEEVO_INC , "Zeevo, Inc."); - map.put(ATMEL_CORPORATION , "Atmel Corporation"); - map.put(MITSUBISHI_ELECTRIC_CORPORATION , "Mitsubishi Electric Corporation"); - map.put(RTX_TELECOM_A_S , "RTX Telecom A/S"); - map.put(KC_TECHNOLOGY_INC , "KC Technology Inc."); - map.put(NEWLOGIC , "NewLogic"); - map.put(TRANSILICA_INC , "Transilica, Inc."); - map.put(ROHDE_SCHWARZ_GMBH_CO_KG , "Rohde & Schwarz GmbH & Co. KG"); - map.put(TTPCOM_LIMITED , "TTPCom Limited"); - map.put(SIGNIA_TECHNOLOGIES_INC , "Signia Technologies, Inc."); - map.put(CONEXANT_SYSTEMS_INC , "Conexant Systems Inc."); - map.put(QUALCOMM , "Qualcomm"); - map.put(INVENTEL , "Inventel"); - map.put(AVM_BERLIN , "AVM Berlin"); - map.put(BANDSPEED_INC , "BandSpeed, Inc."); - map.put(MANSELLA_LTD , "Mansella Ltd"); - map.put(NEC_CORPORATION , "NEC Corporation"); - map.put(WAVEPLUS_TECHNOLOGY_CO_LTD , "WavePlus Technology Co., Ltd."); - map.put(ALCATEL , "Alcatel"); - map.put(PHILIPS_SEMICONDUCTORS , "Philips Semiconductors"); - map.put(C_TECHNOLOGIES , "C Technologies"); - map.put(OPEN_INTERFACE , "Open Interface"); - map.put(R_F_MICRO_DEVICES , "R F Micro Devices"); - map.put(HITACHI_LTD , "Hitachi Ltd"); - map.put(SYMBOL_TECHNOLOGIES_INC , "Symbol Technologies, Inc."); - map.put(TENOVIS , "Tenovis"); - map.put(MACRONIX_INTERNATIONAL_CO_LTD , "Macronix International Co. Ltd."); - map.put(GCT_SEMICONDUCTOR , "GCT Semiconductor"); - map.put(NORWOOD_SYSTEMS , "Norwood Systems"); - map.put(MEWTEL_TECHNOLOGY_INC , "MewTel Technology Inc."); - map.put(ST_MICROELECTRONICS , "ST Microelectronics"); - map.put(SYNOPSIS , "Synopsis"); - map.put(REDM_COMMUNICATIONS_LTD , "Red-M (Communications) Ltd"); - map.put(COMMIL_LTD , "Commil Ltd"); - map.put(COMPUTER_ACCESS_TECHNOLOGY_CORPORATION_CATC , "Computer Access Technology Corporation (CATC)"); - map.put(ECLIPSE_HQ_ESPANA_SL , "Eclipse (HQ Espana) S.L."); - map.put(RENESAS_TECHNOLOGY_CORP , "Renesas Technology Corp."); - map.put(MOBILIAN_CORPORATION , "Mobilian Corporation"); - map.put(TERAX , "Terax"); - map.put(INTEGRATED_SYSTEM_SOLUTION_CORP , "Integrated System Solution Corp."); - map.put(MATSUSHITA_ELECTRIC_INDUSTRIAL_CO_LTD , "Matsushita Electric Industrial Co., Ltd."); - map.put(GENNUM_CORPORATION , "Gennum Corporation"); - map.put(RESEARCH_IN_MOTION , "Research In Motion"); - map.put(IPEXTREME_INC , "IPextreme, Inc."); - map.put(SYSTEMS_AND_CHIPS_INC , "Systems and Chips, Inc."); - map.put(BLUETOOTH_SIG_INC , "Bluetooth SIG, Inc."); - map.put(SEIKO_EPSON_CORPORATION , "Seiko Epson Corporation"); - map.put(INTEGRATED_SILICON_SOLUTION_TAIWAN_INC , "Integrated Silicon Solution Taiwan, Inc."); - map.put(CONWISE_TECHNOLOGY_CORPORATION_LTD , "CONWISE Technology Corporation Ltd"); - map.put(PARROT_SA , "PARROT SA"); - map.put(SOCKET_MOBILE , "Socket Mobile"); - map.put(ATHEROS_COMMUNICATIONS_INC , "Atheros Communications, Inc."); - map.put(MEDIATEK_INC , "MediaTek, Inc."); - map.put(BLUEGIGA , "Bluegiga"); - map.put(MARVELL_TECHNOLOGY_GROUP_LTD , "Marvell Technology Group Ltd."); - map.put(THREE_DSP_CORPORATION , "3DSP Corporation"); - map.put(ACCEL_SEMICONDUCTOR_LTD , "Accel Semiconductor Ltd."); - map.put(CONTINENTAL_AUTOMOTIVE_SYSTEMS , "Continental Automotive Systems"); - map.put(APPLE_INC , "Apple, Inc."); - map.put(STACCATO_COMMUNICATIONS_INC , "Staccato Communications, Inc."); - map.put(AVAGO_TECHNOLOGIES , "Avago Technologies"); - map.put(APT_LICENSING_LTD , "APT Licensing Ltd."); - map.put(SIRF_TECHNOLOGY , "SiRF Technology"); - map.put(TZERO_TECHNOLOGIES_INC , "Tzero Technologies, Inc."); - map.put(JM_CORPORATION , "J&M Corporation"); - map.put(FREE2MOVE_AB , "Free2move AB"); - map.put(THREE_DIJOY_CORPORATION , "3DiJoy Corporation"); - map.put(PLANTRONICS_INC , "Plantronics, Inc."); - map.put(SONY_ERICSSON_MOBILE_COMMUNICATIONS , "Sony Ericsson Mobile Communications"); - map.put(HARMAN_INTERNATIONAL_INDUSTRIES_INC , "Harman International Industries, Inc."); - map.put(VIZIO_INC , "Vizio, Inc."); - map.put(NORDIC_SEMICONDUCTOR_ASA , "Nordic Semiconductor ASA"); - map.put(EM_MICROELECTRONICMARIN_SA , "EM Microelectronic-Marin SA"); - map.put(RALINK_TECHNOLOGY_CORPORATION , "Ralink Technology Corporation"); - map.put(BELKIN_INTERNATIONAL_INC , "Belkin International, Inc."); - map.put(REALTEK_SEMICONDUCTOR_CORPORATION , "Realtek Semiconductor Corporation"); - map.put(STONESTREET_ONE_LLC , "Stonestreet One, LLC"); - map.put(WICENTRIC_INC , "Wicentric, Inc."); - map.put(RIVIERAWAVES_SAS , "RivieraWaves S.A.S"); - map.put(RDA_MICROELECTRONICS , "RDA Microelectronics"); - map.put(GIBSON_GUITARS , "Gibson Guitars"); - map.put(MICOMMAND_INC , "MiCommand Inc."); - map.put(BAND_XI_INTERNATIONAL_LLC , "Band XI International, LLC"); - map.put(HEWLETTPACKARD_COMPANY , "Hewlett-Packard Company"); - map.put(NINE_SOLUTIONS_OY , "9Solutions Oy"); - map.put(GN_NETCOM_A_S , "GN Netcom A/S"); - map.put(GENERAL_MOTORS , "General Motors"); - map.put(AD_ENGINEERING_INC , "A&D Engineering, Inc."); - map.put(MINDTREE_LTD , "MindTree Ltd."); - map.put(POLAR_ELECTRO_OY , "Polar Electro OY"); - map.put(BEAUTIFUL_ENTERPRISE_CO_LTD , "Beautiful Enterprise Co., Ltd."); - map.put(BRIARTEK_INC , "BriarTek, Inc."); - map.put(SUMMIT_DATA_COMMUNICATIONS_INC , "Summit Data Communications, Inc."); - map.put(SOUND_ID , "Sound ID"); - map.put(MONSTER_LLC , "Monster, LLC"); - map.put(CONNECTBLUE_AB , "connectBlue AB"); - map.put(SHANGHAI_SUPER_SMART_ELECTRONICS_CO_LTD , "ShangHai Super Smart Electronics Co. Ltd."); - map.put(GROUP_SENSE_LTD , "Group Sense Ltd."); - map.put(ZOMM_LLC , "Zomm, LLC"); - map.put(SAMSUNG_ELECTRONICS_CO_LTD , "Samsung Electronics Co. Ltd."); - map.put(CREATIVE_TECHNOLOGY_LTD , "Creative Technology Ltd."); - map.put(LAIRD_TECHNOLOGIES , "Laird Technologies"); - map.put(NIKE_INC , "Nike, Inc."); - map.put(LESSWIRE_AG , "lesswire AG"); - map.put(MSTAR_SEMICONDUCTOR_INC , "MStar Semiconductor, Inc."); - map.put(HANLYNN_TECHNOLOGIES , "Hanlynn Technologies"); - map.put(A_R_CAMBRIDGE , "A & R Cambridge"); - map.put(SEERS_TECHNOLOGY_CO_LTD , "Seers Technology Co. Ltd"); - map.put(SPORTS_TRACKING_TECHNOLOGIES_LTD , "Sports Tracking Technologies Ltd."); - map.put(AUTONET_MOBILE , "Autonet Mobile"); - map.put(DELORME_PUBLISHING_COMPANY_INC , "DeLorme Publishing Company, Inc."); - map.put(WUXI_VIMICRO , "WuXi Vimicro"); - map.put(SENNHEISER_COMMUNICATIONS_A_S , "Sennheiser Communications A/S"); - map.put(TIMEKEEPING_SYSTEMS_INC , "TimeKeeping Systems, Inc."); - map.put(LUDUS_HELSINKI_LTD , "Ludus Helsinki Ltd."); - map.put(BLUERADIOS_INC , "BlueRadios, Inc."); - map.put(EQUINOX_AG , "equinox AG"); - map.put(GARMIN_INTERNATIONAL_INC , "Garmin International, Inc."); - map.put(ECOTEST , "Ecotest"); - map.put(GN_RESOUND_A_S , "GN ReSound A/S"); - map.put(JAWBONE , "Jawbone"); - map.put(TOPCORN_POSITIONING_SYSTEMS_LLC , "Topcorn Positioning Systems, LLC"); - map.put(QUALCOMM_RETAIL_SOLUTIONS_INC_FORMERLY_QUALCOMM_LABS_INC , "Qualcomm Retail Solutions, Inc. (formerly Qualcomm Labs, Inc.)"); - map.put(ZSCAN_SOFTWARE , "Zscan Software"); - map.put(QUINTIC_CORP , "Quintic Corp."); - map.put(STOLLMAN_EV_GMBH , "Stollman E+V GmbH"); - map.put(FUNAI_ELECTRIC_CO_LTD , "Funai Electric Co., Ltd."); - map.put(ADVANCED_PANMOBIL_SYSTEMS_GMBH_CO_KG , "Advanced PANMOBIL Systems GmbH & Co. KG"); - map.put(THINKOPTICS_INC , "ThinkOptics, Inc."); - map.put(UNIVERSAL_ELECTRONICS_INC , "Universal Electronics, Inc."); - map.put(AIROHA_TECHNOLOGY_CORP , "Airoha Technology Corp."); - map.put(NEC_LIGHTING_LTD , "NEC Lighting, Ltd."); - map.put(ODM_TECHNOLOGY_INC , "ODM Technology, Inc."); - map.put(CONNECTEDEVICE_LTD , "ConnecteDevice Ltd."); - map.put(ZER01TV_GMBH , "zer01.tv GmbH"); - map.put(ITECH_DYNAMIC_GLOBAL_DISTRIBUTION_LTD , "i.Tech Dynamic Global Distribution Ltd."); - map.put(ALPWISE , "Alpwise"); - map.put(JIANGSU_TOPPOWER_AUTOMOTIVE_ELECTRONICS_CO_LTD , "Jiangsu Toppower Automotive Electronics Co., Ltd."); - map.put(COLORFY_INC , "Colorfy, Inc."); - map.put(GEOFORCE_INC , "Geoforce Inc."); - map.put(BOSE_CORPORATION , "Bose Corporation"); - map.put(SUUNTO_OY , "Suunto Oy"); - map.put(KENSINGTON_COMPUTER_PRODUCTS_GROUP , "Kensington Computer Products Group"); - map.put(SRMEDIZINELEKTRONIK , "SR-Medizinelektronik"); - map.put(VERTU_CORPORATION_LIMITED , "Vertu Corporation Limited"); - map.put(META_WATCH_LTD , "Meta Watch Ltd."); - map.put(LINAK_A_S , "LINAK A/S"); - map.put(OTL_DYNAMICS_LLC , "OTL Dynamics LLC"); - map.put(PANDA_OCEAN_INC , "Panda Ocean Inc."); - map.put(VISTEON_CORPORATION , "Visteon Corporation"); - map.put(ARP_DEVICES_LIMITED , "ARP Devices Limited"); - map.put(MAGNETI_MARELLI_SPA , "Magneti Marelli S.p.A"); - map.put(CAEN_RFID_SRL , "CAEN RFID srl"); - map.put(INGENIEURSYSTEMGRUPPE_ZAHN_GMBH , "Ingenieur-Systemgruppe Zahn GmbH"); - map.put(GREEN_THROTTLE_GAMES , "Green Throttle Games"); - map.put(PETER_SYSTEMTECHNIK_GMBH , "Peter Systemtechnik GmbH"); - map.put(OMEGAWAVE_OY , "Omegawave Oy"); - map.put(CINETIX , "Cinetix"); - map.put(PASSIF_SEMICONDUCTOR_CORP , "Passif Semiconductor Corp"); - map.put(SARIS_CYCLING_GROUP_INC , "Saris Cycling Group, Inc"); - map.put(BEKEY_A_S , "Bekey A/S"); - map.put(CLARINOX_TECHNOLOGIES_PTY_LTD , "Clarinox Technologies Pty. Ltd."); - map.put(BDE_TECHNOLOGY_CO_LTD , "BDE Technology Co., Ltd."); - map.put(SWIRL_NETWORKS , "Swirl Networks"); - map.put(MESO_INTERNATIONAL , "Meso international"); - map.put(TRELAB_LTD , "TreLab Ltd"); - map.put(QUALCOMM_INNOVATION_CENTER_INC_QUIC , "Qualcomm Innovation Center, Inc. (QuIC)"); - map.put(JOHNSON_CONTROLS_INC , "Johnson Controls, Inc."); - map.put(STARKEY_LABORATORIES_INC , "Starkey Laboratories Inc."); - map.put(SPOWER_ELECTRONICS_LIMITED , "S-Power Electronics Limited"); - map.put(ACE_SENSOR_INC , "Ace Sensor Inc"); - map.put(APLIX_CORPORATION , "Aplix Corporation"); - map.put(AAMP_OF_AMERICA , "AAMP of America"); - map.put(STALMART_TECHNOLOGY_LIMITED , "Stalmart Technology Limited"); - map.put(AMICCOM_ELECTRONICS_CORPORATION , "AMICCOM Electronics Corporation"); - map.put(SHENZHEN_EXCELSECU_DATA_TECHNOLOGY_COLTD , "Shenzhen Excelsecu Data Technology Co.,Ltd"); - map.put(GENEQ_INC , "Geneq Inc."); - map.put(ADIDAS_AG , "adidas AG"); - map.put(LG_ELECTRONICS , "LG Electronics"); - map.put(ONSET_COMPUTER_CORPORATION , "Onset Computer Corporation"); - map.put(SELFLY_BV , "Selfly BV"); - map.put(QUUPPA_OY , "Quuppa Oy."); - map.put(GELO_INC , "GeLo Inc"); - map.put(EVLUMA , "Evluma"); - map.put(MC10 , "MC10"); - map.put(BINAURIC_SE , "Binauric SE"); - map.put(BEATS_ELECTRONICS , "Beats Electronics"); - map.put(MICROCHIP_TECHNOLOGY_INC , "Microchip Technology Inc."); - map.put(ELGATO_SYSTEMS_GMBH , "Elgato Systems GmbH"); - map.put(ARCHOS_SA , "ARCHOS SA"); - map.put(DEXCOM_INC , "Dexcom, Inc."); - map.put(POLAR_ELECTRO_EUROPE_BV , "Polar Electro Europe B.V."); - map.put(DIALOG_SEMICONDUCTOR_BV , "Dialog Semiconductor B.V."); - map.put(TAIXINGBANG_TECHNOLOGY_HK_CO_LTD , "Taixingbang Technology (HK) Co,. LTD."); - map.put(KAWANTECH , "Kawantech"); - map.put(AUSTCO_COMMUNICATION_SYSTEMS , "Austco Communication Systems"); - map.put(TIMEX_GROUP_USA_INC , "Timex Group USA, Inc."); - map.put(QUALCOMM_TECHNOLOGIES_INC , "Qualcomm Technologies, Inc."); - map.put(QUALCOMM_CONNECTED_EXPERIENCES_INC , "Qualcomm Connected Experiences, Inc."); - map.put(VOYETRA_TURTLE_BEACH , "Voyetra Turtle Beach"); - map.put(TXTR_GMBH , "txtr GmbH"); - map.put(BIOSENTRONICS , "Biosentronics"); - map.put(PROCTER_GAMBLE , "Procter & Gamble"); - map.put(HOSIDEN_CORPORATION , "Hosiden Corporation"); - map.put(MUZIK_LLC , "Muzik LLC"); - map.put(MISFIT_WEARABLES_CORP , "Misfit Wearables Corp"); - map.put(GOOGLE , "Google"); - map.put(DANLERS_LTD , "Danlers Ltd"); - map.put(SEMILINK_INC , "Semilink Inc"); - map.put(INMUSIC_BRANDS_INC , "inMusic Brands, Inc"); - map.put(LS_RESEARCH_INC , "L.S. Research Inc."); - map.put(EDEN_SOFTWARE_CONSULTANTS_LTD , "Eden Software Consultants Ltd."); - map.put(FRESHTEMP , "Freshtemp"); - map.put(KS_TECHNOLOGIES , "KS Technologies"); - map.put(ACTS_TECHNOLOGIES , "ACTS Technologies"); - map.put(VTRACK_SYSTEMS , "Vtrack Systems"); - map.put(NIELSENKELLERMAN_COMPANY , "Nielsen-Kellerman Company"); - map.put(SERVER_TECHNOLOGY_INC , "Server Technology, Inc."); - map.put(BIORESEARCH_ASSOCIATES , "BioResearch Associates"); - map.put(JOLLY_LOGIC_LLC , "Jolly Logic, LLC"); - map.put(ABOVE_AVERAGE_OUTCOMES_INC , "Above Average Outcomes, Inc."); - map.put(BITSPLITTERS_GMBH , "Bitsplitters GmbH"); - map.put(PAYPAL_INC , "PayPal, Inc."); - map.put(WITRON_TECHNOLOGY_LIMITED , "Witron Technology Limited"); - map.put(MORSE_PROJECT_INC , "Morse Project Inc."); - map.put(KENT_DISPLAYS_INC , "Kent Displays Inc."); - map.put(NAUTILUS_INC , "Nautilus Inc."); - map.put(SMARTIFIER_OY , "Smartifier Oy"); - map.put(ELCOMETER_LIMITED , "Elcometer Limited"); - map.put(VSN_TECHNOLOGIES_INC , "VSN Technologies Inc."); - map.put(ACEUNI_CORP_LTD , "AceUni Corp., Ltd."); - map.put(STICKNFIND , "StickNFind"); - map.put(CRYSTAL_CODE_AB , "Crystal Code AB"); - map.put(KOUKAAM_AS , "KOUKAAM a.s."); - map.put(DELPHI_CORPORATION , "Delphi Corporation"); - map.put(VALENCETECH_LIMITED , "ValenceTech Limited"); - map.put(RESERVED , "Reserved"); - map.put(TYPO_PRODUCTS_LLC , "Typo Products, LLC"); - map.put(TOMTOM_INTERNATIONAL_BV , "TomTom International BV"); - map.put(FUGOO_INC , "Fugoo, Inc"); - map.put(KEISER_CORPORATION , "Keiser Corporation"); - map.put(BANG_OLUFSEN_A_S , "Bang & Olufsen A/S"); - map.put(PLUS_LOCATIONS_SYSTEMS_PTY_LTD , "PLUS Locations Systems Pty Ltd"); - map.put(UBIQUITOUS_COMPUTING_TECHNOLOGY_CORPORATION , "Ubiquitous Computing Technology Corporation"); - map.put(INNOVATIVE_YACHTTER_SOLUTIONS , "Innovative Yachtter Solutions"); - map.put(WILLIAM_DEMANT_HOLDING_A_S , "William Demant Holding A/S"); - map.put(CHICONY_ELECTRONICS_CO_LTD , "Chicony Electronics Co., Ltd."); - map.put(ATUS_BV , "Atus BV"); - map.put(CODEGATE_LTD , "Codegate Ltd."); - map.put(ERI_INC , "ERi, Inc."); - map.put(TRANSDUCERS_DIRECT_LLC , "Transducers Direct, LLC"); - map.put(FUJITSU_TEN_LIMITED , "Fujitsu Ten Limited"); - map.put(AUDI_AG , "Audi AG"); - map.put(HISILICON_TECHNOLOGIES_CO_LTD , "HiSilicon Technologies Co., Ltd."); - map.put(NIPPON_SEIKI_CO_LTD , "Nippon Seiki Co., Ltd."); - map.put(STEELSERIES_APS , "Steelseries ApS"); - map.put(VYZYBL_INC , "vyzybl Inc."); - map.put(OPENBRAIN_TECHNOLOGIES_CO_LTD , "Openbrain Technologies, Co., Ltd."); - map.put(XENSR , "Xensr"); - map.put(ESOLUTIONS , "e.solutions"); - map.put(ONE_OAK_TECHNOLOGIES , "1OAK Technologies"); - map.put(WIMOTO_TECHNOLOGIES_INC , "Wimoto Technologies Inc"); - map.put(RADIUS_NETWORKS_INC , "Radius Networks, Inc."); - map.put(WIZE_TECHNOLOGY_CO_LTD , "Wize Technology Co., Ltd."); - map.put(QUALCOMM_LABS_INC , "Qualcomm Labs, Inc."); - map.put(ARUBA_NETWORKS , "Aruba Networks"); - map.put(BAIDU , "Baidu"); - map.put(ARENDI_AG , "Arendi AG"); - map.put(SKODA_AUTO_AS , "Skoda Auto a.s."); - map.put(VOLKSWAGON_AG , "Volkswagon AG"); - map.put(PORSCHE_AG , "Porsche AG"); - map.put(SINO_WEALTH_ELECTRONIC_LTD , "Sino Wealth Electronic Ltd."); - map.put(AIRTURN_INC , "AirTurn, Inc."); - map.put(KINSA_INC , "Kinsa, Inc."); - map.put(HID_GLOBAL , "HID Global"); - map.put(SEAT_ES , "SEAT es"); - map.put(PROMETHEAN_LTD , "Promethean Ltd."); - map.put(SALUTICA_ALLIED_SOLUTIONS , "Salutica Allied Solutions"); - map.put(GPSI_GROUP_PTY_LTD , "GPSI Group Pty Ltd"); - map.put(NIMBLE_DEVICES_OY , "Nimble Devices Oy"); - map.put(CHANGZHOU_YONGSE_INFOTECH_CO_LTD , "Changzhou Yongse Infotech Co., Ltd"); - map.put(SPORTIQ , "SportIQ"); - map.put(TEMEC_INSTRUMENTS_BV , "TEMEC Instruments B.V."); - map.put(SONY_CORPORATION , "Sony Corporation"); - map.put(ASSA_ABLOY , "ASSA ABLOY"); - map.put(CLARION_CO_LTD , "Clarion Co., Ltd."); - map.put(WAREHOUSE_INNOVATIONS , "Warehouse Innovations"); - map.put(CYPRESS_SEMICONDUCTOR_CORPORATION , "Cypress Semiconductor Corporation"); - map.put(MADS_INC , "MADS Inc"); - map.put(BLUE_MAESTRO_LIMITED , "Blue Maestro Limited"); - map.put(RESOLUTION_PRODUCTS_INC , "Resolution Products, Inc."); - map.put(AIREWEAR_LLC , "Airewear LLC"); - map.put(ETC_SP_ZOO , "ETC sp. z.o.o."); - map.put(PRESTIGIO_PLAZA_LTD , "Prestigio Plaza Ltd."); - - return map; - } - - -} diff --git a/library/src/uk/co/alt236/bluetoothlelib/resolvers/GattAttributeResolver.java b/library/src/uk/co/alt236/bluetoothlelib/resolvers/GattAttributeResolver.java deleted file mode 100644 index a30cca8..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/resolvers/GattAttributeResolver.java +++ /dev/null @@ -1,386 +0,0 @@ -package uk.co.alt236.bluetoothlelib.resolvers; - -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -/** - * - * The UUIDS have been collected from the following sources: - * - * - http://developer.nokia.com/community/wiki/Bluetooth_Services_for_Windows_Phone - * - The Bluez project - * - * @author Alexandros Schillings - * - */ -public class GattAttributeResolver { - public static final String BASE_GUID = "00000001-0000-1000-8000-00805f9b34fb"; - public static final String SERVICE_DISCOVERY_PROTOCOL_SDP = "00000002-0000-1000-8000-00805f9b34fb"; - public static final String USER_DATAGRAM_PROTOCOL_UDP = "00000003-0000-1000-8000-00805f9b34fb"; - public static final String RADIO_FREQUENCY_COMMUNICATION_PROTOCOL_RFCOMM = "00000004-0000-1000-8000-00805f9b34fb"; - public static final String TCP = "00000005-0000-1000-8000-00805f9b34fb"; - public static final String TCSBIN = "00000006-0000-1000-8000-00805f9b34fb"; - public static final String TCSAT = "00000008-0000-1000-8000-00805f9b34fb"; - public static final String OBJECT_EXCHANGE_PROTOCOL_OBEX = "00000009-0000-1000-8000-00805f9b34fb"; - public static final String IP = "0000000a-0000-1000-8000-00805f9b34fb"; - public static final String FTP = "0000000c-0000-1000-8000-00805f9b34fb"; - public static final String HTTP = "0000000e-0000-1000-8000-00805f9b34fb"; - public static final String WSP = "0000000f-0000-1000-8000-00805f9b34fb"; - public static final String BNEP_SVC = "00000010-0000-1000-8000-00805f9b34fb"; - public static final String UPNP_PROTOCOL = "00000011-0000-1000-8000-00805f9b34fb"; - public static final String HIDP = "00000012-0000-1000-8000-00805f9b34fb"; - public static final String HARDCOPY_CONTROL_CHANNEL_PROTOCOL = "00000014-0000-1000-8000-00805f9b34fb"; - public static final String HARDCOPY_DATA_CHANNEL_PROTOCOL = "00000016-0000-1000-8000-00805f9b34fb"; - public static final String HARDCOPY_NOTIFICATION_PROTOCOL = "00000017-0000-1000-8000-00805f9b34fb"; - public static final String VCTP_PROTOCOL = "00000019-0000-1000-8000-00805f9b34fb"; - public static final String VDTP_PROTOCOL = "0000001b-0000-1000-8000-00805f9b34fb"; - public static final String CMPT_PROTOCOL = "0000001d-0000-1000-8000-00805f9b34fb"; - public static final String UDI_C_PLANE_PROTOCOL = "0000001e-0000-1000-8000-00805f9b34fb"; - public static final String MCAP_CONTROL_CHANNEL = "0000001f-0000-1000-8000-00805f9b34fb"; - public static final String MCAP_DATA_CHANNEL = "00000100-0000-1000-8000-00805f9b34fb"; - public static final String L2CAP = "00001000-0000-1000-8000-00805f9b34fb"; - public static final String SERVICE_DISCOVERY_SERVER = "00001001-0000-1000-8000-00805f9b34fb"; - public static final String BROWSE_GROUP_DESCRIPTOR = "00001002-0000-1000-8000-00805f9b34fb"; - public static final String PUBLIC_BROWSE_GROUP = "00001101-0000-1000-8000-00805f9b34fb"; - public static final String SPP = "00001102-0000-1000-8000-00805f9b34fb"; - public static final String LAN_ACCESS_USING_PPP = "00001103-0000-1000-8000-00805f9b34fb"; - public static final String DUN_GW = "00001104-0000-1000-8000-00805f9b34fb"; - public static final String OBEX_SYNC = "00001105-0000-1000-8000-00805f9b34fb"; - public static final String OBEX_OBJECT_PUSH = "00001106-0000-1000-8000-00805f9b34fb"; - public static final String OBEX_FILE_TRANSFER = "00001107-0000-1000-8000-00805f9b34fb"; - public static final String IRMC_SYNC_COMMAND = "00001108-0000-1000-8000-00805f9b34fb"; - public static final String HSP_HS = "00001109-0000-1000-8000-00805f9b34fb"; - public static final String CORDLESS_TELEPHONY = "0000110a-0000-1000-8000-00805f9b34fb"; - public static final String AUDIO_SOURCE = "0000110b-0000-1000-8000-00805f9b34fb"; - public static final String AUDIO_SINK = "0000110c-0000-1000-8000-00805f9b34fb"; - public static final String AV_REMOTE_CONTROL_TARGET = "0000110d-0000-1000-8000-00805f9b34fb"; - public static final String ADVANCED_AUDIO = "0000110e-0000-1000-8000-00805f9b34fb"; - public static final String AVRCP_REMOTE = "0000110f-0000-1000-8000-00805f9b34fb"; - public static final String VIDEO_CONFERENCING = "00001110-0000-1000-8000-00805f9b34fb"; - public static final String INTERCOM = "00001111-0000-1000-8000-00805f9b34fb"; - public static final String FAX = "00001112-0000-1000-8000-00805f9b34fb"; - public static final String HEADSET_PROFILE_HSP_AUDIO_GATEWAY = "00001113-0000-1000-8000-00805f9b34fb"; - public static final String WAP = "00001114-0000-1000-8000-00805f9b34fb"; - public static final String WAP_CLIENT = "00001115-0000-1000-8000-00805f9b34fb"; - public static final String PANU = "00001116-0000-1000-8000-00805f9b34fb"; - public static final String NAP = "00001117-0000-1000-8000-00805f9b34fb"; - public static final String GN = "00001118-0000-1000-8000-00805f9b34fb"; - public static final String DIRECT_PRINTING = "00001119-0000-1000-8000-00805f9b34fb"; - public static final String REFERENCE_PRINTING = "0000111a-0000-1000-8000-00805f9b34fb"; - public static final String IMAGING = "0000111b-0000-1000-8000-00805f9b34fb"; - public static final String IMAGING_RESPONDER = "0000111c-0000-1000-8000-00805f9b34fb"; - public static final String IMAGING_AUTOMATIC_ARCHIVE = "0000111d-0000-1000-8000-00805f9b34fb"; - public static final String IMAGING_REFERENCE_OBJECTS = "0000111e-0000-1000-8000-00805f9b34fb"; - public static final String HANDS_FREE_PROFILE_HFP = "0000111f-0000-1000-8000-00805f9b34fb"; - public static final String HANDS_FREE_PROFILE_HFP_AUDIO_GATEWAY = "00001120-0000-1000-8000-00805f9b34fb"; - public static final String DIRECT_PRINTING_REFERENCE_OBJECTS = "00001121-0000-1000-8000-00805f9b34fb"; - public static final String REFLECTED_UI = "00001122-0000-1000-8000-00805f9b34fb"; - public static final String BASIC_PRINTING = "00001123-0000-1000-8000-00805f9b34fb"; - public static final String PRINTING_STATUS = "00001124-0000-1000-8000-00805f9b34fb"; - public static final String HID = "00001125-0000-1000-8000-00805f9b34fb"; - public static final String HARDCOPY_CABLE_REPLACEMENT = "00001126-0000-1000-8000-00805f9b34fb"; - public static final String HCR_PRINT = "00001127-0000-1000-8000-00805f9b34fb"; - public static final String HCR_SCAN = "00001128-0000-1000-8000-00805f9b34fb"; - public static final String COMMON_ISDN_ACCESS = "00001129-0000-1000-8000-00805f9b34fb"; - public static final String VIDEO_CONFERENCING_GATEWAY = "0000112a-0000-1000-8000-00805f9b34fb"; - public static final String UDIMT = "0000112b-0000-1000-8000-00805f9b34fb"; - public static final String UDITA = "0000112c-0000-1000-8000-00805f9b34fb"; - public static final String AUDIO_VIDEO = "0000112d-0000-1000-8000-00805f9b34fb"; - public static final String SIM_ACCESS = "0000112e-0000-1000-8000-00805f9b34fb"; - public static final String OBEX_PCE = "0000112f-0000-1000-8000-00805f9b34fb"; - public static final String OBEX_PSE = "00001130-0000-1000-8000-00805f9b34fb"; - public static final String OBEX_PBAP = "00001132-0000-1000-8000-00805f9b34fb"; - public static final String OBEX_MAS = "00001133-0000-1000-8000-00805f9b34fb"; - public static final String OBEX_MNS = "00001134-0000-1000-8000-00805f9b34fb"; - public static final String OBEX_MAP = "00001200-0000-1000-8000-00805f9b34fb"; - public static final String PNP = "00001201-0000-1000-8000-00805f9b34fb"; - public static final String GENERIC_NETWORKING = "00001202-0000-1000-8000-00805f9b34fb"; - public static final String GENERIC_FILE_TRANSFER = "00001203-0000-1000-8000-00805f9b34fb"; - public static final String GENERIC_AUDIO = "00001204-0000-1000-8000-00805f9b34fb"; - public static final String GENERIC_TELEPHONY = "00001205-0000-1000-8000-00805f9b34fb"; - public static final String UPNP = "00001206-0000-1000-8000-00805f9b34fb"; - public static final String UPNP_IP = "00001300-0000-1000-8000-00805f9b34fb"; - public static final String ESDP_UPNP_IP_PAN = "00001301-0000-1000-8000-00805f9b34fb"; - public static final String ESDP_UPNP_IP_LAP = "00001302-0000-1000-8000-00805f9b34fb"; - public static final String ESDP_UPNP_L2CAP = "00001303-0000-1000-8000-00805f9b34fb"; - public static final String VIDEO_DISTRIBUTION_PROFILE_VDP_SOURCE = "00001304-0000-1000-8000-00805f9b34fb"; - public static final String VIDEO_DISTRIBUTION_PROFILE_VDP_SINK = "00001305-0000-1000-8000-00805f9b34fb"; - public static final String VIDEO_DISTRIBUTION_PROFILE_VDP = "00001400-0000-1000-8000-00805f9b34fb"; - public static final String HEALTH_DEVICE_PROFILE_HDP = "00001401-0000-1000-8000-00805f9b34fb"; - public static final String HEALTH_DEVICE_PROFILE_HDP_SOURCE = "00001402-0000-1000-8000-00805f9b34fb"; - public static final String HEALTH_DEVICE_PROFILE_HDP_SINK = "00001800-0000-1000-8000-00805f9b34fb"; - public static final String GAP = "00001801-0000-1000-8000-00805f9b34fb"; - public static final String GATT = "00001802-0000-1000-8000-00805f9b34fb"; - public static final String IMMEDIATE_ALERT = "00001803-0000-1000-8000-00805f9b34fb"; - public static final String LINK_LOSS = "00001804-0000-1000-8000-00805f9b34fb"; - public static final String TX_POWER = "00001809-0000-1000-8000-00805f9b34fb"; - public static final String HEALTH_THERMOMETER = "0000180a-0000-1000-8000-00805f9b34fb"; - public static final String DEVICE_INFORMATION = "0000180d-0000-1000-8000-00805f9b34fb"; - public static final String HEART_RATE = "00001816-0000-1000-8000-00805f9b34fb"; - public static final String CYCLING_SC = "00002902-0000-1000-8000-00805f9b34fb"; - public static final String CLIENT_CHARACTERISTIC_CONFIG = "00002a00-0000-1000-8000-00805f9b34fb"; - public static final String DEVICE_NAME = "00002a01-0000-1000-8000-00805f9b34fb"; - public static final String APPEARANCE = "00002a02-0000-1000-8000-00805f9b34fb"; - public static final String PERIPHERAL_PRIVACY_FLAG = "00002a03-0000-1000-8000-00805f9b34fb"; - public static final String RECONNECTION_ADDRESS = "00002a04-0000-1000-8000-00805f9b34fb"; - public static final String PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS = "00002a05-0000-1000-8000-00805f9b34fb"; - public static final String SERVICE_CHANGED = "00002a06-0000-1000-8000-00805f9b34fb"; - public static final String ALERT_LEVEL = "00002a07-0000-1000-8000-00805f9b34fb"; - public static final String TX_POWER_LEVEL = "00002a08-0000-1000-8000-00805f9b34fb"; - public static final String DATE_TIME = "00002a09-0000-1000-8000-00805f9b34fb"; - public static final String DAY_OF_WEEK = "00002a0a-0000-1000-8000-00805f9b34fb"; - public static final String DAY_DATE_TIME = "00002a0c-0000-1000-8000-00805f9b34fb"; - public static final String EXACT_TIME_256 = "00002a0d-0000-1000-8000-00805f9b34fb"; - public static final String DST_OFFSET = "00002a0e-0000-1000-8000-00805f9b34fb"; - public static final String TIME_ZONE = "00002a0f-0000-1000-8000-00805f9b34fb"; - public static final String LOCAL_TIME_INFORMATION = "00002a11-0000-1000-8000-00805f9b34fb"; - public static final String TIME_WITH_DST = "00002a12-0000-1000-8000-00805f9b34fb"; - public static final String TIME_ACCURACY = "00002a13-0000-1000-8000-00805f9b34fb"; - public static final String TIME_SOURCE = "00002a14-0000-1000-8000-00805f9b34fb"; - public static final String REFERENCE_TIME_INFORMATION = "00002a16-0000-1000-8000-00805f9b34fb"; - public static final String TIME_UPDATE_CONTROL_POINT = "00002a17-0000-1000-8000-00805f9b34fb"; - public static final String TIME_UPDATE_STATE = "00002a1c-0000-1000-8000-00805f9b34fb"; - public static final String TEMPERATURE_MEASUREMENT = "00002a1d-0000-1000-8000-00805f9b34fb"; - public static final String TEMPERATURE_TYPE = "00002a1e-0000-1000-8000-00805f9b34fb"; - public static final String INTERMEDIATE_TEMPERATURE = "00002a21-0000-1000-8000-00805f9b34fb"; - public static final String MEASUREMENT_INTERVAL = "00002a23-0000-1000-8000-00805f9b34fb"; - public static final String SYSTEM_ID = "00002a24-0000-1000-8000-00805f9b34fb"; - public static final String MODEL_NUMBER_STRING = "00002a25-0000-1000-8000-00805f9b34fb"; - public static final String SERIAL_NUMBER_STRING = "00002a26-0000-1000-8000-00805f9b34fb"; - public static final String FIRMWARE_REVISION_STRING = "00002a27-0000-1000-8000-00805f9b34fb"; - public static final String HARDWARE_REVISION_STRING = "00002a28-0000-1000-8000-00805f9b34fb"; - public static final String SOFTWARE_REVISION_STRING = "00002a29-0000-1000-8000-00805f9b34fb"; - public static final String MANUFACTURER_NAME_STRING = "00002a2a-0000-1000-8000-00805f9b34fb"; - public static final String IEEE_1107320601_REGULATORY = "00002a2b-0000-1000-8000-00805f9b34fb"; - public static final String CURRENT_TIME = "00002a35-0000-1000-8000-00805f9b34fb"; - public static final String BLOOD_PRESSURE_MEASUREMENT = "00002a36-0000-1000-8000-00805f9b34fb"; - public static final String INTERMEDIATE_CUFF_PRESSURE = "00002a37-0000-1000-8000-00805f9b34fb"; - public static final String HEART_RATE_MEASUREMENT = "00002a38-0000-1000-8000-00805f9b34fb"; - public static final String BODY_SENSOR_LOCATION = "00002a39-0000-1000-8000-00805f9b34fb"; - public static final String HEART_RATE_CONTROL_POINT = "00002a3f-0000-1000-8000-00805f9b34fb"; - public static final String ALERT_STATUS = "00002a40-0000-1000-8000-00805f9b34fb"; - public static final String RINGER_CONTROL_POINT = "00002a41-0000-1000-8000-00805f9b34fb"; - public static final String RINGER_SETTING = "00002a42-0000-1000-8000-00805f9b34fb"; - public static final String ALERT_CATEGORY_ID_BIT_MASK = "00002a43-0000-1000-8000-00805f9b34fb"; - public static final String ALERT_CATEGORY_ID = "00002a44-0000-1000-8000-00805f9b34fb"; - public static final String ALERT_NOTIFICATION_CONTROL_POINT = "00002a45-0000-1000-8000-00805f9b34fb"; - public static final String UNREAD_ALERT_STATUS = "00002a46-0000-1000-8000-00805f9b34fb"; - public static final String NEW_ALERT = "00002a47-0000-1000-8000-00805f9b34fb"; - public static final String SUPPORTED_NEW_ALERT_CATEGORY = "00002a48-0000-1000-8000-00805f9b34fb"; - public static final String SUPPORTED_UNREAD_ALERT_CATEGORY = "00002a49-0000-1000-8000-00805f9b34fb"; - public static final String BLOOD_PRESSURE_FEATURE = "00002a50-0000-1000-8000-00805f9b34fb"; - public static final String PNPID = "00002a55-0000-1000-8000-00805f9b34fb"; - public static final String SC_CONTROL_POINT = "00002a5b-0000-1000-8000-00805f9b34fb"; - public static final String CSC_MEASUREMENT = "00002a5c-0000-1000-8000-00805f9b34fb"; - public static final String CSC_FEATURE = "00002a5d-0000-1000-8000-00805f9b34fb"; - public static final String SENSOR_LOCATION = "831c4071-7bc8-4a9c-a01c-15df25a4adbc"; - public static final String ACTIVESYNC = "831c4071-7bc8-4a9c-a01c-15df25a4adbc"; - public static final String ESTIMOTE_SERVICE = "b9403000-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_UUID = "b9403003-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_MAJOR = "b9403001-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_MINOR = "b9403002-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_BATTERY = "b9403041-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_TEMPERATURE = "b9403021-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_POWER = "b9403011-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_ADVERTISING_INTERVAL = "b9403012-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_VERSION_SERVICE = "b9404000-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_SOFTWARE_VERSION = "b9404001-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_HARDWARE_VERSION = "b9404002-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_AUTHENTICATION_SERVICE = "b9402000-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_ADVERTISING_SEED = "b9402001-f5f8-466e-aff9-25556b57fe6d"; - public static final String ESTIMOTE_ADVERTISING_VECTOR = "b9402002-f5f8-466e-aff9-25556b57fe6d"; - - - - private final static Map sGattAttributesMap = populateGattAttributesMap(); - - public static String getAttributeName(String uuid, String fallback){ - final String name = sGattAttributesMap.get(uuid.toLowerCase(Locale.US)); - return name == null ? fallback : name; - } - - private static Map populateGattAttributesMap() { - final Map map = new HashMap(); - - map.put(BASE_GUID , "Service Discovery Protocol (SDP)"); - map.put(SERVICE_DISCOVERY_PROTOCOL_SDP , "User Datagram Protocol (UDP)"); - map.put(USER_DATAGRAM_PROTOCOL_UDP , "Radio Frequency Communication Protocol (RFCOMM)"); - map.put(RADIO_FREQUENCY_COMMUNICATION_PROTOCOL_RFCOMM , "TCP"); - map.put(TCP , "TCSBIN"); - map.put(TCSBIN , "TCSAT"); - map.put(TCSAT , "Object Exchange Protocol (OBEX)"); - map.put(OBJECT_EXCHANGE_PROTOCOL_OBEX , "IP"); - map.put(IP , "FTP"); - map.put(FTP , "HTTP"); - map.put(HTTP , "WSP"); - map.put(WSP , "BNEP_SVC"); - map.put(BNEP_SVC , "UPNP Protocol"); - map.put(UPNP_PROTOCOL , "HIDP"); - map.put(HIDP , "Hardcopy Control Channel Protocol"); - map.put(HARDCOPY_CONTROL_CHANNEL_PROTOCOL , "Hardcopy Data Channel Protocol"); - map.put(HARDCOPY_DATA_CHANNEL_PROTOCOL , "Hardcopy Notification Protocol"); - map.put(HARDCOPY_NOTIFICATION_PROTOCOL , "VCTP Protocol"); - map.put(VCTP_PROTOCOL , "VDTP Protocol"); - map.put(VDTP_PROTOCOL , "CMPT Protocol"); - map.put(CMPT_PROTOCOL , "UDI C Plane Protocol"); - map.put(UDI_C_PLANE_PROTOCOL , "MCAP Control Channel"); - map.put(MCAP_CONTROL_CHANNEL , "MCAP Data Channel"); - map.put(MCAP_DATA_CHANNEL , "L2CAP"); - map.put(L2CAP , "Service Discovery Server"); - map.put(SERVICE_DISCOVERY_SERVER , "Browse Group Descriptor"); - map.put(BROWSE_GROUP_DESCRIPTOR , "Public Browse Group"); - map.put(PUBLIC_BROWSE_GROUP , "SPP"); - map.put(SPP , "LAN Access Using PPP"); - map.put(LAN_ACCESS_USING_PPP , "DUN_GW"); - map.put(DUN_GW , "OBEX_SYNC"); - map.put(OBEX_SYNC , "OBEX Object Push"); - map.put(OBEX_OBJECT_PUSH , "OBEX File Transfer"); - map.put(OBEX_FILE_TRANSFER , "IrMC Sync Command"); - map.put(IRMC_SYNC_COMMAND , "HSP_HS"); - map.put(HSP_HS , "Cordless Telephony"); - map.put(CORDLESS_TELEPHONY , "Audio Source"); - map.put(AUDIO_SOURCE , "Audio Sink"); - map.put(AUDIO_SINK , "AV Remote Control Target"); - map.put(AV_REMOTE_CONTROL_TARGET , "ADVANCED_AUDIO"); - map.put(ADVANCED_AUDIO , "AVRCP_REMOTE"); - map.put(AVRCP_REMOTE , "Video Conferencing"); - map.put(VIDEO_CONFERENCING , "Intercom"); - map.put(INTERCOM , "FAX"); - map.put(FAX , "Headset Profile (HSP) - Audio Gateway"); - map.put(HEADSET_PROFILE_HSP_AUDIO_GATEWAY , "WAP"); - map.put(WAP , "WAP Client"); - map.put(WAP_CLIENT , "PANU"); - map.put(PANU , "NAP"); - map.put(NAP , "GN"); - map.put(GN , "Direct Printing"); - map.put(DIRECT_PRINTING , "Reference Printing"); - map.put(REFERENCE_PRINTING , "Imaging"); - map.put(IMAGING , "Imaging Responder"); - map.put(IMAGING_RESPONDER , "Imaging Automatic Archive"); - map.put(IMAGING_AUTOMATIC_ARCHIVE , "Imaging Reference Objects"); - map.put(IMAGING_REFERENCE_OBJECTS , "Hands Free Profile (HFP)"); - map.put(HANDS_FREE_PROFILE_HFP , "Hands Free Profile (HFP) – Audio Gateway"); - map.put(HANDS_FREE_PROFILE_HFP_AUDIO_GATEWAY , "Direct Printing Reference Objects"); - map.put(DIRECT_PRINTING_REFERENCE_OBJECTS , "Reflected UI"); - map.put(REFLECTED_UI , "Basic Printing"); - map.put(BASIC_PRINTING , "Printing Status"); - map.put(PRINTING_STATUS , "HID"); - map.put(HID , "Hardcopy Cable Replacement"); - map.put(HARDCOPY_CABLE_REPLACEMENT , "HCR Print"); - map.put(HCR_PRINT , "HCR Scan"); - map.put(HCR_SCAN , "Common ISDN Access"); - map.put(COMMON_ISDN_ACCESS , "Video Conferencing Gateway"); - map.put(VIDEO_CONFERENCING_GATEWAY , "UDIMT"); - map.put(UDIMT , "UDITA"); - map.put(UDITA , "Audio Video"); - map.put(AUDIO_VIDEO , "SIM Access"); - map.put(SIM_ACCESS , "OBEX PCE"); - map.put(OBEX_PCE , "OBEX PSE"); - map.put(OBEX_PSE , "OBEX PBAP"); - map.put(OBEX_PBAP , "OBEX MAS"); - map.put(OBEX_MAS , "OBEX MNS"); - map.put(OBEX_MNS , "OBEX MAP"); - map.put(OBEX_MAP , "PNP"); - map.put(PNP , "Generic Networking"); - map.put(GENERIC_NETWORKING , "Generic File Transfer"); - map.put(GENERIC_FILE_TRANSFER , "Generic Audio"); - map.put(GENERIC_AUDIO , "Generic Telephony"); - map.put(GENERIC_TELEPHONY , "UPNP"); - map.put(UPNP , "UPNP IP"); - map.put(UPNP_IP , "ESDP UPnP IP PAN"); - map.put(ESDP_UPNP_IP_PAN , "ESDP UPnP IP LAP"); - map.put(ESDP_UPNP_IP_LAP , "ESDP Upnp L2CAP"); - map.put(ESDP_UPNP_L2CAP , "Video Distribution Profile (VDP) - Source"); - map.put(VIDEO_DISTRIBUTION_PROFILE_VDP_SOURCE , "Video Distribution Profile (VDP) - Sink"); - map.put(VIDEO_DISTRIBUTION_PROFILE_VDP_SINK , "Video Distribution Profile (VDP)"); - map.put(VIDEO_DISTRIBUTION_PROFILE_VDP , "Health Device Profile (HDP)"); - map.put(HEALTH_DEVICE_PROFILE_HDP , "Health Device Profile (HDP) - Source"); - map.put(HEALTH_DEVICE_PROFILE_HDP_SOURCE , "Health Device Profile (HDP) - Sink"); - map.put(HEALTH_DEVICE_PROFILE_HDP_SINK , "GAP"); - map.put(GAP , "GATT"); - map.put(GATT , "IMMEDIATE_ALERT"); - map.put(IMMEDIATE_ALERT , "LINK_LOSS"); - map.put(LINK_LOSS , "TX_POWER"); - map.put(TX_POWER , "Health Thermometer"); - map.put(HEALTH_THERMOMETER , "Device Information"); - map.put(DEVICE_INFORMATION , "HEART_RATE"); - map.put(HEART_RATE , "CYCLING_SC"); - map.put(CYCLING_SC , "CLIENT_CHARACTERISTIC_CONFIG"); - map.put(CLIENT_CHARACTERISTIC_CONFIG , "Device Name"); - map.put(DEVICE_NAME , "Appearance"); - map.put(APPEARANCE , "Peripheral Privacy Flag"); - map.put(PERIPHERAL_PRIVACY_FLAG , "Reconnection Address"); - map.put(RECONNECTION_ADDRESS , "Peripheral Preferred Connection Parameters"); - map.put(PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS , "Service Changed"); - map.put(SERVICE_CHANGED , "Alert Level"); - map.put(ALERT_LEVEL , "Tx Power Level"); - map.put(TX_POWER_LEVEL , "Date Time"); - map.put(DATE_TIME , "Day of Week"); - map.put(DAY_OF_WEEK , "Day Date Time"); - map.put(DAY_DATE_TIME , "Exact Time 256"); - map.put(EXACT_TIME_256 , "DST Offset"); - map.put(DST_OFFSET , "Time Zone"); - map.put(TIME_ZONE , "Local Time Information"); - map.put(LOCAL_TIME_INFORMATION , "Time with DST"); - map.put(TIME_WITH_DST , "Time Accuracy"); - map.put(TIME_ACCURACY , "Time Source"); - map.put(TIME_SOURCE , "Reference Time Information"); - map.put(REFERENCE_TIME_INFORMATION , "Time Update Control Point"); - map.put(TIME_UPDATE_CONTROL_POINT , "Time Update State"); - map.put(TIME_UPDATE_STATE , "Temperature Measurement"); - map.put(TEMPERATURE_MEASUREMENT , "Temperature Type"); - map.put(TEMPERATURE_TYPE , "Intermediate Temperature"); - map.put(INTERMEDIATE_TEMPERATURE , "Measurement Interval"); - map.put(MEASUREMENT_INTERVAL , "System ID"); - map.put(SYSTEM_ID , "Model Number String"); - map.put(MODEL_NUMBER_STRING , "Serial Number String"); - map.put(SERIAL_NUMBER_STRING , "Firmware Revision String"); - map.put(FIRMWARE_REVISION_STRING , "Hardware Revision String"); - map.put(HARDWARE_REVISION_STRING , "Software Revision String"); - map.put(SOFTWARE_REVISION_STRING , "Manufacturer Name String"); - map.put(MANUFACTURER_NAME_STRING , "IEEE 11073-20601 Regulatory"); - map.put(IEEE_1107320601_REGULATORY , "Current Time"); - map.put(CURRENT_TIME , "Blood Pressure Measurement"); - map.put(BLOOD_PRESSURE_MEASUREMENT , "Intermediate Cuff Pressure"); - map.put(INTERMEDIATE_CUFF_PRESSURE , "Heart Rate Measurement"); - map.put(HEART_RATE_MEASUREMENT , "Body Sensor Location"); - map.put(BODY_SENSOR_LOCATION , "Heart Rate Control Point"); - map.put(HEART_RATE_CONTROL_POINT , "Alert Status"); - map.put(ALERT_STATUS , "Ringer Control Point"); - map.put(RINGER_CONTROL_POINT , "Ringer Setting"); - map.put(RINGER_SETTING , "Alert Category ID Bit Mask"); - map.put(ALERT_CATEGORY_ID_BIT_MASK , "Alert Category ID"); - map.put(ALERT_CATEGORY_ID , "Alert Notification Control Point"); - map.put(ALERT_NOTIFICATION_CONTROL_POINT , "Unread Alert Status"); - map.put(UNREAD_ALERT_STATUS , "New Alert"); - map.put(NEW_ALERT , "Supported New Alert Category"); - map.put(SUPPORTED_NEW_ALERT_CATEGORY , "Supported Unread Alert Category"); - map.put(SUPPORTED_UNREAD_ALERT_CATEGORY , "Blood Pressure Feature"); - map.put(BLOOD_PRESSURE_FEATURE , "PNPID"); - map.put(PNPID , "SC_CONTROL_POINT"); - map.put(SC_CONTROL_POINT , "CSC_MEASUREMENT"); - map.put(CSC_MEASUREMENT , "CSC_FEATURE"); - map.put(CSC_FEATURE , "SENSOR_LOCATION"); - map.put(SENSOR_LOCATION , "ActiveSync"); - map.put(ACTIVESYNC , "ActiveSync"); - map.put(ESTIMOTE_SERVICE , "Estimote Service"); - map.put(ESTIMOTE_UUID , "Estimote UUID"); - map.put(ESTIMOTE_MAJOR , "Estimote Major"); - map.put(ESTIMOTE_MINOR , "Estimote Minor"); - map.put(ESTIMOTE_BATTERY , "Estimote Battery"); - map.put(ESTIMOTE_TEMPERATURE , "Estimote Temperature"); - map.put(ESTIMOTE_POWER , "Estimote Power"); - map.put(ESTIMOTE_ADVERTISING_INTERVAL , "Estimote Advertising Interval"); - map.put(ESTIMOTE_VERSION_SERVICE , "Estimote Version Service"); - map.put(ESTIMOTE_SOFTWARE_VERSION , "Estimote Software Version"); - map.put(ESTIMOTE_HARDWARE_VERSION , "Estimote Hardware Version"); - map.put(ESTIMOTE_AUTHENTICATION_SERVICE , "Estimote Authentication Service"); - map.put(ESTIMOTE_ADVERTISING_SEED , "Estimote Advertising Seed"); - map.put(ESTIMOTE_ADVERTISING_VECTOR , "Estimote Advertising Vector"); - - - - return map; - } -} diff --git a/library/src/uk/co/alt236/bluetoothlelib/util/AdRecordUtils.java b/library/src/uk/co/alt236/bluetoothlelib/util/AdRecordUtils.java deleted file mode 100644 index 2509751..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/util/AdRecordUtils.java +++ /dev/null @@ -1,120 +0,0 @@ -package uk.co.alt236.bluetoothlelib.util; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; -import android.annotation.SuppressLint; -import android.util.SparseArray; - -public class AdRecordUtils { - - public static String getRecordDataAsString(final AdRecord nameRecord) { - if(nameRecord == null){return new String();} - return new String(nameRecord.getData()); - } - - public static byte[] getServiceData(final AdRecord serviceData) { - if (serviceData == null) {return null;} - if (serviceData.getType() != AdRecord.TYPE_SERVICE_DATA) return null; - - final byte[] raw = serviceData.getData(); - //Chop out the uuid - return Arrays.copyOfRange(raw, 2, raw.length); - } - - public static int getServiceDataUuid(final AdRecord serviceData) { - if (serviceData == null) {return -1;} - if (serviceData.getType() != AdRecord.TYPE_SERVICE_DATA) return -1; - - final byte[] raw = serviceData.getData(); - //Find UUID data in byte array - int uuid = (raw[1] & 0xFF) << 8; - uuid += (raw[0] & 0xFF); - - return uuid; - } - - /* - * Read out all the AD structures from the raw scan record - */ - public static List parseScanRecordAsList(final byte[] scanRecord) { - final List records = new ArrayList(); - - int index = 0; - while (index < scanRecord.length) { - final int length = scanRecord[index++]; - //Done once we run out of records - if (length == 0) break; - - final int type = ByteUtils.getIntFromByte(scanRecord[index]); - - //Done if our record isn't a valid type - if (type == 0) break; - - final byte[] data = Arrays.copyOfRange(scanRecord, index+1, index+length); - - records.add(new AdRecord(length, type, data)); - - //Advance - index += length; - } - - return Collections.unmodifiableList(records); - } - - @SuppressLint("UseSparseArrays") - public static Map parseScanRecordAsMap(final byte[] scanRecord) { - final Map records = new HashMap(); - - int index = 0; - while (index < scanRecord.length) { - final int length = scanRecord[index++]; - //Done once we run out of records - if (length == 0) break; - - final int type = ByteUtils.getIntFromByte(scanRecord[index]); - - //Done if our record isn't a valid type - if (type == 0) break; - - final byte[] data = Arrays.copyOfRange(scanRecord, index+1, index+length); - - records.put(type, new AdRecord(length, type, data)); - - //Advance - index += length; - } - - return Collections.unmodifiableMap(records); - } - - public static SparseArray parseScanRecordAsSparseArray(byte[] scanRecord) { - final SparseArray records = new SparseArray(); - - int index = 0; - while (index < scanRecord.length) { - final int length = scanRecord[index++]; - //Done once we run out of records - if (length == 0) break; - - final int type = ByteUtils.getIntFromByte(scanRecord[index]); - - //Done if our record isn't a valid type - if (type == 0) break; - - final byte[] data = Arrays.copyOfRange(scanRecord, index+1, index+length); - - records.put(type, new AdRecord(length, type, data)); - - //Advance - index += length; - } - - return records; - } -} diff --git a/library/src/uk/co/alt236/bluetoothlelib/util/ByteUtils.java b/library/src/uk/co/alt236/bluetoothlelib/util/ByteUtils.java deleted file mode 100644 index 7745688..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/util/ByteUtils.java +++ /dev/null @@ -1,124 +0,0 @@ -package uk.co.alt236.bluetoothlelib.util; - -import java.nio.ByteBuffer; - -public class ByteUtils { - - /** The Constant HEXES. */ - private static final String HEXES = "0123456789ABCDEF"; - - /** - * Gets a pretty representation of a Byte Array as a HEX String. - * - * Sample output: [01, 30, FF, AA] - * - * @param array the array - * @return the string - */ - public static String byteArrayToHexString(final byte[] array){ - final StringBuffer sb = new StringBuffer(); - boolean firstEntry = true; - sb.append('['); - - for ( final byte b : array ) { - if(!firstEntry){ - sb.append(", "); - } - sb.append(HEXES.charAt((b & 0xF0) >> 4)); - sb.append(HEXES.charAt((b & 0x0F))); - firstEntry = false; - } - - sb.append(']'); - return sb.toString(); - } - - /** - * Checks to see if a byte arry starts with another byte array. - * - * @param array the array - * @param prefix the prefix - * @return true, if successful - */ - public static boolean doesArrayBeginWith(byte[] array, byte[] prefix){ - if(array.length < prefix.length){return false;} - - for(int i = 0; i < prefix.length; i++){ - if(array[i] != prefix[i]){ - return false; - } - } - - return true; - } - - /** - * Converts a byte array with a length of 2 into an int - * - * @param input the input - * @return the int from the array - */ - public static int getIntFrom2ByteArray(byte[] input){ - final byte[] result = new byte[4]; - - result[0] = 0; - result[1] = 0; - result[2] = input[0]; - result[3] = input[1]; - - return ByteUtils.getIntFromByteArray(result); - } - - /** - * Converts a byte to an int, preserving the sign. - * - * For example, FF will be converted to 255 and not -1. - * - * @param bite the bite - * @return the int from byte - */ - public static int getIntFromByte(final byte bite){ - return Integer.valueOf(bite & 0xFF); - } - - /** - * Converts a byte array to an int. - * - * @param bytes the bytes - * @return the int from byte array - */ - public static int getIntFromByteArray(final byte[] bytes) { - return ByteBuffer.wrap(bytes).getInt(); - } - - /** - * Converts a byte array to a long. - * - * @param bytes the bytes - * @return the long from byte array - */ - public static long getLongFromByteArray(final byte[] bytes) { - return ByteBuffer.wrap(bytes).getLong(); - } - - - /** - * Inverts an array - * - * @param array the array - * @return the byte[] - */ - public static byte[] invertArray(byte[] array){ - final int size = array.length; - byte temp; - - for (int i = 0; i < size/2; i++) - { - temp = array[i]; - array[i] = array[size-1 - i]; - array[size-1 - i] = temp; - } - - return array; - } -} diff --git a/library/src/uk/co/alt236/bluetoothlelib/util/IBeaconUtils.java b/library/src/uk/co/alt236/bluetoothlelib/util/IBeaconUtils.java deleted file mode 100644 index 2fe5e1c..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/util/IBeaconUtils.java +++ /dev/null @@ -1,89 +0,0 @@ -package uk.co.alt236.bluetoothlelib.util; - -import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; -import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; - -public class IBeaconUtils { - private static final double DISTANCE_THRESHOLD_WTF = 0.0; - private static final double DISTANCE_THRESHOLD_IMMEDIATE = 0.5; - private static final double DISTANCE_THRESHOLD_NEAR = 3.0; - - private static final byte[] MANUFACTURER_DATA_IBEACON_PREFIX = new byte[]{0x4C, 0x00, 0x02, 0x15}; - - public static IBeaconDistanceDescriptor getDistanceDescriptor(double accuracy){ - if(accuracy < DISTANCE_THRESHOLD_WTF){ - return IBeaconDistanceDescriptor.UNKNOWN; - } - - if(accuracy < DISTANCE_THRESHOLD_IMMEDIATE){ - return IBeaconDistanceDescriptor.IMMEDIATE; - } - - if(accuracy < DISTANCE_THRESHOLD_NEAR){ - return IBeaconDistanceDescriptor.NEAR; - } - - return IBeaconDistanceDescriptor.FAR; - } - - /** - * Calculates the accuracy of an RSSI reading. - * - * The code was taken from {@link http://stackoverflow.com/questions/20416218/understanding-ibeacon-distancing} - * - * @param txPower the calibrated TX power of an iBeacon - * @param rssi the RSSI value of the iBeacon - * @return - */ - public static double calculateAccuracy(int txPower, double rssi) { - if (rssi == 0) { - return -1.0; // if we cannot determine accuracy, return -1. - } - - double ratio = rssi*1.0/txPower; - if (ratio < 1.0) { - return Math.pow(ratio,10); - } - else { - final double accuracy = (0.89976)*Math.pow(ratio,7.7095) + 0.111; - return accuracy; - } - } - - /** - * Ascertains whether a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} is an iBeacon; - * - * @param device a {@link uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice} device. - * @return - */ - public static boolean isThisAnIBeacon(BluetoothLeDevice device){ - return isThisAnIBeacon( - device.getAdRecordStore().getRecordDataAsString(AdRecord.TYPE_MANUFACTURER_SPECIFIC_DATA).getBytes()); - } - - /** - * Ascertains whether a Manufacturer Data byte array belongs to an iBeacon; - * - * @param scanRecord a Bluetooth LE device's manufacturerData. - * @return - */ - public static boolean isThisAnIBeacon(byte[] manufacturerData){ - if(manufacturerData == null){return false;} - - // An iBeacon record must be at least 25 chars long - if(!(manufacturerData.length >= 25)){return false;} - - if(ByteUtils.doesArrayBeginWith(manufacturerData, MANUFACTURER_DATA_IBEACON_PREFIX)){ - return true; - } - - return false; - } - - public enum IBeaconDistanceDescriptor{ - IMMEDIATE, - NEAR, - FAR, - UNKNOWN, - } -} diff --git a/library/src/uk/co/alt236/bluetoothlelib/util/LimitedLinkHashMap.java b/library/src/uk/co/alt236/bluetoothlelib/util/LimitedLinkHashMap.java deleted file mode 100644 index a95cc35..0000000 --- a/library/src/uk/co/alt236/bluetoothlelib/util/LimitedLinkHashMap.java +++ /dev/null @@ -1,20 +0,0 @@ -package uk.co.alt236.bluetoothlelib.util; - -import java.util.LinkedHashMap; -import java.util.Map; - -public class LimitedLinkHashMap extends LinkedHashMap{ - private static final long serialVersionUID = -5375660288461724925L; - - private final int mMaxSize; - - public LimitedLinkHashMap(int maxSize){ - super(maxSize + 1, 1, false); - mMaxSize = maxSize; - } - - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return this.size() > mMaxSize; - } -} diff --git a/sample_app/.classpath b/sample_app/.classpath deleted file mode 100644 index c54e8eb..0000000 --- a/sample_app/.classpath +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/sample_app/.factorypath b/sample_app/.factorypath deleted file mode 100644 index 946a26f..0000000 --- a/sample_app/.factorypath +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/sample_app/.gitignore b/sample_app/.gitignore index 47ab45a..796b96d 100644 --- a/sample_app/.gitignore +++ b/sample_app/.gitignore @@ -1,6 +1 @@ -/bin -/gen -local.properties -.idea/ -lint.xml -/.apt_generated +/build diff --git a/sample_app/.project b/sample_app/.project deleted file mode 100644 index f0df143..0000000 --- a/sample_app/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - Bluetooth LE Scanner - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - diff --git a/sample_app/.settings/org.eclipse.jdt.apt.core.prefs b/sample_app/.settings/org.eclipse.jdt.apt.core.prefs deleted file mode 100644 index 7d52ece..0000000 --- a/sample_app/.settings/org.eclipse.jdt.apt.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.apt.aptEnabled=true -org.eclipse.jdt.apt.genSrcDir=.apt_generated -org.eclipse.jdt.apt.reconcileEnabled=true diff --git a/sample_app/.settings/org.eclipse.jdt.core.prefs b/sample_app/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index f2b863f..0000000 --- a/sample_app/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.processAnnotations=enabled -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/sample_app/build.gradle b/sample_app/build.gradle new file mode 100644 index 0000000..38e6eb0 --- /dev/null +++ b/sample_app/build.gradle @@ -0,0 +1,50 @@ +apply plugin: 'com.android.application' + +final int versionMajor = 1 +final int versionMinor = 0 +final int versionPatch = 0 +final int androidVersionCode = 5 + +final int targetSdk = rootProject.targetSdkVersion; +final int minSdkRed = rootProject.minSdkVersion; +final String semanticVersion = "${versionMajor}.${versionMinor}.${versionPatch}" + +repositories { + maven { + url "https://repo.commonsware.com.s3.amazonaws.com" + } +} + +dependencies { + compile 'com.jakewharton:butterknife:7.0.1' + compile 'com.android.support:appcompat-v7:22.2.0' + compile 'com.commonsware.cwac:merge:1.1.0' + compile fileTree(include: ['*.jar'], dir: 'libs') + compile project(':library') +} + +android { + compileSdkVersion rootProject.compileSdkVersion + buildToolsVersion rootProject.buildToolsVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + defaultConfig { + minSdkVersion minSdkRed + targetSdkVersion targetSdk + versionCode androidVersionCode + versionName semanticVersion + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + productFlavors { + } +} diff --git a/sample_app/libs/android-support-v4.jar b/sample_app/libs/android-support-v4.jar deleted file mode 100644 index 96644ed..0000000 Binary files a/sample_app/libs/android-support-v4.jar and /dev/null differ diff --git a/sample_app/libs/butterknife-4.0.1.jar b/sample_app/libs/butterknife-4.0.1.jar deleted file mode 100644 index e048516..0000000 Binary files a/sample_app/libs/butterknife-4.0.1.jar and /dev/null differ diff --git a/sample_app/libs/merge-1.0.1.jar b/sample_app/libs/merge-1.0.1.jar deleted file mode 100644 index fb49c15..0000000 Binary files a/sample_app/libs/merge-1.0.1.jar and /dev/null differ diff --git a/sample_app/libs/sacklist-1.0.0.jar b/sample_app/libs/sacklist-1.0.0.jar deleted file mode 100644 index 3a49a76..0000000 Binary files a/sample_app/libs/sacklist-1.0.0.jar and /dev/null differ diff --git a/sample_app/proguard-project.txt b/sample_app/proguard-rules.pro similarity index 62% rename from sample_app/proguard-project.txt rename to sample_app/proguard-rules.pro index f2fe155..f8c620f 100644 --- a/sample_app/proguard-project.txt +++ b/sample_app/proguard-rules.pro @@ -1,11 +1,8 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. +# in /home/alex/Dev/android-sdk-linux/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html diff --git a/sample_app/project.properties b/sample_app/project.properties deleted file mode 100644 index 3a06472..0000000 --- a/sample_app/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=Google Inc.:Google APIs:19 -android.library.reference.1=../library diff --git a/sample_app/res/layout/actionbar_progress_indeterminate.xml b/sample_app/res/layout/actionbar_progress_indeterminate.xml deleted file mode 100644 index ba1c316..0000000 --- a/sample_app/res/layout/actionbar_progress_indeterminate.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - \ No newline at end of file diff --git a/sample_app/res/layout/activity_details.xml b/sample_app/res/layout/activity_details.xml deleted file mode 100644 index e853847..0000000 --- a/sample_app/res/layout/activity_details.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - \ No newline at end of file diff --git a/sample_app/res/layout/list_item_view_device_info.xml b/sample_app/res/layout/list_item_view_device_info.xml deleted file mode 100644 index 1ae0aac..0000000 --- a/sample_app/res/layout/list_item_view_device_info.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sample_app/res/layout/list_item_view_textview.xml b/sample_app/res/layout/list_item_view_textview.xml deleted file mode 100644 index 1f00684..0000000 --- a/sample_app/res/layout/list_item_view_textview.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/sample_app/res/values/colors.xml b/sample_app/res/values/colors.xml deleted file mode 100644 index b8f6e52..0000000 --- a/sample_app/res/values/colors.xml +++ /dev/null @@ -1,3 +0,0 @@ - - #66e0e0e0 - diff --git a/sample_app/sample_app.iml b/sample_app/sample_app.iml new file mode 100644 index 0000000..756895c --- /dev/null +++ b/sample_app/sample_app.iml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample_app/AndroidManifest.xml b/sample_app/src/main/AndroidManifest.xml similarity index 66% rename from sample_app/AndroidManifest.xml rename to sample_app/src/main/AndroidManifest.xml index bfcd7a6..7546b22 100644 --- a/sample_app/AndroidManifest.xml +++ b/sample_app/src/main/AndroidManifest.xml @@ -1,43 +1,38 @@ - + xmlns:android="http://schemas.android.com/apk/res/android"> - - - - + + + android:required="false"/> + android:theme="@style/AppTheme"> + android:label="@string/app_name"> - + - + + android:label="@string/app_name"> - + + android:enabled="true"/> \ No newline at end of file diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/activities/DeviceControlActivity.java b/sample_app/src/main/java/uk/co/alt236/btlescan/activities/DeviceControlActivity.java new file mode 100644 index 0000000..5e3753b --- /dev/null +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/activities/DeviceControlActivity.java @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package uk.co.alt236.btlescan.activities; + +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.ExpandableListView; +import android.widget.SimpleExpandableListAdapter; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import butterknife.Bind; +import butterknife.ButterKnife; +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.R; +import uk.co.alt236.btlescan.services.BluetoothLeService; + +/** + * For a given BLE device, this Activity provides the user interface to connect, display data, + * and display GATT services and characteristics supported by the device. The Activity + * communicates with {@code BluetoothLeService}, which in turn interacts with the + * Bluetooth LE API. + */ +public class DeviceControlActivity extends AppCompatActivity { + public static final String EXTRA_DEVICE = "extra_device"; + private final static String TAG = DeviceControlActivity.class.getSimpleName(); + private static final String LIST_NAME = "NAME"; + private static final String LIST_UUID = "UUID"; + @Bind(R.id.gatt_services_list) + protected ExpandableListView mGattServicesList; + @Bind(R.id.connection_state) + protected TextView mConnectionState; + @Bind(R.id.uuid) + protected TextView mGattUUID; + @Bind(R.id.description) + protected TextView mGattUUIDDesc; + @Bind(R.id.data_as_string) + protected TextView mDataAsString; + @Bind(R.id.data_as_array) + protected TextView mDataAsArray; + private BluetoothGattCharacteristic mNotifyCharacteristic; + private BluetoothLeService mBluetoothLeService; + private List> mGattCharacteristics = new ArrayList<>(); + // If a given GATT characteristic is selected, check for supported features. This sample + // demonstrates 'Read' and 'Notify' features. See + // http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete + // list of supported characteristic features. + private final ExpandableListView.OnChildClickListener servicesListClickListner = new ExpandableListView.OnChildClickListener() { + @Override + public boolean onChildClick(final ExpandableListView parent, final View v, final int groupPosition, final int childPosition, final long id) { + if (mGattCharacteristics != null) { + final BluetoothGattCharacteristic characteristic = mGattCharacteristics.get(groupPosition).get(childPosition); + final int charaProp = characteristic.getProperties(); + if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) { + // If there is an active notification on a characteristic, clear + // it first so it doesn't update the data field on the user interface. + if (mNotifyCharacteristic != null) { + mBluetoothLeService.setCharacteristicNotification(mNotifyCharacteristic, false); + mNotifyCharacteristic = null; + } + mBluetoothLeService.readCharacteristic(characteristic); + } + if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { + mNotifyCharacteristic = characteristic; + mBluetoothLeService.setCharacteristicNotification(characteristic, true); + } + return true; + } + return false; + } + }; + private String mDeviceAddress; + // Code to manage Service lifecycle. + private final ServiceConnection mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(final ComponentName componentName, final IBinder service) { + mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService(); + if (!mBluetoothLeService.initialize()) { + Log.e(TAG, "Unable to initialize Bluetooth"); + finish(); + } + // Automatically connects to the device upon successful start-up initialization. + mBluetoothLeService.connect(mDeviceAddress); + } + + @Override + public void onServiceDisconnected(final ComponentName componentName) { + mBluetoothLeService = null; + } + }; + private String mDeviceName; + private boolean mConnected = false; + private String mExportString; + // Handles various events fired by the Service. + // ACTION_GATT_CONNECTED: connected to a GATT server. + // ACTION_GATT_DISCONNECTED: disconnected from a GATT server. + // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services. + // ACTION_DATA_AVAILABLE: received data from the device. + // this can be a result of read or notification operations. + private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + final String action = intent.getAction(); + if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { + mConnected = true; + updateConnectionState(R.string.connected); + invalidateOptionsMenu(); + } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { + mConnected = false; + updateConnectionState(R.string.disconnected); + invalidateOptionsMenu(); + clearUI(); + } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { + // Show all the supported services and characteristics on the user interface. + displayGattServices(mBluetoothLeService.getSupportedGattServices()); + } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) { + final String noData = getString(R.string.no_data); + final String uuid = intent.getStringExtra(BluetoothLeService.EXTRA_UUID_CHAR); + final byte[] dataArr = intent.getByteArrayExtra(BluetoothLeService.EXTRA_DATA_RAW); + + mGattUUID.setText(tryString(uuid, noData)); + mGattUUIDDesc.setText(GattAttributeResolver.getAttributeName(uuid, getString(R.string.unknown))); + mDataAsArray.setText(ByteUtils.byteArrayToHexString(dataArr)); + mDataAsString.setText(new String(dataArr)); + } + } + }; + + private void clearUI() { + mGattServicesList.setAdapter((SimpleExpandableListAdapter) null); + mGattUUID.setText(R.string.no_data); + mGattUUIDDesc.setText(R.string.no_data); + mDataAsArray.setText(R.string.no_data); + mDataAsString.setText(R.string.no_data); + } + + // Demonstrates how to iterate through the supported GATT Services/Characteristics. + // In this sample, we populate the data structure that is bound to the ExpandableListView + // on the UI. + private void displayGattServices(final List gattServices) { + if (gattServices == null) return; + generateExportString(gattServices); + + String uuid = null; + final String unknownServiceString = getResources().getString(R.string.unknown_service); + final String unknownCharaString = getResources().getString(R.string.unknown_characteristic); + final List> gattServiceData = new ArrayList<>(); + final List>> gattCharacteristicData = new ArrayList<>(); + mGattCharacteristics = new ArrayList<>(); + + // Loops through available GATT Services. + for (final BluetoothGattService gattService : gattServices) { + final Map currentServiceData = new HashMap<>(); + uuid = gattService.getUuid().toString(); + currentServiceData.put(LIST_NAME, GattAttributeResolver.getAttributeName(uuid, unknownServiceString)); + currentServiceData.put(LIST_UUID, uuid); + gattServiceData.add(currentServiceData); + + final List> gattCharacteristicGroupData = new ArrayList<>(); + final List gattCharacteristics = gattService.getCharacteristics(); + final List charas = new ArrayList<>(); + + // Loops through available Characteristics. + for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { + charas.add(gattCharacteristic); + final Map currentCharaData = new HashMap<>(); + uuid = gattCharacteristic.getUuid().toString(); + currentCharaData.put(LIST_NAME, GattAttributeResolver.getAttributeName(uuid, unknownCharaString)); + currentCharaData.put(LIST_UUID, uuid); + gattCharacteristicGroupData.add(currentCharaData); + } + + mGattCharacteristics.add(charas); + gattCharacteristicData.add(gattCharacteristicGroupData); + } + + final SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter( + this, + gattServiceData, + android.R.layout.simple_expandable_list_item_2, + new String[]{LIST_NAME, LIST_UUID}, + new int[]{android.R.id.text1, android.R.id.text2}, + gattCharacteristicData, + android.R.layout.simple_expandable_list_item_2, + new String[]{LIST_NAME, LIST_UUID}, + new int[]{android.R.id.text1, android.R.id.text2} + ); + + mGattServicesList.setAdapter(gattServiceAdapter); + invalidateOptionsMenu(); + } + + private void generateExportString(final List gattServices) { + final String unknownServiceString = getResources().getString(R.string.unknown_service); + final String unknownCharaString = getResources().getString(R.string.unknown_characteristic); + final StringBuilder exportBuilder = new StringBuilder(); + + exportBuilder.append("Device Name: "); + exportBuilder.append(mDeviceName); + exportBuilder.append('\n'); + exportBuilder.append("Device Address: "); + exportBuilder.append(mDeviceAddress); + exportBuilder.append('\n'); + exportBuilder.append('\n'); + + exportBuilder.append("Services:"); + exportBuilder.append("--------------------------"); + exportBuilder.append('\n'); + + String uuid = null; + for (final BluetoothGattService gattService : gattServices) { + uuid = gattService.getUuid().toString(); + + exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownServiceString)); + exportBuilder.append(" ("); + exportBuilder.append(uuid); + exportBuilder.append(')'); + exportBuilder.append('\n'); + + final List gattCharacteristics = gattService.getCharacteristics(); + for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { + uuid = gattCharacteristic.getUuid().toString(); + + exportBuilder.append('\t'); + exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownCharaString)); + exportBuilder.append(" ("); + exportBuilder.append(uuid); + exportBuilder.append(')'); + exportBuilder.append('\n'); + } + + exportBuilder.append('\n'); + exportBuilder.append('\n'); + } + + exportBuilder.append("--------------------------"); + exportBuilder.append('\n'); + + mExportString = exportBuilder.toString(); + } + + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_gatt_services); + + final Intent intent = getIntent(); + final BluetoothLeDevice device = intent.getParcelableExtra(EXTRA_DEVICE); + mDeviceName = device.getName(); + mDeviceAddress = device.getAddress(); + + ButterKnife.bind(this); + + // Sets up UI references. + ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress); + mGattServicesList.setOnChildClickListener(servicesListClickListner); + + getSupportActionBar().setTitle(mDeviceName); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + final Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); + bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE); + } + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + getMenuInflater().inflate(R.menu.gatt_services, menu); + if (mConnected) { + menu.findItem(R.id.menu_connect).setVisible(false); + menu.findItem(R.id.menu_disconnect).setVisible(true); + } else { + menu.findItem(R.id.menu_connect).setVisible(true); + menu.findItem(R.id.menu_disconnect).setVisible(false); + } + + if (mExportString == null) { + menu.findItem(R.id.menu_share).setVisible(false); + } else { + menu.findItem(R.id.menu_share).setVisible(true); + } + + return true; + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unbindService(mServiceConnection); + mBluetoothLeService = null; + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_connect: + mBluetoothLeService.connect(mDeviceAddress); + return true; + case R.id.menu_disconnect: + mBluetoothLeService.disconnect(); + return true; + case android.R.id.home: + onBackPressed(); + return true; + case R.id.menu_share: + final Intent intent = new Intent(android.content.Intent.ACTION_SEND); + final String subject = getString(R.string.exporter_email_device_services_subject, mDeviceName, mDeviceAddress); + + intent.setType("text/plain"); + intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject); + intent.putExtra(android.content.Intent.EXTRA_TEXT, mExportString); + + startActivity(Intent.createChooser( + intent, + getString(R.string.exporter_email_device_list_picker_text))); + + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + protected void onPause() { + super.onPause(); + unregisterReceiver(mGattUpdateReceiver); + } + + @Override + protected void onResume() { + super.onResume(); + registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter()); + if (mBluetoothLeService != null) { + final boolean result = mBluetoothLeService.connect(mDeviceAddress); + Log.d(TAG, "Connect request result=" + result); + } + } + + private void updateConnectionState(final int resourceId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + final int colourId; + + switch (resourceId) { + case R.string.connected: + colourId = android.R.color.holo_green_dark; + break; + case R.string.disconnected: + colourId = android.R.color.holo_red_dark; + break; + default: + colourId = android.R.color.black; + break; + } + + mConnectionState.setText(resourceId); + mConnectionState.setTextColor(getResources().getColor(colourId)); + } + }); + } + + private static IntentFilter makeGattUpdateIntentFilter() { + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); + intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); + intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE); + return intentFilter; + } + + private static String tryString(final String string, final String fallback) { + if (string == null) { + return fallback; + } else { + return string; + } + } +} \ No newline at end of file diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/activities/DeviceDetailsActivity.java b/sample_app/src/main/java/uk/co/alt236/btlescan/activities/DeviceDetailsActivity.java new file mode 100644 index 0000000..1e3e10f --- /dev/null +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/activities/DeviceDetailsActivity.java @@ -0,0 +1,240 @@ +package uk.co.alt236.btlescan.activities; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; + +import com.commonsware.cwac.merge.MergeAdapter; + +import java.util.Collection; +import java.util.Locale; + +import butterknife.Bind; +import butterknife.ButterKnife; +import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; +import uk.co.alt236.bluetoothlelib.device.BluetoothService; +import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconType; +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconUtils; +import uk.co.alt236.bluetoothlelib.device.beacon.ibeacon.IBeaconManufacturerData; +import uk.co.alt236.bluetoothlelib.resolvers.CompanyIdentifierResolver; +import uk.co.alt236.bluetoothlelib.util.AdRecordUtils; +import uk.co.alt236.bluetoothlelib.util.ByteUtils; +import uk.co.alt236.btlescan.R; +import uk.co.alt236.btlescan.util.TimeFormatter; + +public class DeviceDetailsActivity extends AppCompatActivity { + public static final String EXTRA_DEVICE = "extra_device"; + @Bind(android.R.id.list) + protected ListView mList; + @Nullable + @Bind(android.R.id.empty) + protected View mEmpty; + private BluetoothLeDevice mDevice; + + private void appendAdRecordView(final MergeAdapter adapter, final String title, final AdRecord record) { + final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_adrecord, null); + final TextView tvString = (TextView) lt.findViewById(R.id.data_as_string); + final TextView tvArray = (TextView) lt.findViewById(R.id.data_as_array); + final TextView tvTitle = (TextView) lt.findViewById(R.id.title); + + tvTitle.setText(title); + tvString.setText("'" + AdRecordUtils.getRecordDataAsString(record) + "'"); + tvArray.setText("'" + ByteUtils.byteArrayToHexString(record.getData()) + "'"); + + adapter.addView(lt); + } + + private void appendDeviceInfo(final MergeAdapter adapter, final BluetoothLeDevice device) { + final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_device_info, null); + final TextView tvName = (TextView) lt.findViewById(R.id.deviceName); + final TextView tvAddress = (TextView) lt.findViewById(R.id.deviceAddress); + final TextView tvClass = (TextView) lt.findViewById(R.id.deviceClass); + final TextView tvMajorClass = (TextView) lt.findViewById(R.id.deviceMajorClass); + final TextView tvServices = (TextView) lt.findViewById(R.id.deviceServiceList); + final TextView tvBondingState = (TextView) lt.findViewById(R.id.deviceBondingState); + + tvName.setText(device.getName()); + tvAddress.setText(device.getAddress()); + tvClass.setText(device.getBluetoothDeviceClassName()); + tvMajorClass.setText(device.getBluetoothDeviceMajorClassName()); + tvBondingState.setText(device.getBluetoothDeviceBondState()); + + final String supportedServices; + if(device.getBluetoothDeviceKnownSupportedServices().isEmpty()){ + supportedServices = getString(R.string.no_known_services); + } else { + final StringBuilder sb = new StringBuilder(); + + for(final BluetoothService service : device.getBluetoothDeviceKnownSupportedServices()){ + if(sb.length() > 0){ + sb.append(", "); + } + + sb.append(service); + } + supportedServices = sb.toString(); + } + + tvServices.setText(supportedServices); + + adapter.addView(lt); + } + + private void appendHeader(final MergeAdapter adapter, final String title) { + final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_header, null); + final TextView tvTitle = (TextView) lt.findViewById(R.id.title); + tvTitle.setText(title); + + adapter.addView(lt); + } + + private void appendIBeaconInfo(final MergeAdapter adapter, final IBeaconManufacturerData iBeaconData) { + final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_ibeacon_details, null); + final TextView tvCompanyId = (TextView) lt.findViewById(R.id.companyId); + final TextView tvAdvert = (TextView) lt.findViewById(R.id.advertisement); + final TextView tvUUID = (TextView) lt.findViewById(R.id.uuid); + final TextView tvMajor = (TextView) lt.findViewById(R.id.major); + final TextView tvMinor = (TextView) lt.findViewById(R.id.minor); + final TextView tvTxPower = (TextView) lt.findViewById(R.id.txpower); + + tvCompanyId.setText( + CompanyIdentifierResolver.getCompanyName(iBeaconData.getCompanyIdentifier(), getString(R.string.unknown)) + + " (" + hexEncode(iBeaconData.getCompanyIdentifier()) + ")"); + tvAdvert.setText(iBeaconData.getIBeaconAdvertisement() + " (" + hexEncode(iBeaconData.getIBeaconAdvertisement()) + ")"); + tvUUID.setText(iBeaconData.getUUID()); + tvMajor.setText(iBeaconData.getMajor() + " (" + hexEncode(iBeaconData.getMajor()) + ")"); + tvMinor.setText(iBeaconData.getMinor() + " (" + hexEncode(iBeaconData.getMinor()) + ")"); + tvTxPower.setText(iBeaconData.getCalibratedTxPower() + " (" + hexEncode(iBeaconData.getCalibratedTxPower()) + ")"); + + adapter.addView(lt); + } + + private void appendRssiInfo(final MergeAdapter adapter, final BluetoothLeDevice device) { + final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_rssi_info, null); + final TextView tvFirstTimestamp = (TextView) lt.findViewById(R.id.firstTimestamp); + final TextView tvFirstRssi = (TextView) lt.findViewById(R.id.firstRssi); + final TextView tvLastTimestamp = (TextView) lt.findViewById(R.id.lastTimestamp); + final TextView tvLastRssi = (TextView) lt.findViewById(R.id.lastRssi); + final TextView tvRunningAverageRssi = (TextView) lt.findViewById(R.id.runningAverageRssi); + + tvFirstTimestamp.setText(formatTime(device.getFirstTimestamp())); + tvFirstRssi.setText(formatRssi(device.getFirstRssi())); + tvLastTimestamp.setText(formatTime(device.getTimestamp())); + tvLastRssi.setText(formatRssi(device.getRssi())); + tvRunningAverageRssi.setText(formatRssi(device.getRunningAverageRssi())); + + adapter.addView(lt); + } + + private void appendSimpleText(final MergeAdapter adapter, final byte[] data) { + appendSimpleText(adapter, ByteUtils.byteArrayToHexString(data)); + } + + private void appendSimpleText(final MergeAdapter adapter, final String data) { + final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_textview, null); + final TextView tvData = (TextView) lt.findViewById(R.id.data); + + tvData.setText(data); + + adapter.addView(lt); + } + + + private String formatRssi(final double rssi) { + return getString(R.string.formatter_db, String.valueOf(rssi)); + } + + private String formatRssi(final int rssi) { + return getString(R.string.formatter_db, String.valueOf(rssi)); + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_details); + ButterKnife.bind(this); + + mList.setEmptyView(mEmpty); + + mDevice = getIntent().getParcelableExtra(EXTRA_DEVICE); + + pupulateDetails(mDevice); + } + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + getMenuInflater().inflate(R.menu.details, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_connect: + + final Intent intent = new Intent(this, DeviceControlActivity.class); + intent.putExtra(DeviceControlActivity.EXTRA_DEVICE, mDevice); + + startActivity(intent); + + break; + } + return true; + } + + private void pupulateDetails(final BluetoothLeDevice device) { + final MergeAdapter adapter = new MergeAdapter(); + + if (device == null) { + appendHeader(adapter, getString(R.string.header_device_info)); + appendSimpleText(adapter, getString(R.string.invalid_device_data)); + } else { + appendHeader(adapter, getString(R.string.header_device_info)); + appendDeviceInfo(adapter, device); + + appendHeader(adapter, getString(R.string.header_rssi_info)); + appendRssiInfo(adapter, device); + + appendHeader(adapter, getString(R.string.header_scan_record)); + appendSimpleText(adapter, device.getScanRecord()); + + final Collection adRecords = device.getAdRecordStore().getRecordsAsCollection(); + if (adRecords.size() > 0) { + appendHeader(adapter, getString(R.string.header_raw_ad_records)); + + for (final AdRecord record : adRecords) { + + appendAdRecordView( + adapter, + "#" + record.getType() + " " + record.getHumanReadableType(), + record); + } + } + + final boolean isIBeacon = BeaconUtils.getBeaconType(device) == BeaconType.IBEACON; + if (isIBeacon) { + final IBeaconManufacturerData iBeaconData = new IBeaconManufacturerData(device); + appendHeader(adapter, getString(R.string.header_ibeacon_data)); + appendIBeaconInfo(adapter, iBeaconData); + } + + } + mList.setAdapter(adapter); + } + + private static String formatTime(final long time) { + return TimeFormatter.getIsoDateTime(time); + } + + private static String hexEncode(final int integer) { + return "0x" + Integer.toHexString(integer).toUpperCase(Locale.US); + } +} diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/activities/MainActivity.java b/sample_app/src/main/java/uk/co/alt236/btlescan/activities/MainActivity.java new file mode 100644 index 0000000..153f522 --- /dev/null +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/activities/MainActivity.java @@ -0,0 +1,206 @@ +package uk.co.alt236.btlescan.activities; + +import android.app.AlertDialog; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.text.SpannableString; +import android.text.method.LinkMovementMethod; +import android.text.util.Linkify; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.TextView; + +import butterknife.Bind; +import butterknife.ButterKnife; +import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; +import uk.co.alt236.btlescan.R; +import uk.co.alt236.btlescan.adapters.LeDeviceListAdapter; +import uk.co.alt236.btlescan.containers.BluetoothLeDeviceStore; +import uk.co.alt236.btlescan.util.BluetoothLeScanner; +import uk.co.alt236.btlescan.util.BluetoothUtils; +import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor; + +public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { + @Bind(R.id.tvBluetoothLe) + protected TextView mTvBluetoothLeStatus; + @Bind(R.id.tvBluetoothStatus) + protected TextView mTvBluetoothStatus; + @Bind(R.id.tvItemCount) + protected TextView mTvItemCount; + @Bind(android.R.id.list) + protected ListView mList; + @Bind(android.R.id.empty) + protected View mEmpty; + + private BluetoothUtils mBluetoothUtils; + private BluetoothLeScanner mScanner; + private LeDeviceListAdapter mLeDeviceListAdapter; + private BluetoothLeDeviceStore mDeviceStore; + + private final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { + @Override + public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) { + + final BluetoothLeDevice deviceLe = new BluetoothLeDevice(device, rssi, scanRecord, System.currentTimeMillis()); + mDeviceStore.addDevice(deviceLe); + final EasyObjectCursor c = mDeviceStore.getDeviceCursor(); + + runOnUiThread(new Runnable() { + @Override + public void run() { + mLeDeviceListAdapter.swapCursor(c); + updateItemCount(mLeDeviceListAdapter.getCount()); + } + }); + } + }; + + private void displayAboutDialog() { + // REALLY REALLY LAZY LINKIFIED DIALOG + final int paddingSizeDp = 5; + final float scale = getResources().getDisplayMetrics().density; + final int dpAsPixels = (int) (paddingSizeDp * scale + 0.5f); + + final TextView textView = new TextView(this); + final SpannableString text = new SpannableString(getString(R.string.about_dialog_text)); + + textView.setText(text); + textView.setAutoLinkMask(RESULT_OK); + textView.setMovementMethod(LinkMovementMethod.getInstance()); + textView.setPadding(dpAsPixels, dpAsPixels, dpAsPixels, dpAsPixels); + + Linkify.addLinks(text, Linkify.ALL); + new AlertDialog.Builder(this) + .setTitle(R.string.menu_about) + .setCancelable(false) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(final DialogInterface dialog, final int id) { + } + }) + .setView(textView) + .show(); + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); + mList.setEmptyView(mEmpty); + mList.setOnItemClickListener(this); + mDeviceStore = new BluetoothLeDeviceStore(); + mBluetoothUtils = new BluetoothUtils(this); + mScanner = new BluetoothLeScanner(mLeScanCallback, mBluetoothUtils); + updateItemCount(0); + } + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + if (!mScanner.isScanning()) { + menu.findItem(R.id.menu_stop).setVisible(false); + menu.findItem(R.id.menu_scan).setVisible(true); + menu.findItem(R.id.menu_refresh).setActionView(null); + } else { + menu.findItem(R.id.menu_stop).setVisible(true); + menu.findItem(R.id.menu_scan).setVisible(false); + menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_progress_indeterminate); + } + + if (mList.getCount() > 0) { + menu.findItem(R.id.menu_share).setVisible(true); + } else { + menu.findItem(R.id.menu_share).setVisible(false); + } + + return true; + } + + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long l) { + final BluetoothLeDevice device = mLeDeviceListAdapter.getItem(position); + if (device == null) return; + + final Intent intent = new Intent(this, DeviceDetailsActivity.class); + intent.putExtra(DeviceDetailsActivity.EXTRA_DEVICE, device); + + startActivity(intent); + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_scan: + startScan(); + break; + case R.id.menu_stop: + mScanner.scanLeDevice(-1, false); + invalidateOptionsMenu(); + break; + case R.id.menu_about: + displayAboutDialog(); + break; + case R.id.menu_share: + mDeviceStore.shareDataAsEmail(this); + } + return true; + } + + @Override + protected void onPause() { + super.onPause(); + mScanner.scanLeDevice(-1, false); + } + + @Override + public void onResume() { + super.onResume(); + final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn(); + final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported(); + + if (mIsBluetoothOn) { + mTvBluetoothStatus.setText(R.string.on); + } else { + mTvBluetoothStatus.setText(R.string.off); + } + + if (mIsBluetoothLePresent) { + mTvBluetoothLeStatus.setText(R.string.supported); + } else { + mTvBluetoothLeStatus.setText(R.string.not_supported); + } + + invalidateOptionsMenu(); + } + + private void startScan() { + final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn(); + final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported(); + mDeviceStore.clear(); + updateItemCount(0); + + mLeDeviceListAdapter = new LeDeviceListAdapter(this, mDeviceStore.getDeviceCursor()); + mList.setAdapter(mLeDeviceListAdapter); + + mBluetoothUtils.askUserToEnableBluetoothIfNeeded(); + if (mIsBluetoothOn && mIsBluetoothLePresent) { + mScanner.scanLeDevice(-1, true); + invalidateOptionsMenu(); + } + } + + private void updateItemCount(final int count) { + mTvItemCount.setText( + getString( + R.string.formatter_item_count, + String.valueOf(count))); + } + +} diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/adapters/LeDeviceListAdapter.java b/sample_app/src/main/java/uk/co/alt236/btlescan/adapters/LeDeviceListAdapter.java new file mode 100644 index 0000000..97cbdbc --- /dev/null +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/adapters/LeDeviceListAdapter.java @@ -0,0 +1,126 @@ +package uk.co.alt236.btlescan.adapters; + +import android.app.Activity; +import android.support.v4.widget.SimpleCursorAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconType; +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconUtils; +import uk.co.alt236.bluetoothlelib.device.beacon.ibeacon.IBeaconDevice; +import uk.co.alt236.btlescan.R; +import uk.co.alt236.btlescan.util.Constants; +import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor; + +// Adapter for holding devices found through scanning. +public class LeDeviceListAdapter extends SimpleCursorAdapter { + private final LayoutInflater mInflator; + private final Activity mActivity; + + public LeDeviceListAdapter(final Activity activity, final EasyObjectCursor cursor) { + super(activity, R.layout.list_item_device, cursor, new String[0], new int[0], 0); + mInflator = activity.getLayoutInflater(); + mActivity = activity; + } + + @SuppressWarnings("unchecked") + @Override + public EasyObjectCursor getCursor() { + return ((EasyObjectCursor) super.getCursor()); + } + + @Override + public BluetoothLeDevice getItem(final int i) { + return getCursor().getItem(i); + } + + @Override + public long getItemId(final int i) { + return i; + } + + @Override + public View getView(final int i, View view, final ViewGroup viewGroup) { + final ViewHolder viewHolder; + // General ListView optimization code. + if (view == null) { + view = mInflator.inflate(R.layout.list_item_device, null); + viewHolder = new ViewHolder(); + viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address); + viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name); + viewHolder.deviceRssi = (TextView) view.findViewById(R.id.device_rssi); + viewHolder.deviceIcon = (ImageView) view.findViewById(R.id.device_icon); + viewHolder.deviceLastUpdated = (TextView) view.findViewById(R.id.device_last_update); + viewHolder.ibeaconMajor = (TextView) view.findViewById(R.id.ibeacon_major); + viewHolder.ibeaconMinor = (TextView) view.findViewById(R.id.ibeacon_minor); + viewHolder.ibeaconDistance = (TextView) view.findViewById(R.id.ibeacon_distance); + viewHolder.ibeaconUUID = (TextView) view.findViewById(R.id.ibeacon_uuid); + viewHolder.ibeaconTxPower = (TextView) view.findViewById(R.id.ibeacon_tx_power); + viewHolder.ibeaconSection = view.findViewById(R.id.ibeacon_section); + viewHolder.ibeaconDistanceDescriptor = (TextView) view.findViewById(R.id.ibeacon_distance_descriptor); + view.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) view.getTag(); + } + + final BluetoothLeDevice device = getCursor().getItem(i); + final String deviceName = device.getName(); + final double rssi = device.getRssi(); + + if (deviceName != null && deviceName.length() > 0) { + viewHolder.deviceName.setText(deviceName); + } else { + viewHolder.deviceName.setText(R.string.unknown_device); + } + + if (BeaconUtils.getBeaconType(device) == BeaconType.IBEACON) { + final IBeaconDevice iBeacon = new IBeaconDevice(device); + final String accuracy = Constants.DOUBLE_TWO_DIGIT_ACCURACY.format(iBeacon.getAccuracy()); + + viewHolder.deviceIcon.setImageResource(R.drawable.ic_device_ibeacon); + viewHolder.ibeaconSection.setVisibility(View.VISIBLE); + viewHolder.ibeaconMajor.setText(String.valueOf(iBeacon.getMajor())); + viewHolder.ibeaconMinor.setText(String.valueOf(iBeacon.getMinor())); + viewHolder.ibeaconTxPower.setText(String.valueOf(iBeacon.getCalibratedTxPower())); + viewHolder.ibeaconUUID.setText(iBeacon.getUUID()); + viewHolder.ibeaconDistance.setText( + mActivity.getString(R.string.formatter_meters, accuracy)); + viewHolder.ibeaconDistanceDescriptor.setText(iBeacon.getDistanceDescriptor().toString()); + } else { + viewHolder.deviceIcon.setImageResource(R.drawable.ic_bluetooth); + viewHolder.ibeaconSection.setVisibility(View.GONE); + } + + final String rssiString = + mActivity.getString(R.string.formatter_db, String.valueOf(rssi)); + final String runningAverageRssiString = + mActivity.getString(R.string.formatter_db, String.valueOf(device.getRunningAverageRssi())); + + viewHolder.deviceLastUpdated.setText( + android.text.format.DateFormat.format( + Constants.TIME_FORMAT, new java.util.Date(device.getTimestamp()))); + viewHolder.deviceAddress.setText(device.getAddress()); + viewHolder.deviceRssi.setText(rssiString + " / " + runningAverageRssiString); + return view; + } + + static class ViewHolder { + TextView deviceName; + TextView deviceAddress; + TextView deviceRssi; + TextView ibeaconUUID; + TextView ibeaconMajor; + TextView ibeaconMinor; + TextView ibeaconTxPower; + TextView ibeaconDistance; + TextView ibeaconDistanceDescriptor; + TextView deviceLastUpdated; + View ibeaconSection; + ImageView deviceIcon; + } + +} \ No newline at end of file diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/containers/BluetoothLeDeviceStore.java b/sample_app/src/main/java/uk/co/alt236/btlescan/containers/BluetoothLeDeviceStore.java new file mode 100644 index 0000000..8b99033 --- /dev/null +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/containers/BluetoothLeDeviceStore.java @@ -0,0 +1,181 @@ +package uk.co.alt236.btlescan.containers; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconType; +import uk.co.alt236.bluetoothlelib.device.beacon.BeaconUtils; +import uk.co.alt236.bluetoothlelib.device.beacon.ibeacon.IBeaconDevice; +import uk.co.alt236.bluetoothlelib.util.ByteUtils; +import uk.co.alt236.btlescan.R; +import uk.co.alt236.btlescan.util.CsvWriterHelper; +import uk.co.alt236.btlescan.util.TimeFormatter; +import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor; + +public class BluetoothLeDeviceStore { + private final Map mDeviceMap; + + + public BluetoothLeDeviceStore() { + mDeviceMap = new HashMap<>(); + } + + public void addDevice(final BluetoothLeDevice device) { + if (mDeviceMap.containsKey(device.getAddress())) { + mDeviceMap.get(device.getAddress()).updateRssiReading(device.getTimestamp(), device.getRssi()); + } else { + mDeviceMap.put(device.getAddress(), device); + } + } + + public void clear() { + mDeviceMap.clear(); + } + + public EasyObjectCursor getDeviceCursor() { + return new EasyObjectCursor<>( + BluetoothLeDevice.class, + getDeviceList(), + "address"); + } + + public List getDeviceList() { + final List methodResult = new ArrayList<>(mDeviceMap.values()); + + Collections.sort(methodResult, new Comparator() { + + @Override + public int compare(final BluetoothLeDevice arg0, final BluetoothLeDevice arg1) { + return arg0.getAddress().compareToIgnoreCase(arg1.getAddress()); + } + }); + + return methodResult; + } + + private String getListAsCsv() { + final List list = getDeviceList(); + final StringBuilder sb = new StringBuilder(); + sb.append(CsvWriterHelper.addStuff("mac")); + sb.append(CsvWriterHelper.addStuff("name")); + sb.append(CsvWriterHelper.addStuff("firstTimestamp")); + sb.append(CsvWriterHelper.addStuff("firstRssi")); + sb.append(CsvWriterHelper.addStuff("currentTimestamp")); + sb.append(CsvWriterHelper.addStuff("currentRssi")); + sb.append(CsvWriterHelper.addStuff("adRecord")); + sb.append(CsvWriterHelper.addStuff("iBeacon")); + sb.append(CsvWriterHelper.addStuff("uuid")); + sb.append(CsvWriterHelper.addStuff("major")); + sb.append(CsvWriterHelper.addStuff("minor")); + sb.append(CsvWriterHelper.addStuff("txPower")); + sb.append(CsvWriterHelper.addStuff("distance")); + sb.append(CsvWriterHelper.addStuff("accuracy")); + sb.append('\n'); + + for (final BluetoothLeDevice device : list) { + sb.append(CsvWriterHelper.addStuff(device.getAddress())); + sb.append(CsvWriterHelper.addStuff(device.getName())); + sb.append(CsvWriterHelper.addStuff(TimeFormatter.getIsoDateTime(device.getFirstTimestamp()))); + sb.append(CsvWriterHelper.addStuff(device.getFirstRssi())); + sb.append(CsvWriterHelper.addStuff(TimeFormatter.getIsoDateTime(device.getTimestamp()))); + sb.append(CsvWriterHelper.addStuff(device.getRssi())); + sb.append(CsvWriterHelper.addStuff(ByteUtils.byteArrayToHexString(device.getScanRecord()))); + final boolean isIBeacon = BeaconUtils.getBeaconType(device) == BeaconType.IBEACON; + final String uuid; + final String minor; + final String major; + final String txPower; + final String distance; + final String accuracy; + + if (isIBeacon) { + final IBeaconDevice beacon = new IBeaconDevice(device); + uuid = String.valueOf(beacon.getUUID()); + minor = String.valueOf(beacon.getMinor()); + major = String.valueOf(beacon.getMajor()); + txPower = String.valueOf(beacon.getCalibratedTxPower()); + distance = beacon.getDistanceDescriptor().toString().toLowerCase(Locale.US); + accuracy = String.valueOf(beacon.getAccuracy()); + } else { + uuid = ""; + minor = ""; + major = ""; + txPower = ""; + distance = ""; + accuracy = ""; + } + + sb.append(CsvWriterHelper.addStuff(isIBeacon)); + sb.append(CsvWriterHelper.addStuff(uuid)); + sb.append(CsvWriterHelper.addStuff(minor)); + sb.append(CsvWriterHelper.addStuff(major)); + sb.append(CsvWriterHelper.addStuff(txPower)); + sb.append(CsvWriterHelper.addStuff(distance)); + sb.append(CsvWriterHelper.addStuff(accuracy)); + + sb.append('\n'); + } + + return sb.toString(); + } + + public void shareDataAsEmail(final Context context) { + final long timeInMillis = System.currentTimeMillis(); + + final String to = null; + final String subject = context.getString( + R.string.exporter_email_device_list_subject, + TimeFormatter.getIsoDateTime(timeInMillis)); + + final String message = context.getString(R.string.exporter_email_device_list_body); + + final Intent i = new Intent(Intent.ACTION_SEND); + i.setType("plain/text"); + try { + final File outputDir = context.getCacheDir(); + final File outputFile = File.createTempFile("bluetooth_le_" + timeInMillis, ".csv", outputDir); + outputFile.setReadable(true, false); + generateFile(outputFile, getListAsCsv()); + i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(outputFile)); + i.putExtra(Intent.EXTRA_EMAIL, new String[]{to}); + i.putExtra(Intent.EXTRA_SUBJECT, subject); + i.putExtra(Intent.EXTRA_TEXT, message); + context.startActivity(Intent.createChooser(i, context.getString(R.string.exporter_email_device_list_picker_text))); + + } catch (final IOException e) { + e.printStackTrace(); + } + } + + private static FileWriter generateFile(final File file, final String contents) { + FileWriter writer = null; + try { + writer = new FileWriter(file); + writer.append(contents); + writer.flush(); + + } catch (final IOException e) { + e.printStackTrace(); + } finally { + try { + writer.close(); + } catch (final IOException e) { + e.printStackTrace(); + } + } + return writer; + } +} diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/services/BluetoothLeService.java b/sample_app/src/main/java/uk/co/alt236/btlescan/services/BluetoothLeService.java new file mode 100644 index 0000000..bc33610 --- /dev/null +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/services/BluetoothLeService.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package uk.co.alt236.btlescan.services; + +import android.app.Service; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +import java.util.List; + +/** + * Service for managing connection and data communication with a GATT server hosted on a + * given Bluetooth LE device. + */ +public class BluetoothLeService extends Service { + public final static String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; + public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; + public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; + public final static String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; + public final static String EXTRA_DATA_RAW = "com.example.bluetooth.le.EXTRA_DATA_RAW"; + public final static String EXTRA_UUID_CHAR = "com.example.bluetooth.le.EXTRA_UUID_CHAR"; + private final static String TAG = BluetoothLeService.class.getSimpleName(); + private static final int STATE_DISCONNECTED = 0; + private static final int STATE_CONNECTING = 1; + private static final int STATE_CONNECTED = 2; + private final IBinder mBinder = new LocalBinder(); + private BluetoothManager mBluetoothManager; + private BluetoothAdapter mBluetoothAdapter; + private String mBluetoothDeviceAddress; + private BluetoothGatt mBluetoothGatt; + private int mConnectionState = STATE_DISCONNECTED; + // Implements callback methods for GATT events that the app cares about. For example, + // connection change and services discovered. + private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { + @Override + public void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { + broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); + } + + @Override + public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); + } + } + + @Override + public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) { + final String intentAction; + if (newState == BluetoothProfile.STATE_CONNECTED) { + intentAction = ACTION_GATT_CONNECTED; + mConnectionState = STATE_CONNECTED; + broadcastUpdate(intentAction); + Log.i(TAG, "Connected to GATT server."); + // Attempts to discover services after successful connection. + Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices()); + + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + intentAction = ACTION_GATT_DISCONNECTED; + mConnectionState = STATE_DISCONNECTED; + Log.i(TAG, "Disconnected from GATT server."); + broadcastUpdate(intentAction); + } + } + + @Override + public void onServicesDiscovered(final BluetoothGatt gatt, final int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); + } else { + Log.w(TAG, "onServicesDiscovered received: " + status); + } + } + }; + + private void broadcastUpdate(final String action) { + final Intent intent = new Intent(action); + sendBroadcast(intent); + } + + private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { + final Intent intent = new Intent(action); + intent.putExtra(EXTRA_UUID_CHAR, characteristic.getUuid().toString()); + + // Always try to add the RAW value + final byte[] data = characteristic.getValue(); + if (data != null && data.length > 0) { + intent.putExtra(EXTRA_DATA_RAW, data); + } + + sendBroadcast(intent); + } + + /** + * After using a given BLE device, the app must call this method to ensure resources are + * released properly. + */ + public void close() { + if (mBluetoothGatt == null) { + return; + } + mBluetoothGatt.close(); + mBluetoothGatt = null; + } + + /** + * Connects to the GATT server hosted on the Bluetooth LE device. + * + * @param address The device address of the destination device. + * @return Return true if the connection is initiated successfully. The connection result + * is reported asynchronously through the + * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} + * callback. + */ + public boolean connect(final String address) { + if (mBluetoothAdapter == null || address == null) { + Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); + return false; + } + + // Previously connected device. Try to reconnect. + if (mBluetoothDeviceAddress != null + && address.equals(mBluetoothDeviceAddress) + && mBluetoothGatt != null) { + + Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection."); + if (mBluetoothGatt.connect()) { + mConnectionState = STATE_CONNECTING; + return true; + } else { + return false; + } + } + + final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); + if (device == null) { + Log.w(TAG, "Device not found. Unable to connect."); + return false; + } + // We want to directly connect to the device, so we are setting the autoConnect + // parameter to false. + mBluetoothGatt = device.connectGatt(this, false, mGattCallback); + Log.d(TAG, "Trying to create a new connection."); + mBluetoothDeviceAddress = address; + mConnectionState = STATE_CONNECTING; + return true; + } + + /** + * Disconnects an existing connection or cancel a pending connection. The disconnection result + * is reported asynchronously through the + * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} + * callback. + */ + public void disconnect() { + if (mBluetoothAdapter == null || mBluetoothGatt == null) { + Log.w(TAG, "BluetoothAdapter not initialized"); + return; + } + mBluetoothGatt.disconnect(); + } + + /** + * Retrieves a list of supported GATT services on the connected device. This should be + * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully. + * + * @return A {@code List} of supported services. + */ + public List getSupportedGattServices() { + if (mBluetoothGatt == null) return null; + + return mBluetoothGatt.getServices(); + } + + /** + * Initializes a reference to the local Bluetooth adapter. + * + * @return Return true if the initialization is successful. + */ + public boolean initialize() { + // For API level 18 and above, get a reference to BluetoothAdapter through + // BluetoothManager. + if (mBluetoothManager == null) { + mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + if (mBluetoothManager == null) { + Log.e(TAG, "Unable to initialize BluetoothManager."); + return false; + } + } + + mBluetoothAdapter = mBluetoothManager.getAdapter(); + if (mBluetoothAdapter == null) { + Log.e(TAG, "Unable to obtain a BluetoothAdapter."); + return false; + } + + return true; + } + + @Override + public IBinder onBind(final Intent intent) { + return mBinder; + } + + @Override + public boolean onUnbind(final Intent intent) { + // After using a given device, you should make sure that BluetoothGatt.close() is called + // such that resources are cleaned up properly. In this particular example, close() is + // invoked when the UI is disconnected from the Service. + close(); + return super.onUnbind(intent); + } + + /** + * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported + * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} + * callback. + * + * @param characteristic The characteristic to read from. + */ + public void readCharacteristic(final BluetoothGattCharacteristic characteristic) { + if (mBluetoothAdapter == null || mBluetoothGatt == null) { + Log.w(TAG, "BluetoothAdapter not initialized"); + return; + } + mBluetoothGatt.readCharacteristic(characteristic); + } + + /** + * Enables or disables notification on a give characteristic. + * + * @param characteristic Characteristic to act on. + * @param enabled If true, enable notification. False otherwise. + */ + public void setCharacteristicNotification(final BluetoothGattCharacteristic characteristic, final boolean enabled) { + if (mBluetoothAdapter == null || mBluetoothGatt == null) { + Log.w(TAG, "BluetoothAdapter not initialized"); + return; + } + mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); + } + + public class LocalBinder extends Binder { + public BluetoothLeService getService() { + return BluetoothLeService.this; + } + } +} \ No newline at end of file diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/util/BluetoothLeScanner.java b/sample_app/src/main/java/uk/co/alt236/btlescan/util/BluetoothLeScanner.java new file mode 100644 index 0000000..b08c13d --- /dev/null +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/util/BluetoothLeScanner.java @@ -0,0 +1,48 @@ +package uk.co.alt236.btlescan.util; + +import android.bluetooth.BluetoothAdapter; +import android.os.Handler; +import android.util.Log; + +public class BluetoothLeScanner { + private final Handler mHandler; + private final BluetoothAdapter.LeScanCallback mLeScanCallback; + private final BluetoothUtils mBluetoothUtils; + private boolean mScanning; + + public BluetoothLeScanner(final BluetoothAdapter.LeScanCallback leScanCallback, final BluetoothUtils bluetoothUtils) { + mHandler = new Handler(); + mLeScanCallback = leScanCallback; + mBluetoothUtils = bluetoothUtils; + } + + public boolean isScanning() { + return mScanning; + } + + public void scanLeDevice(final int duration, final boolean enable) { + if (enable) { + if (mScanning) { + return; + } + Log.d("TAG", "~ Starting Scan"); + // Stops scanning after a pre-defined scan period. + if (duration > 0) { + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + Log.d("TAG", "~ Stopping Scan (timeout)"); + mScanning = false; + mBluetoothUtils.getBluetoothAdapter().stopLeScan(mLeScanCallback); + } + }, duration); + } + mScanning = true; + mBluetoothUtils.getBluetoothAdapter().startLeScan(mLeScanCallback); + } else { + Log.d("TAG", "~ Stopping Scan"); + mScanning = false; + mBluetoothUtils.getBluetoothAdapter().stopLeScan(mLeScanCallback); + } + } +} diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/util/BluetoothUtils.java b/sample_app/src/main/java/uk/co/alt236/btlescan/util/BluetoothUtils.java new file mode 100644 index 0000000..87d5bda --- /dev/null +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/util/BluetoothUtils.java @@ -0,0 +1,43 @@ +package uk.co.alt236.btlescan.util; + +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; + +public final class BluetoothUtils { + public final static int REQUEST_ENABLE_BT = 2001; + private final Activity mActivity; + private final BluetoothAdapter mBluetoothAdapter; + + public BluetoothUtils(final Activity activity) { + mActivity = activity; + final BluetoothManager btManager = (BluetoothManager) mActivity.getSystemService(Context.BLUETOOTH_SERVICE); + mBluetoothAdapter = btManager.getAdapter(); + } + + public void askUserToEnableBluetoothIfNeeded() { + if (isBluetoothLeSupported() && (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())) { + final Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + mActivity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); + } + } + + public BluetoothAdapter getBluetoothAdapter() { + return mBluetoothAdapter; + } + + public boolean isBluetoothLeSupported() { + return mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE); + } + + public boolean isBluetoothOn() { + if (mBluetoothAdapter == null) { + return false; + } else { + return mBluetoothAdapter.isEnabled(); + } + } +} diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/util/Constants.java b/sample_app/src/main/java/uk/co/alt236/btlescan/util/Constants.java new file mode 100644 index 0000000..f0b7b59 --- /dev/null +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/util/Constants.java @@ -0,0 +1,8 @@ +package uk.co.alt236.btlescan.util; + +import java.text.DecimalFormat; + +public class Constants { + public static final DecimalFormat DOUBLE_TWO_DIGIT_ACCURACY = new DecimalFormat("#.##"); + public static final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; +} diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/util/CsvWriterHelper.java b/sample_app/src/main/java/uk/co/alt236/btlescan/util/CsvWriterHelper.java new file mode 100644 index 0000000..f8bff7d --- /dev/null +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/util/CsvWriterHelper.java @@ -0,0 +1,26 @@ +package uk.co.alt236.btlescan.util; + +public class CsvWriterHelper { + private static final String QUOTE = "\""; + + public static String addStuff(final Integer text) { + return QUOTE + text + QUOTE + ","; + } + + public static String addStuff(final Long text) { + return QUOTE + text + QUOTE + ","; + } + + public static String addStuff(final boolean value) { + return QUOTE + value + QUOTE + ","; + } + + public static String addStuff(String text) { + if (text == null) { + text = ""; + } + text = text.replace(QUOTE, "'"); + + return QUOTE + text.trim() + QUOTE + ","; + } +} diff --git a/sample_app/src/main/java/uk/co/alt236/btlescan/util/TimeFormatter.java b/sample_app/src/main/java/uk/co/alt236/btlescan/util/TimeFormatter.java new file mode 100644 index 0000000..7d43826 --- /dev/null +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/util/TimeFormatter.java @@ -0,0 +1,18 @@ +package uk.co.alt236.btlescan.util; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +public class TimeFormatter { + private final static String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS zzz"; + private final static SimpleDateFormat ISO_FORMATTER = new UtcDateFormatter(ISO_FORMAT, Locale.US); + + public static String getIsoDateTime(final Date date) { + return ISO_FORMATTER.format(date); + } + + public static String getIsoDateTime(final long millis) { + return getIsoDateTime(new Date(millis)); + } +} diff --git a/sample_app/src/uk/co/alt236/btlescan/util/UtcDateFormatter.java b/sample_app/src/main/java/uk/co/alt236/btlescan/util/UtcDateFormatter.java similarity index 50% rename from sample_app/src/uk/co/alt236/btlescan/util/UtcDateFormatter.java rename to sample_app/src/main/java/uk/co/alt236/btlescan/util/UtcDateFormatter.java index f0dca69..0b7176b 100644 --- a/sample_app/src/uk/co/alt236/btlescan/util/UtcDateFormatter.java +++ b/sample_app/src/main/java/uk/co/alt236/btlescan/util/UtcDateFormatter.java @@ -1,72 +1,74 @@ -/******************************************************************************* +/** + * **************************************************************************** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * + *

* GenieConnect Ltd. ("COMPANY") CONFIDENTIAL * Unpublished Copyright (c) 2010-2013 GenieConnect Ltd., All Rights Reserved. - * + *

* NOTICE: - * All information contained herein is, and remains the property of COMPANY. - * The intellectual and technical concepts contained herein are proprietary to - * COMPANY and may be covered by U.S. and Foreign Patents, patents in process, and - * are protected by trade secret or copyright law. Dissemination of this - * information or reproduction of this material is strictly forbidden unless prior - * written permission is obtained from COMPANY. Access to the source code - * contained herein is hereby forbidden to anyone except current COMPANY employees, + * All information contained herein is, and remains the property of COMPANY. + * The intellectual and technical concepts contained herein are proprietary to + * COMPANY and may be covered by U.S. and Foreign Patents, patents in process, and + * are protected by trade secret or copyright law. Dissemination of this + * information or reproduction of this material is strictly forbidden unless prior + * written permission is obtained from COMPANY. Access to the source code + * contained herein is hereby forbidden to anyone except current COMPANY employees, * managers or contractors who have executed Confidentiality and Non-disclosure * agreements explicitly covering such access. - * - * The copyright notice above does not evidence any actual or intended publication - * or disclosure of this source code, which includes information that is + *

+ * The copyright notice above does not evidence any actual or intended publication + * or disclosure of this source code, which includes information that is * confidential and/or proprietary, and is a trade secret, of COMPANY. - * - * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, OR PUBLIC - * DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT THE EXPRESS WRITTEN - * CONSENT OF COMPANY IS STRICTLY PROHIBITED, AND IN VIOLATION OF APPLICABLE LAWS - * AND INTERNATIONAL TREATIES. THE RECEIPT OR POSSESSION OF THIS SOURCE CODE - * AND/OR RELATED INFORMATION DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, - * DISCLOSE OR DISTRIBUTE ITS CONTENTS, OR TO MANUFACTURE, USE, OR SELL ANYTHING + *

+ * ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC PERFORMANCE, OR PUBLIC + * DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT THE EXPRESS WRITTEN + * CONSENT OF COMPANY IS STRICTLY PROHIBITED, AND IN VIOLATION OF APPLICABLE LAWS + * AND INTERNATIONAL TREATIES. THE RECEIPT OR POSSESSION OF THIS SOURCE CODE + * AND/OR RELATED INFORMATION DOES NOT CONVEY OR IMPLY ANY RIGHTS TO REPRODUCE, + * DISCLOSE OR DISTRIBUTE ITS CONTENTS, OR TO MANUFACTURE, USE, OR SELL ANYTHING * THAT IT MAY DESCRIBE, IN WHOLE OR IN PART. - ******************************************************************************/ + * **************************************************************************** + */ package uk.co.alt236.btlescan.util; +import android.annotation.SuppressLint; + import java.text.DateFormatSymbols; import java.util.Locale; import java.util.TimeZone; -import android.annotation.SuppressLint; +public class UtcDateFormatter extends java.text.SimpleDateFormat { + private static final long serialVersionUID = 1L; -public class UtcDateFormatter extends java.text.SimpleDateFormat{ - private static final long serialVersionUID = 1L; - - private static final String TIME_ZONE_STRING = "UTC"; - private static final TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone(TIME_ZONE_STRING); - - @SuppressLint("SimpleDateFormat") - public UtcDateFormatter(String template){ - super(template); - super.setTimeZone(TIME_ZONE_UTC); - } - - @SuppressLint("SimpleDateFormat") - public UtcDateFormatter(String template, DateFormatSymbols symbols){ - super(template, symbols); - super.setTimeZone(TIME_ZONE_UTC); - } - - public UtcDateFormatter(String template, Locale locale){ - super(template, locale); - super.setTimeZone(TIME_ZONE_UTC); - } - - /* - * This function will throw an UnsupportedOperationException. - * You are not be able to change the TimeZone of this object - * - * (non-Javadoc) - * @see java.text.DateFormat#setTimeZone(java.util.TimeZone) - */ - @Override - public void setTimeZone(TimeZone timezone){ - throw new UnsupportedOperationException("This SimpleDateFormat can only be in " + TIME_ZONE_STRING); - } + private static final String TIME_ZONE_STRING = "UTC"; + private static final TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone(TIME_ZONE_STRING); + + @SuppressLint("SimpleDateFormat") + public UtcDateFormatter(final String template) { + super(template); + super.setTimeZone(TIME_ZONE_UTC); + } + + @SuppressLint("SimpleDateFormat") + public UtcDateFormatter(final String template, final DateFormatSymbols symbols) { + super(template, symbols); + super.setTimeZone(TIME_ZONE_UTC); + } + + public UtcDateFormatter(final String template, final Locale locale) { + super(template, locale); + super.setTimeZone(TIME_ZONE_UTC); + } + + /* + * This function will throw an UnsupportedOperationException. + * You are not be able to change the TimeZone of this object + * + * (non-Javadoc) + * @see java.text.DateFormat#setTimeZone(java.util.TimeZone) + */ + @Override + public void setTimeZone(final TimeZone timezone) { + throw new UnsupportedOperationException("This SimpleDateFormat can only be in " + TIME_ZONE_STRING); + } } diff --git a/sample_app/res/drawable-hdpi/ic_action_share.png b/sample_app/src/main/res/drawable-hdpi/ic_action_share.png similarity index 100% rename from sample_app/res/drawable-hdpi/ic_action_share.png rename to sample_app/src/main/res/drawable-hdpi/ic_action_share.png diff --git a/sample_app/res/drawable-hdpi/ic_launcher.png b/sample_app/src/main/res/drawable-hdpi/ic_launcher.png similarity index 100% rename from sample_app/res/drawable-hdpi/ic_launcher.png rename to sample_app/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/sample_app/res/drawable-mdpi/ic_action_share.png b/sample_app/src/main/res/drawable-mdpi/ic_action_share.png similarity index 100% rename from sample_app/res/drawable-mdpi/ic_action_share.png rename to sample_app/src/main/res/drawable-mdpi/ic_action_share.png diff --git a/sample_app/res/drawable-mdpi/ic_launcher.png b/sample_app/src/main/res/drawable-mdpi/ic_launcher.png similarity index 100% rename from sample_app/res/drawable-mdpi/ic_launcher.png rename to sample_app/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/sample_app/res/drawable-xhdpi/ic_action_share.png b/sample_app/src/main/res/drawable-xhdpi/ic_action_share.png similarity index 100% rename from sample_app/res/drawable-xhdpi/ic_action_share.png rename to sample_app/src/main/res/drawable-xhdpi/ic_action_share.png diff --git a/sample_app/res/drawable-xhdpi/ic_bluetooth.png b/sample_app/src/main/res/drawable-xhdpi/ic_bluetooth.png similarity index 100% rename from sample_app/res/drawable-xhdpi/ic_bluetooth.png rename to sample_app/src/main/res/drawable-xhdpi/ic_bluetooth.png diff --git a/sample_app/res/drawable-xhdpi/ic_bluetooth_on.png b/sample_app/src/main/res/drawable-xhdpi/ic_bluetooth_on.png similarity index 100% rename from sample_app/res/drawable-xhdpi/ic_bluetooth_on.png rename to sample_app/src/main/res/drawable-xhdpi/ic_bluetooth_on.png diff --git a/sample_app/res/drawable-xhdpi/ic_device_ibeacon.png b/sample_app/src/main/res/drawable-xhdpi/ic_device_ibeacon.png similarity index 100% rename from sample_app/res/drawable-xhdpi/ic_device_ibeacon.png rename to sample_app/src/main/res/drawable-xhdpi/ic_device_ibeacon.png diff --git a/sample_app/res/drawable-xhdpi/ic_launcher.png b/sample_app/src/main/res/drawable-xhdpi/ic_launcher.png similarity index 100% rename from sample_app/res/drawable-xhdpi/ic_launcher.png rename to sample_app/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/sample_app/res/drawable-xxhdpi/ic_action_share.png b/sample_app/src/main/res/drawable-xxhdpi/ic_action_share.png similarity index 100% rename from sample_app/res/drawable-xxhdpi/ic_action_share.png rename to sample_app/src/main/res/drawable-xxhdpi/ic_action_share.png diff --git a/sample_app/res/drawable-xxhdpi/ic_launcher.png b/sample_app/src/main/res/drawable-xxhdpi/ic_launcher.png similarity index 100% rename from sample_app/res/drawable-xxhdpi/ic_launcher.png rename to sample_app/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/sample_app/src/main/res/layout/actionbar_progress_indeterminate.xml b/sample_app/src/main/res/layout/actionbar_progress_indeterminate.xml new file mode 100644 index 0000000..e408d2b --- /dev/null +++ b/sample_app/src/main/res/layout/actionbar_progress_indeterminate.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/sample_app/src/main/res/layout/activity_details.xml b/sample_app/src/main/res/layout/activity_details.xml new file mode 100644 index 0000000..3100292 --- /dev/null +++ b/sample_app/src/main/res/layout/activity_details.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/sample_app/res/layout/activity_gatt_services.xml b/sample_app/src/main/res/layout/activity_gatt_services.xml similarity index 69% rename from sample_app/res/layout/activity_gatt_services.xml rename to sample_app/src/main/res/layout/activity_gatt_services.xml index b60e56f..af806fb 100644 --- a/sample_app/res/layout/activity_gatt_services.xml +++ b/sample_app/src/main/res/layout/activity_gatt_services.xml @@ -15,13 +15,13 @@ limitations under the License. --> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin"> + android:useDefaultMargins="true"> + android:text="@string/label_device_address"/> + style="@style/GridLayoutDataTextView"/> + android:text="@string/label_state"/> + style="@style/GridLayoutDataTextView"/> + android:background="@android:color/holo_blue_dark"/> + android:useDefaultMargins="true"> + android:text="@string/label_uuid"/> + style="@style/GridLayoutDataTextViewMonospace"/> + android:text="@string/label_desc"/> + style="@style/GridLayoutDataTextViewMonospace"/> + android:text="@string/label_as_string"/> + style="@style/GridLayoutDataTextViewMonospace"/> + android:text="@string/label_as_array"/> + style="@style/GridLayoutDataTextViewMonospace"/> + android:background="@android:color/holo_blue_dark"/> + android:layout_below="@id/upperSepparator"/> \ No newline at end of file diff --git a/sample_app/res/layout/activity_main.xml b/sample_app/src/main/res/layout/activity_main.xml similarity index 73% rename from sample_app/res/layout/activity_main.xml rename to sample_app/src/main/res/layout/activity_main.xml index 995149d..ca783c9 100644 --- a/sample_app/res/layout/activity_main.xml +++ b/sample_app/src/main/res/layout/activity_main.xml @@ -1,20 +1,20 @@ + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + tools:context=".MainActivity"> + android:paddingBottom="@dimen/activity_vertical_margin"> + android:textStyle="bold"/> + android:textSize="12sp"/> + android:textStyle="bold"/> + android:textSize="12sp"/> + android:background="@android:color/holo_blue_dark"/> + android:orientation="vertical"> + android:orientation="vertical"> + android:background="@android:color/holo_blue_dark"/> + android:text="@string/formatter_item_count"/> + android:layout_alignParentTop="true" + android:orientation="vertical"> + android:layout_height="match_parent"/> + android:text="@string/no_data"/> diff --git a/sample_app/res/layout/list_item_device.xml b/sample_app/src/main/res/layout/list_item_device.xml similarity index 82% rename from sample_app/res/layout/list_item_device.xml rename to sample_app/src/main/res/layout/list_item_device.xml index 62be902..4a2bc2d 100644 --- a/sample_app/res/layout/list_item_device.xml +++ b/sample_app/src/main/res/layout/list_item_device.xml @@ -15,34 +15,34 @@ limitations under the License. --> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="top" + android:orientation="horizontal"> + android:paddingTop="5dp" + android:src="@drawable/ic_bluetooth"/> + android:orientation="vertical"> + android:textSize="24sp"/> + android:columnCount="2"> + android:textStyle="bold"/> + android:typeface="monospace"/> + android:textStyle="bold"/> + android:textSize="12sp"/> + android:textStyle="bold"/> + android:textSize="12sp"/> + android:columnCount="4"> @@ -105,7 +105,7 @@ android:paddingRight="5dp" android:text="@string/label_uuid" android:textSize="12sp" - android:textStyle="bold" /> + android:textStyle="bold"/> + android:textSize="12sp"/> @@ -123,14 +123,14 @@ android:paddingRight="5dp" android:text="@string/label_major" android:textSize="12sp" - android:textStyle="bold" /> + android:textStyle="bold"/> + android:textSize="12sp"/> + android:textStyle="bold"/> + android:textSize="12sp"/> @@ -156,14 +156,14 @@ android:paddingRight="5dp" android:text="@string/label_tx_power" android:textSize="12sp" - android:textStyle="bold" /> + android:textStyle="bold"/> + android:textSize="12sp"/> + android:textStyle="bold"/> + android:textSize="12sp"/> @@ -187,9 +187,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingRight="5dp" - android:text="Descriptor:" + android:text="@string/label_decriptor" android:textSize="12sp" - android:textStyle="bold" /> + android:textStyle="bold"/> + android:textSize="12sp"/> diff --git a/sample_app/res/layout/list_item_view_adrecord.xml b/sample_app/src/main/res/layout/list_item_view_adrecord.xml similarity index 62% rename from sample_app/res/layout/list_item_view_adrecord.xml rename to sample_app/src/main/res/layout/list_item_view_adrecord.xml index e5cd158..b020ead 100644 --- a/sample_app/res/layout/list_item_view_adrecord.xml +++ b/sample_app/src/main/res/layout/list_item_view_adrecord.xml @@ -1,43 +1,43 @@ + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="top" + android:orientation="vertical" + android:paddingBottom="5dp"> + android:textStyle="bold"/> + android:useDefaultMargins="true"> + android:text="@string/label_as_string"/> + style="@style/GridLayoutDataTextViewMonospace"/> + android:text="@string/label_as_array"/> + style="@style/GridLayoutDataTextViewMonospace"/> \ No newline at end of file diff --git a/sample_app/src/main/res/layout/list_item_view_device_info.xml b/sample_app/src/main/res/layout/list_item_view_device_info.xml new file mode 100644 index 0000000..239480f --- /dev/null +++ b/sample_app/src/main/res/layout/list_item_view_device_info.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample_app/res/layout/list_item_view_header.xml b/sample_app/src/main/res/layout/list_item_view_header.xml similarity index 58% rename from sample_app/res/layout/list_item_view_header.xml rename to sample_app/src/main/res/layout/list_item_view_header.xml index b218ade..e92024e 100644 --- a/sample_app/res/layout/list_item_view_header.xml +++ b/sample_app/src/main/res/layout/list_item_view_header.xml @@ -1,10 +1,10 @@ + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="top" + android:orientation="vertical" + android:paddingBottom="5dp"> + android:textStyle="bold"/> + android:background="@android:color/holo_blue_dark"/> \ No newline at end of file diff --git a/sample_app/res/layout/list_item_view_ibeacon_details.xml b/sample_app/src/main/res/layout/list_item_view_ibeacon_details.xml similarity index 64% rename from sample_app/res/layout/list_item_view_ibeacon_details.xml rename to sample_app/src/main/res/layout/list_item_view_ibeacon_details.xml index dc6ddf6..aaddcc1 100644 --- a/sample_app/res/layout/list_item_view_ibeacon_details.xml +++ b/sample_app/src/main/res/layout/list_item_view_ibeacon_details.xml @@ -1,76 +1,76 @@ + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="top" + android:orientation="vertical" + android:paddingBottom="5dp"> + android:useDefaultMargins="true"> + android:text="@string/label_company_id"/> + style="@style/GridLayoutDataTextView"/> + android:text="@string/label_advertisement"/> + style="@style/GridLayoutDataTextView"/> + android:text="@string/label_uuid"/> + style="@style/GridLayoutDataTextView"/> + android:text="@string/label_major"/> + style="@style/GridLayoutDataTextView"/> + android:text="@string/label_minor"/> + style="@style/GridLayoutDataTextView"/> + android:text="@string/label_tx_power"/> + style="@style/GridLayoutDataTextView"/> \ No newline at end of file diff --git a/sample_app/res/layout/list_item_view_rssi_info.xml b/sample_app/src/main/res/layout/list_item_view_rssi_info.xml similarity index 63% rename from sample_app/res/layout/list_item_view_rssi_info.xml rename to sample_app/src/main/res/layout/list_item_view_rssi_info.xml index 2bc97c5..4e1163d 100644 --- a/sample_app/res/layout/list_item_view_rssi_info.xml +++ b/sample_app/src/main/res/layout/list_item_view_rssi_info.xml @@ -1,66 +1,66 @@ + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="top" + android:orientation="vertical" + android:paddingBottom="5dp"> + android:useDefaultMargins="true"> + android:text="@string/label_first_timestamp"/> + style="@style/GridLayoutDataTextView"/> + android:text="@string/label_first_rssi"/> + style="@style/GridLayoutDataTextView"/> + android:text="@string/label_last_timestamp"/> + style="@style/GridLayoutDataTextView"/> + android:text="@string/label_last_rssi"/> + style="@style/GridLayoutDataTextView"/> + android:text="@string/label_running_average_rssi"/> + style="@style/GridLayoutDataTextView"/> \ No newline at end of file diff --git a/sample_app/src/main/res/layout/list_item_view_textview.xml b/sample_app/src/main/res/layout/list_item_view_textview.xml new file mode 100644 index 0000000..5131ef2 --- /dev/null +++ b/sample_app/src/main/res/layout/list_item_view_textview.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/sample_app/res/menu/details.xml b/sample_app/src/main/res/menu/details.xml similarity index 84% rename from sample_app/res/menu/details.xml rename to sample_app/src/main/res/menu/details.xml index 4bf3056..dde8544 100644 --- a/sample_app/res/menu/details.xml +++ b/sample_app/src/main/res/menu/details.xml @@ -14,14 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. --> -

+ + android:title="@string/menu_connect" + app:showAsAction="always|withText"/> \ No newline at end of file diff --git a/sample_app/res/menu/gatt_services.xml b/sample_app/src/main/res/menu/gatt_services.xml similarity index 75% rename from sample_app/res/menu/gatt_services.xml rename to sample_app/src/main/res/menu/gatt_services.xml index 5c2909b..f5ed52b 100644 --- a/sample_app/res/menu/gatt_services.xml +++ b/sample_app/src/main/res/menu/gatt_services.xml @@ -14,28 +14,30 @@ See the License for the specific language governing permissions and limitations under the License. --> - + + android:title="" + app:showAsAction="always"/> + android:title="@string/menu_share" + app:showAsAction="ifRoom"/> + android:title="@string/menu_connect" + app:showAsAction="ifRoom|withText"/> + android:title="@string/menu_disconnect" + app:showAsAction="ifRoom|withText"/> \ No newline at end of file diff --git a/sample_app/res/menu/main.xml b/sample_app/src/main/res/menu/main.xml similarity index 73% rename from sample_app/res/menu/main.xml rename to sample_app/src/main/res/menu/main.xml index c1afc17..e0858d8 100644 --- a/sample_app/res/menu/main.xml +++ b/sample_app/src/main/res/menu/main.xml @@ -14,33 +14,36 @@ See the License for the specific language governing permissions and limitations under the License. --> - + + android:title="" + app:showAsAction="always"/> + + android:title="@string/menu_scan" + app:showAsAction="ifRoom|withText"/> + android:title="@string/menu_stop" + app:showAsAction="ifRoom|withText"/> + android:orderInCategory="102" + android:title="@string/menu_share" + app:showAsAction="ifRoom"/> + android:title="@string/menu_about" + app:showAsAction="never"/> \ No newline at end of file diff --git a/sample_app/res/values-sw600dp/dimens.xml b/sample_app/src/main/res/values-sw600dp/dimens.xml similarity index 100% rename from sample_app/res/values-sw600dp/dimens.xml rename to sample_app/src/main/res/values-sw600dp/dimens.xml diff --git a/sample_app/res/values-sw720dp-land/dimens.xml b/sample_app/src/main/res/values-sw720dp-land/dimens.xml similarity index 100% rename from sample_app/res/values-sw720dp-land/dimens.xml rename to sample_app/src/main/res/values-sw720dp-land/dimens.xml diff --git a/sample_app/res/values-v11/styles.xml b/sample_app/src/main/res/values-v11/styles.xml similarity index 77% rename from sample_app/res/values-v11/styles.xml rename to sample_app/src/main/res/values-v11/styles.xml index 3c02242..c0ef0ed 100644 --- a/sample_app/res/values-v11/styles.xml +++ b/sample_app/src/main/res/values-v11/styles.xml @@ -4,7 +4,7 @@ Base application theme for API 11+. This theme completely replaces AppBaseTheme from res/values/styles.xml on API 11+ devices. --> - diff --git a/sample_app/res/values-v14/styles.xml b/sample_app/src/main/res/values-v14/styles.xml similarity index 79% rename from sample_app/res/values-v14/styles.xml rename to sample_app/src/main/res/values-v14/styles.xml index a91fd03..664f4f1 100644 --- a/sample_app/res/values-v14/styles.xml +++ b/sample_app/src/main/res/values-v14/styles.xml @@ -5,7 +5,7 @@ AppBaseTheme from BOTH res/values/styles.xml and res/values-v11/styles.xml on API 14+ devices. --> - diff --git a/sample_app/src/main/res/values/colors.xml b/sample_app/src/main/res/values/colors.xml new file mode 100644 index 0000000..c64f146 --- /dev/null +++ b/sample_app/src/main/res/values/colors.xml @@ -0,0 +1,3 @@ + + #66e0e0e0 + diff --git a/sample_app/res/values/dimens.xml b/sample_app/src/main/res/values/dimens.xml similarity index 100% rename from sample_app/res/values/dimens.xml rename to sample_app/src/main/res/values/dimens.xml diff --git a/sample_app/res/values/strings.xml b/sample_app/src/main/res/values/strings.xml similarity index 92% rename from sample_app/res/values/strings.xml rename to sample_app/src/main/res/values/strings.xml index 7d0bf1d..84c3f4f 100644 --- a/sample_app/res/values/strings.xml +++ b/sample_app/src/main/res/values/strings.xml @@ -15,6 +15,7 @@ Unknown characteristic Unknown device Unknown service + No known services %sm @@ -53,6 +54,8 @@ Desc: Device address: Device Class: + Major Class: + Services: Device Name: Distance: First RSSI: @@ -67,5 +70,6 @@ State: TX Power: UUID: - + Updated: + Descriptor: \ No newline at end of file diff --git a/sample_app/res/values/styles.xml b/sample_app/src/main/res/values/styles.xml similarity index 85% rename from sample_app/res/values/styles.xml rename to sample_app/src/main/res/values/styles.xml index 9cf5fd9..c503b3f 100644 --- a/sample_app/res/values/styles.xml +++ b/sample_app/src/main/res/values/styles.xml @@ -4,7 +4,7 @@ Base application theme, dependent on API level. This theme is replaced by AppBaseTheme from res/values-vXX/styles.xml on newer devices. --> - - - diff --git a/sample_app/src/uk/co/alt236/btlescan/activities/DeviceControlActivity.java b/sample_app/src/uk/co/alt236/btlescan/activities/DeviceControlActivity.java deleted file mode 100644 index 314f41e..0000000 --- a/sample_app/src/uk/co/alt236/btlescan/activities/DeviceControlActivity.java +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package uk.co.alt236.btlescan.activities; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -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.R; -import uk.co.alt236.btlescan.services.BluetoothLeService; -import android.app.Activity; -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattService; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.IBinder; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.ExpandableListView; -import android.widget.SimpleExpandableListAdapter; -import android.widget.TextView; -import butterknife.ButterKnife; -import butterknife.InjectView; - -/** - * For a given BLE device, this Activity provides the user interface to connect, display data, - * and display GATT services and characteristics supported by the device. The Activity - * communicates with {@code BluetoothLeService}, which in turn interacts with the - * Bluetooth LE API. - */ -public class DeviceControlActivity extends Activity { - private final static String TAG = DeviceControlActivity.class.getSimpleName(); - public static final String EXTRA_DEVICE = "extra_device"; - - private static final String LIST_NAME = "NAME"; - private static final String LIST_UUID = "UUID"; - - private BluetoothGattCharacteristic mNotifyCharacteristic; - private BluetoothLeService mBluetoothLeService; - private List> mGattCharacteristics = new ArrayList>(); - private String mDeviceAddress; - private String mDeviceName; - - @InjectView(R.id.gatt_services_list) ExpandableListView mGattServicesList; - @InjectView(R.id.connection_state) TextView mConnectionState; - - @InjectView(R.id.uuid) TextView mGattUUID; - @InjectView(R.id.description) TextView mGattUUIDDesc; - @InjectView(R.id.data_as_string) TextView mDataAsString; - @InjectView(R.id.data_as_array) TextView mDataAsArray; - - private boolean mConnected = false; - private String mExportString; - private BluetoothLeDevice mDevice; - - // Code to manage Service lifecycle. - private final ServiceConnection mServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName componentName, IBinder service) { - mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService(); - if (!mBluetoothLeService.initialize()) { - Log.e(TAG, "Unable to initialize Bluetooth"); - finish(); - } - // Automatically connects to the device upon successful start-up initialization. - mBluetoothLeService.connect(mDeviceAddress); - } - - @Override - public void onServiceDisconnected(ComponentName componentName) { - mBluetoothLeService = null; - } - }; - - // Handles various events fired by the Service. - // ACTION_GATT_CONNECTED: connected to a GATT server. - // ACTION_GATT_DISCONNECTED: disconnected from a GATT server. - // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services. - // ACTION_DATA_AVAILABLE: received data from the device. - // this can be a result of read or notification operations. - private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { - mConnected = true; - updateConnectionState(R.string.connected); - invalidateOptionsMenu(); - } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { - mConnected = false; - updateConnectionState(R.string.disconnected); - invalidateOptionsMenu(); - clearUI(); - } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { - // Show all the supported services and characteristics on the user interface. - displayGattServices(mBluetoothLeService.getSupportedGattServices()); - } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) { - final String noData = getString(R.string.no_data); - final String uuid = intent.getStringExtra(BluetoothLeService.EXTRA_UUID_CHAR); - final byte[] dataArr = intent.getByteArrayExtra(BluetoothLeService.EXTRA_DATA_RAW); - - mGattUUID.setText(tryString(uuid, noData)); - mGattUUIDDesc.setText(GattAttributeResolver.getAttributeName(uuid, getString(R.string.unknown))); - mDataAsArray.setText(ByteUtils.byteArrayToHexString(dataArr)); - mDataAsString.setText(new String(dataArr)); - } - } - }; - - // If a given GATT characteristic is selected, check for supported features. This sample - // demonstrates 'Read' and 'Notify' features. See - // http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete - // list of supported characteristic features. - private final ExpandableListView.OnChildClickListener servicesListClickListner = new ExpandableListView.OnChildClickListener() { - @Override - public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { - if (mGattCharacteristics != null) { - final BluetoothGattCharacteristic characteristic = mGattCharacteristics.get(groupPosition).get(childPosition); - final int charaProp = characteristic.getProperties(); - if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) { - // If there is an active notification on a characteristic, clear - // it first so it doesn't update the data field on the user interface. - if (mNotifyCharacteristic != null) { - mBluetoothLeService.setCharacteristicNotification(mNotifyCharacteristic, false); - mNotifyCharacteristic = null; - } - mBluetoothLeService.readCharacteristic(characteristic); - } - if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { - mNotifyCharacteristic = characteristic; - mBluetoothLeService.setCharacteristicNotification(characteristic, true); - } - return true; - } - return false; - } - }; - - private void clearUI() { - mGattServicesList.setAdapter((SimpleExpandableListAdapter) null); - mGattUUID.setText(R.string.no_data); - mGattUUIDDesc.setText(R.string.no_data); - mDataAsArray.setText(R.string.no_data); - mDataAsString.setText(R.string.no_data); - } - - private void generateExportString(List gattServices){ - final String unknownServiceString = getResources().getString(R.string.unknown_service); - final String unknownCharaString = getResources().getString(R.string.unknown_characteristic); - final StringBuilder exportBuilder = new StringBuilder(); - - exportBuilder.append("Device Name: "); - exportBuilder.append(mDeviceName); - exportBuilder.append('\n'); - exportBuilder.append("Device Address: "); - exportBuilder.append(mDeviceAddress); - exportBuilder.append('\n'); - exportBuilder.append('\n'); - - exportBuilder.append("Services:"); - exportBuilder.append("--------------------------"); - exportBuilder.append('\n'); - - String uuid = null; - for (BluetoothGattService gattService : gattServices) { - uuid = gattService.getUuid().toString(); - - exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownServiceString)); - exportBuilder.append(" ("); - exportBuilder.append(uuid); - exportBuilder.append(')'); - exportBuilder.append('\n'); - - final List gattCharacteristics = gattService.getCharacteristics(); - for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { - uuid = gattCharacteristic.getUuid().toString(); - - exportBuilder.append('\t'); - exportBuilder.append(GattAttributeResolver.getAttributeName(uuid, unknownCharaString)); - exportBuilder.append(" ("); - exportBuilder.append(uuid); - exportBuilder.append(')'); - exportBuilder.append('\n'); - } - - exportBuilder.append('\n'); - exportBuilder.append('\n'); - } - - exportBuilder.append("--------------------------"); - exportBuilder.append('\n'); - - mExportString = exportBuilder.toString(); - } - - // Demonstrates how to iterate through the supported GATT Services/Characteristics. - // In this sample, we populate the data structure that is bound to the ExpandableListView - // on the UI. - private void displayGattServices(List gattServices) { - if (gattServices == null) return; - generateExportString(gattServices); - - String uuid = null; - final String unknownServiceString = getResources().getString(R.string.unknown_service); - final String unknownCharaString = getResources().getString(R.string.unknown_characteristic); - final List> gattServiceData = new ArrayList>(); - final List>> gattCharacteristicData = new ArrayList>>(); - mGattCharacteristics = new ArrayList>(); - - // Loops through available GATT Services. - for (BluetoothGattService gattService : gattServices) { - final Map currentServiceData = new HashMap(); - uuid = gattService.getUuid().toString(); - currentServiceData.put(LIST_NAME, GattAttributeResolver.getAttributeName(uuid, unknownServiceString)); - currentServiceData.put(LIST_UUID, uuid); - gattServiceData.add(currentServiceData); - - final List> gattCharacteristicGroupData = new ArrayList>(); - final List gattCharacteristics = gattService.getCharacteristics(); - final List charas = new ArrayList(); - - // Loops through available Characteristics. - for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { - charas.add(gattCharacteristic); - final Map currentCharaData = new HashMap(); - uuid = gattCharacteristic.getUuid().toString(); - currentCharaData.put(LIST_NAME, GattAttributeResolver.getAttributeName(uuid, unknownCharaString)); - currentCharaData.put(LIST_UUID, uuid); - gattCharacteristicGroupData.add(currentCharaData); - } - - mGattCharacteristics.add(charas); - gattCharacteristicData.add(gattCharacteristicGroupData); - } - - final SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter( - this, - gattServiceData, - android.R.layout.simple_expandable_list_item_2, - new String[] {LIST_NAME, LIST_UUID}, - new int[] { android.R.id.text1, android.R.id.text2 }, - gattCharacteristicData, - android.R.layout.simple_expandable_list_item_2, - new String[] {LIST_NAME, LIST_UUID}, - new int[] { android.R.id.text1, android.R.id.text2 } - ); - - mGattServicesList.setAdapter(gattServiceAdapter); - invalidateOptionsMenu(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_gatt_services); - - final Intent intent = getIntent(); - mDevice = intent.getParcelableExtra(EXTRA_DEVICE); - mDeviceName = mDevice.getName(); - mDeviceAddress = mDevice.getAddress(); - - ButterKnife.inject(this); - - // Sets up UI references. - ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress); - mGattServicesList.setOnChildClickListener(servicesListClickListner); - - getActionBar().setTitle(mDeviceName); - getActionBar().setDisplayHomeAsUpEnabled(true); - - final Intent gattServiceIntent = new Intent(this, BluetoothLeService.class); - bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.gatt_services, menu); - if (mConnected) { - menu.findItem(R.id.menu_connect).setVisible(false); - menu.findItem(R.id.menu_disconnect).setVisible(true); - } else { - menu.findItem(R.id.menu_connect).setVisible(true); - menu.findItem(R.id.menu_disconnect).setVisible(false); - } - - if(mExportString == null){ - menu.findItem(R.id.menu_share).setVisible(false); - } else { - menu.findItem(R.id.menu_share).setVisible(true); - } - - return true; - } - - @Override - protected void onDestroy() { - super.onDestroy(); - unbindService(mServiceConnection); - mBluetoothLeService = null; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch(item.getItemId()) { - case R.id.menu_connect: - mBluetoothLeService.connect(mDeviceAddress); - return true; - case R.id.menu_disconnect: - mBluetoothLeService.disconnect(); - return true; - case android.R.id.home: - onBackPressed(); - return true; - case R.id.menu_share: - final Intent intent = new Intent(android.content.Intent.ACTION_SEND); - final String subject = getString(R.string.exporter_email_device_services_subject, mDeviceName, mDeviceAddress); - - intent.setType("text/plain"); - intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject); - intent.putExtra(android.content.Intent.EXTRA_TEXT, mExportString); - - startActivity(Intent.createChooser( - intent, - getString(R.string.exporter_email_device_list_picker_text))); - - return true; - } - return super.onOptionsItemSelected(item); - } - - @Override - protected void onPause() { - super.onPause(); - unregisterReceiver(mGattUpdateReceiver); - } - - @Override - protected void onResume() { - super.onResume(); - registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter()); - if (mBluetoothLeService != null) { - final boolean result = mBluetoothLeService.connect(mDeviceAddress); - Log.d(TAG, "Connect request result=" + result); - } - } - - private void updateConnectionState(final int resourceId) { - runOnUiThread(new Runnable() { - @Override - public void run() { - final int colourId; - - switch(resourceId){ - case R.string.connected: - colourId = android.R.color.holo_green_dark; - break; - case R.string.disconnected: - colourId = android.R.color.holo_red_dark; - break; - default: - colourId = android.R.color.black; - break; - } - - mConnectionState.setText(resourceId); - mConnectionState.setTextColor(getResources().getColor(colourId)); - } - }); - } - - private static IntentFilter makeGattUpdateIntentFilter() { - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED); - intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED); - intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED); - intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE); - return intentFilter; - } - - private static String tryString(String string, String fallback){ - if(string == null){ - return fallback; - } else{ - return string; - } - } -} \ No newline at end of file diff --git a/sample_app/src/uk/co/alt236/btlescan/activities/DeviceDetailsActivity.java b/sample_app/src/uk/co/alt236/btlescan/activities/DeviceDetailsActivity.java deleted file mode 100644 index 6f0870b..0000000 --- a/sample_app/src/uk/co/alt236/btlescan/activities/DeviceDetailsActivity.java +++ /dev/null @@ -1,206 +0,0 @@ -package uk.co.alt236.btlescan.activities; - -import java.util.Collection; -import java.util.Locale; - -import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; -import uk.co.alt236.bluetoothlelib.device.adrecord.AdRecord; -import uk.co.alt236.bluetoothlelib.device.mfdata.IBeaconManufacturerData; -import uk.co.alt236.bluetoothlelib.resolvers.CompanyIdentifierResolver; -import uk.co.alt236.bluetoothlelib.util.AdRecordUtils; -import uk.co.alt236.bluetoothlelib.util.ByteUtils; -import uk.co.alt236.bluetoothlelib.util.IBeaconUtils; -import uk.co.alt236.btlescan.R; -import uk.co.alt236.btlescan.util.TimeFormatter; -import android.app.ListActivity; -import android.content.Intent; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.widget.LinearLayout; -import android.widget.TextView; -import butterknife.ButterKnife; - -import com.commonsware.cwac.merge.MergeAdapter; - -public class DeviceDetailsActivity extends ListActivity{ - public static final String EXTRA_DEVICE = "extra_device"; - - private BluetoothLeDevice mDevice; - - private void appendAdRecordView(MergeAdapter adapter, String title, AdRecord record){ - final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_adrecord, null); - final TextView tvString = (TextView) lt.findViewById(R.id.data_as_string); - final TextView tvArray = (TextView) lt.findViewById(R.id.data_as_array); - final TextView tvTitle = (TextView) lt.findViewById(R.id.title); - - tvTitle.setText(title ); - tvString.setText("'" + AdRecordUtils.getRecordDataAsString(record) + "'"); - tvArray.setText("'" + ByteUtils.byteArrayToHexString(record.getData()) + "'"); - - adapter.addView(lt); - } - - private void appendDeviceInfo(MergeAdapter adapter, BluetoothLeDevice device){ - final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_device_info, null); - final TextView tvName = (TextView) lt.findViewById(R.id.deviceName); - final TextView tvAddress = (TextView) lt.findViewById(R.id.deviceAddress); - final TextView tvClass = (TextView) lt.findViewById(R.id.deviceClass); - final TextView tvBondingState = (TextView) lt.findViewById(R.id.deviceBondingState); - - tvName.setText(device.getName()); - tvAddress.setText(device.getAddress()); - tvClass.setText(device.getBluetoothDeviceClassName()); - tvBondingState.setText(device.getBluetoothDeviceBondState()); - - adapter.addView(lt); - } - - private void appendHeader(MergeAdapter adapter, String title){ - final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_header, null); - final TextView tvTitle = (TextView) lt.findViewById(R.id.title); - tvTitle.setText(title); - - adapter.addView(lt); - } - - private void appendIBeaconInfo(MergeAdapter adapter, IBeaconManufacturerData iBeaconData){ - final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_ibeacon_details, null); - final TextView tvCompanyId = (TextView) lt.findViewById(R.id.companyId); - final TextView tvAdvert = (TextView) lt.findViewById(R.id.advertisement); - final TextView tvUUID = (TextView) lt.findViewById(R.id.uuid); - final TextView tvMajor = (TextView) lt.findViewById(R.id.major); - final TextView tvMinor = (TextView) lt.findViewById(R.id.minor); - final TextView tvTxPower = (TextView) lt.findViewById(R.id.txpower); - - tvCompanyId.setText( - CompanyIdentifierResolver.getCompanyName(iBeaconData.getCompanyIdentifier(), getString(R.string.unknown)) - + " (" + hexEncode(iBeaconData.getCompanyIdentifier()) + ")"); - tvAdvert.setText(iBeaconData.getIBeaconAdvertisement() + " (" + hexEncode( iBeaconData.getIBeaconAdvertisement() ) + ")"); - tvUUID.setText(iBeaconData.getUUID().toString()); - tvMajor.setText(iBeaconData.getMajor() + " (" + hexEncode( iBeaconData.getMajor() ) + ")"); - tvMinor.setText(iBeaconData.getMinor() + " (" + hexEncode( iBeaconData.getMinor() ) + ")"); - tvTxPower.setText(iBeaconData.getCalibratedTxPower() + " (" + hexEncode( iBeaconData.getCalibratedTxPower() ) + ")"); - - adapter.addView(lt); - } - - private void appendRssiInfo(MergeAdapter adapter, BluetoothLeDevice device){ - final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_rssi_info, null); - final TextView tvFirstTimestamp = (TextView) lt.findViewById(R.id.firstTimestamp); - final TextView tvFirstRssi = (TextView) lt.findViewById(R.id.firstRssi); - final TextView tvLastTimestamp = (TextView) lt.findViewById(R.id.lastTimestamp); - final TextView tvLastRssi = (TextView) lt.findViewById(R.id.lastRssi); - final TextView tvRunningAverageRssi = (TextView) lt.findViewById(R.id.runningAverageRssi); - - tvFirstTimestamp.setText(formatTime(device.getFirstTimestamp())); - tvFirstRssi.setText(formatRssi(device.getFirstRssi())); - tvLastTimestamp.setText(formatTime(device.getTimestamp())); - tvLastRssi.setText(formatRssi(device.getRssi())); - tvRunningAverageRssi.setText(formatRssi(device.getRunningAverageRssi())); - - adapter.addView(lt); - } - - private void appendSimpleText(MergeAdapter adapter, byte data[]){ - appendSimpleText(adapter, ByteUtils.byteArrayToHexString(data)); - } - - private void appendSimpleText(MergeAdapter adapter, String data){ - final LinearLayout lt = (LinearLayout) getLayoutInflater().inflate(R.layout.list_item_view_textview, null); - final TextView tvData = (TextView) lt.findViewById(R.id.data); - - tvData.setText(data); - - adapter.addView(lt); - } - - - private String formatRssi(double rssi){ - return getString(R.string.formatter_db, String.valueOf(rssi)); - } - - private String formatRssi(int rssi){ - return getString(R.string.formatter_db, String.valueOf(rssi)); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_details); - ButterKnife.inject(this); - - mDevice = getIntent().getParcelableExtra(EXTRA_DEVICE); - - pupulateDetails(mDevice); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.details, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_connect: - - final Intent intent = new Intent(this, DeviceControlActivity.class); - intent.putExtra(DeviceControlActivity.EXTRA_DEVICE, mDevice); - - startActivity(intent); - - break; - } - return true; - } - - private void pupulateDetails(BluetoothLeDevice device) { - final MergeAdapter adapter = new MergeAdapter(); - - if(device == null){ - appendHeader(adapter, getString(R.string.header_device_info)); - appendSimpleText(adapter, getString(R.string.invalid_device_data)); - } else { - appendHeader(adapter, getString(R.string.header_device_info)); - appendDeviceInfo(adapter, device); - - appendHeader(adapter, getString(R.string.header_rssi_info)); - appendRssiInfo(adapter, device); - - appendHeader(adapter, getString(R.string.header_scan_record)); - appendSimpleText(adapter, device.getScanRecord()); - - final Collection adRecords = device.getAdRecordStore().getRecordsAsCollection(); - if(adRecords.size() > 0){ - appendHeader(adapter, getString(R.string.header_raw_ad_records)); - - for(final AdRecord record : adRecords){ - - appendAdRecordView( - adapter, - "#" + record.getType() + " " + record.getHumanReadableType(), - record); - } - } - - final boolean isIBeacon = IBeaconUtils.isThisAnIBeacon(device); - if(isIBeacon){ - final IBeaconManufacturerData iBeaconData = new IBeaconManufacturerData(device); - appendHeader(adapter, getString(R.string.header_ibeacon_data)); - appendIBeaconInfo(adapter, iBeaconData); - } - - } - getListView().setAdapter(adapter); - } - - private static String formatTime(long time){ - return TimeFormatter.getIsoDateTime(time); - } - - private static String hexEncode(int integer){ - return "0x" + Integer.toHexString(integer).toUpperCase(Locale.US); - } -} diff --git a/sample_app/src/uk/co/alt236/btlescan/activities/MainActivity.java b/sample_app/src/uk/co/alt236/btlescan/activities/MainActivity.java deleted file mode 100644 index eb3a5cd..0000000 --- a/sample_app/src/uk/co/alt236/btlescan/activities/MainActivity.java +++ /dev/null @@ -1,196 +0,0 @@ -package uk.co.alt236.btlescan.activities; - -import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; -import uk.co.alt236.btlescan.R; -import uk.co.alt236.btlescan.adapters.LeDeviceListAdapter; -import uk.co.alt236.btlescan.containers.BluetoothLeDeviceStore; -import uk.co.alt236.btlescan.util.BluetoothLeScanner; -import uk.co.alt236.btlescan.util.BluetoothUtils; -import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor; -import android.app.AlertDialog; -import android.app.ListActivity; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.text.SpannableString; -import android.text.method.LinkMovementMethod; -import android.text.util.Linkify; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.ListView; -import android.widget.TextView; -import butterknife.ButterKnife; -import butterknife.InjectView; - -public class MainActivity extends ListActivity { - @InjectView(R.id.tvBluetoothLe) TextView mTvBluetoothLeStatus; - @InjectView(R.id.tvBluetoothStatus) TextView mTvBluetoothStatus; - @InjectView(R.id.tvItemCount) TextView mTvItemCount; - - private BluetoothUtils mBluetoothUtils; - private BluetoothLeScanner mScanner; - private LeDeviceListAdapter mLeDeviceListAdapter; - private BluetoothLeDeviceStore mDeviceStore; - - 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()); - mDeviceStore.addDevice(deviceLe); - final EasyObjectCursor c = mDeviceStore.getDeviceCursor(); - - runOnUiThread(new Runnable() { - @Override - public void run() { - mLeDeviceListAdapter.swapCursor(c); - updateItemCount(mLeDeviceListAdapter.getCount()); - } - }); - } - }; - - private void updateItemCount(int count){ - mTvItemCount.setText( - getString( - R.string.formatter_item_count, - String.valueOf(count))); - } - - private void displayAboutDialog(){ - // REALLY REALLY LAZY LINKIFIED DIALOG - final int paddingSizeDp = 5; - final float scale = getResources().getDisplayMetrics().density; - final int dpAsPixels = (int) (paddingSizeDp * scale + 0.5f); - - final TextView textView=new TextView(this); - final SpannableString text = new SpannableString(getString(R.string.about_dialog_text)); - - textView.setText(text); - textView.setAutoLinkMask(RESULT_OK); - textView.setMovementMethod(LinkMovementMethod.getInstance()); - textView.setPadding(dpAsPixels, dpAsPixels, dpAsPixels, dpAsPixels); - - Linkify.addLinks(text, Linkify.ALL); - new AlertDialog.Builder(this) - .setTitle(R.string.menu_about) - .setCancelable(false) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) {} - }) - .setView(textView) - .show(); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - ButterKnife.inject(this); - - mDeviceStore = new BluetoothLeDeviceStore(); - mBluetoothUtils = new BluetoothUtils(this); - mScanner = new BluetoothLeScanner(mLeScanCallback, mBluetoothUtils); - updateItemCount(0); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.main, menu); - if (!mScanner.isScanning()) { - menu.findItem(R.id.menu_stop).setVisible(false); - menu.findItem(R.id.menu_scan).setVisible(true); - menu.findItem(R.id.menu_refresh).setActionView(null); - } else { - menu.findItem(R.id.menu_stop).setVisible(true); - menu.findItem(R.id.menu_scan).setVisible(false); - menu.findItem(R.id.menu_refresh).setActionView(R.layout.actionbar_progress_indeterminate); - } - - if(getListView().getCount() > 0){ - menu.findItem(R.id.menu_share).setVisible(true); - } else { - menu.findItem(R.id.menu_share).setVisible(false); - } - - return true; - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - final BluetoothLeDevice device = (BluetoothLeDevice) mLeDeviceListAdapter.getItem(position); - if (device == null) return; - - - final Intent intent = new Intent(this, DeviceDetailsActivity.class); - intent.putExtra(DeviceDetailsActivity.EXTRA_DEVICE, device); - - startActivity(intent); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_scan: - startScan(); - break; - case R.id.menu_stop: - mScanner.scanLeDevice(-1, false); - invalidateOptionsMenu(); - break; - case R.id.menu_about: - displayAboutDialog(); - break; - case R.id.menu_share: - mDeviceStore.shareDataAsEmail(this); - } - return true; - } - - @Override - protected void onPause() { - super.onPause(); - mScanner.scanLeDevice(-1, false); - } - - @Override - public void onResume(){ - super.onResume(); - final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn(); - final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported(); - - if(mIsBluetoothOn){ - mTvBluetoothStatus.setText(R.string.on); - } else { - mTvBluetoothStatus.setText(R.string.off); - } - - if(mIsBluetoothLePresent){ - mTvBluetoothLeStatus.setText(R.string.supported); - } else { - mTvBluetoothLeStatus.setText(R.string.not_supported); - } - - invalidateOptionsMenu(); - } - - private void startScan(){ - final boolean mIsBluetoothOn = mBluetoothUtils.isBluetoothOn(); - final boolean mIsBluetoothLePresent = mBluetoothUtils.isBluetoothLeSupported(); - mDeviceStore.clear(); - updateItemCount(0); - - mLeDeviceListAdapter = new LeDeviceListAdapter(this, mDeviceStore.getDeviceCursor()); - setListAdapter(mLeDeviceListAdapter); - - mBluetoothUtils.askUserToEnableBluetoothIfNeeded(); - if(mIsBluetoothOn && mIsBluetoothLePresent){ - mScanner.scanLeDevice(-1, true); - invalidateOptionsMenu(); - } - } - -} diff --git a/sample_app/src/uk/co/alt236/btlescan/adapters/LeDeviceListAdapter.java b/sample_app/src/uk/co/alt236/btlescan/adapters/LeDeviceListAdapter.java deleted file mode 100644 index 38d3892..0000000 --- a/sample_app/src/uk/co/alt236/btlescan/adapters/LeDeviceListAdapter.java +++ /dev/null @@ -1,123 +0,0 @@ -package uk.co.alt236.btlescan.adapters; - -import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; -import uk.co.alt236.bluetoothlelib.device.IBeaconDevice; -import uk.co.alt236.bluetoothlelib.util.IBeaconUtils; -import uk.co.alt236.btlescan.R; -import uk.co.alt236.btlescan.util.Constants; -import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor; -import android.app.Activity; -import android.support.v4.widget.SimpleCursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; -// Adapter for holding devices found through scanning. -public class LeDeviceListAdapter extends SimpleCursorAdapter { - private final LayoutInflater mInflator; - private final Activity mActivity; - - public LeDeviceListAdapter(Activity activity, EasyObjectCursor cursor) { - super(activity, R.layout.list_item_device, cursor, new String[0], new int[0], 0); - mInflator = activity.getLayoutInflater(); - mActivity = activity; - } - - @SuppressWarnings("unchecked") - @Override - public EasyObjectCursor getCursor(){ - return ((EasyObjectCursor) super.getCursor()); - } - - @Override - public BluetoothLeDevice getItem(int i){ - return getCursor().getItem(i); - } - - @Override - public long getItemId(int i) { - return i; - } - - @Override - public View getView(int i, View view, ViewGroup viewGroup) { - ViewHolder viewHolder; - // General ListView optimization code. - if (view == null) { - view = mInflator.inflate(R.layout.list_item_device, null); - viewHolder = new ViewHolder(); - viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address); - viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name); - viewHolder.deviceRssi = (TextView) view.findViewById(R.id.device_rssi); - viewHolder.deviceIcon = (ImageView) view.findViewById(R.id.device_icon); - viewHolder.deviceLastUpdated = (TextView) view.findViewById(R.id.device_last_update); - viewHolder.ibeaconMajor = (TextView) view.findViewById(R.id.ibeacon_major); - viewHolder.ibeaconMinor = (TextView) view.findViewById(R.id.ibeacon_minor); - viewHolder.ibeaconDistance = (TextView) view.findViewById(R.id.ibeacon_distance); - viewHolder.ibeaconUUID = (TextView) view.findViewById(R.id.ibeacon_uuid); - viewHolder.ibeaconTxPower = (TextView) view.findViewById(R.id.ibeacon_tx_power); - viewHolder.ibeaconSection = (View) view.findViewById(R.id.ibeacon_section); - viewHolder.ibeaconDistanceDescriptor = (TextView) view.findViewById(R.id.ibeacon_distance_descriptor); - view.setTag(viewHolder); - } else { - viewHolder = (ViewHolder) view.getTag(); - } - - final BluetoothLeDevice device = getCursor().getItem(i); - final String deviceName = device.getName(); - final double rssi = device.getRssi(); - - if (deviceName != null && deviceName.length() > 0){ - viewHolder.deviceName.setText(deviceName); - } else{ - viewHolder.deviceName.setText(R.string.unknown_device); - } - - if (IBeaconUtils.isThisAnIBeacon(device)){ - final IBeaconDevice iBeacon = new IBeaconDevice(device); - final String accuracy = Constants.DOUBLE_TWO_DIGIT_ACCURACY.format(iBeacon.getAccuracy()); - - viewHolder.deviceIcon.setImageResource(R.drawable.ic_device_ibeacon); - viewHolder.ibeaconSection.setVisibility(View.VISIBLE); - viewHolder.ibeaconMajor.setText(String.valueOf(iBeacon.getMajor())); - viewHolder.ibeaconMinor.setText(String.valueOf(iBeacon.getMinor())); - viewHolder.ibeaconTxPower.setText(String.valueOf(iBeacon.getCalibratedTxPower())); - viewHolder.ibeaconUUID.setText(iBeacon.getUUID()); - viewHolder.ibeaconDistance.setText( - mActivity.getString(R.string.formatter_meters, accuracy)); - viewHolder.ibeaconDistanceDescriptor.setText(iBeacon.getDistanceDescriptor().toString()); - } else { - viewHolder.deviceIcon.setImageResource(R.drawable.ic_bluetooth); - viewHolder.ibeaconSection.setVisibility(View.GONE); - } - - final String rssiString = - mActivity.getString(R.string.formatter_db, String.valueOf(rssi)); - final String runningAverageRssiString = - mActivity.getString(R.string.formatter_db, String.valueOf(device.getRunningAverageRssi())); - - viewHolder.deviceLastUpdated.setText( - android.text.format.DateFormat.format( - Constants.TIME_FORMAT, new java.util.Date(device.getTimestamp()))); - viewHolder.deviceAddress.setText(device.getAddress()); - viewHolder.deviceRssi.setText(rssiString + " / " + runningAverageRssiString); - return view; - } - - static class ViewHolder { - TextView deviceName; - TextView deviceAddress; - TextView deviceRssi; - TextView ibeaconUUID; - TextView ibeaconMajor; - TextView ibeaconMinor; - TextView ibeaconTxPower; - TextView ibeaconDistance; - TextView ibeaconDistanceDescriptor; - TextView deviceLastUpdated; - View ibeaconSection; - ImageView deviceIcon; - } - -} \ No newline at end of file diff --git a/sample_app/src/uk/co/alt236/btlescan/containers/BluetoothLeDeviceStore.java b/sample_app/src/uk/co/alt236/btlescan/containers/BluetoothLeDeviceStore.java deleted file mode 100644 index 147ca27..0000000 --- a/sample_app/src/uk/co/alt236/btlescan/containers/BluetoothLeDeviceStore.java +++ /dev/null @@ -1,182 +0,0 @@ -package uk.co.alt236.btlescan.containers; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import uk.co.alt236.bluetoothlelib.device.BluetoothLeDevice; -import uk.co.alt236.bluetoothlelib.device.IBeaconDevice; -import uk.co.alt236.bluetoothlelib.util.ByteUtils; -import uk.co.alt236.bluetoothlelib.util.IBeaconUtils; -import uk.co.alt236.btlescan.R; -import uk.co.alt236.btlescan.util.CsvWriterHelper; -import uk.co.alt236.btlescan.util.TimeFormatter; -import uk.co.alt236.easycursor.objectcursor.EasyObjectCursor; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; - -public class BluetoothLeDeviceStore { - private final Map mDeviceMap; - - - public BluetoothLeDeviceStore(){ - mDeviceMap = new HashMap(); - } - - public void addDevice(BluetoothLeDevice device){ - if(mDeviceMap.containsKey(device.getAddress())){ - mDeviceMap.get(device.getAddress()).updateRssiReading(device.getTimestamp(), device.getRssi()); - } else { - mDeviceMap.put(device.getAddress(), device); - } - } - - public void clear(){ - mDeviceMap.clear(); - } - - - private FileWriter generateFile(File file, String contents){ - FileWriter writer = null; - try { - writer = new FileWriter(file); - writer.append(contents); - writer.flush(); - - } catch (IOException e) { - e.printStackTrace(); - }finally{ - try { - writer.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return writer; - } - - public EasyObjectCursor getDeviceCursor(){ - return new EasyObjectCursor( - BluetoothLeDevice.class, - getDeviceList(), - "address"); - } - - public List getDeviceList(){ - final List methodResult = new ArrayList(mDeviceMap.values()); - - Collections.sort(methodResult, new Comparator() { - - @Override - public int compare(BluetoothLeDevice arg0, BluetoothLeDevice arg1) { - return arg0.getAddress().compareToIgnoreCase(arg1.getAddress()); - } - }); - - return methodResult; - } - - - private String getListAsCsv(){ - final List list = getDeviceList(); - final StringBuilder sb = new StringBuilder(); - sb.append(CsvWriterHelper.addStuff("mac")); - sb.append(CsvWriterHelper.addStuff("name")); - sb.append(CsvWriterHelper.addStuff("firstTimestamp")); - sb.append(CsvWriterHelper.addStuff("firstRssi")); - sb.append(CsvWriterHelper.addStuff("currentTimestamp")); - sb.append(CsvWriterHelper.addStuff("currentRssi")); - sb.append(CsvWriterHelper.addStuff("adRecord")); - sb.append(CsvWriterHelper.addStuff("iBeacon")); - sb.append(CsvWriterHelper.addStuff("uuid")); - sb.append(CsvWriterHelper.addStuff("major")); - sb.append(CsvWriterHelper.addStuff("minor")); - sb.append(CsvWriterHelper.addStuff("txPower")); - sb.append(CsvWriterHelper.addStuff("distance")); - sb.append(CsvWriterHelper.addStuff("accuracy")); - sb.append('\n'); - - for(BluetoothLeDevice device : list){ - sb.append(CsvWriterHelper.addStuff(device.getAddress())); - sb.append(CsvWriterHelper.addStuff(device.getName())); - sb.append(CsvWriterHelper.addStuff(TimeFormatter.getIsoDateTime(device.getFirstTimestamp()))); - sb.append(CsvWriterHelper.addStuff(device.getFirstRssi())); - sb.append(CsvWriterHelper.addStuff(TimeFormatter.getIsoDateTime(device.getTimestamp()))); - sb.append(CsvWriterHelper.addStuff(device.getRssi())); - sb.append(CsvWriterHelper.addStuff(ByteUtils.byteArrayToHexString(device.getScanRecord()))); - final boolean isIBeacon = IBeaconUtils.isThisAnIBeacon(device); - final String uuid; - final String minor; - final String major; - final String txPower; - final String distance; - final String accuracy; - - if(isIBeacon){ - final IBeaconDevice beacon = new IBeaconDevice(device); - uuid = String.valueOf(beacon.getUUID()); - minor = String.valueOf(beacon.getMinor()); - major = String.valueOf(beacon.getMajor()); - txPower = String.valueOf(beacon.getCalibratedTxPower()); - distance = beacon.getDistanceDescriptor().toString().toLowerCase(Locale.US); - accuracy = String.valueOf(beacon.getAccuracy()); - } else { - uuid = ""; - minor = ""; - major = ""; - txPower = ""; - distance = ""; - accuracy = ""; - } - - sb.append(CsvWriterHelper.addStuff(isIBeacon)); - sb.append(CsvWriterHelper.addStuff(uuid)); - sb.append(CsvWriterHelper.addStuff(minor)); - sb.append(CsvWriterHelper.addStuff(major)); - sb.append(CsvWriterHelper.addStuff(txPower)); - sb.append(CsvWriterHelper.addStuff(distance)); - sb.append(CsvWriterHelper.addStuff(accuracy)); - - sb.append('\n'); - } - - return sb.toString(); - } - - - public void shareDataAsEmail(Context context){ - final long timeInMillis = System.currentTimeMillis(); - - final String to = null; - final String subject = context.getString( - R.string.exporter_email_device_list_subject, - TimeFormatter.getIsoDateTime(timeInMillis)); - - final String message = context.getString(R.string.exporter_email_device_list_body); - - final Intent i = new Intent(Intent.ACTION_SEND); - i.setType("plain/text"); - try { - final File outputDir = context.getCacheDir(); - final File outputFile = File.createTempFile("bluetooth_le_" + timeInMillis, ".csv", outputDir); - outputFile.setReadable(true, false); - generateFile(outputFile, getListAsCsv()); - i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(outputFile)); - i.putExtra(Intent.EXTRA_EMAIL, new String[] { to }); - i.putExtra(Intent.EXTRA_SUBJECT, subject); - i.putExtra(Intent.EXTRA_TEXT, message); - context.startActivity(Intent.createChooser(i, context.getString(R.string.exporter_email_device_list_picker_text))); - - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/sample_app/src/uk/co/alt236/btlescan/services/BluetoothLeService.java b/sample_app/src/uk/co/alt236/btlescan/services/BluetoothLeService.java deleted file mode 100644 index 03f7539..0000000 --- a/sample_app/src/uk/co/alt236/btlescan/services/BluetoothLeService.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package uk.co.alt236.btlescan.services; - -import java.util.List; - -import android.app.Service; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCallback; -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattService; -import android.bluetooth.BluetoothManager; -import android.bluetooth.BluetoothProfile; -import android.content.Context; -import android.content.Intent; -import android.os.Binder; -import android.os.IBinder; -import android.util.Log; - -/** - * Service for managing connection and data communication with a GATT server hosted on a - * given Bluetooth LE device. - */ -public class BluetoothLeService extends Service { - private final static String TAG = BluetoothLeService.class.getSimpleName(); - - private BluetoothManager mBluetoothManager; - private BluetoothAdapter mBluetoothAdapter; - private String mBluetoothDeviceAddress; - private BluetoothGatt mBluetoothGatt; - private int mConnectionState = STATE_DISCONNECTED; - - private static final int STATE_DISCONNECTED = 0; - private static final int STATE_CONNECTING = 1; - private static final int STATE_CONNECTED = 2; - - public final static String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; - public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; - public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; - public final static String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; - public final static String EXTRA_DATA_RAW = "com.example.bluetooth.le.EXTRA_DATA_RAW"; - public final static String EXTRA_UUID_CHAR = "com.example.bluetooth.le.EXTRA_UUID_CHAR"; - - // Implements callback methods for GATT events that the app cares about. For example, - // connection change and services discovered. - private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { - @Override - public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { - String intentAction; - if (newState == BluetoothProfile.STATE_CONNECTED) { - intentAction = ACTION_GATT_CONNECTED; - mConnectionState = STATE_CONNECTED; - broadcastUpdate(intentAction); - Log.i(TAG, "Connected to GATT server."); - // Attempts to discover services after successful connection. - Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices()); - - } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { - intentAction = ACTION_GATT_DISCONNECTED; - mConnectionState = STATE_DISCONNECTED; - Log.i(TAG, "Disconnected from GATT server."); - broadcastUpdate(intentAction); - } - } - - @Override - public void onServicesDiscovered(BluetoothGatt gatt, int status) { - if (status == BluetoothGatt.GATT_SUCCESS) { - broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); - } else { - Log.w(TAG, "onServicesDiscovered received: " + status); - } - } - - @Override - public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - if (status == BluetoothGatt.GATT_SUCCESS) { - broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); - } - } - - @Override - public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); - } - }; - - private void broadcastUpdate(final String action) { - final Intent intent = new Intent(action); - sendBroadcast(intent); - } - - private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { - final Intent intent = new Intent(action); - intent.putExtra(EXTRA_UUID_CHAR, characteristic.getUuid().toString()); - - // Always try to add the RAW value - final byte[] data = characteristic.getValue(); - if (data != null && data.length > 0) { - intent.putExtra(EXTRA_DATA_RAW, data); - } - - sendBroadcast(intent); - } - - public class LocalBinder extends Binder { - public BluetoothLeService getService() { - return BluetoothLeService.this; - } - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - @Override - public boolean onUnbind(Intent intent) { - // After using a given device, you should make sure that BluetoothGatt.close() is called - // such that resources are cleaned up properly. In this particular example, close() is - // invoked when the UI is disconnected from the Service. - close(); - return super.onUnbind(intent); - } - - private final IBinder mBinder = new LocalBinder(); - - /** - * Initializes a reference to the local Bluetooth adapter. - * - * @return Return true if the initialization is successful. - */ - public boolean initialize() { - // For API level 18 and above, get a reference to BluetoothAdapter through - // BluetoothManager. - if (mBluetoothManager == null) { - mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); - if (mBluetoothManager == null) { - Log.e(TAG, "Unable to initialize BluetoothManager."); - return false; - } - } - - mBluetoothAdapter = mBluetoothManager.getAdapter(); - if (mBluetoothAdapter == null) { - Log.e(TAG, "Unable to obtain a BluetoothAdapter."); - return false; - } - - return true; - } - - /** - * Connects to the GATT server hosted on the Bluetooth LE device. - * - * @param address The device address of the destination device. - * - * @return Return true if the connection is initiated successfully. The connection result - * is reported asynchronously through the - * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} - * callback. - */ - public boolean connect(final String address) { - if (mBluetoothAdapter == null || address == null) { - Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); - return false; - } - - // Previously connected device. Try to reconnect. - if (mBluetoothDeviceAddress != null - && address.equals(mBluetoothDeviceAddress) - && mBluetoothGatt != null) { - - Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection."); - if (mBluetoothGatt.connect()) { - mConnectionState = STATE_CONNECTING; - return true; - } else { - return false; - } - } - - final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); - if (device == null) { - Log.w(TAG, "Device not found. Unable to connect."); - return false; - } - // We want to directly connect to the device, so we are setting the autoConnect - // parameter to false. - mBluetoothGatt = device.connectGatt(this, false, mGattCallback); - Log.d(TAG, "Trying to create a new connection."); - mBluetoothDeviceAddress = address; - mConnectionState = STATE_CONNECTING; - return true; - } - - /** - * Disconnects an existing connection or cancel a pending connection. The disconnection result - * is reported asynchronously through the - * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} - * callback. - */ - public void disconnect() { - if (mBluetoothAdapter == null || mBluetoothGatt == null) { - Log.w(TAG, "BluetoothAdapter not initialized"); - return; - } - mBluetoothGatt.disconnect(); - } - - /** - * After using a given BLE device, the app must call this method to ensure resources are - * released properly. - */ - public void close() { - if (mBluetoothGatt == null) { - return; - } - mBluetoothGatt.close(); - mBluetoothGatt = null; - } - - /** - * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported - * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} - * callback. - * - * @param characteristic The characteristic to read from. - */ - public void readCharacteristic(BluetoothGattCharacteristic characteristic) { - if (mBluetoothAdapter == null || mBluetoothGatt == null) { - Log.w(TAG, "BluetoothAdapter not initialized"); - return; - } - mBluetoothGatt.readCharacteristic(characteristic); - } - - /** - * Enables or disables notification on a give characteristic. - * - * @param characteristic Characteristic to act on. - * @param enabled If true, enable notification. False otherwise. - */ - public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { - if (mBluetoothAdapter == null || mBluetoothGatt == null) { - Log.w(TAG, "BluetoothAdapter not initialized"); - return; - } - mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); - } - - /** - * Retrieves a list of supported GATT services on the connected device. This should be - * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully. - * - * @return A {@code List} of supported services. - */ - public List getSupportedGattServices() { - if (mBluetoothGatt == null) return null; - - return mBluetoothGatt.getServices(); - } -} \ No newline at end of file diff --git a/sample_app/src/uk/co/alt236/btlescan/util/BluetoothLeScanner.java b/sample_app/src/uk/co/alt236/btlescan/util/BluetoothLeScanner.java deleted file mode 100644 index 38d1f79..0000000 --- a/sample_app/src/uk/co/alt236/btlescan/util/BluetoothLeScanner.java +++ /dev/null @@ -1,46 +0,0 @@ -package uk.co.alt236.btlescan.util; - -import android.bluetooth.BluetoothAdapter; -import android.os.Handler; -import android.util.Log; - -public class BluetoothLeScanner { - private final Handler mHandler; - private final BluetoothAdapter.LeScanCallback mLeScanCallback; - private final BluetoothUtils mBluetoothUtils; - private boolean mScanning; - - public BluetoothLeScanner(BluetoothAdapter.LeScanCallback leScanCallback, BluetoothUtils bluetoothUtils){ - mHandler = new Handler(); - mLeScanCallback = leScanCallback; - mBluetoothUtils = bluetoothUtils; - } - - public boolean isScanning() { - return mScanning; - } - - public void scanLeDevice(final int duration, final boolean enable) { - if (enable) { - if(mScanning){return;} - Log.d("TAG", "~ Starting Scan"); - // Stops scanning after a pre-defined scan period. - if(duration > 0){ - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - Log.d("TAG", "~ Stopping Scan (timeout)"); - mScanning = false; - mBluetoothUtils.getBluetoothAdapter().stopLeScan(mLeScanCallback); - } - }, duration); - } - mScanning = true; - mBluetoothUtils.getBluetoothAdapter().startLeScan(mLeScanCallback); - } else { - Log.d("TAG", "~ Stopping Scan"); - mScanning = false; - mBluetoothUtils.getBluetoothAdapter().stopLeScan(mLeScanCallback); - } - } -} diff --git a/sample_app/src/uk/co/alt236/btlescan/util/BluetoothUtils.java b/sample_app/src/uk/co/alt236/btlescan/util/BluetoothUtils.java deleted file mode 100644 index 3bf7dd6..0000000 --- a/sample_app/src/uk/co/alt236/btlescan/util/BluetoothUtils.java +++ /dev/null @@ -1,45 +0,0 @@ -package uk.co.alt236.btlescan.util; - -import android.app.Activity; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothManager; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; - -public final class BluetoothUtils { - private final Activity mActivity; - private final BluetoothAdapter mBluetoothAdapter; - private final BluetoothManager mBluetoothManager; - - public final static int REQUEST_ENABLE_BT = 2001; - - public BluetoothUtils(Activity activity){ - mActivity = activity; - mBluetoothManager = (BluetoothManager) mActivity.getSystemService(Context.BLUETOOTH_SERVICE); - mBluetoothAdapter = mBluetoothManager.getAdapter(); - } - - public void askUserToEnableBluetoothIfNeeded(){ - if (isBluetoothLeSupported() && (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())) { - final Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); - mActivity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); - } - } - - public BluetoothAdapter getBluetoothAdapter(){ - return mBluetoothAdapter; - } - - public boolean isBluetoothLeSupported(){ - return mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE); - } - - public boolean isBluetoothOn(){ - if (mBluetoothAdapter == null) { - return false; - } else { - return mBluetoothAdapter.isEnabled(); - } - } -} diff --git a/sample_app/src/uk/co/alt236/btlescan/util/Constants.java b/sample_app/src/uk/co/alt236/btlescan/util/Constants.java deleted file mode 100644 index 87376a8..0000000 --- a/sample_app/src/uk/co/alt236/btlescan/util/Constants.java +++ /dev/null @@ -1,8 +0,0 @@ -package uk.co.alt236.btlescan.util; - -import java.text.DecimalFormat; - -public class Constants { - public static final DecimalFormat DOUBLE_TWO_DIGIT_ACCURACY = new DecimalFormat("#.##"); - public static final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; -} diff --git a/sample_app/src/uk/co/alt236/btlescan/util/CsvWriterHelper.java b/sample_app/src/uk/co/alt236/btlescan/util/CsvWriterHelper.java deleted file mode 100644 index b827126..0000000 --- a/sample_app/src/uk/co/alt236/btlescan/util/CsvWriterHelper.java +++ /dev/null @@ -1,23 +0,0 @@ -package uk.co.alt236.btlescan.util; - -public class CsvWriterHelper { - private static final String QUOTE = "\""; - public static String addStuff(Integer text){ - return QUOTE + text + QUOTE + ","; - } - - public static String addStuff(Long text){ - return QUOTE + text + QUOTE + ","; - } - - public static String addStuff(boolean value){ - return QUOTE + value + QUOTE + ","; - } - - public static String addStuff(String text){ - if(text == null){text = "";} - text = text.replace(QUOTE, "'"); - - return QUOTE + text.trim() + QUOTE + ","; - } -} diff --git a/sample_app/src/uk/co/alt236/btlescan/util/TimeFormatter.java b/sample_app/src/uk/co/alt236/btlescan/util/TimeFormatter.java deleted file mode 100644 index 8c7f925..0000000 --- a/sample_app/src/uk/co/alt236/btlescan/util/TimeFormatter.java +++ /dev/null @@ -1,18 +0,0 @@ -package uk.co.alt236.btlescan.util; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -public class TimeFormatter { - private final static String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS zzz"; - private final static SimpleDateFormat ISO_FORMATTER = new UtcDateFormatter(ISO_FORMAT, Locale.US); - - public static String getIsoDateTime(Date date){ - return ISO_FORMATTER.format(date); - } - - public static String getIsoDateTime(long millis){ - return getIsoDateTime(new Date(millis)); - } -} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..ca218b5 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +include ':library' +include ':sample_app'