├── .gitignore ├── .idea ├── caches │ ├── build_file_checksums.ser │ └── gradle_models.ser ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── markdown-exported-files.xml ├── markdown-navigator.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── workspace.xml ├── APIDemo ├── .gitignore ├── build.gradle ├── libs │ ├── bluefire-api-v27.0.aar │ ├── commons-codec-1.10.jar │ └── commons-lang3-3.3.2.jar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── bluefire │ │ └── apidemo │ │ ├── Comm.java │ │ ├── CommBLE.java │ │ ├── CommBT2.java │ │ ├── Main.java │ │ └── Service.java │ └── res │ ├── drawable │ └── bluefire.png │ ├── layout │ └── main.xml │ └── values │ ├── strings.xml │ └── styles.xml ├── BlueFire API Issue Report.docx ├── BlueFire API Issue Report.pdf ├── BlueFire-API-for-Android-Studio.iml ├── BueFire Android API v26.0.pdf ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties ├── output.json └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | #Android Studio 2 | 3 | *.iml 4 | .gradle 5 | /local.properties 6 | /.idea/workspace.xml 7 | /.idea/libraries 8 | 9 | # Ignore Gradle GUI config 10 | gradle-app.setting 11 | 12 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 13 | !gradle-wrapper.jar 14 | 15 | # Cache of project 16 | .gradletasknamecache 17 | 18 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 19 | # gradle/wrapper/gradle-wrapper.properties 20 | 21 | # ========================= 22 | # Operating System Files 23 | # ========================= 24 | 25 | # OSX 26 | # ========================= 27 | 28 | .DS_Store 29 | /build 30 | /captures 31 | .externalNativeBuild 32 | .AppleDouble 33 | .LSOverride 34 | 35 | # Thumbnails 36 | ._* 37 | 38 | # Files that might appear in the root of a volume 39 | .DocumentRevisions-V100 40 | .fseventsd 41 | .Spotlight-V100 42 | .TemporaryItems 43 | .Trashes 44 | .VolumeIcon.icns 45 | 46 | # Directories potentially created on remote AFP share 47 | .AppleDB 48 | .AppleDesktop 49 | Network Trash Folder 50 | Temporary Items 51 | .apdisk 52 | 53 | # Windows 54 | # ========================= 55 | 56 | # Windows image file caches 57 | Thumbs.db 58 | ehthumbs.db 59 | 60 | # Folder config file 61 | Desktop.ini 62 | 63 | # Recycle Bin used on file shares 64 | $RECYCLE.BIN/ 65 | 66 | # Windows Installer files 67 | *.cab 68 | *.msi 69 | *.msm 70 | *.msp 71 | 72 | # Windows shortcuts 73 | *.lnk 74 | 75 | # Visual Studio 76 | *.scc 77 | vssver2.* 78 | 79 | # Development 80 | Build_Instructions 81 | 82 | APIDemo/gen/ 83 | APIDemo/src/main/resources/ 84 | gen/ 85 | 86 | /APIDemo/build/generated/ 87 | /APIDemo/build/intermediates/ 88 | /APIDemo/build/tmp 89 | /APIDemo/build/outputs/logs/ 90 | /build/ 91 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueFire-LLC/BlueFire-API-for-Android-Studio/62d5696102f6bcefa3ec0cd9b0b1f965f81ab739/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/caches/gradle_models.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueFire-LLC/BlueFire-API-for-Android-Studio/62d5696102f6bcefa3ec0cd9b0b1f965f81ab739/.idea/caches/gradle_models.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 30 | 31 | 53 | 54 | 55 | 57 | 58 | 59 | 61 | 62 | 63 |
64 | 65 | 66 | 67 | xmlns:android 68 | ^$ 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | xmlns:.* 78 | ^$ 79 | 80 | 81 | BY_NAME 82 | 83 |
84 |
85 | 86 | 87 | 88 | .*:id 89 | http://schemas.android.com/apk/res/android 90 | 91 | 92 | 93 |
94 |
95 | 96 | 97 | 98 | .*:name 99 | http://schemas.android.com/apk/res/android 100 | 101 | 102 | 103 |
104 |
105 | 106 | 107 | 108 | name 109 | ^$ 110 | 111 | 112 | 113 |
114 |
115 | 116 | 117 | 118 | style 119 | ^$ 120 | 121 | 122 | 123 |
124 |
125 | 126 | 127 | 128 | .* 129 | ^$ 130 | 131 | 132 | BY_NAME 133 | 134 |
135 |
136 | 137 | 138 | 139 | .*:layout_width 140 | http://schemas.android.com/apk/res/android 141 | 142 | 143 | 144 |
145 |
146 | 147 | 148 | 149 | .*:layout_height 150 | http://schemas.android.com/apk/res/android 151 | 152 | 153 | 154 |
155 |
156 | 157 | 158 | 159 | .*:layout_.* 160 | http://schemas.android.com/apk/res/android 161 | 162 | 163 | BY_NAME 164 | 165 |
166 |
167 | 168 | 169 | 170 | .*:width 171 | http://schemas.android.com/apk/res/android 172 | 173 | 174 | BY_NAME 175 | 176 |
177 |
178 | 179 | 180 | 181 | .*:height 182 | http://schemas.android.com/apk/res/android 183 | 184 | 185 | BY_NAME 186 | 187 |
188 |
189 | 190 | 191 | 192 | .* 193 | http://schemas.android.com/apk/res/android 194 | 195 | 196 | BY_NAME 197 | 198 |
199 |
200 | 201 | 202 | 203 | .* 204 | .* 205 | 206 | 207 | BY_NAME 208 | 209 |
210 |
211 |
212 |
213 |
214 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/markdown-exported-files.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 34 | 35 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 25 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Android 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 66 | 67 | 68 | 69 | 70 | 1.8 71 | 72 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /APIDemo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /APIDemo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 31 5 | defaultConfig { 6 | multiDexEnabled false 7 | minSdkVersion 24 8 | } 9 | buildTypes { 10 | release { 11 | minifyEnabled false 12 | } 13 | } 14 | } 15 | repositories { 16 | google() 17 | } 18 | allprojects { 19 | repositories { 20 | google() 21 | } 22 | } 23 | dependencies { 24 | implementation fileTree(include: ['*.jar'], dir: 'libs') 25 | implementation 'androidx.activity:activity:1.4.0' 26 | implementation 'androidx.fragment:fragment:1.4.1' 27 | implementation files('libs/bluefire-api-v27.0.aar') 28 | 29 | } -------------------------------------------------------------------------------- /APIDemo/libs/bluefire-api-v27.0.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueFire-LLC/BlueFire-API-for-Android-Studio/62d5696102f6bcefa3ec0cd9b0b1f965f81ab739/APIDemo/libs/bluefire-api-v27.0.aar -------------------------------------------------------------------------------- /APIDemo/libs/commons-codec-1.10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueFire-LLC/BlueFire-API-for-Android-Studio/62d5696102f6bcefa3ec0cd9b0b1f965f81ab739/APIDemo/libs/commons-codec-1.10.jar -------------------------------------------------------------------------------- /APIDemo/libs/commons-lang3-3.3.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueFire-LLC/BlueFire-API-for-Android-Studio/62d5696102f6bcefa3ec0cd9b0b1f965f81ab739/APIDemo/libs/commons-lang3-3.3.2.jar -------------------------------------------------------------------------------- /APIDemo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Mark\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /APIDemo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /APIDemo/src/main/java/com/bluefire/apidemo/Comm.java: -------------------------------------------------------------------------------- 1 | package com.bluefire.apidemo; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.content.Context; 5 | import android.os.Handler; 6 | import android.os.Message; 7 | 8 | import com.bluefire.api.BFComm; 9 | import com.bluefire.api.BlueFire; 10 | import com.bluefire.api.ConnectionStates; 11 | import com.bluefire.api.Const; 12 | import com.bluefire.api.Helper; 13 | 14 | import java.io.IOException; 15 | import java.util.LinkedList; 16 | import java.util.Queue; 17 | import java.util.Timer; 18 | import java.util.TimerTask; 19 | 20 | public class Comm extends BFComm 21 | { 22 | //region Declaratives 23 | 24 | // Android specific 25 | protected Context Context; 26 | protected Handler ConnectionHandler; 27 | 28 | protected String DeviceName = ""; 29 | protected String DeviceAddress = ""; 30 | 31 | protected String BFDeviceName = ""; 32 | 33 | private boolean IsClearingInputPackets; 34 | private Queue InputPackets = new LinkedList();; 35 | 36 | private boolean IsQueueingData; 37 | protected boolean IsSendingData; 38 | private boolean IsClearingSendBuffer; 39 | private Queue SendBuffer = new LinkedList(); 40 | 41 | private Timer DisconnectTimer; 42 | private int WaitForDisconnectTime = 8 * Const.OneSecond; 43 | 44 | private String CommMessage = ""; 45 | protected ConnectionStates ConnectionState = ConnectionStates.NA; 46 | private ConnectionStates CurrentState = ConnectionStates.NotConnected; // needs to be NotConnected 47 | 48 | private Queue StateQueue = new LinkedList(); 49 | private UpdateStateThreading UpdateStateThread; 50 | 51 | protected BluetoothAdapter BTAdapter; 52 | 53 | protected BlueFire blueFire; 54 | 55 | //endregion 56 | 57 | //region Valid Adapter 58 | 59 | protected Boolean IsValidAdapter() 60 | { 61 | return IsValidAdapter(DeviceName, DeviceAddress); 62 | } 63 | 64 | protected boolean IsValidAdapter(String BTDeviceName, String BTDeviceAddress) 65 | { 66 | // Check for a last connected device 67 | if (IsLastConnectedAdapterSet() && !BTDeviceAddress.equals("") && BTDeviceAddress.equals(blueFire.AdapterId())) 68 | return true; 69 | 70 | // Check for not finding a last connected device 71 | else if (IsLastConnectedAdapterSet()) 72 | return false; 73 | 74 | // Check for a BlueFire device 75 | else if (BTDeviceName.equals(BFDeviceName)) 76 | return true; 77 | 78 | return false; 79 | } 80 | 81 | //endregion 82 | 83 | //region Last Connected Adapter 84 | 85 | // IsLastConnectedId Property 86 | protected boolean IsLastConnectedAdapter() 87 | { 88 | return IsLastConnectedAdapter(DeviceAddress); 89 | } 90 | 91 | // IsLastConnectedId Property 92 | protected boolean IsLastConnectedAdapter(String BTDeviceAddress) 93 | { 94 | return ((blueFire.ConnectToLastAdapter() || blueFire.SecureAdapter()) && BTDeviceAddress.equals(blueFire.AdapterId())); 95 | } 96 | 97 | protected boolean IsLastConnectedAdapterSet() 98 | { 99 | return ((blueFire.ConnectToLastAdapter() || blueFire.SecureAdapter()) && !blueFire.AdapterId().equals("")); 100 | } 101 | 102 | //endregion 103 | 104 | //region Timeout 105 | 106 | private boolean AdapterIsBusy; 107 | private boolean IsDataTimedOut = false; 108 | private long PrevDataTime = Helper.GetDateTimeNowMs(); 109 | 110 | private void ResetTimeOut() 111 | { 112 | IsDataTimedOut = false; 113 | 114 | PrevDataTime = Helper.GetDateTimeNowMs(); 115 | } 116 | 117 | protected boolean CheckDataTimeOut() 118 | { 119 | // No data, check for timeout 120 | // Note, only timeout once a cycle 121 | if (!IsDataTimedOut) 122 | { 123 | // Check if the adapter is busy 124 | if (blueFire.BusyWaitTime > 0) 125 | { 126 | if (!AdapterIsBusy) 127 | { 128 | AdapterIsBusy = true; 129 | ResetTimeOut(); 130 | } 131 | } 132 | else 133 | { 134 | if (AdapterIsBusy) 135 | { 136 | AdapterIsBusy = false; 137 | ResetTimeOut(); 138 | } 139 | } 140 | 141 | // Check for a data timeout 142 | if (Helper.TimeDiff(PrevDataTime) > blueFire.DataTimeoutInterval) 143 | { 144 | IsDataTimedOut = true; 145 | return true; 146 | } 147 | } 148 | return false; 149 | } 150 | 151 | //endregion 152 | 153 | //region Constructor 154 | 155 | public Comm(Context context, Handler connectionHandler, BlueFire blueFire) 156 | { 157 | Context = context; 158 | 159 | ConnectionHandler = connectionHandler; 160 | 161 | this.blueFire = blueFire; 162 | 163 | UpdateStateThread = new UpdateStateThreading(); 164 | UpdateStateThread.start(); 165 | } 166 | 167 | //endregion 168 | 169 | //region Bluetooth 170 | 171 | private boolean EnableBluetooth() 172 | { 173 | try 174 | { 175 | // Initializes a Bluetooth adapter. 176 | BTAdapter = BluetoothAdapter.getDefaultAdapter(); 177 | 178 | if (BTAdapter == null) 179 | return false; 180 | 181 | if (!BTAdapter.isEnabled()) 182 | return TurnBluetoothOn(); 183 | 184 | return true; 185 | } 186 | catch (Exception ex) 187 | { 188 | RaiseSystemError("CommBLE EnableBluetooth", ex); 189 | return false; 190 | } 191 | } 192 | 193 | protected boolean TurnBluetoothOn() 194 | { 195 | try 196 | { 197 | if (BTAdapter == null) 198 | return false; 199 | 200 | // Bluetooth is not on, turn it on 201 | BTAdapter.enable(); 202 | 203 | // Wait for Bluetooth to turn on 204 | int WaitTime = 3 * Const.OneSecond; 205 | while (!BTAdapter.isEnabled()) 206 | { 207 | Helper.Sleep(100); 208 | WaitTime -= 100; 209 | if (WaitTime <= 0) 210 | break; 211 | } 212 | if (!BTAdapter.isEnabled()) 213 | return false; 214 | 215 | Helper.Sleep(100); // allow bluetooth to initialize itself 216 | 217 | return true; 218 | 219 | } catch (Exception ex) 220 | { 221 | RaiseSystemError ("CommBT2.TurnBluetoothOn", ex); 222 | return false; 223 | } 224 | } 225 | 226 | protected void TurnBluetoothOff() 227 | { 228 | if (BTAdapter == null) 229 | return; 230 | 231 | BTAdapter.disable(); 232 | 233 | // Wait for Bluetooth to turn off 234 | int WaitTime = 3 * Const.OneSecond; 235 | while (BTAdapter.isEnabled()) 236 | { 237 | Helper.Sleep(100); 238 | WaitTime -= 100; 239 | if (WaitTime <= 0) 240 | break; 241 | } 242 | 243 | Helper.Sleep(100); // allow bluetooth to initialize itself 244 | } 245 | 246 | private boolean RecycleBluetooth() 247 | { 248 | TurnBluetoothOff(); 249 | 250 | if (!TurnBluetoothOn()) 251 | return false; 252 | else 253 | Helper.Sleep(3 * Const.OneSecond); // give Bluetooth time to find the adapter 254 | 255 | return true; 256 | } 257 | 258 | //endregion 259 | 260 | //region Initialization 261 | 262 | // Override in CommBLE/CommBT2 263 | protected void Initialize() 264 | { 265 | } 266 | 267 | private void InitializeData(boolean isReconnecting) 268 | { 269 | ClearSendBuffer(); 270 | ClearInputPackets(); 271 | 272 | PreviousId = ""; 273 | 274 | IsConnected = false; 275 | IsSendingData = false; 276 | 277 | blueFire.BusyWaitTime = 0; 278 | 279 | if (!isReconnecting) 280 | { 281 | IsReconnecting = false; 282 | ReconnectAttempt = 0; 283 | } 284 | } 285 | 286 | private void InitializeState() 287 | { 288 | StateQueue.clear(); 289 | } 290 | 291 | private void InitializeComm() 292 | { 293 | UpdateState(ConnectionStates.Initializing); 294 | 295 | Initialize(); 296 | 297 | UpdateState(ConnectionStates.Initialized); 298 | } 299 | 300 | //endregion 301 | 302 | //region Connection 303 | 304 | // Connect method 305 | @Override 306 | public boolean Connect() 307 | { 308 | return Connect(false); 309 | } 310 | private boolean Connect(boolean isReconnecting) 311 | { 312 | InitializeData(isReconnecting); 313 | 314 | if (!isReconnecting) 315 | InitializeState(); 316 | 317 | InitializeComm(); 318 | 319 | if (!isReconnecting) 320 | UpdateState(ConnectionStates.Connecting); 321 | else 322 | UpdateState(ConnectionStates.Reconnecting); 323 | 324 | if (!StartConnection()) // this will block 325 | AdapterNotConnected(); 326 | 327 | return (ConnectionState == ConnectionStates.Connected); 328 | } 329 | 330 | // Override in CommBLE/CommBT2 331 | protected boolean StartConnection() 332 | { 333 | return InitializeConnection(); 334 | } 335 | 336 | protected boolean InitializeConnection() 337 | { 338 | IsConnected = false; 339 | IsConnecting = true; 340 | 341 | ClearInputPackets(); 342 | 343 | if (!EnableBluetooth()) 344 | return false; 345 | 346 | return true; 347 | } 348 | 349 | // Override in CommBLE/CommBT2 350 | protected void AdapterConnected() 351 | { 352 | IsConnecting = false; 353 | IsConnected = true; 354 | 355 | AdapterId = DeviceAddress; 356 | 357 | ResetTimeOut(); 358 | 359 | // Check for reconnecting 360 | if (IsReconnecting) 361 | AdapterReconnected(); 362 | else 363 | UpdateState(ConnectionStates.Connected); 364 | } 365 | 366 | // Override in CommBLE/CommBT2 367 | protected void AdapterNotConnected() 368 | { 369 | try 370 | { 371 | IsConnecting = false; 372 | IsConnected = false; 373 | 374 | Disconnect(); 375 | 376 | if (IsReconnecting) 377 | Reconnect(); 378 | else 379 | UpdateState(ConnectionStates.NotConnected); 380 | } 381 | catch (Exception ex){} 382 | } 383 | 384 | //endregion 385 | 386 | //region Reconnection 387 | 388 | public boolean Reconnect() 389 | { 390 | // Check for no reconnect attempts or non-compatible adapter 391 | if (blueFire.MaxReconnectAttempts() == 0 || !blueFire.IsCompatible()) 392 | { 393 | StopReconnection(true); 394 | return false; 395 | } 396 | 397 | // Check for exceeding reconnect attempts 398 | if (ReconnectAttempt >= blueFire.MaxReconnectAttempts()) 399 | { 400 | AdapterNotReconnected(); 401 | return false; 402 | } 403 | 404 | // Set for reconnecting 405 | IsReconnecting = true; 406 | 407 | ReconnectAttempt++; 408 | 409 | // android.util.Log.d("BlueFire", "Reconnect, Attempt=" + ReconnectAttempts); 410 | 411 | // Check for recycling Bluetooth. 412 | // Note, must be before Dispose. 413 | if (ReconnectAttempt == (blueFire.BluetoothRecycleAttempt())) 414 | { 415 | if (!RecycleBluetooth()) 416 | ReconnectAttempt = blueFire.MaxReconnectAttempts(); 417 | } 418 | 419 | // Try to reconnect 420 | return Connect(IsReconnecting); 421 | } 422 | 423 | public void StopReconnection(boolean DisconnectAdapter) 424 | { 425 | IsReconnecting = false; 426 | 427 | if (DisconnectAdapter) 428 | Disconnect(); 429 | } 430 | 431 | private void AdapterReconnected() 432 | { 433 | StopReconnection(false); 434 | 435 | IsReconnecting = false; 436 | 437 | UpdateState(ConnectionStates.Reconnected); 438 | } 439 | 440 | private void AdapterNotReconnected() 441 | { 442 | StopReconnection(true); 443 | 444 | ConnectionStates State; 445 | 446 | if (IsConnecting) 447 | State = ConnectionStates.NotConnected; 448 | else 449 | State = ConnectionStates.NotReconnected; 450 | 451 | AdapterNotConnected(); 452 | 453 | UpdateState(State); 454 | } 455 | 456 | //endregion 457 | 458 | //region Disconnection 459 | 460 | // Disconnect Method 461 | // Override in CommBLE/CommBT2 462 | protected boolean Disconnect() 463 | { 464 | return Disconnect(false); 465 | } 466 | @Override 467 | public boolean Disconnect(boolean WaitForDisconnect) 468 | { 469 | try 470 | { 471 | if (ConnectionState == ConnectionStates.Disconnecting || ConnectionState == ConnectionStates.Disconnected) 472 | return false; 473 | 474 | InitializeData(IsReconnecting); 475 | 476 | return true; 477 | } 478 | catch (Exception ex) 479 | { 480 | RaiseSystemError("Comm.Disconnect", ex); 481 | return false; 482 | } 483 | } 484 | 485 | protected void CheckWaitForDisconnect(boolean WaitForDisconnect) 486 | { 487 | if (WaitForDisconnect) 488 | { 489 | DisconnectTimer = new Timer(); 490 | DisconnectTimer.schedule(new DisconnectTimedOut(), WaitForDisconnectTime, Long.MAX_VALUE); 491 | } 492 | else 493 | UpdateState(ConnectionStates.Disconnected); 494 | } 495 | 496 | private class DisconnectTimedOut extends TimerTask 497 | { 498 | @Override 499 | public void run() 500 | { 501 | UpdateState(ConnectionStates.Disconnected); 502 | } 503 | }; 504 | 505 | //endregion 506 | 507 | //region Get Data 508 | 509 | @Override 510 | public boolean IsDataAvailable() 511 | { 512 | if (InputPackets.size() > 0) 513 | return true; 514 | 515 | if (CheckDataTimeOut()) 516 | RaiseDataTimeout(); 517 | 518 | return false; 519 | } 520 | 521 | // Get message data from the adapter 522 | // The data stream looks like: 523 | // Length (2) - Length of Requested Data, not Checksum 524 | // Length Checksum (1) 525 | // Message Data (n) 526 | // Data Checksum (1) 527 | // 528 | @Override 529 | public byte[] GetData() 530 | { 531 | try 532 | { 533 | if (!IsDataAvailable()) 534 | return new byte[0]; 535 | 536 | byte[] DataBuffer = null; 537 | try 538 | { 539 | DataBuffer = (byte[]) InputPackets.poll(); 540 | } 541 | catch (Exception ex){ } 542 | 543 | if (DataBuffer == null) // just in case ... 544 | return new byte[0]; 545 | 546 | // Check for an invalid length and ignore the packet 547 | if (!IsValidDataLength(DataBuffer)) 548 | return new byte[0]; 549 | 550 | // Check for a checksum error and ignore the packet 551 | byte[] MessageData = GetMessageData(DataBuffer); 552 | if (MessageData == null) 553 | return new byte[0]; 554 | 555 | // Return the message data 556 | return MessageData; 557 | } 558 | catch (Exception ex) 559 | { 560 | RaiseDataError("Comm.GetData", ex); 561 | return null; 562 | } 563 | } 564 | 565 | // Get the data length from the adapter message 566 | protected int GetDataLength(byte[] DataBuffer) 567 | { 568 | try 569 | { 570 | if (DataBuffer.length < 3) 571 | return -1; 572 | 573 | // Get and check the message length 574 | int Length1 = DataBuffer[0]; 575 | int Length2 = DataBuffer[1]; 576 | int Length3 = DataBuffer[2]; 577 | 578 | // Check the length bytes 579 | if (Length3 != (byte)(Length1 ^ Length2)) 580 | { 581 | // Don't log this error because it can occur quite often when the data rate is high 582 | //Helper.LogMessage("Comm.GetDataLength - Packet Data Length Error - Invalid Length"); 583 | return -1; 584 | } 585 | 586 | // Get the message data length 587 | int DataLength = (Length1 << 8) | Length2; 588 | if (DataLength < 0) 589 | { 590 | // Don't log this error because it can occur quite often when the data rate is high 591 | //Helper.LogMessage("Comm.GetDataLength - Packet Data Length Error - Negative Length"); 592 | return -1; 593 | } 594 | 595 | return DataLength; 596 | } 597 | catch (Exception ex) 598 | { 599 | RaiseDataError("Comm.GetDataLength", ex); 600 | return -1; 601 | } 602 | } 603 | 604 | // Get the data length from the adapter message 605 | protected boolean IsValidDataLength(byte[] DataBuffer) 606 | { 607 | try 608 | { 609 | // Get the message data length 610 | int DataLength = GetDataLength(DataBuffer); 611 | if (DataLength < 0) 612 | return false; 613 | 614 | // Validate the data length. 615 | // StartIndex(3) + Data Length + Checksum(1) must be <= to DataBuffer Length 616 | if ((StartIndex + DataLength + 1) > DataBuffer.length) 617 | { 618 | // Don't log this error because it can occur quite often when the data rate is high 619 | //Helper.LogMessage("Comm.IsValidDataLength - Packet Data Length Error - Length Too Long, DataLength=" + DataLength + ", BufferLength=" + DataBuffer.Length); 620 | return false; 621 | } 622 | 623 | return true; 624 | } 625 | catch (Exception ex) 626 | { 627 | RaiseDataError("Comm.IsValidDataLength", ex); 628 | return false; 629 | } 630 | } 631 | 632 | // Get the message data from the adapter message 633 | private byte[] GetMessageData(byte[] DataBuffer) 634 | { 635 | try 636 | { 637 | // Get the data length 638 | int DataLength = GetDataLength(DataBuffer); 639 | 640 | // Validate checksum on the message data 641 | byte Checksum = Helper.CalcChecksum(DataBuffer, StartIndex, DataLength); 642 | if (Checksum != DataBuffer[StartIndex + DataLength]) 643 | { 644 | return null; 645 | } 646 | 647 | // Return the message data (excluding the checksum) 648 | byte[] MessageData = new byte[DataLength]; 649 | System.arraycopy(DataBuffer, StartIndex, MessageData, 0, DataLength); 650 | 651 | return MessageData; 652 | } 653 | catch (Exception ex) 654 | { 655 | RaiseDataError("Comm.GetMessageData", ex); 656 | return null; 657 | } 658 | } 659 | 660 | //endregion 661 | 662 | //region Send Adapter Data 663 | 664 | @Override 665 | public void SendData(byte[] DataBuffer) 666 | { 667 | int DataIndex = 0; 668 | int DataLength; 669 | byte[] SendBuffer; 670 | 671 | try 672 | { 673 | if (DataBuffer.length == 0) 674 | return; 675 | 676 | // Get the length of data to send 677 | DataLength = DataBuffer.length; 678 | 679 | // Build the data stream to send the data. 680 | // Include space for Length and Checksum. 681 | SendBuffer = new byte[2 + DataLength + 1]; // Length Bytes + Data + CheckSum 682 | 683 | // Include the data length in the send buffer. 684 | // Start calculating the checksum. 685 | byte[] LengthBytes = Helper.Short2Bytes(DataLength); 686 | SendBuffer[DataIndex] = LengthBytes[0]; 687 | SendBuffer[DataIndex + 1] = LengthBytes[1]; 688 | int Checksum = Helper.JavaUnsignedByte(SendBuffer[DataIndex]) + Helper.JavaUnsignedByte(SendBuffer[DataIndex + 1]); // checksum 689 | DataIndex += 2; 690 | 691 | // Include the data in the send buffer 692 | for (int i = 0; i < DataLength; i++) 693 | { 694 | SendBuffer[DataIndex] = DataBuffer[i]; 695 | Checksum += Helper.JavaUnsignedByte(DataBuffer[i]); 696 | DataIndex++; 697 | } 698 | 699 | // Calculate the Checksum and store it at the end of the send buffer 700 | SendBuffer[DataIndex] = (byte)(Helper.GenerateChecksum(Checksum)); 701 | 702 | // Send the request for data 703 | AddSendBuffer(SendBuffer); 704 | } 705 | catch (Exception ex) 706 | { 707 | ClearSendBuffer(); 708 | RaiseDataError("Comm.SendPID", ex); 709 | return; 710 | } 711 | } 712 | 713 | protected void AddSendBuffer(byte[] DataBuffer) 714 | { 715 | try 716 | { 717 | while (IsSendingData || IsClearingSendBuffer) 718 | Helper.Sleep(10); 719 | 720 | if (SendBuffer == null) // Comm null while waiting for IsSendingData 721 | return; 722 | 723 | IsQueueingData = true; 724 | 725 | SendBuffer.add(DataBuffer); 726 | } 727 | catch (Exception ex) 728 | { 729 | int BufferCount = SendBuffer.size(); 730 | ClearSendBuffer(); 731 | RaiseDataError("Comm.AddSendBuffer, Buffer Size=" + BufferCount, ex); 732 | } 733 | 734 | IsQueueingData = false; 735 | } 736 | 737 | private void ClearSendBuffer() 738 | { 739 | IsClearingSendBuffer = true; 740 | 741 | try 742 | { 743 | SendBuffer.clear(); 744 | } 745 | catch (Exception ex) { } // Android can throw an exception 746 | 747 | IsClearingSendBuffer = false; 748 | } 749 | 750 | // Check if there is data in the buffer and send it 751 | @Override 752 | public void CheckSendBuffer() 753 | { 754 | try 755 | { 756 | if (!IsConnected) 757 | return; 758 | 759 | // Ignore if writing data to the buffer 760 | if (IsSendingData) 761 | return; 762 | 763 | if (IsClearingSendBuffer) 764 | return; 765 | 766 | if (blueFire.BusyWaitTime > 0) 767 | return; 768 | 769 | if (SendBuffer.isEmpty()) 770 | return; 771 | 772 | while (IsQueueingData) 773 | Helper.Sleep(10); 774 | 775 | if (SendBuffer == null) // Comm null while waiting for IsQueueingData 776 | return; 777 | 778 | IsSendingData = true; 779 | 780 | byte[] DataBuffer = (byte[])SendBuffer.poll(); 781 | 782 | // Ignore if not connected or no data 783 | if (!IsConnected || DataBuffer == null || DataBuffer.length == 0) 784 | { 785 | IsSendingData = false; 786 | return; 787 | } 788 | 789 | SendAdapterData(DataBuffer, DataBuffer.length); 790 | } 791 | catch (IOException ex) { } // This occurs when going out of BT range 792 | catch (Exception ex) 793 | { 794 | RaiseDataError("Comm.CheckSendBuffer", ex); 795 | } 796 | 797 | IsSendingData = false; 798 | } 799 | 800 | // Send Adapter Data Method 801 | protected boolean SendAdapterData(byte[] DataBuffer, int DataLength) throws Exception 802 | { 803 | if (!IsConnected) 804 | return false; 805 | 806 | return true; 807 | } 808 | 809 | //endregion 810 | 811 | //region Receive Adapter Data 812 | 813 | // Queue the input data for processing. 814 | // The data stream looks like: 815 | // Length (2) - Length of Requested Data, not Checksum 816 | // Length Checksum (1) 817 | // Message Data (n) 818 | // Data Checksum (1) 819 | protected void ReceiveAdapterData(byte[] DataBuffer) 820 | { 821 | try 822 | { 823 | if (DataBuffer.length == 0) // should not occur but just in case ... 824 | return; 825 | 826 | // Reset data timeout 827 | ResetTimeOut(); 828 | 829 | blueFire.BusyWaitTime = 0; 830 | 831 | // Queue the data for processing later 832 | while (IsClearingInputPackets) 833 | Helper.Sleep(10); 834 | 835 | if (InputPackets == null) // Comm null while waiting for IsSendingData 836 | return; 837 | 838 | // Limit number of input packets 839 | if (InputPackets.size() > blueFire.MaxCommBuffer()) 840 | ClearInputPackets(); 841 | 842 | try 843 | { 844 | InputPackets.add(DataBuffer); 845 | } 846 | catch (Exception ex) {} 847 | } 848 | catch (Exception ex) 849 | { 850 | ClearInputPackets(); 851 | } 852 | } 853 | 854 | protected void ClearInputPackets() 855 | { 856 | IsClearingInputPackets = true; 857 | 858 | try 859 | { 860 | InputPackets.clear(); 861 | } 862 | catch (Exception ex){ } // Android can throw an exception 863 | 864 | IsClearingInputPackets = false; 865 | } 866 | 867 | //endregion 868 | 869 | //region Raise Events 870 | 871 | protected void RaiseNotification(String Location, String Message) 872 | { 873 | RaiseNotification(Location, Message, false); 874 | } 875 | protected void RaiseNotification(String Location, String Message, boolean Force) 876 | { 877 | if (!Location.equals("")) 878 | Message = Location + " - " + Message; 879 | 880 | if (!blueFire.NotificationsOn() && !Force) 881 | return; 882 | 883 | // Save previous connection state 884 | ConnectionStates CurrentState = ConnectionState; 885 | 886 | // Notify the app 887 | UpdateState(ConnectionStates.Notification, Message); 888 | 889 | // Restore connection state 890 | ConnectionState = CurrentState; 891 | } 892 | 893 | // Raise Data Timeout 894 | protected void RaiseDataTimeout() 895 | { 896 | UpdateState(ConnectionStates.DataTimeout); 897 | 898 | Reconnect(); 899 | } 900 | 901 | // Raise Data Error 902 | protected void RaiseDataError(String Message) 903 | { 904 | RaiseDataError(Message, null); 905 | 906 | Reconnect(); 907 | } 908 | 909 | private void RaiseDataError(String Message, Exception ex) 910 | { 911 | Message = Helper.GetErrorMessage("", Message, ex); 912 | 913 | UpdateState(ConnectionStates.DataError, Message); 914 | 915 | Reconnect(); 916 | } 917 | 918 | // Raise System Error 919 | protected void RaiseSystemError(String Location, String Message) 920 | { 921 | RaiseSystemError(Location, Message, null); 922 | } 923 | 924 | protected void RaiseSystemError(String Location, Exception ex) 925 | { 926 | RaiseSystemError(Location, "", ex); 927 | } 928 | 929 | protected void RaiseSystemError(String Location, String Message, Exception ex) 930 | { 931 | Message = Helper.GetErrorMessage(Location, Message, ex); 932 | 933 | UpdateState(ConnectionStates.SystemError, Message); 934 | } 935 | 936 | //endregion 937 | 938 | //region Update State 939 | 940 | protected void UpdateState(ConnectionStates State) 941 | { 942 | UpdateState(State, ""); 943 | } 944 | private void UpdateState(ConnectionStates State, String Message) 945 | { 946 | //Helper.LogMessage("Comm UpdateState, Queued State=" + State); 947 | 948 | Message HandleMessage = new Message(); 949 | HandleMessage.what = State.ordinal(); 950 | HandleMessage.obj = Message; 951 | 952 | ConnectionState = ConnectionStates.values()[HandleMessage.what]; 953 | CommMessage = (String)HandleMessage.obj; 954 | 955 | StateQueue.add(HandleMessage); 956 | } 957 | 958 | private class UpdateStateThreading extends Thread 959 | { 960 | public void run() 961 | { 962 | try 963 | { 964 | int Retry = blueFire.EventHandlerRetry; 965 | 966 | while (true) 967 | { 968 | if (!StateQueue.isEmpty()) 969 | { 970 | // if (StateQueue.size() > 1) 971 | // Helper.LogMessage("Comm UpdateStateThreading, StateQueue Size=" + StateQueue.size()); 972 | 973 | Message HandleMessage = StateQueue.peek(); // get message 974 | if (HandleMessage != null) 975 | { 976 | ConnectionStates CommState = ConnectionStates.values()[HandleMessage.what]; 977 | String CommMessage = (String) HandleMessage.obj; 978 | 979 | //Helper.LogMessage("Comm UpdateStateThreading, Sending State=" + ConnectionState); 980 | 981 | ConnectionHandler.obtainMessage(CommState.ordinal(), CommMessage).sendToTarget(); 982 | 983 | boolean MessageReceived = !ConnectionHandler.hasMessages(CommState.ordinal(), CommMessage); 984 | if (!MessageReceived) // check if message received 985 | { 986 | int Delay = 0; 987 | while (Delay < blueFire.EventHandlerDelay && !MessageReceived) 988 | { 989 | Helper.Sleep(10); 990 | Delay += 10; 991 | MessageReceived = !ConnectionHandler.hasMessages(CommState.ordinal(), CommMessage); 992 | } 993 | if (!MessageReceived) 994 | { 995 | Helper.LogMessage("Comm UpdateStateThreading, ***** Missing State=" + CommState + " *****"); 996 | ConnectionHandler.removeMessages(CommState.ordinal(), CommMessage); // remove sent message 997 | Retry -= blueFire.EventHandlerDelay; 998 | if (Retry <= 0) 999 | { 1000 | Helper.LogMessage("Comm UpdateStateThreading, ***** Ignoring State=" + CommState + " *****"); 1001 | Retry = blueFire.EventHandlerRetry; 1002 | MessageReceived = true; // set ignored message as received so it can be removed from the queue 1003 | } 1004 | } 1005 | } 1006 | if (MessageReceived) 1007 | StateQueue.poll(); // remove message 1008 | } 1009 | } 1010 | Helper.Sleep(1); // allow other threads to execute 1011 | } 1012 | } 1013 | catch (Exception ex) 1014 | { 1015 | RaiseDataError("Comm.UpdateStateThreading", ex); 1016 | } 1017 | } 1018 | } 1019 | 1020 | //endregion 1021 | 1022 | //region Dispose 1023 | 1024 | @Override 1025 | public void Dispose() 1026 | { 1027 | try 1028 | { 1029 | Disconnect(); 1030 | } 1031 | catch (Exception ex) 1032 | { 1033 | } 1034 | } 1035 | 1036 | //endregion 1037 | } 1038 | -------------------------------------------------------------------------------- /APIDemo/src/main/java/com/bluefire/apidemo/CommBLE.java: -------------------------------------------------------------------------------- 1 | package com.bluefire.apidemo; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.bluetooth.BluetoothGatt; 5 | import android.bluetooth.BluetoothGattCallback; 6 | import android.bluetooth.BluetoothGattCharacteristic; 7 | import android.bluetooth.BluetoothGattDescriptor; 8 | import android.bluetooth.BluetoothGattService; 9 | import android.bluetooth.BluetoothProfile; 10 | import android.bluetooth.le.BluetoothLeScanner; 11 | import android.bluetooth.le.ScanCallback; 12 | import android.bluetooth.le.ScanFilter; 13 | import android.bluetooth.le.ScanResult; 14 | import android.bluetooth.le.ScanSettings; 15 | import android.bluetooth.le.ScanSettings.Builder; 16 | import android.content.Context; 17 | import android.os.Handler; 18 | 19 | import com.bluefire.api.BFCommBLE; 20 | import com.bluefire.api.BlueFire; 21 | import com.bluefire.api.ConnectionStates; 22 | import com.bluefire.api.Const; 23 | import com.bluefire.api.Helper; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.Timer; 28 | import java.util.TimerTask; 29 | import java.util.UUID; 30 | 31 | import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES; 32 | import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED; 33 | 34 | public class CommBLE extends Comm 35 | { 36 | //region Declaratives 37 | 38 | private BluetoothLeScanner BTScanner; 39 | 40 | private BluetoothDevice BTDevice; 41 | private BluetoothGatt BLEGatt; 42 | 43 | private int CurrentState = BluetoothProfile.STATE_DISCONNECTED; 44 | 45 | private int GattStatus = -1; 46 | 47 | private final int GattStatus_LossPower = 8; // not defined in GattStatus 48 | 49 | protected BluetoothGattCharacteristic J1939DataCharacteristic; 50 | protected BluetoothGattCharacteristic J1708DataCharacteristic; 51 | 52 | protected BluetoothGattCharacteristic ClientPacket1Characteristic; 53 | protected BluetoothGattCharacteristic ClientPacket2Characteristic; 54 | protected BluetoothGattCharacteristic ClientPacket3Characteristic; 55 | 56 | private BluetoothGattCharacteristic ClientCharacteristic; 57 | 58 | private BluetoothGattService GenericAccess; 59 | private BluetoothGattService GenericAttribute; 60 | 61 | private BluetoothGattService AdapterService; 62 | private BluetoothGattService ClientService; 63 | 64 | private ArrayList IsCharacteristicNotification = new ArrayList(); 65 | 66 | protected boolean IsWritingData; 67 | private int WriteTimeout; 68 | private int WriteTimeoutMax = Const.OneSecond; 69 | 70 | protected boolean IsUpdatingDescriptor; 71 | private int UpdateDescriptorTimeout; 72 | private final int UpdateDescriptorTimeoutMax = 500; // ms 73 | 74 | private Timer NotificationsTimer; 75 | protected final int NotificationsInterval = 100; // ms 76 | 77 | // BLE Scanning 78 | 79 | protected boolean IsScanning; 80 | 81 | protected boolean IsWaitingForAdapter; 82 | protected boolean IsAdapterConnected; 83 | 84 | private final int NoSignal = -128; 85 | private int RawSignalStrength = NoSignal; 86 | 87 | private List DeviceAddresses = new ArrayList(); 88 | 89 | private boolean IsAdvertisementTimedOut; 90 | 91 | private Timer AdvertisementTimer; 92 | private Timer DiscoveryTimer; 93 | 94 | private boolean WaitForDisconnect; 95 | private DisconnectThreading DisconnectThread; 96 | 97 | //endregion 98 | 99 | //region Constructor 100 | 101 | public CommBLE(Context context, Handler connectionHandler, BlueFire blueFire) 102 | { 103 | super(context, connectionHandler, blueFire); 104 | 105 | BFDeviceName = BFCommBLE.AdapterName; 106 | } 107 | 108 | //endregion 109 | 110 | //region Initialize 111 | 112 | @Override 113 | protected void Initialize() 114 | { 115 | super.Initialize(); 116 | 117 | IsScanning = false; 118 | IsAdapterConnected = false; 119 | } 120 | 121 | //endregion 122 | 123 | //region Connection 124 | 125 | @Override 126 | protected boolean StartConnection() 127 | { 128 | if (!super.StartConnection()) 129 | return false; 130 | 131 | try 132 | { 133 | // Check for the ConnectToLastAdapter set by the app 134 | if (IsLastConnectedAdapterSet()) 135 | { 136 | if (GetRemoteDevice(blueFire.AdapterId())) 137 | { 138 | AdapterConnected("last/secured", blueFire.AdapterId()); 139 | return true; 140 | } 141 | // Did not find last connected adapter. 142 | // Note, it is possible to not find the last connected adapter even though it is plugged in 143 | // so we have to allow for this and drop into the scan. 144 | RaiseNotification("CommBLE", "Failed to find or connect to last/secured adapter '" + blueFire.AdapterId() + "'"); 145 | } 146 | else // Check for a previous adapter 147 | { 148 | if (!PreviousId.equals("")) 149 | { 150 | if (GetRemoteDevice(PreviousId)) 151 | { 152 | AdapterConnected("previous", PreviousId); 153 | return true; 154 | } 155 | RaiseNotification("CommBLE", "Failed to find or connect to previous adapter '" + PreviousId + "'. Scanning for new adapter." + GetConnectionData()); 156 | } 157 | } 158 | 159 | // Did not find an adapter, re-initialize the connection for scanning 160 | if (!InitializeConnection()) 161 | return false; 162 | 163 | // Scan for an adapter 164 | if (!ScanForDevice()) 165 | { 166 | RaiseNotification("CommBLE", "Failed to find an adapter." + GetConnectionData()); 167 | return false; 168 | } 169 | 170 | // Found an adapter, attempt to connect to it 171 | StartDiscoveryTimer(); 172 | 173 | if (!ConnectGatt(BTDevice)) 174 | return false; 175 | 176 | if (WaitForConnection()) // this will block 177 | { 178 | AdapterConnected("scanned", DeviceAddress); 179 | return true; 180 | } 181 | else 182 | { 183 | RaiseNotification("CommBLE", "Failed to connect to scanned adapter '" + DeviceAddress + "'" + GetConnectionData()); 184 | return false; 185 | } 186 | 187 | } catch (Exception ex) 188 | { 189 | RaiseSystemError("CommBLE.StartConnection", ex); 190 | return false; 191 | } 192 | } 193 | 194 | private String GetConnectionData() 195 | { 196 | return (Const.CrLf + "ConnectToLastAdapter=" + blueFire.ConnectToLastAdapter() + ", SecureAdapter=" + blueFire.SecureAdapter() + ", AdapterId='" + blueFire.AdapterId() + "'" + ", ConnectionState=" + this.ConnectionState + ", GattStatus=" + GattStatus); 197 | } 198 | 199 | private boolean GetRemoteDevice(String AdapterId) 200 | { 201 | // Attempt to connect directly to the adapter without scanning 202 | BTDevice = BTAdapter.getRemoteDevice(AdapterId); 203 | 204 | // Check if did not find an adapter. 205 | // Note, it is possible to not find an adapter even though it is plugged in 206 | // and the adapter id is correct. 207 | if (BTDevice.getName() == null) 208 | return false; 209 | 210 | // Found a device (real or cached) 211 | // Note, if the mobile device has previously connected to an adapter it will 212 | // be cached and the device returned even if it's not present. 213 | DeviceName = BTDevice.getName(); 214 | DeviceAddress = BTDevice.getAddress(); 215 | 216 | // Attempt to connect to it. 217 | StartDiscoveryTimer(); 218 | 219 | if (!ConnectGatt(BTDevice)) 220 | return false; 221 | 222 | if (WaitForConnection()) // this will block 223 | return true; 224 | 225 | // Failed to connect. 226 | // Note, the discovery timer will disconnect Gatt. 227 | return false; 228 | } 229 | 230 | //endregion 231 | 232 | //region Discovery 233 | 234 | private void StartDiscoveryTimer() 235 | { 236 | // Note, do not set the State to Discovering because BLE doesn't really doesn't have a discovery, 237 | // we just use this for a connection timeout. 238 | // UpdateState(ConnectionStates.Discovering); 239 | 240 | DiscoveryTimer = new Timer(); 241 | DiscoveryTimer.schedule(new DiscoveryTimedOut(), blueFire.DiscoveryTimeout(), Long.MAX_VALUE); 242 | } 243 | 244 | private class DiscoveryTimedOut extends TimerTask 245 | { 246 | @Override 247 | public void run() 248 | { 249 | try 250 | { 251 | // Stop the discovery timer (but don't nuke it) 252 | DiscoveryTimer.cancel(); 253 | 254 | // Check if still scanning. 255 | // Note, if an adapter is found the scan will be stopped 256 | if (IsScanning) 257 | StopScan(); 258 | 259 | Disconnect(); 260 | 261 | AdapterNotConnected(); 262 | 263 | } catch (Exception ex) 264 | { 265 | } 266 | } 267 | }; 268 | 269 | private void StopDiscoveryTimer() 270 | { 271 | try 272 | { 273 | if (DiscoveryTimer != null) 274 | { 275 | DiscoveryTimer.cancel(); 276 | DiscoveryTimer.purge(); 277 | DiscoveryTimer = null; 278 | } 279 | } 280 | catch(Exception ex){} 281 | } 282 | 283 | //endregion 284 | 285 | //region Scanning 286 | 287 | private boolean ScanForDevice() 288 | { 289 | StartScan(); 290 | 291 | WaitForAdapter(); 292 | 293 | StopScan(); 294 | 295 | if (DeviceName == "") 296 | return false; 297 | 298 | // Check for not last adapter id 299 | if (!IsValidAdapter()) 300 | return false; 301 | 302 | return true; 303 | } 304 | 305 | public boolean WaitForAdapter() 306 | { 307 | while (IsWaitingForAdapter && !IsAdvertisementTimedOut) 308 | Helper.Sleep(10); 309 | 310 | StopAdvertisementTimer(); 311 | 312 | return !IsAdvertisementTimedOut; 313 | } 314 | 315 | protected boolean WaitForConnection() 316 | { 317 | while (!IsAdapterConnected && (ConnectionState == ConnectionStates.Connecting ||ConnectionState == ConnectionStates.Reconnecting)) 318 | Helper.Sleep(100); 319 | 320 | StopDiscoveryTimer(); 321 | 322 | return IsAdapterConnected; 323 | } 324 | 325 | // Method to scan BLE Devices. The status of the scan will be detected in the BluetoothAdapter.LeScanCallback 326 | private void StartScan() 327 | { 328 | try 329 | { 330 | IsScanning = true; 331 | 332 | RawSignalStrength = NoSignal; 333 | 334 | DeviceName = ""; 335 | DeviceAddress = ""; 336 | DeviceAddresses.clear(); 337 | 338 | StartAdvertisementTimer(blueFire.AdvertisementTimeout()); 339 | 340 | if (BTScanner == null) 341 | BTScanner = BTAdapter.getBluetoothLeScanner(); 342 | 343 | Builder SettingsBuilder = new Builder(); 344 | SettingsBuilder.setCallbackType(CALLBACK_TYPE_ALL_MATCHES); 345 | SettingsBuilder.setScanMode(SCAN_MODE_BALANCED); 346 | ScanSettings Settings = SettingsBuilder.build(); 347 | 348 | List Filters = new ArrayList(); 349 | ScanFilter.Builder FilterBuilder = new ScanFilter.Builder(); 350 | FilterBuilder.setDeviceName(BFCommBLE.AdapterName); 351 | ScanFilter Filter = FilterBuilder.build(); 352 | Filters.add(Filter); 353 | 354 | BTScanner.startScan(Filters, Settings, BLEScanCallback); 355 | 356 | } catch (Exception ex) 357 | { 358 | IsScanning = false; 359 | RaiseSystemError("CommBLE StartScan", ex); 360 | } 361 | } 362 | 363 | private void StopScan() 364 | { 365 | try 366 | { 367 | if (!IsScanning) 368 | return; 369 | 370 | IsScanning = false; 371 | 372 | StopAdvertisementTimer(); 373 | 374 | if (!BTAdapter.isEnabled()) 375 | return; 376 | 377 | if (BTScanner != null) 378 | BTScanner.stopScan(BLEScanCallback); 379 | 380 | } catch (Exception ex) 381 | { 382 | RaiseSystemError("CommBLE StopScan", ex); 383 | } 384 | } 385 | 386 | private ScanCallback BLEScanCallback = new ScanCallback() 387 | { 388 | // Call back for BLE Scan. 389 | // This call back is called when a BLE device is found near by. 390 | 391 | @Override 392 | public void onScanResult(int CallbackType, ScanResult Result) 393 | { 394 | try 395 | { 396 | // Ignore if scanning has been stopped 397 | if (!IsScanning) 398 | return; 399 | 400 | if (!IsWaitingForAdapter) 401 | return; 402 | 403 | BluetoothDevice AdDevice = Result.getDevice(); 404 | 405 | String AdDeviceName = AdDevice.getName() + ""; // in case it is null; 406 | String AdDeviceAddress = AdDevice.getAddress(); 407 | int AdSignalStrength = Result.getRssi(); 408 | 409 | // Ignore duplicate adapter events 410 | if (DeviceAddresses.contains(AdDeviceAddress)) 411 | return; 412 | else 413 | DeviceAddresses.add(AdDeviceAddress); 414 | 415 | // Ignore if not a valid BlueFire adapter. 416 | // Note, this will check for a BlueFire adapter that is the correct adapter 417 | // if the last connected adapter is set, or a any BlueFire adapter. 418 | if (!IsValidAdapter(AdDeviceName, AdDeviceAddress)) 419 | return; 420 | 421 | // Check for finding a last connected adapter 422 | if (IsLastConnectedAdapter(AdDeviceAddress)) 423 | { 424 | BTDevice = AdDevice; 425 | DeviceName = AdDeviceName; 426 | DeviceAddress = AdDeviceAddress; 427 | IsWaitingForAdapter = false; 428 | return; 429 | } 430 | 431 | // Keep looking for an adapter with the best signal. 432 | // Note, this will continue until the WaitingForAdapter times out. 433 | if (AdSignalStrength > RawSignalStrength) 434 | { 435 | BTDevice = AdDevice; 436 | DeviceName = AdDeviceName; 437 | DeviceAddress = AdDeviceAddress; 438 | RawSignalStrength = AdSignalStrength; 439 | } 440 | 441 | // Keep waiting for another adapter 442 | StartAdvertisementTimer(Const.OneSecond); 443 | 444 | } catch (Exception ex) 445 | { 446 | RaiseSystemError("CommBLE OnLeScan", ex); 447 | } 448 | } 449 | 450 | @Override 451 | public void onScanFailed(int ErrorCode) 452 | { 453 | RaiseSystemError("CommBLE onScanFailed","BLEScanCallback Failed, ErrorCode=" + ErrorCode); 454 | } 455 | }; 456 | 457 | //endregion 458 | 459 | //region Advertising 460 | 461 | private void StartAdvertisementTimer(int TimeoutInterval) 462 | { 463 | IsWaitingForAdapter = true; 464 | IsAdvertisementTimedOut = false; 465 | 466 | AdvertisementTimer = new Timer(); 467 | AdvertisementTimer.schedule(new AdvertisementTimedOut(), TimeoutInterval, Long.MAX_VALUE); 468 | } 469 | 470 | private void StopAdvertisementTimer() 471 | { 472 | IsWaitingForAdapter = false; 473 | try 474 | { 475 | if (AdvertisementTimer != null) 476 | { 477 | AdvertisementTimer.cancel(); 478 | AdvertisementTimer.purge(); 479 | AdvertisementTimer = null; 480 | } 481 | } 482 | catch(Exception ex){} 483 | } 484 | 485 | private class AdvertisementTimedOut extends TimerTask 486 | { 487 | @Override 488 | public void run() 489 | { 490 | try 491 | { 492 | // Stop the discovery timer (but don't nuke it) 493 | StopAdvertisementTimer(); 494 | //AdvertisementTimer.cancel(); 495 | 496 | IsAdvertisementTimedOut = true; 497 | 498 | if (IsScanning) 499 | StopScan(); 500 | 501 | } catch (Exception ex) 502 | { 503 | } 504 | } 505 | }; 506 | 507 | //endregion 508 | 509 | //region GATT Connection 510 | 511 | protected boolean ConnectGatt(BluetoothDevice Device) 512 | { 513 | try 514 | { 515 | // Get the connection status of the device 516 | if (CurrentState != BluetoothProfile.STATE_DISCONNECTED) 517 | DisconnectGatt(); 518 | 519 | CurrentState = BluetoothProfile.STATE_DISCONNECTED; 520 | 521 | // android.util.Log.d("BlueFire", "CommBLE ConnectGatt, Connecting Gatt"); 522 | 523 | BLEGatt = Device.connectGatt(Context, true, BLEGattCallback); 524 | 525 | return true; 526 | 527 | } catch (Exception ex) 528 | { 529 | if (ex.getMessage().contains("null object reference")) 530 | return false; 531 | 532 | RaiseSystemError("CommBLE ConnectGatt", ex); 533 | return false; 534 | } 535 | } 536 | 537 | //endregion 538 | 539 | //region GATT Callback 540 | 541 | private BluetoothGattCallback BLEGattCallback = new BluetoothGattCallback() 542 | { 543 | // Check if the adapter connection changed (eg. connected) 544 | @Override 545 | public void onConnectionStateChange(BluetoothGatt Gatt, int Status, int NewState) 546 | { 547 | if (Gatt == null || BLEGatt == null) 548 | return; 549 | 550 | GattStatus = Status; 551 | 552 | try 553 | { 554 | switch (Status) 555 | { 556 | case BluetoothGatt.GATT_FAILURE: 557 | case BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION: 558 | case BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION: 559 | case BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH: 560 | case BluetoothGatt.GATT_INVALID_OFFSET: 561 | case BluetoothGatt.GATT_READ_NOT_PERMITTED: 562 | case BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED: 563 | case BluetoothGatt.GATT_WRITE_NOT_PERMITTED: 564 | StopScan(); 565 | RaiseDataError("CommBLE onConnectionStateChange, Gatt Status=" + Status); 566 | return; 567 | case 61: 568 | StopScan(); 569 | RaiseDataError("CommBLE onConnectionStateChange, Gatt Status=" + Status); 570 | return; 571 | } 572 | 573 | if (NewState == CurrentState) // no change 574 | return; 575 | 576 | CurrentState = NewState; 577 | 578 | // android.util.Log.d("BlueFire", "CommBLE BLEGattCallback, New State=" + NewState + ", Current State=" + CurrentState); 579 | 580 | switch (NewState) 581 | { 582 | case BluetoothProfile.STATE_CONNECTING: 583 | break; 584 | 585 | case BluetoothProfile.STATE_CONNECTED: 586 | StopScan(); 587 | 588 | if (IsConnecting) 589 | BLEGatt.discoverServices(); 590 | break; 591 | 592 | case BluetoothProfile.STATE_DISCONNECTING: 593 | if (IsConnecting || IsConnected) 594 | AdapterNotConnected(); 595 | break; 596 | 597 | case BluetoothProfile.STATE_DISCONNECTED: 598 | // android.util.Log.d("BlueFire", "CommBLE BLEGattCallback, Gatt Disconnected, closing Gatt, State=" + NewState + ", Current State=" + CurrentState); 599 | 600 | // Close the Gatt connection. 601 | // Note, this must be before AdapterNotConnected. 602 | CloseGatt(false); 603 | 604 | // Check for notifying the app 605 | if (IsConnecting || IsConnected) 606 | AdapterNotConnected(); 607 | break; 608 | } 609 | } catch (Exception ex) 610 | { 611 | RaiseSystemError("CommBLE OnConnectionStateChange", ex); 612 | } 613 | } 614 | 615 | // Check for discovering the adapter 616 | @Override 617 | public void onServicesDiscovered(BluetoothGatt Gatt, int Status) 618 | { 619 | if (Status != BluetoothGatt.GATT_SUCCESS) 620 | { 621 | RaiseDataError("CommBLE OnServicesDiscovered, Gatt Status=" + Status); 622 | return; 623 | } 624 | 625 | // Get the Gatt services and characteristics 626 | AdapterService = null; 627 | ClientService = null; 628 | 629 | GetGattServices(); 630 | } 631 | 632 | @Override 633 | public void onCharacteristicChanged(BluetoothGatt Gatt, BluetoothGattCharacteristic Characteristic) 634 | { 635 | // GATT Characteristic data changed 636 | String UUID = Characteristic.getUuid().toString().toUpperCase(); 637 | 638 | //Helper.DebugPrint("CommBLE OnCharacteristicChanged - UUID=" + UUID); 639 | 640 | if (UUID.equals(BFCommBLE.J1939DataCharacteristicUUID) || 641 | UUID.equals(BFCommBLE.J1708DataCharacteristicUUID) || 642 | UUID.equals(BFCommBLE.ClientPacket1CharacteristicUUID) || 643 | UUID.equals(BFCommBLE.ClientPacket2CharacteristicUUID) || 644 | UUID.equals(BFCommBLE.ClientPacket3CharacteristicUUID)) 645 | // Characteristic data changed, queue it for processing 646 | ReceiveAdapterData(Characteristic.getValue()); 647 | } 648 | 649 | @Override 650 | public void onCharacteristicWrite(BluetoothGatt Gatt, BluetoothGattCharacteristic Characteristic, int Status) 651 | { 652 | IsWritingData = false; 653 | 654 | //Helper.DebugPrint("onCharacteristicWrite - IsWritingData="+IsWritingData); 655 | } 656 | 657 | @Override 658 | public void onDescriptorWrite(BluetoothGatt Gatt, BluetoothGattDescriptor Descriptor, int Status) 659 | { 660 | // Helper.DebugPrint("CommBLE OnDescriptorWrite - Descriptor=" + Descriptor.Characteristic.Uuid.ToString().ToUpper() + ", Status=" + Status.ToString()); 661 | 662 | IsUpdatingDescriptor = false; 663 | } 664 | }; 665 | 666 | //endregion 667 | 668 | //region GATT Services 669 | 670 | private void GetGattServices() 671 | { 672 | try 673 | { 674 | List GattServices = (List) BLEGatt.getServices(); 675 | 676 | if (GattServices == null) 677 | return; 678 | 679 | // Loops through available GATT Services. 680 | for (BluetoothGattService GattService : GattServices) 681 | { 682 | String UUID = GattService.getUuid().toString().toUpperCase(); 683 | 684 | if (UUID.equals(BFCommBLE.GenericAccessServiceUUID)) 685 | GenericAccess = GattService; 686 | 687 | else if (UUID.equals(BFCommBLE.GenericAttributeServiceUUID)) 688 | GenericAttribute = GattService; 689 | 690 | else if (UUID.equals(BFCommBLE.AdapterServiceUUID)) 691 | { 692 | AdapterService = GattService; 693 | 694 | for (int i = 0; i < AdapterService.getCharacteristics().size(); i++) // count should be 2 695 | { 696 | BluetoothGattCharacteristic ServiceCharacteristic = AdapterService.getCharacteristics().get(i); 697 | String AdapterCharacteristicValue = ServiceCharacteristic.getUuid().toString().toUpperCase(); 698 | 699 | if (AdapterCharacteristicValue.equals(BFCommBLE.J1939DataCharacteristicUUID)) 700 | J1939DataCharacteristic = ServiceCharacteristic; 701 | 702 | else if (AdapterCharacteristicValue.equals(BFCommBLE.J1708DataCharacteristicUUID)) 703 | J1708DataCharacteristic = ServiceCharacteristic; 704 | } 705 | } 706 | 707 | else if (UUID.equals(BFCommBLE.ClientServiceUUID)) 708 | { 709 | ClientService = GattService; 710 | 711 | for (int i = 0; i < ClientService.getCharacteristics().size(); i++) // count should be 3 712 | { 713 | BluetoothGattCharacteristic ServiceCharacteristic = ClientService.getCharacteristics().get(i); 714 | String AdapterCharacteristicValue = ServiceCharacteristic.getUuid().toString().toUpperCase(); 715 | 716 | if (AdapterCharacteristicValue.equals(BFCommBLE.ClientPacket1CharacteristicUUID)) 717 | ClientPacket1Characteristic = ServiceCharacteristic; 718 | 719 | else if (AdapterCharacteristicValue.equals(BFCommBLE.ClientPacket2CharacteristicUUID)) 720 | ClientPacket2Characteristic = ServiceCharacteristic; 721 | 722 | else if (AdapterCharacteristicValue.equals(BFCommBLE.ClientPacket3CharacteristicUUID)) 723 | ClientPacket3Characteristic = ServiceCharacteristic; 724 | } 725 | } 726 | } 727 | 728 | if (AdapterService == null || ClientService == null) 729 | { 730 | String Message = ""; 731 | 732 | if (AdapterService == null && ClientService == null) 733 | Message = "BlueFire and Client Services."; 734 | 735 | else if (AdapterService == null) 736 | Message = "BlueFire Service."; 737 | 738 | else if (ClientService == null) 739 | Message += "Client Service."; 740 | 741 | RaiseDataError("CommBLE GetGattServices, Failed to Discover " + Message); 742 | return; 743 | } 744 | 745 | // All characteristics have been discovered, enable notifications 746 | StartNotifications(); 747 | } catch (Exception ex) 748 | { 749 | RaiseSystemError("CommBLE GetGattServices", ex); 750 | } 751 | } 752 | 753 | //endregion 754 | 755 | //region GATT Notifications 756 | 757 | protected void StartNotifications() 758 | { 759 | // Must start notifications from a timer to execute outside of Gatt event 760 | NotificationsTimer = new Timer(); 761 | NotificationsTimer.schedule(new NotificationsTimedOut(), NotificationsInterval, NotificationsInterval); 762 | } 763 | 764 | // This is a one time shot 765 | private class NotificationsTimedOut extends TimerTask 766 | { 767 | @Override 768 | public void run() 769 | { 770 | StopNotificationsTimer(); 771 | 772 | if (StartAllNotifications()) 773 | // All characteristics notifications have been enabled, adapter is connected 774 | IsAdapterConnected = true; 775 | } 776 | }; 777 | 778 | private void StopNotificationsTimer() 779 | { 780 | try 781 | { 782 | if (NotificationsTimer != null) 783 | { 784 | NotificationsTimer.cancel(); 785 | NotificationsTimer.purge(); 786 | NotificationsTimer = null; 787 | } 788 | } 789 | catch(Exception ex){} 790 | } 791 | 792 | private boolean StartAllNotifications() 793 | { 794 | IsCharacteristicNotification.clear(); 795 | 796 | if (!StartStopNotifications(AdapterService, true)) 797 | return false; 798 | 799 | if (!StartStopNotifications(ClientService, true)) 800 | return false; 801 | 802 | return true; 803 | } 804 | 805 | private boolean StopNotifications() 806 | { 807 | if (!StartStopNotifications(AdapterService, false)) 808 | return false; 809 | 810 | if (!StartStopNotifications(ClientService, false)) 811 | return false; 812 | 813 | return true; 814 | } 815 | 816 | protected boolean StartAdapterNotifications() 817 | { 818 | return StartStopNotifications(AdapterService, true); 819 | } 820 | 821 | protected boolean StopAdapterNotifications() 822 | { 823 | return StartStopNotifications(AdapterService, false); 824 | } 825 | 826 | private boolean StartStopNotifications(BluetoothGattService Service, boolean Start) 827 | { 828 | try 829 | { 830 | // if (BTDevice == null || BLEGatt == null || Service == null || Service.getCharacteristics() == null) 831 | // return false; 832 | 833 | if (CurrentState != BluetoothProfile.STATE_CONNECTED) 834 | return false; 835 | 836 | int CharacteristicsCount = Service.getCharacteristics().size(); 837 | 838 | for (int i = 0; i < CharacteristicsCount; i++) 839 | { 840 | // if (Service == null) 841 | // return false; 842 | 843 | BluetoothGattCharacteristic ServiceCharacteristic = Service.getCharacteristics().get(i); 844 | 845 | // if (Service == null || Service.getCharacteristics() == null || ServiceCharacteristic == null) 846 | // return false; 847 | 848 | if (Start) 849 | { 850 | if (!StartNotification(ServiceCharacteristic)) 851 | return false; 852 | } 853 | else 854 | { 855 | if (!StopNotification(ServiceCharacteristic)) 856 | return false; 857 | } 858 | 859 | WaitForDescriptorUpdate(); 860 | } 861 | 862 | return true; 863 | 864 | } catch (Exception ex) 865 | { 866 | if (ex.getMessage().contains("null object reference")) 867 | return false; 868 | 869 | RaiseSystemError("CommBLE StartStopNotifications - Start=" + Start, ex); 870 | return false; 871 | } 872 | } 873 | 874 | protected boolean StartNotification(BluetoothGattCharacteristic ServiceCharacteristic) 875 | { 876 | try 877 | { 878 | // if (BTDevice == null || BLEGatt == null || ServiceCharacteristic == null) 879 | // return false; 880 | 881 | String CharacteristicUUID = ServiceCharacteristic.getUuid().toString().toUpperCase(); 882 | 883 | IsCharacteristicNotification.remove(CharacteristicUUID); 884 | 885 | // if (BLEGatt == null || ServiceCharacteristic == null) 886 | // return false; 887 | 888 | BLEGatt.setCharacteristicNotification(ServiceCharacteristic, true); 889 | 890 | // if (BLEGatt == null || ServiceCharacteristic == null) 891 | // return false; 892 | 893 | BluetoothGattDescriptor Descriptor = ServiceCharacteristic.getDescriptor(UUID.fromString(BFCommBLE.ClientCharacteristicConfigUUID)); 894 | 895 | // if (Descriptor == null) 896 | // return false; 897 | 898 | Descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 899 | 900 | // if (BLEGatt == null) 901 | // return false; 902 | 903 | BLEGatt.writeDescriptor(Descriptor); 904 | 905 | IsCharacteristicNotification.add(CharacteristicUUID); 906 | 907 | return true; 908 | 909 | } catch (Exception ex) 910 | { 911 | if (ex.getMessage().contains("null object reference")) 912 | return false; 913 | 914 | RaiseSystemError("CommBLE.StartNotification", ex); 915 | return false; 916 | } 917 | } 918 | 919 | private boolean StopNotification(BluetoothGattCharacteristic ServiceCharacteristic) 920 | { 921 | try 922 | { 923 | // if (BTDevice == null || BLEGatt == null || ServiceCharacteristic == null) 924 | // return false; 925 | 926 | String CharacteristicUUID = ServiceCharacteristic.getUuid().toString().toUpperCase(); 927 | 928 | if (!IsCharacteristicNotification.contains(CharacteristicUUID)) 929 | return false; 930 | 931 | // if (BLEGatt == null || ServiceCharacteristic == null) 932 | // return false; 933 | 934 | BLEGatt.setCharacteristicNotification(ServiceCharacteristic, false); 935 | 936 | // if (BLEGatt == null || ServiceCharacteristic == null) 937 | // return false; 938 | 939 | BluetoothGattDescriptor Descriptor = ServiceCharacteristic.getDescriptor(UUID.fromString(BFCommBLE.ClientCharacteristicConfigUUID)); 940 | 941 | // if (Descriptor == null) 942 | // return false; 943 | 944 | Descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); 945 | 946 | // if (BLEGatt == null) 947 | // return false; 948 | 949 | BLEGatt.writeDescriptor(Descriptor); 950 | 951 | IsCharacteristicNotification.remove(CharacteristicUUID); 952 | 953 | return true; 954 | 955 | } catch (Exception ex) 956 | { 957 | if (ex.getMessage().contains("null object reference")) 958 | return false; 959 | 960 | RaiseSystemError("CommBLE.StopNotification", ex); 961 | return false; 962 | } 963 | } 964 | 965 | private void WaitForDescriptorUpdate() 966 | { 967 | IsUpdatingDescriptor = true; 968 | 969 | UpdateDescriptorTimeout = 0; 970 | while (IsUpdatingDescriptor && UpdateDescriptorTimeout < UpdateDescriptorTimeoutMax) 971 | { 972 | Helper.Sleep(10); 973 | UpdateDescriptorTimeout += 10; 974 | } 975 | } 976 | 977 | //endregion 978 | 979 | //region GATT Disconnection 980 | 981 | private boolean IsClosingGatt; 982 | private boolean IsDisconnectingGatt; 983 | 984 | private void DisconnectGatt() 985 | { 986 | try 987 | { 988 | if (BLEGatt == null) 989 | return; 990 | 991 | if (IsDisconnectingGatt) 992 | return; 993 | 994 | if (IsClosingGatt) 995 | return; 996 | 997 | IsDisconnectingGatt = true; 998 | 999 | // Disconnect the Gatt connection. 1000 | // android.util.Log.d("BlueFire", "CommBLE DisconnectGatt, Disconnecting Gatt"); 1001 | BLEGatt.disconnect(); 1002 | 1003 | // Must give Bluetooth time to close the Gatt connection or be reconnected. 1004 | // Note, the Gatt connection is closed in the callback. 1005 | int Timeout = blueFire.BleDisconnectWaitTime(); 1006 | while (true) 1007 | { 1008 | // Check if Gatt has been closed 1009 | if (BLEGatt == null) 1010 | break; 1011 | 1012 | // Check if Gatt has been reconnected 1013 | if (CurrentState == BluetoothProfile.STATE_CONNECTED) 1014 | break; 1015 | 1016 | // Check for timeout waiting for Gatt to close or be reconnected 1017 | if (Timeout <= 0) 1018 | { 1019 | // android.util.Log.d("BlueFire", "CommBLE DisconnectGatt, Timeout waiting for GATT to close, State=" + CurrentState); 1020 | CloseGatt(true); 1021 | break; 1022 | } 1023 | Helper.Sleep(100); 1024 | Timeout-= 100; 1025 | } 1026 | 1027 | IsDisconnectingGatt = false; 1028 | 1029 | } catch (Exception ex) 1030 | { 1031 | RaiseSystemError("CommBLE DisconnectGatt", ex); 1032 | 1033 | IsDisconnectingGatt = false; 1034 | } 1035 | } 1036 | 1037 | private void CloseGatt(boolean WaitForClose) 1038 | { 1039 | try 1040 | { 1041 | if (BLEGatt == null) 1042 | return; 1043 | 1044 | if (IsClosingGatt) 1045 | return; 1046 | 1047 | IsClosingGatt = true; 1048 | 1049 | // Close the Gatt connection. 1050 | // android.util.Log.d("BlueFire", "CommBLE CloseGatt, Closing Gatt, WaitForClose=" + WaitForClose); 1051 | BLEGatt.close(); 1052 | 1053 | // Give Bluetooth time to fully close the Gatt connection 1054 | if (WaitForClose) 1055 | Helper.Sleep(blueFire.BleDisconnectWaitTime()); 1056 | 1057 | BLEGatt = null; // not a good idea in case the wait time is not enough 1058 | 1059 | } catch (Exception ex) 1060 | { 1061 | RaiseSystemError("CommBLE CloseGatt", ex); 1062 | } 1063 | 1064 | IsClosingGatt = false; 1065 | } 1066 | 1067 | //endregion 1068 | 1069 | //region Adapter Connected 1070 | 1071 | @Override 1072 | protected void AdapterConnected() 1073 | { 1074 | StopDiscoveryTimer(); 1075 | 1076 | super.AdapterConnected(); 1077 | } 1078 | 1079 | private void AdapterConnected(String Text, String DeviceAddress) 1080 | { 1081 | RaiseNotification("CommBLE", "Connected to " + Text + " adapter '" + DeviceAddress + "'"); 1082 | 1083 | AdapterConnected(); 1084 | } 1085 | 1086 | @Override 1087 | protected void AdapterNotConnected() 1088 | { 1089 | try 1090 | { 1091 | super.AdapterNotConnected(); 1092 | 1093 | } catch (Exception ex) 1094 | { 1095 | } 1096 | } 1097 | 1098 | //endregion 1099 | 1100 | //region Send Adapter Data 1101 | 1102 | @Override 1103 | protected boolean SendAdapterData(byte[] DataBuffer, int DataLength) 1104 | { 1105 | try 1106 | { 1107 | if (!IsAdapterConnected) 1108 | return false; 1109 | 1110 | super.SendAdapterData(DataBuffer, DataLength); 1111 | 1112 | byte PacketNo; 1113 | int PacketLength; 1114 | 1115 | if (DataLength <= BFCommBLE.ClientPacket1Length) 1116 | { 1117 | PacketNo = BFCommBLE.ClientPacket1; 1118 | PacketLength = BFCommBLE.ClientPacket1Length; 1119 | } 1120 | else if (DataLength <= BFCommBLE.ClientPacket2Length) 1121 | { 1122 | PacketNo = BFCommBLE.ClientPacket2; 1123 | PacketLength = BFCommBLE.ClientPacket2Length; 1124 | } 1125 | else if (DataLength <= BFCommBLE.ClientPacket3Length) 1126 | { 1127 | PacketNo = BFCommBLE.ClientPacket3; 1128 | PacketLength = BFCommBLE.ClientPacket3Length; 1129 | } 1130 | else 1131 | { 1132 | RaiseSystemError("CommBLE.SendClientData", new Exception("Invalid Client Data Length=" + DataLength)); 1133 | return false; 1134 | } 1135 | 1136 | // Copy the data allowing room for the packet no. 1137 | byte[] PacketData = new byte[PacketLength + 1]; // add a byte for the Packet No 1138 | 1139 | PacketData[0] = PacketNo; 1140 | System.arraycopy(DataBuffer, 0, PacketData, 1, DataLength); 1141 | 1142 | IsWritingData = true; 1143 | 1144 | // Send the data to the adapter using the correct characteristic 1145 | if (!WriteData(PacketData)) 1146 | { 1147 | IsWritingData = false; 1148 | return false; 1149 | } 1150 | 1151 | // Wait for data to be sent 1152 | if (!WaitForWrittenData()) 1153 | { 1154 | IsWritingData = false; 1155 | if (IsAdapterConnected && IsConnected) 1156 | RaiseNotification("CommBLE.SendClientData", "BLE Write Timeout"); 1157 | 1158 | return false; 1159 | } 1160 | 1161 | IsWritingData = false; 1162 | 1163 | return true; 1164 | } 1165 | catch (Exception ex) 1166 | { 1167 | RaiseSystemError("CommBLE.SendClientData", ex); 1168 | return false; 1169 | } 1170 | } 1171 | 1172 | // Write data to the adapter 1173 | private boolean WriteData(byte[] PacketData) 1174 | { 1175 | try 1176 | { 1177 | // if (BLEGatt == null) 1178 | // return false; 1179 | 1180 | // Send the data to the adapter using the correct characteristic 1181 | switch (PacketData[0]) 1182 | { 1183 | case BFCommBLE.ClientPacket1: 1184 | ClientCharacteristic = ClientPacket1Characteristic; 1185 | break; 1186 | 1187 | case BFCommBLE.ClientPacket2: 1188 | ClientCharacteristic = ClientPacket2Characteristic; 1189 | break; 1190 | 1191 | case BFCommBLE.ClientPacket3: 1192 | ClientCharacteristic = ClientPacket3Characteristic; 1193 | break; 1194 | 1195 | default: 1196 | return false; 1197 | } 1198 | 1199 | // if (ClientCharacteristic == null) 1200 | // return false; 1201 | 1202 | ClientCharacteristic.setValue(PacketData); 1203 | 1204 | BLEGatt.writeCharacteristic(ClientCharacteristic); 1205 | 1206 | return true; 1207 | } 1208 | catch (Exception ex) 1209 | { 1210 | if (ex.getMessage().contains("null object reference")) 1211 | return false; 1212 | 1213 | RaiseSystemError("CommBLE.WriteData", ex); 1214 | return false; 1215 | } 1216 | } 1217 | 1218 | private boolean WaitForWrittenData() 1219 | { 1220 | WriteTimeout = 0; 1221 | 1222 | // Wait for data to be written 1223 | while (IsWritingData && IsAdapterConnected && WriteTimeout < WriteTimeoutMax) 1224 | { 1225 | Helper.Sleep(10); 1226 | WriteTimeout += 10; 1227 | } 1228 | 1229 | // Check for a timeout writing data 1230 | if (WriteTimeout >= WriteTimeoutMax) 1231 | return false; 1232 | 1233 | // No timeout 1234 | return true; 1235 | } 1236 | 1237 | //endregion 1238 | 1239 | //region Receive Adapter Data 1240 | 1241 | @Override 1242 | protected void ReceiveAdapterData(byte[] DataBuffer) 1243 | { 1244 | super.ReceiveAdapterData(DataBuffer); 1245 | } 1246 | 1247 | //endregion 1248 | 1249 | //region Disconnect 1250 | 1251 | @Override 1252 | protected boolean Disconnect() 1253 | { 1254 | return Disconnect(false); 1255 | } 1256 | 1257 | @Override 1258 | public boolean Disconnect(boolean WaitForDisconnect) 1259 | { 1260 | // Note, it is critical that Gatt is disconnected because if it is not the 1261 | // adapter will not advertise itself. 1262 | 1263 | try 1264 | { 1265 | if (!super.Disconnect(WaitForDisconnect)) 1266 | { 1267 | DisconnectGatt(); 1268 | return false; 1269 | } 1270 | 1271 | IsAdapterConnected = false; 1272 | 1273 | if (ConnectionState == ConnectionStates.Disconnecting || this.ConnectionState == ConnectionStates.Disconnected) 1274 | { 1275 | DisconnectGatt(); 1276 | return false; 1277 | } 1278 | 1279 | UpdateState(ConnectionStates.Disconnecting); 1280 | 1281 | if (BTAdapter == null) 1282 | { 1283 | DisconnectGatt(); 1284 | AdapterNotConnected(); 1285 | return false; 1286 | } 1287 | 1288 | // Disconnect Bluetooth 1289 | // Note, this must be done in a thread to allow the Disconnecting state 1290 | // to be processed by the event handlers. 1291 | 1292 | this.WaitForDisconnect = WaitForDisconnect; 1293 | DisconnectThread = new DisconnectThreading(); 1294 | DisconnectThread.start(); 1295 | 1296 | return true; 1297 | 1298 | } catch (Exception ex) 1299 | { 1300 | DisconnectGatt(); 1301 | RaiseSystemError("CommBLE.Disconnect", ex); 1302 | return false; 1303 | } 1304 | } 1305 | 1306 | private class DisconnectThreading extends Thread 1307 | { 1308 | public void run() 1309 | { 1310 | try 1311 | { 1312 | // Stop scanning 1313 | StopScan(); 1314 | 1315 | // Stop discovering 1316 | StopDiscoveryTimer(); 1317 | 1318 | // Stop adapter sending notifications 1319 | StopNotifications(); 1320 | 1321 | ClearInputPackets(); 1322 | 1323 | // Disconnect Gatt 1324 | DisconnectGatt(); 1325 | 1326 | if (BTDevice != null) 1327 | BTDevice = null; 1328 | 1329 | CheckWaitForDisconnect(WaitForDisconnect); 1330 | 1331 | } catch (Exception ex) 1332 | { 1333 | DisconnectGatt(); 1334 | RaiseSystemError("CommBLE.DisconnectThreading", ex); 1335 | } 1336 | } 1337 | } 1338 | 1339 | //endregion 1340 | 1341 | //region Dispose 1342 | 1343 | @Override 1344 | public void Dispose() 1345 | { 1346 | super.Dispose(); 1347 | } 1348 | 1349 | //endregion 1350 | } 1351 | -------------------------------------------------------------------------------- /APIDemo/src/main/java/com/bluefire/apidemo/CommBT2.java: -------------------------------------------------------------------------------- 1 | package com.bluefire.apidemo; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.bluetooth.BluetoothSocket; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.IntentFilter; 9 | import android.os.Handler; 10 | 11 | import com.bluefire.api.BFCommBT2; 12 | import com.bluefire.api.BlueFire; 13 | import com.bluefire.api.ConnectionStates; 14 | import com.bluefire.api.Helper; 15 | 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.io.OutputStream; 19 | import java.util.Set; 20 | import java.util.Timer; 21 | import java.util.TimerTask; 22 | 23 | public class CommBT2 extends Comm 24 | { 25 | //region Declaratives 26 | 27 | private BluetoothSocket Socket = null; 28 | 29 | private InputStream InStream = null; 30 | private OutputStream OutStream = null; 31 | 32 | private boolean BTpairing = false; 33 | private boolean BTreceiving = false; 34 | private boolean BTpairingError = false; 35 | private boolean BTfoundDevice = false; 36 | private boolean BTreceiverUnregistered = false; 37 | 38 | private Timer DiscoveryTimer; 39 | 40 | // Get Data 41 | 42 | private int DataByte; 43 | private int BufferIndex; 44 | private byte[] StreamBuffer = new byte[BFCommBT2.MaxMessageSize]; // length bytes + max message data + checksum 45 | 46 | private int DataLength; 47 | private int MessageLength; 48 | 49 | //endregion 50 | 51 | //region Constructor 52 | 53 | public CommBT2(Context context, Handler connectionHandler, BlueFire blueFire) 54 | { 55 | super(context, connectionHandler, blueFire); 56 | 57 | BFDeviceName = BFCommBT2.AdapterName; 58 | } 59 | 60 | //endregion 61 | 62 | //region Initialize 63 | 64 | @Override 65 | protected void Initialize() 66 | { 67 | super.Initialize(); 68 | 69 | BTpairing = false; 70 | BTreceiving = false; 71 | BTfoundDevice = false; 72 | BTpairingError = false; 73 | BTreceiverUnregistered = false; 74 | 75 | BTreceiving = true; // ignore discovery timeout 76 | } 77 | 78 | //endregion 79 | 80 | //region Connection 81 | 82 | @Override 83 | protected boolean StartConnection() 84 | { 85 | if (!super.StartConnection()) 86 | return false; 87 | 88 | try 89 | { 90 | // Check for a paired last connected device 91 | if (IsLastConnectedDevice()) 92 | return true; 93 | 94 | // Check for not finding a last connected device 95 | if (IsLastConnectedAdapterSet()) 96 | return false; 97 | 98 | // Check for a paired BlueFire device 99 | if (IsBlueFireDevice()) 100 | return true; 101 | 102 | // Last device is not connected, start the discovery process. 103 | // Note, this is faster than trying to connect to each paired device. 104 | if (ConnectionState == ConnectionStates.Connecting || ConnectionState == ConnectionStates.Reconnecting) 105 | StartDiscovery(); 106 | 107 | return true; 108 | 109 | } catch (Exception ex) 110 | { 111 | RaiseSystemError ("CommBT2.StartConnection", ex); 112 | return false; 113 | } 114 | } 115 | 116 | private Boolean IsLastConnectedDevice() 117 | { 118 | // Get all paired devices 119 | Set BTpairedDevices = BTAdapter.getBondedDevices(); 120 | 121 | // Check for last connected device 122 | for (BluetoothDevice BTdevice : BTpairedDevices) 123 | if (IsValidAdapter(BTdevice.getName(), BTdevice.getAddress())) 124 | return ConnectDevice(BTdevice); // try to connect to the adapter 125 | 126 | return false; 127 | } 128 | 129 | private Boolean IsBlueFireDevice() 130 | { 131 | // Get all paired devices 132 | Set BTpairedDevices = BTAdapter.getBondedDevices(); 133 | 134 | // Try to connect to the adapter 135 | for (BluetoothDevice BTdevice : BTpairedDevices) 136 | if (BTdevice.getName().equals(BFDeviceName)) 137 | if (ConnectDevice(BTdevice)) // try to connect to the adapter and retry 138 | return true; 139 | 140 | return false; 141 | } 142 | 143 | // Connect to the Bluetooth device and Pair if needed 144 | private boolean ConnectDevice(BluetoothDevice BTdevice) 145 | { 146 | try 147 | { 148 | BTpairing = true; // ignore discovery timeout just in case pairing is needed 149 | 150 | // Get a BluetoothSocket to connect with the given BluetoothDevice 151 | try { 152 | if (Socket != null) 153 | { 154 | Socket.close(); 155 | InStream = null; 156 | OutStream = null; 157 | } 158 | 159 | if (blueFire.UseInsecureConnection()) 160 | Socket = BTdevice.createInsecureRfcommSocketToServiceRecord(BFCommBT2.BT_UUID); 161 | else 162 | Socket = BTdevice.createRfcommSocketToServiceRecord(BFCommBT2.BT_UUID); 163 | } 164 | catch (Exception e) 165 | { 166 | BTpairing = false; 167 | return false; 168 | } 169 | // Cancel discovery because it will slow down the connection 170 | if (BTAdapter.isDiscovering()) 171 | BTAdapter.cancelDiscovery(); 172 | 173 | // Connect the device through the socket. This will block until it succeeds or throws an exception. 174 | // Note, This will initiate pairing if needed. 175 | while (!Socket.isConnected()) 176 | { 177 | Helper.Sleep(100); 178 | try 179 | { 180 | // Note, Socket.Connect can take from 1 to 3 seconds. 181 | // Have witnessed retries of more than 20. 182 | if (Socket != null) 183 | Socket.connect(); 184 | } 185 | catch (Exception ex2) 186 | { 187 | BTpairing = false; 188 | return false; 189 | } 190 | if (Socket == null) // disconnected 191 | { 192 | BTpairing = false; 193 | return false; 194 | } 195 | } 196 | InStream = Socket.getInputStream(); 197 | OutStream = Socket.getOutputStream(); 198 | 199 | DeviceName = BTdevice.getName(); 200 | DeviceAddress = BTdevice.getAddress(); 201 | 202 | BTpairing = false; 203 | 204 | AdapterConnected(); 205 | 206 | return true; // connected 207 | } 208 | catch (Exception ex) 209 | { 210 | RaiseSystemError ("CommBT2.ConnectDevice", ex); 211 | BTpairingError = true; 212 | return false; 213 | } 214 | } 215 | 216 | //endregion 217 | 218 | //region Discovery 219 | 220 | private void StartDiscovery() 221 | { 222 | UpdateState(ConnectionStates.Discovering); 223 | 224 | // Only allow discovery a set amount of time to find the adapter 225 | DiscoveryTimer = new Timer(); 226 | DiscoveryTimer.schedule(new DiscoveryTimedOut(), blueFire.DiscoveryTimeout(), blueFire.DiscoveryTimeout()); 227 | 228 | BTpairing = false; 229 | BTreceiving = true; 230 | BTpairingError = false; 231 | BTreceiverUnregistered = false; 232 | 233 | IntentFilter ActionFoundFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 234 | ActionFoundFilter.addAction(BluetoothDevice.ACTION_NAME_CHANGED); 235 | Context.registerReceiver(DiscoveryReceiver, ActionFoundFilter); 236 | 237 | // Start discovery. 238 | // Note, discovery of the device will be handled in the Broadcast Receiver. 239 | BTAdapter.startDiscovery(); 240 | 241 | // Block until discovery finishes 242 | while (ConnectionState == ConnectionStates.Discovering) 243 | Helper.Sleep(100); 244 | } 245 | 246 | private class DiscoveryTimedOut extends TimerTask 247 | { 248 | @Override 249 | public void run() 250 | { 251 | // Check for BT receiving 252 | if (BTreceiving) 253 | { 254 | BTreceiving = false; 255 | return; 256 | } 257 | // Check for BT pairing 258 | if (BTpairing) 259 | { 260 | BTpairing = false; 261 | return; 262 | } 263 | 264 | // Stop the discovery timer (but don't nuke it) 265 | if (DiscoveryTimer != null) // just to be safe ... 266 | DiscoveryTimer.cancel(); 267 | 268 | // Unregister the discovery receiver 269 | CancelDiscovery(); 270 | 271 | // Did not find an adapter, display a message to the user. 272 | // Note, all messages to the user must be done here in this timer thread instead of in 273 | // the receiver. 274 | if (!BTpairingError) 275 | AdapterNotConnected(); 276 | } 277 | }; 278 | 279 | private void StopDiscoveryTimer() 280 | { 281 | try 282 | { 283 | if (DiscoveryTimer != null) 284 | { 285 | DiscoveryTimer.cancel(); 286 | DiscoveryTimer.purge(); 287 | DiscoveryTimer = null; 288 | } 289 | } 290 | catch (Exception ex){} 291 | } 292 | 293 | // Return from Bluetooth discovery broadcasts 294 | 295 | private void CancelDiscovery() 296 | { 297 | BTAdapter.cancelDiscovery(); 298 | Helper.Sleep(100); // give time for all receiver events to fire 299 | 300 | while (BTAdapter.isDiscovering()) 301 | Helper.Sleep(100); // give time for all receiver events to fire 302 | 303 | UnregisterReceiver(); 304 | } 305 | 306 | //endregion 307 | 308 | //region Broadcast Receiver 309 | 310 | protected final BroadcastReceiver DiscoveryReceiver = new BroadcastReceiver() { 311 | 312 | public void onReceive(Context context, Intent intent) { 313 | 314 | try 315 | { 316 | if (BTreceiverUnregistered) 317 | return; 318 | 319 | String action = intent.getAction(); 320 | 321 | // Check for discovery finding a device. 322 | // Note, if there are multiple devices, discovery will find the one that is connected. 323 | if ((action.equals(BluetoothDevice.ACTION_FOUND) || action.equals(BluetoothDevice.ACTION_NAME_CHANGED)) && !BTfoundDevice) 324 | { 325 | BTreceiving = true; // ignore discovery timeout 326 | 327 | // Get the Bluetooth device 328 | BluetoothDevice BTdevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 329 | 330 | if (BTdevice == null) 331 | return; 332 | 333 | String BTName = BTdevice.getName(); 334 | if (BTName == null) 335 | return; 336 | 337 | // Check for a BlueFire adapter 338 | if (BTName.equals(BFDeviceName)) 339 | { 340 | // Yes is is, stop the receiver and try to connect to it 341 | BTfoundDevice = true; 342 | 343 | CancelDiscovery(); 344 | 345 | ConnectDevice(BTdevice); 346 | 347 | if (!IsConnected) // needed for bug when reconnecting with no pairing 348 | ConnectDevice(BTdevice); 349 | 350 | BTfoundDevice = false; 351 | } 352 | } 353 | } 354 | catch (Exception ex) 355 | { 356 | CancelDiscovery(); 357 | RaiseSystemError ("CommBT2.BroadcastReceiver", ex); 358 | BTpairingError = true; 359 | } 360 | } 361 | }; 362 | 363 | private void UnregisterReceiver() 364 | { 365 | // This is the only way to handle receiver already unregistered 366 | try 367 | { 368 | BTreceiverUnregistered = true; 369 | Context.unregisterReceiver(DiscoveryReceiver); 370 | } 371 | catch(Exception e) {} 372 | } 373 | 374 | //endregion 375 | 376 | //region Adapter Connected 377 | 378 | @Override 379 | protected void AdapterConnected() 380 | { 381 | StopDiscoveryTimer(); 382 | 383 | super.AdapterConnected(); 384 | } 385 | 386 | //endregion 387 | 388 | //region Is Data Available 389 | 390 | @Override 391 | public boolean IsDataAvailable() 392 | { 393 | try 394 | { 395 | // Check for any queued packets 396 | if (super.IsDataAvailable()) 397 | return true; 398 | 399 | // Check for disconnection 400 | if (InStream == null) 401 | return false; 402 | 403 | // Check for any input packets 404 | if (InStream.available() == 0) 405 | return false; 406 | 407 | byte[] DataBuffer = GetPacketData(); 408 | if (DataBuffer.length == 0) 409 | return false; 410 | 411 | // Add input packet to the queue 412 | ReceiveAdapterData(DataBuffer); 413 | 414 | return true; 415 | } 416 | catch (Exception ex) 417 | { 418 | RaiseSystemError ("CommBT2.IsDataAvailable", ex); 419 | return false; 420 | } 421 | } 422 | 423 | //endregion 424 | 425 | //region Get Packet Data 426 | 427 | // Get message data from the adapter 428 | // The data stream looks like: 429 | // Length (2) - Length of Requested Data, not Checksum 430 | // Length Check (1) 431 | // Message Data (n) 432 | // Data Checksum (1) 433 | 434 | private byte[] GetPacketData() 435 | { 436 | DataLength = 0; 437 | BufferIndex = 0; 438 | MessageLength = -1; 439 | byte[] MessageData; 440 | 441 | try 442 | { 443 | // Wait until an entire message is received from the adapter 444 | while (true) 445 | { 446 | if (InStream == null) // disconnected 447 | return new byte[0]; 448 | 449 | if (InStream.available() > 0) 450 | DataByte = InStream.read(); 451 | else 452 | DataByte = -1; 453 | 454 | if (DataByte < 0) 455 | if (CheckDataTimeOut()) 456 | { 457 | RaiseDataTimeout(); 458 | return new byte[0]; 459 | } 460 | else 461 | continue; 462 | 463 | // Get the data byte 464 | StreamBuffer[BufferIndex] = (byte)DataByte; 465 | 466 | // Check for receiving the length bytes 467 | if (BufferIndex == (BFCommBT2.NofLengthBytes - 1)) 468 | { 469 | DataLength = GetDataLength(StreamBuffer); 470 | if (DataLength == 0) 471 | return new byte[0]; 472 | MessageLength = BFCommBT2.NofLengthBytes + DataLength + 1; // length bytes + message data + checksum 473 | } 474 | 475 | // Check for receiving the entire message 476 | else if (BufferIndex == (MessageLength - 1)) 477 | { 478 | MessageData = new byte[MessageLength]; 479 | System.arraycopy(StreamBuffer, 0,MessageData, 0, MessageLength); 480 | return MessageData; 481 | } 482 | 483 | BufferIndex++; 484 | } 485 | } 486 | catch (Exception ex) 487 | { 488 | if (InStream == null) // disconnected 489 | return new byte[0]; 490 | else 491 | { 492 | RaiseSystemError ("CommBT2.GetPacketData", ex); 493 | return new byte[0]; 494 | } 495 | } 496 | } 497 | 498 | //endregion 499 | 500 | //region Send Adapter Data 501 | 502 | @Override 503 | protected boolean SendAdapterData(byte[] DataBuffer, int DataLength) throws Exception 504 | { 505 | return SendAdapterData(DataBuffer, DataLength, true); 506 | } 507 | 508 | // Send Client Data to the BlueFire 509 | private boolean SendAdapterData(byte[] DataBuffer, int DataLength, boolean AddPrefix) throws Exception 510 | { 511 | try 512 | { 513 | if (OutStream == null || DataBuffer.length == 0) 514 | return false; 515 | 516 | super.SendAdapterData(DataBuffer, DataLength); 517 | 518 | // Add message prefix and suffix 519 | if (AddPrefix) 520 | { 521 | byte[] SendBuffer = new byte[BFCommBT2.MessagePrefix.length + DataLength + BFCommBT2.MessageSuffix.length]; 522 | System.arraycopy(BFCommBT2.MessagePrefix, 0, SendBuffer, 0, BFCommBT2.MessagePrefix.length); 523 | System.arraycopy(DataBuffer, 0, SendBuffer, BFCommBT2.MessagePrefix.length, DataBuffer.length); 524 | System.arraycopy(BFCommBT2.MessageSuffix, 0, SendBuffer, BFCommBT2.MessagePrefix.length + DataLength, BFCommBT2.MessageSuffix.length); 525 | // Send the data 526 | OutStream.write(SendBuffer, 0, SendBuffer.length); 527 | } 528 | else // Send the data without a prefix and suffix 529 | OutStream.write(DataBuffer, 0, DataLength); 530 | 531 | return true; 532 | } 533 | catch (IOException ex) { } // This occurs when going out of BT range 534 | catch (Exception ex) 535 | { 536 | RaiseSystemError("CommBT2.SendClientData", ex); 537 | } 538 | return false; 539 | } 540 | 541 | //region Receive Adapter Data 542 | 543 | @Override 544 | protected void ReceiveAdapterData(byte[] DataBuffer) 545 | { 546 | super.ReceiveAdapterData(DataBuffer); 547 | } 548 | 549 | //endregion 550 | 551 | //endregion 552 | 553 | //region Disconnect 554 | 555 | @Override 556 | protected boolean Disconnect() 557 | { 558 | return Disconnect(false); 559 | } 560 | 561 | @Override 562 | public boolean Disconnect(boolean WaitForDisconnect) 563 | { 564 | if (!super.Disconnect(WaitForDisconnect)) 565 | return false; 566 | 567 | UpdateState(ConnectionStates.Disconnecting); 568 | 569 | if (BTAdapter == null) 570 | { 571 | AdapterNotConnected(); 572 | return false; 573 | } 574 | 575 | try 576 | { 577 | Context.unregisterReceiver(DiscoveryReceiver); 578 | } 579 | catch (Exception e) {} 580 | 581 | try 582 | { 583 | if (DiscoveryTimer != null) 584 | { 585 | DiscoveryTimer.cancel(); 586 | DiscoveryTimer = null; 587 | } 588 | 589 | if (InStream != null) 590 | { 591 | InStream.close(); 592 | InStream = null; 593 | } 594 | if (OutStream != null) 595 | { 596 | OutStream.close(); 597 | OutStream = null; 598 | } 599 | if (Socket != null) 600 | { 601 | Socket.close(); 602 | Socket = null; 603 | } 604 | 605 | // Unregister the discovery receiver 606 | CancelDiscovery(); 607 | 608 | CheckWaitForDisconnect(WaitForDisconnect); 609 | 610 | return true; 611 | } 612 | catch (Exception ex) 613 | { 614 | RaiseSystemError("CommBT2.Disconnect", ex); 615 | return false; 616 | } 617 | } 618 | 619 | //endregion 620 | 621 | //region Dispose 622 | 623 | @Override 624 | public void Dispose() 625 | { 626 | super.Dispose(); 627 | } 628 | 629 | //endregion 630 | } 631 | -------------------------------------------------------------------------------- /APIDemo/src/main/java/com/bluefire/apidemo/Service.java: -------------------------------------------------------------------------------- 1 | package com.bluefire.apidemo; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.os.Handler; 6 | import android.os.Message; 7 | import android.os.Process; 8 | import android.util.Log; 9 | 10 | import com.bluefire.api.BlueFire; 11 | import com.bluefire.api.CANBusSpeeds; 12 | import com.bluefire.api.ConnectionStates; 13 | import com.bluefire.api.Const; 14 | import com.bluefire.api.RetrievalMethods; 15 | import com.bluefire.api.Vehicle; 16 | 17 | import java.util.LinkedList; 18 | import java.util.Queue; 19 | 20 | public class Service 21 | { 22 | private BlueFire blueFire; 23 | 24 | private ServiceThread serviceThread; 25 | 26 | private boolean serviceIsRunning; 27 | 28 | // Android process pid for the activity or service that instantiates the API. 29 | private int servicePid; 30 | private boolean killService= false; 31 | 32 | private ConnectionStates connectionState = ConnectionStates.NotConnected; 33 | private String connectionMessage = ""; 34 | 35 | private Queue EventsQueue = new LinkedList(); 36 | private ReceiveEventsThreading ReceiveEventsThread; 37 | 38 | private boolean isKeyOn; 39 | 40 | private boolean isConnecting; 41 | private boolean isConnected; 42 | 43 | private boolean IsRetrievingEngineVIN; 44 | 45 | private RetrievalMethods retrievalMethod; 46 | private int retrievalInterval; 47 | 48 | // Application settings 49 | private boolean appUseBLE; 50 | private boolean appUseBT21; 51 | 52 | private boolean appIgnoreJ1939; 53 | private boolean appIgnoreJ1708; 54 | 55 | private int appLedBrightness; 56 | 57 | private int appDiscoveryTimeOut; 58 | private int appMaxConnectAttempts; 59 | private int appMaxReconnectAttempts; 60 | private int appBluetoothRecycleAttempt; 61 | private int appBleDisconnectWaitTime; 62 | 63 | private String appDeviceId = ""; 64 | private String appAdapterId = ""; 65 | private boolean appConnectToLastAdapter; 66 | 67 | private String appUserName = ""; 68 | private String appPassword = ""; 69 | private boolean appSecureDevice = false; 70 | private boolean appSecureAdapter = false; 71 | 72 | private boolean appOptimizeDataRetrieval = false; 73 | 74 | private Context serviceContext; 75 | 76 | public Service(Context context) 77 | { 78 | serviceContext = context; // the API requires a context 79 | 80 | // Set to kill the service when the user exits. 81 | // Note, this is recommended as it will ensure that all API resources such as 82 | // Bluetooth (BLE GATT) are released. 83 | //killService = true; 84 | killService = false; // for testing 85 | servicePid = Process.myPid(); 86 | 87 | // Set app variables 88 | appUseBLE = true; 89 | appUseBT21 = false; 90 | 91 | appIgnoreJ1939 = false; 92 | appIgnoreJ1708 = true; 93 | 94 | appLedBrightness = 100; 95 | 96 | appConnectToLastAdapter = false; 97 | 98 | appOptimizeDataRetrieval = true; 99 | 100 | // Setup to receive API events 101 | ReceiveEventsThread = new ReceiveEventsThreading(); 102 | ReceiveEventsThread.start(); 103 | } 104 | 105 | public void startService() 106 | { 107 | // Initiate the API 108 | blueFire = new BlueFire(serviceContext, eventHandler); 109 | 110 | appDiscoveryTimeOut = blueFire.DiscoveryTimeoutDefault; 111 | appMaxConnectAttempts = blueFire.MaxConnectAttemptsDefault; 112 | appMaxReconnectAttempts = blueFire.MaxReconnectAttemptsDefault; 113 | appBluetoothRecycleAttempt = blueFire.BluetoothRecycleAttemptDefault; 114 | appBleDisconnectWaitTime = blueFire.BleDisconnectWaitTimeDefault; 115 | 116 | // Simulate a service 117 | serviceIsRunning = true; 118 | 119 | serviceThread = new ServiceThread(); 120 | serviceThread.start(); 121 | } 122 | 123 | public void stopService() 124 | { 125 | serviceIsRunning = false; 126 | 127 | // Clear previous data from the CAN Filter 128 | blueFire.StopDataRetrieval(); 129 | 130 | disconnectAdapter(); 131 | 132 | blueFire.Dispose(); 133 | 134 | // Kill the service to ensure all resources (like BLE) are released. 135 | // Note, this will close the BLE GATT connection if for some reason 136 | // Android is keeping it open. 137 | // Note, the app will be killed but most likely Android will restart it 138 | // so it will show up under Settings/Apps but all the App and API resources 139 | // will be stopped and restarted. 140 | if (killService) 141 | Process.killProcess(servicePid); 142 | } 143 | 144 | private class ServiceThread extends Thread 145 | { 146 | public void run() 147 | { 148 | // Connect to the adapter 149 | connectAdapter(); 150 | } 151 | } 152 | 153 | // Connect 154 | public void connectAdapter() 155 | { 156 | try 157 | { 158 | isConnecting = true; 159 | isConnected = false; 160 | 161 | connectionState = ConnectionStates.NA; 162 | 163 | logNotifications("Connecting..."); 164 | 165 | // Initialize adapter properties (in case they were changed) 166 | initializeAdapter(); 167 | 168 | // Note, this is a blocking call and must run in it's own thread. 169 | blueFire.Connect(); 170 | } 171 | catch (Exception ex) {} 172 | } 173 | 174 | private void initializeAdapter() 175 | { 176 | // Set Bluetooth adapter type 177 | blueFire.UseBLE = appUseBLE; 178 | blueFire.UseBT21 = appUseBT21; 179 | 180 | // Set to ignore data bus settings 181 | blueFire.SetIgnoreJ1939(appIgnoreJ1939); 182 | blueFire.SetIgnoreJ1708(appIgnoreJ1708); 183 | 184 | // Set the BLE Disconnect Wait Timeout. 185 | // Note, in order for BLE to release the connection to the adapter and allow reconnects 186 | // or subsequent connects, it must be completely closed. Unfortunately Android does not 187 | // have a way to detect this other than waiting a set amount of time after disconnecting 188 | // from the adapter. This wait time can vary with the Android version and the make and 189 | // model of the mobile device. The default is 2 seconds. If your app experiences numerous 190 | // unable to connect and BlueFire LE fails to show up under Bluetooth settings, try increasing 191 | // this value. 192 | blueFire.SetBleDisconnectWaitTime(appBleDisconnectWaitTime); 193 | 194 | // Set the Bluetooth discovery timeout. 195 | // Note, depending on the number of Bluetooth devices present on the mobile device, 196 | // discovery could take a long time. 197 | // Note, if this is set to a high value, the app needs to provide the user with the 198 | // capability of canceling the discovery. 199 | blueFire.SetDiscoveryTimeout(appDiscoveryTimeOut); 200 | 201 | // Set number of Bluetooth connection attempts. 202 | // Note, if the mobile device does not connect, try setting this to a value that 203 | // allows for a consistent connection. If you're using multiple adapters and have 204 | // connection problems, un-pair all devices before connecting. 205 | blueFire.SetMaxConnectAttempts(appMaxConnectAttempts); 206 | blueFire.SetMaxReconnectAttempts(appMaxReconnectAttempts); 207 | blueFire.SetBluetoothRecycleAttempt(appBluetoothRecycleAttempt); 208 | 209 | // Set the device and adapter ids 210 | blueFire.SetDeviceId(appDeviceId); 211 | blueFire.SetAdapterId(appAdapterId); 212 | 213 | // Set the connect to last adapter setting 214 | blueFire.SetConnectToLastAdapter(appConnectToLastAdapter); 215 | 216 | // Set the adapter security parameters 217 | blueFire.SetSecurity(appSecureDevice, appSecureAdapter, appUserName, appPassword); 218 | 219 | // Set to optimize data retrieval 220 | blueFire.SetOptimizeDataRetrieval(appOptimizeDataRetrieval); 221 | } 222 | 223 | private void disconnectAdapter() 224 | { 225 | try 226 | { 227 | // Note, with Firmware 3.11 there is no need to wait for the adapter 228 | // to disconnect. 229 | boolean WaitForDisconnect = isConnected; // just for code clarity 230 | blueFire.Disconnect(WaitForDisconnect); 231 | } 232 | catch(Exception e) {} 233 | } 234 | 235 | private void adapterDisconnected() 236 | { 237 | logNotifications("Adapter disconnected."); 238 | 239 | adapterNotConnected(); 240 | } 241 | 242 | private void adapterNotConnected() 243 | { 244 | logNotifications("Adapter not connected."); 245 | 246 | isConnected = false; 247 | isConnecting = false; 248 | 249 | logStatus(); 250 | } 251 | 252 | private void adapterReconnecting() 253 | { 254 | logNotifications("Adapter re-connecting."); 255 | 256 | isConnected = false; 257 | isConnecting = true; 258 | } 259 | 260 | private void adapterReconnected() 261 | { 262 | logNotifications("Adapter re-connected."); 263 | 264 | adapterConnected(); 265 | } 266 | 267 | private void adapterNotReconnected() 268 | { 269 | logNotifications("Adapter not re-connected."); 270 | 271 | adapterNotConnected(); 272 | } 273 | 274 | private void adapterNotAuthenticated() 275 | { 276 | logNotifications("Adapter not authenticated."); 277 | 278 | adapterNotConnected(); 279 | } 280 | 281 | private void adapterConnected() 282 | { 283 | logNotifications("Adapter connected."); 284 | 285 | isConnected = true; 286 | isConnecting = false; 287 | 288 | // Get adapter data 289 | getAdapterData(); 290 | } 291 | 292 | // Start retrieving data after connecting to the adapter 293 | private void getAdapterData() 294 | { 295 | // Check for an incompatible version. 296 | if (!blueFire.IsCompatible()) 297 | { 298 | logNotifications("Incompatible Adapter."); 299 | 300 | disconnectAdapter(); 301 | return; 302 | } 303 | 304 | // Get the adapter id 305 | appDeviceId = blueFire.DeviceId(); 306 | appAdapterId = blueFire.AdapterId(); 307 | 308 | // Get any adapter messages 309 | blueFire.GetMessages(); 310 | 311 | // Start retrieving truck data 312 | getTruckData(); 313 | } 314 | 315 | private void getTruckData() 316 | { 317 | // Clear previous data from the CAN Filter 318 | blueFire.StopDataRetrieval(); 319 | 320 | if (Vehicle.EngineVIN == Const.NA) 321 | { 322 | IsRetrievingEngineVIN = true; 323 | blueFire.GetEngineVIN(); 324 | } 325 | 326 | retrievalMethod = RetrievalMethods.OnChange; // do not use OnInterval with this many data requests 327 | retrievalInterval = blueFire.MinInterval(); // should be MinInterval or greater with this many requests 328 | int hoursInterval = 30 * Const.OneSecond; // hours only change every 3 minutes 329 | 330 | // Request data from the adapter. 331 | // Note, be careful not to request too much data at one time otherwise you run the risk of filling up 332 | // the CAN Filter buffer. You can experiment with combining data retrievals to determine how much you can 333 | // request before filling the CAN Filter buffer (you get an error if you do). 334 | 335 | blueFire.GetEngineData1(retrievalMethod, retrievalInterval); // RPM, Percent Torque, Driver Torque, Torque Mode 336 | blueFire.GetEngineData2(retrievalMethod, retrievalInterval); // Percent Load, Accelerator Pedal Position 337 | blueFire.GetEngineData3(retrievalMethod, retrievalInterval); // Vehicle Speed, Max Set Speed, Brake Switch, Clutch Switch, Park Brake Switch, Cruise Control Settings and Switches 338 | blueFire.GetOdometer(retrievalMethod, retrievalInterval); // Distance and Odometer 339 | blueFire.GetEngineHours(retrievalMethod, hoursInterval); // Total Engine Hours, Total Idle Hours 340 | blueFire.GetBrakeData(retrievalMethod, retrievalInterval); // Application Pressure, Primary Pressure, Secondary Pressure 341 | blueFire.GetBatteryVoltage(retrievalMethod, retrievalInterval); // Battery Voltage 342 | blueFire.GetFuelData(retrievalMethod, retrievalInterval); // Fuel Levels, Fuel Used, Idle Fuel Used, Fuel Rate, Instant Fuel Economy, Avg Fuel Economy, Throttle Position 343 | blueFire.GetTemps(retrievalMethod, retrievalInterval); // Oil Temp, Coolant Temp, Intake Manifold Temperature 344 | blueFire.GetPressures(retrievalMethod, retrievalInterval); // Oil Pressure, Coolant Pressure, Intake Manifold(Boost) Pressure 345 | blueFire.GetCoolantLevel(retrievalMethod, retrievalInterval); // Coolant Level 346 | } 347 | 348 | private int TimeToWrite = 9999; 349 | 350 | private void checkTruckData() 351 | { 352 | // Check the data you requested to see which one changed that triggered the DataAvailable 353 | // event. If you're not concerned with data throughput for processing the data, you can just 354 | // process all the data whether it changed or not. 355 | 356 | if (IsRetrievingEngineVIN && Vehicle.EngineVIN != Const.NA) 357 | { 358 | IsRetrievingEngineVIN = false; 359 | blueFire.StopRetrievingEngineVIN(); 360 | logNotifications("Engine VIN=" + Vehicle.EngineVIN); 361 | } 362 | 363 | if (TimeToWrite > 50) 364 | { 365 | TimeToWrite = 0; 366 | logNotifications("RPM=" + Vehicle.RPM); 367 | } 368 | else 369 | TimeToWrite ++; 370 | 371 | // if (Vehicle.RPM > 0) 372 | // logNotifications("RPM=" + Vehicle.RPM); 373 | // logNotifications("PctLoad=" + Vehicle.PctLoad); 374 | // logNotifications("Speed=" + Vehicle.Speed); 375 | } 376 | 377 | private void checkKeyState() 378 | { 379 | boolean keyIsOn = blueFire.IsKeyOn(); 380 | 381 | if (isKeyOn != keyIsOn) 382 | { 383 | if (keyIsOn) 384 | logNotifications("Key is On"); 385 | else 386 | logNotifications("Key is Off"); 387 | 388 | // Double check key change by retrieving IsCANAvailable and IsJ1708Available. 389 | // Note, only do this on change of state, not constantly. 390 | blueFire.GetKeyState(); 391 | 392 | isKeyOn = keyIsOn; 393 | } 394 | } 395 | 396 | private void CANStarting() 397 | { 398 | // Get the CAN bus speed 399 | CANBusSpeeds CANBusSpeed = blueFire.CANBusSpeed(); 400 | 401 | String Message; 402 | if (blueFire.IsOBD2()) 403 | Message = "OBD2"; 404 | else 405 | Message = "J1939"; 406 | Message += " is starting, CAN bus speed is "; 407 | 408 | switch (CANBusSpeed) 409 | { 410 | case K250: 411 | Message += "250K."; 412 | break; 413 | case K500: 414 | Message += "500K."; 415 | break; 416 | default: 417 | Message += "unknown."; 418 | break; 419 | } 420 | logNotifications(Message); 421 | 422 | // Key is on so double check the key state 423 | checkKeyState(); 424 | 425 | // Re-retrieve truck data 426 | getTruckData(); 427 | } 428 | 429 | private void j1708Restarting() 430 | { 431 | // Re-retrieve truck data 432 | getTruckData(); 433 | } 434 | 435 | private void processEvent(Message msg) 436 | { 437 | try 438 | { 439 | connectionState = ConnectionStates.values()[msg.arg1]; 440 | connectionMessage = (String)msg.obj; 441 | 442 | logStatus(); 443 | 444 | switch (connectionState) 445 | { 446 | case Connecting: 447 | case Discovering: 448 | case Disconnecting: 449 | case Connected: 450 | break; 451 | 452 | case IsReady: 453 | adapterConnected(); 454 | break; 455 | 456 | case NotAuthenticated: 457 | adapterNotAuthenticated(); 458 | break; 459 | 460 | case Disconnected: 461 | adapterDisconnected(); 462 | break; 463 | 464 | case Reconnecting: 465 | adapterReconnecting(); 466 | break; 467 | 468 | case Reconnected: 469 | adapterReconnected(); 470 | break; 471 | 472 | case NotReconnected: 473 | adapterNotReconnected(); 474 | break; 475 | 476 | case CANStarting: 477 | CANStarting(); 478 | break; 479 | 480 | case J1708Restarting: 481 | j1708Restarting(); 482 | break; 483 | 484 | case NotConnected: 485 | adapterNotConnected(); 486 | break; 487 | 488 | case CANFilterFull: 489 | logNotifications("The CAN Filter is Full. Some data will not be retrieved."); 490 | break; 491 | 492 | case DataError: 493 | logNotifications("Adapter Data Error. " + connectionMessage); 494 | break; 495 | 496 | case Notification: 497 | logNotifications("API notification. " + connectionMessage); 498 | break; 499 | 500 | case AdapterMessage: 501 | logNotifications("Adapter message. " + connectionMessage); 502 | break; 503 | 504 | case AdapterReboot: 505 | logNotifications("Adapter Rebooting - " + connectionMessage); 506 | break; 507 | 508 | case DataTimeout: 509 | logNotifications("Adapter Data Timeout - Lost connection with the Adapter"); 510 | break; 511 | 512 | case BluetoothTimeout: 513 | adapterNotConnected(); 514 | logNotifications("Adapter Bluetooth Timeout - Unable to connect to Bluetooth."); 515 | break; 516 | 517 | case AdapterTimeout: 518 | adapterNotConnected(); 519 | logNotifications("Adapter Connection Timeout - Bluetooth unable to connect to the Adapter."); 520 | break; 521 | 522 | case SystemError: 523 | adapterNotConnected(); 524 | logNotifications("API System error. " + connectionMessage); 525 | break; 526 | 527 | case DataChanged: 528 | checkTruckData(); 529 | break; 530 | } 531 | 532 | } 533 | catch (Exception e) {} 534 | } 535 | 536 | // BlueFire Event Handler Thread 537 | private class ReceiveEventsThreading extends Thread 538 | { 539 | public void run() 540 | { 541 | while (true) 542 | { 543 | if (!EventsQueue.isEmpty()) 544 | { 545 | Message handleMessage = EventsQueue.poll(); 546 | if (handleMessage != null) 547 | processEvent(handleMessage); 548 | } 549 | threadSleep(1); // allow other threads to execute 550 | } 551 | } 552 | } 553 | 554 | // BlueFire Event Handler 555 | private Handler eventHandler = new Handler() 556 | { 557 | @Override 558 | @SuppressLint("HandlerLeak") 559 | public void handleMessage(Message msg) 560 | { 561 | Message handleMessage = new Message(); 562 | handleMessage.what = msg.what; 563 | handleMessage.obj = msg.obj; 564 | 565 | EventsQueue.add(handleMessage); 566 | } 567 | }; 568 | 569 | private void logStatus() 570 | { 571 | logNotifications(connectionState.toString()); 572 | } 573 | 574 | private void logNotifications(String message) 575 | { 576 | if (!message.equals("")) 577 | Log.d("BlueFire", message); 578 | } 579 | 580 | private void threadSleep(int Interval) 581 | { 582 | try 583 | { 584 | Thread.sleep(Interval); 585 | } 586 | catch(Exception ex) {} 587 | } 588 | 589 | } 590 | -------------------------------------------------------------------------------- /APIDemo/src/main/res/drawable/bluefire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueFire-LLC/BlueFire-API-for-Android-Studio/62d5696102f6bcefa3ec0cd9b0b1f965f81ab739/APIDemo/src/main/res/drawable/bluefire.png -------------------------------------------------------------------------------- /APIDemo/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BlueFire-API-for-Android-Studio 3 | 4 | -------------------------------------------------------------------------------- /APIDemo/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 13 | 14 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /BlueFire API Issue Report.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueFire-LLC/BlueFire-API-for-Android-Studio/62d5696102f6bcefa3ec0cd9b0b1f965f81ab739/BlueFire API Issue Report.docx -------------------------------------------------------------------------------- /BlueFire API Issue Report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueFire-LLC/BlueFire-API-for-Android-Studio/62d5696102f6bcefa3ec0cd9b0b1f965f81ab739/BlueFire API Issue Report.pdf -------------------------------------------------------------------------------- /BlueFire-API-for-Android-Studio.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /BueFire Android API v26.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueFire-LLC/BlueFire-API-for-Android-Studio/62d5696102f6bcefa3ec0cd9b0b1f965f81ab739/BueFire Android API v26.0.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BlueFire-API-for-Android 2 | Android API for direct connection to the BlueFire J1939/J1708 Bluetooth Data Adapters. 3 | 4 | Version 1:
    5 |
  • Initial version. 6 |
7 | 8 | Version 2:
    9 |
  • Code updates. 10 |
11 | 12 | Version 3:
    13 |
  • Handles Adapter looping errors. 14 |
15 | 16 | Version 4:
    17 |
  • Added Adapter Name and Password retrieval and update. 18 |
  • Connects to any adapter that starts with the Adapter Name. 19 |
  • Reboots the adapter on receiving adapter errors (firmware version 2.12). 20 |
  • Supports Adapter Firmware 2.13. 21 |
22 | 23 | Version 5:
    24 |
  • Added Adapter Name update to Main page. 25 |
  • Added Truck Make, Model, Serial No, and Unit No to Next Pages. 26 |
  • Added App Settings class for BlueFire settings. 27 |
  • Added rebooting the adapter when disconnecting. 28 |
  • Added LastConnectedId and ConnectToLastAdapter Bluetooth settings. 29 |
  • Added Incompatible version check.
  • 30 |
      31 |
    • Adapter Firmware 2.7 and less
    • 32 |
    • Adapter Firmware 3.0 and greater
    • 33 |
    34 |
35 | 36 | Version 6:
    37 |
  • Created an API document. Contact BlueFire Support for a copy. 38 |
  • Removed exposure to the Comm, J1939, and J1587 classes, and, moved all properties and methods to the Adapter class. 39 |
  • Added option to set the Interval on retrieving truck data (default is on change of data). This is useful when the data is coming in too fast (ie. RPM) and you want to slow it down. 40 |
  • Added SendPGN method and PGNData property for sending non-API defined PGNs. 41 |
  • Added sample code for SendPGN and MonitorPGN. 42 |
  • Added commons-codec-1.10.jar to the project libs folder. This is only required for the API Demo App. 43 |
  • Added a projects docs folder that contains the commons javadoc files. You must set the Javadoc Location project property to point to this folder. 44 |
45 | 46 | Version 7:
    47 |
  • GetVehicleData is now threaded for performance. 48 |
  • Compatible with Adapter Firmware 3.x. 49 |
50 | 51 | Version 8:
    52 |
  • Fixed retrieving J1939 Component Id (Make, Model, Serial No, Unit No). 53 |
  • Added retrieving J1708 VIN, Component Id and Faults. 54 |
  • Added additional exception handling. 55 |
56 | 57 | Version 9:
    58 |
  • Added additional exception handling. 59 |
  • Added additional debug logging to the Demo App (Main). 60 |
61 | 62 | Version 10:
    63 |
  • Added J1708 filtering. 64 |
  • Truck numerical data is initialized to -1. 65 |
  • Demo App (Main) shows NA if truck data is negative (no data). 66 |
67 | 68 | Version 11:
    69 |
  • Renamed FWVersion property to FirmwareVersion. 70 |
  • Renamed HWVersion property to HardwareVersion. 71 |
  • J1587 filtering caters for a 0 value where appropriate. 72 |
73 | 74 | Version 12:
    75 |
  • Supports Adapter Firmwares 3.4+. 76 |
  • Added User Name and Password authentication. 77 |
  • Added updating adapter data (led brightness) while offline to the Demo App (Main). 78 |
79 | 80 | Version 13:
    81 |
  • Added Key State to API Demo (Key On/Off). 82 |
  • Renamed source folder and apk from "bluefire.apidemo" to "com.bluefire.apidemo". 83 |
84 | 85 | Version 14:
    86 |
  • Minor improvements to the Bluetooth Discovery process. 87 |
  • Changed SetMaxConnectRetrys default to 10 retries. 88 |
  • Changed SetDiscoveryTimeOut default to 30 seconds. 89 |
90 | 91 | Version 15:
    92 |
  • Supports Adapter Firmware 3.7. 93 |
  • GetVehicleData, GetFuelData, and GetEngineHours retrieves data more accurately (firmware 3.6 and lower work, but better performance with firmware 3.7). 94 |
95 | 96 | Version 16:
    97 |
  • When J1939 and J1708 data are retrieved, J1939 data will take precedence over J1708 data. 98 |
99 | 100 | Version 17:
    101 |
  • Supports Adapter Firmware 3.8+. 102 |
  • Added Get/Set PerformanceMode that will improve the retrieval of PGNs that have an interval of one second. 103 |
  • Minor improvement to the retrieval of vehicle data (vin, make, model, serial no). 104 |
105 | 106 | Version 18:
    107 |
  • App heartbeat will be sent to the adapter every second. 108 |
109 | 110 | Version 19:
    111 |
  • Supports the BLE (Bluetooth Low Energy) adapters (see limitations below). 112 |
  • The API will automatically find and select the correct adapter (BLE or BT21 (Bluetooth Classic)). 113 |
  • Added properties UseBLE, UseBT21 that will improve connection speed if set. 114 |
  • Added properties IsUsingBLE, IsUsingBT21 that will be set according to the type of Bluetooth connected to. 115 |
  • Added property MinInterval that defaults to 500 ms for BLE adapters. 116 |
  • Supports the new 500K CAN adapter (green Deutsch connector). 117 |
  • Removed the Settings class from the Demo App. 118 |
  • No longer compatible with Adapter firmware 2.x. 119 |
  • Requires Android 4+ for Bluetooth Classic adapter and 5+ for BLE adapter. 120 |
  • Supports the Android Studio IDE. 121 |
122 | 123 | Version 19.1:
    124 |
  • Patch for discovering an adapter using Bluetooth Classic and Android 6.0+. 125 |
126 | 127 | Version 20.0:
    128 |
  • Renamed the Adapter class to the BlueFire class to avoid confusion with the Android Adapter widget. 129 |
  • Added an Adapter BT2.1 and a BLE checkbox that will select the appropriate Adapter type. Leaving both unchecked will cause the API to auto select the Adapter type. 130 |
  • Added GetTransmissionGears method that will retrieve the current and selected gears from the transmission ECM if the data is available. 131 |
132 | 133 | Version 20.1:
    134 |
  • Removed IsUsingBT21 and IsUsingBLE properties in lieu of using properties UseBT21 and UseBLE which if not set will be set automatically by the API. 135 |
  • The Demo App's Disconnect button will be shown immediately after connecting to allow for disconnecting while the API is attempting to discover an Adapter. 136 |
137 | 138 | Version 20.2:
    139 |
  • The API will only raise Connection State 'Reconnected' when the Adapter is reconnected. Connection State 'AdapterConnected' will only be raised upon initial connection. 140 |
  • The Demo App has been modified to reflect the above 'Reconnected' Connection State. 141 |
  • Added API method ResetAdapter which will reset the Adapter to factory settings. 142 |
  • The Demo App will edit for invalid hex characters in Send PGN Data. 143 |
  • Fixed API fatal exception in SendPGN when sending data. 144 |
145 | 146 | Version 21.0:
    147 |
  • Added optional Source and MID parameters to the GetVehicleInfo method. 148 |
  • Added optional Source and MID parameters to the GetFaults method. 149 |
  • Removed property IsFaultDataChanged. 150 |
  • Added Truck property IsJ1587Fault. 151 |
  • All methods take Source, PGN, and MID as integers. 152 |
  • Demo App shows Fault source. 153 |
154 | 155 | Version 22.0:
    156 |
  • Added support for Adapter Firmware 3.10 and ELD Recording (see API 22.0 documentation). 157 |
  • Changed and renamed many properties and methods. Refer to the API documentation for all the changes. 158 |
  • Backward compatible with Adapter Firmware 3.7 to 3.9 (no ELD Recording). 159 |
  • No additional functionality except for features in Firmware 3.10 and ELD Recording. 160 |
  • Added Adapter Id security. 161 |
  • Fixed BLE issues however using the BLE Adapter requires Android 6+. 162 |
  • New Demo App that supports ELD Recording. 163 |
  • Demo App request location permissions if connecting to a BLE adapter. 164 |
  • API and Demo App are compiled with minimum Android version of 23. 165 |
  • Demo App no longer references commons-codec-1.10. 166 |
167 | 168 | Version 22.1:
    169 |
  • Changed ConnectionStates, SleepModes, and ELD.RecordIds to be exposed outside of the BlueFire class. 170 |
  • Internal changes. 171 |
172 | 173 | Version 22.2:
    174 |
  • Requires Adapter Firmware Beta 3.10.3 for ELD functionality. 175 |
  • Removed duplicate ELD records. 176 |
  • Better re-connection while ELD recording. 177 |
178 | 179 | Version 22.3:
    180 |
  • Compatible with Adapter Firmware Beta 3.10.5 for ELD functionality. 181 |
  • Better time sync with the Adapter. 182 |
183 | 184 | Version 22.4:
    185 |
  • Requires Adapter Firmware Beta 3.10.5 for ELD functionality. 186 |
  • Added getEngineVIN method. 187 |
  • Added synchronization to Truck Data methods. 188 |
  • Added retrievalMethod parameter to Truck Data methods. 189 |
  • Demo App only retrieves truck data when navigating to the specific data page. 190 |
  • API documentation has been updated to reflect the above changes. 191 |
192 | 193 | Version 22.5:
    194 |
  • Requires Adapter Firmware Beta 3.10.6 for ELD functionality. 195 |
  • Fixed J1708 data retrieval. 196 |
  • Added property AndroidVersion. 197 |
  • Added property SyncTimeout. 198 |
  • Added ConnectionState CANFilterFull. 199 |
  • Renamed property DiscoveryTimeOut to DiscoveryTimeout. 200 |
  • Renamed property MaxConnectRetrys to MaxConnectAttempts. 201 |
  • Added method ELD.SetStreaming. 202 |
  • Added property ELD.LocalRecordNo. 203 |
  • Added ELD Enum RecordingModes. 204 |
  • Added property ELD.RecordingMode and method SetRecordingMode. 205 |
  • Added property ELD.IsRecordingLocally. 206 |
  • Added property ELD.IsRecordingConnected. 207 |
  • Added property ELD.IsRecordingDisconnected. 208 |
  • Included the API documentation in the GitHub repository. 209 |
  • Added to the API documentation Appendix instructions for manually resetting the Adapter. 210 |
211 | 212 | Version 22.6:
    213 |
  • Requires Adapter Firmware Beta 3.10.8 for ELD functionality. 214 |
  • Removed VIN from GetVehicleData method. 215 |
  • Added Boolean return value to Truck Data methods for synchronized calls. 216 |
  • Added synchronized VIN retrieval to Demo App. 217 |
  • ELD rules are sent to the Adapter from the API. 218 |
  • The Adapter will reboot after ELD StopEngine if the API is not connected. 219 |
220 | 221 | Version 22.7:
    222 |
  • Requires Adapter Firmware 3.10 for ELD functionality. 223 |
  • Fixed issue with retrieving VIN and Vehicle Data in the Demo App. 224 |
225 | 226 | Version 22.8:
    227 |
  • Fixed compatability with Firmware 3.9 and below. 228 |
  • API will generate and update the Adapter Serial Number if it is missing. This can occur if the adapter firmware is flashed over the top of an older incompatible firmware. 229 |
230 | 231 | Version 22.9:
    232 |
  • Requires Adapter Firmware 3.11. Note, Firmware 3.10 is broken and should be updated to 3.11. 233 |
  • Compatable with Firmware 3.9 and below. 234 |
  • API method GetEngineVIN automatically sets the sync timeout if RetrievalMethod Synchronized is specified. The default sync timeout is 2 seconds. 235 |
  • The call to VIN retrieval SetSyncTimeout in the Demo App has been commented out to allow the API method to set the default. 236 |
  • Faults have been moved to their own page in the Demo App in order to allow faults to be retrieved by themselves (recommended). 237 |
  • VIN and Truck data (Component Id) retrieval have been improved. It is recommended to retrieve VIN and Truck data before retrieving any other data. 238 |
239 | 240 | Version 22.10:
    241 |
  • The API now disconnects properly from the Adapter while ELD is recording. 242 |
  • The Demo App disconnectAdapter method WaitForDisconnect parameter is set to false for Adapter Firmware 3.11. 243 |
244 | 245 | Version 22.11:
    246 |
  • The SetDiscoveryTimeOut method is renamed to SetDiscoveryTimeout (TimeOut to Timeout). 247 |
  • Added SetAdvertisementTimeout method for use in very crowded BLE areas (like trade shows). 248 |
  • For BLE adapters, if the ConnectToLastAdapter and SetSecurity(SecureAdapter) are not set, the API will connect to the adapter with the strongest signal. 249 |
250 | 251 | Version 22.12:
    252 |
  • Added method GetDistance which is the same as GetOdometer (GetOdometer actually calls GetDistance). 253 |
  • Added properties Truck.HiResDistance, LoResDistance, HiResOdometer, and LoResOdometer. 254 |
  • Truck.Odometer now returns the OEM distance (previously it returned Engine distance). 255 |
  • Truck.Odometer will return -1 if the OEM distance is not available (e.g. Volvo trucks). 256 |
  • Truck.Distance and Truck.Odometer returns the hi-resolution value unless it is not available in which case it returns the lo-resolution value. 257 |
  • Note that hi-resolution distance is at a 1 second ECM refresh rate while lo-resolution is at a 100 ms ECM refresh rate. 258 |
  • Modified the Demo App to reflect the above changes. 259 |
260 | 261 | Version 22.13:
    262 |
  • Added a Service class that emulates using the API with an Android service. 263 |
  • Added a Start and Stop Service buttons to the Demo App. 264 |
265 | 266 | Version 23.0:
    267 |
  • GetVehicleData is renamed to GetEngineId. 268 |
  • Added methods GetVIN and GetComponentId for retrieving non-engine ECM data. 269 |
  • Added synchronized calling to GetEngineVIN, GetVIN, GetEngineId, and GetComponentId. 270 |
  • The SetSyncTimeout method is replaced with the SyncTimeout passed as the Interval parameter along with the Synchronized Retrieval Method parameter. 271 |
  • Fixed the OnChange Retrieval Method that caused issues with the Adapter. 272 |
  • Added a Notification ConnectionState that will return any API notifications. 273 |
  • Added SetHeartbeatOn method that will turn the Adapter heartbeat on/off. Use with caution. 274 |
  • Added SetNotificationsOn method that will turn Adapter notifications on/off. 275 |
  • Change the Engine VIN/Id page in the Demo App to show using synchronous retrieval. 276 |
  • Added a VIN/ComponentId page to the Demo App that shows using asynchronous retrieval. 277 |
  • Added a Test All button to the Demo App that retrieves all the data at once to test loading the connection. 278 |
  • Improved connection reliability with beta Firmware 3.12.x. 279 |
280 | 281 | Version 23.1:
    282 |
  • Added property OptimizeDataRetrieval that optimizes retrieval of data when the same data is available from both J1939 and J1708 ECMs. Recommended. 283 |
  • Added a J1708Restarting ConnectionState that will be raised if J1708 data retrieval is restarting (see Demo App). 284 |
  • Not selecting a connection type (BLE or BT21) will auto connect properly. 285 |
  • The Demo App re-retrieves data when the ConnectionState J1708Restarting is raised. 286 |
  • The Demo App shows Key On/Off properly for J1708 vehicles. 287 |
  • Better J1708 data retrieval with Firmware 3.12. 288 |
  • No longer supports Android 4+ and Android 5+. 289 |
  • Compatible with Android 7.1 devices that use Android 6 BLE firmware. 290 |
291 | 292 | Version 23.2:
    293 |
  • Added retrieval of transmission temperature (Truck.TransTemp) to method GetTemps. 294 |
  • Added retrieval of primary and secondary fuel gauge levels (Truck.PrimaryFuelLevel and SecondaryFuelLevel to GetFuelData. 295 |
  • Changed property OptimizeDataRetrieval to OptimizeDataRetrieval() and SetOptimizeDataRetrieval(boolean value). 296 |
  • Improved OptimizeDataRetrieval. Note, this update is required if using OptimizeDataRetrieval. 297 |
  • The Demo App shows Key Off when disconnected (see checkKeyState and showConnectButton). 298 |
  • Critical patch for retrieving Adapter settings (sleep mode, led brightness, ignore databuses, j1708 availability). 299 |
300 | 301 | Version 23.3:
    302 |
  • Fixed bug in 23.2 that caused GetEngineVIN and GetEngineId to error. 303 |
  • Added methods GetTruckVIN and GetTruckId for retrieving the OEM VIN and Component Id (Make/Model/SerialNo). 304 |
  • Added methods StopRetrievingEngineVIN and StopRetrievingEngineId. Call these after Truck.EngineVIN and/or Truck.EngineMake have been retrieved. 305 |
  • Added methods StopRetrievingTruckVIN and StopRetrievingTruckId. Call these after Truck.VIN and/or Truck.Make have been retrieved. 306 |
  • Removed methods GetVIN and GetComponentId. 307 |
  • Specifying OnChange Retrieval Method now works. The previous release changed it internally to OnInterval. 308 |
  • Added AdapterMessage ConnectionState that will be raised when there is a message from the Adapter. 309 |
  • Further improvements to J1708 data retrieval and connection stability. 310 |
  • Improved API and Adapter error reporting. 311 |
  • Updated the Demo App and Service to demonstrate the above changes. 312 |
313 | 314 | Version 23.4:
    315 |
  • Critical patch to fix BLE connection issues. 316 |
  • Fixed J1939 ELD VIN characters being truncated from a 17 character VIN. 317 |
  • Better re-connection when the adapter reboots. 318 |
  • Added property HardwareType with values HardwareTypes.HW_1_1 (old adapter), HW_6_Pin, and HW_9_Pin. 319 |
  • Renamed PerformanceMode to IsPerformanceModeOn. 320 |
  • Renamed SetPerformanceMode to SetPerformanceModeOn. 321 |
  • Added PerformanceInterval and SetPerformanceInterval. 322 |
  • Removed the ELD Waiting RecordId as it is no longer sent by the adapter. 323 |
  • Added getTruckInfoThread to Demo App. 324 |
  • Disconnecting the adapter in the Demo App no longer stops ELD recording. 325 |
326 | 327 | Version 23.5:
    328 |
  • The default interval for GetEngineVIN and GetTruckVIN is changed from 2 seconds to 3 seconds. 329 |
  • The default interval for GetEngineId and GetTruckId is changed from 2 seconds to 5 seconds. 330 |
  • ELD.Date returns the correct UTC date. 331 |
  • Renamed method ELD.Time to ELD.Date. 332 |
  • Added ELD.LocalDate that returns the local date of the device the API is running on. 333 |
  • Added ELD local date to the Demo App. 334 |
335 | 336 | Version 23.6:
    337 |
  • Added ELD.StartUpload and ELD.StopUpload methods that must be called prior to and after uploading ELD records. 338 |
  • Slightly faster connection if using SetConnectToLastAdapter especially in crowded Bluetooth areas. 339 |
  • Added ConnectionState 'Heartbeat' that will be raised when a heartbeat is received from the adapter. 340 |
  • ConnectionState 'DataChanged' is no longer raised when a heartbeat is received from the adapter. It is now only raised when actual data is received from the adapter. 341 |
  • The Demo App sets WaitForDisconnect prior to disconnecting. This is highly recommended if re-connection is possible immediately after disconnecting. 342 |
  • ConnectionState 'Connected' has been removed from the Demo App. This was confusing as it only appled to the Bluetooth connection and not the adapter connection. 343 |
  • The Demo App shows the heartbeat count when the ConnectionState 'Heartbeat' is raised. 344 |
  • In the Demo App, showStatus{} is moved from the beginning to the end of the event handler. 345 |
  • Added ConnectionState 'Heartbeat' to the Demo App's event handler. 346 |
  • Enabled 'LED Brightness' and 'Connect to Last Adapter' in the Demo App so that it can be changed prior to connecting to the adapter. 347 |
  • The ELD Upload and Delete buttons are enabled in the Demo App anytime there are ELD records no matter if ELD recording is occurring or not. 348 |
349 | 350 | Version 23.7:
    351 |
  • Added Security setting Secure Device which secures the device (phone, tablet, etc) to an adapter. One device can be secured to many adapters (one to many relationship). 352 |
  • Security setting Secure Adapter remains unchanged and secures the device to a single adapter and secures the adapter to the one device (one to one relationship).) 353 |
  • Security setting UserName and Password secures the device to an adapter. A device can be secured to many adapters and many adapters can be secured to a device (many to many relationship). 354 |
  • Security (UserName, Password, Adapter Id, Device Id) are all encrypted with AES encryption. 355 |
  • Requires Adapter Firmware 3.14 for all security updates. 356 |
  • Fixed Bluetooth Classic (BT21) reconnection issues. Please see the documentation for important information about Bluetooth Classic reconnection. 357 |
  • Fixed Bluetooth Classic 'Connect to Last Adapter' not working. 358 |
  • The API will automatically reconnect when the IgnoreJ1939 or Ignore1708 are changed. 359 |
  • With Firmware 3.14, ELD uploading with only 'Record while Disconnected' set will not perform any connected recording while uploading. 360 |
  • Compatible with Adapter Firmware 3.13 and below. For Firmware 3.11 - 3.13 only Secure Adapter is available. For Firmware 3.9 and below, only UserName and Password security is available. 361 |
  • Updated the Demo App to reflect the above changes. 362 |
363 | 364 | Version 23.8:
    365 |
  • Critical patch for retrieving data after a reconnection. 366 |
  • MaxConnectAttempts now works for BLE adapters. 367 |
  • The default MaxConnectAttempts is changed from 10 to 5; 368 |
  • Added SetBluetoothRecycleAttempt method that will recycle (turn off/on) Bluetooth at the specified connection and reconnection attempt. The default is 2 (second attempt). 369 |
  • Updated the Demo App to reflect the above changes. 370 |
  • Updated the documentation to reflect the above changes. 371 |
372 | 373 | Version 23.9:
    374 |
  • Added method SetIgnoreDatabuses that will update the IgnoreJ1939 and IgnoreJ1708 settings and send them to the adapter. 375 |
  • Added ConnectionState ELDConnected that will be raised after the API receives ELD startup data from the adapter. 376 |
  • The ConnectionState Authenticated will now be raised after the API receives startup data from the adapter. This data includes PerformanceMode, SleepMode, LEDBrightness, IgnoreJ1939, IgnoreJ1708, HardwareType and any messages. 377 |
  • The UseBLE, UseBT21, IgnoreJ1939, and IgnoreJ1708 settings will be set appropriately if the HardwareType is HW_6_Pin. 378 |
  • Added CheckKeyState to showHeartbeat in the Demo app to ensure that the key state is checked if the ECMs are powered down. 379 |
  • Updated the Demo App to reflect the above changes. 380 |
  • Updated the documentation to reflect the above changes. 381 |
382 | 383 | Version 23.10:
    384 |
  • Improved adapter connection and reconnection. The last, secured, or previous adapter id will be used to attempt a direct connection to the adapter prior to scanning for an adapter. 385 |
  • Added default values MinIntervalDefault, DiscoveryTimeoutDefault, MaxConnectAttemptsDefault, MaxReconnectAttemptsDefault, BluetoothRecycleAttemptDefault, and AdvertisementTimeoutDefault. 386 |
  • Added API connection notifications. 387 |
  • The SetNotificationsOn method will start/stop API notifications along with Adapter notifications. 388 |
  • The Demo app saves settings when they are changed. 389 |
390 | 391 | Version 23.11:
    392 |
  • Added BleDisconnectWaitTime property and SetBleDisconnectWaitTime method that will wait for BLE to close the GATT connection. The default is 2000 (2 seconds). 393 |
  • Bluetooth is no longer turned off when the the API disconnects from the adapter if Bluetooth was turned on to connect the first time. 394 |
  • Added properties ConnectToLastAdapter, MaxConnectAttempts, MaxReconnectAttempts, BluetoothRecycleAttempt. 395 |
  • Updated the Demo App to reflect the above changes. 396 |
397 | 398 | Version 23.12:
    399 |
  • Added method GetELDData that will only retrieve RPM, Speed, Distance/Odometer, and Total Hours with optimum settings. See the Demo app getTruckData, group 6. 400 |
  • Added method GetKeyState that will double check if the ECM is powered up and sending data. See the Demo app function checkKeyState for how to use it properly. 401 |
  • Added ConnectionState J1939Started that will be raised when the API is connected and the J1939 CAN bus is available (key is turned on). See the Demo app function setJ1939Starting. 402 |
  • Added property CANBusSpeed that will be returned when the ConnectionState J1939Started is raised. 403 |
  • Updated the Demo App to reflect the above changes. 404 |
405 | 406 | Version 23.13:
    407 |
  • More reliable event handling. 408 |
  • Faster BLE re-connection and elimination of the BluetoothGattCallback.onConnectionStateChange exception. 409 |
  • The UseBLE, UseBT21 will not be changed if the HardwareType is HW_6_Pin. This is to allow for BT21 and BLE 6-pin adapters. The IgnoreJ1939, and IgnoreJ1708 settings will still be set appropriately. 410 |
  • ConnectionState J1939Starting will be raised only after connecting to the adapter, either when the key is turned on or immediately after authentication if the key is already on. 411 |
  • Added option for the Demo App to kill itself on exiting (see onBackPressed). 412 |
413 | 414 | Version 23.14:
    415 |
  • GetELDData settings are changed to RPM and Speed - OnInterval, one second; Distance/Odometer - OnChange, 5 seconds; Total Hours - OnChange, 10 seconds. 416 |
  • Invalid J1587 MIDs are ignored. 417 |
  • Internal exception handling. 418 |
419 | 420 | Version 24.0:
    421 |
  • Requires Adapter Firmware 3.15 unless otherwise noted. 422 |
  • Added OBD2 support. 423 |
  • Added property IsOBD2 which will be set when CAN is starting (CANStarting, see below). 424 |
  • Added property IgnoreOBD2 which when set to false (connecting to OBD2) will set IgnoreJ1939 and IgnoreJ1708 true. 425 |
  • Property IgnoreOBD2 accepts the OBD2.CANSettings parameter. Warning! Changing this from the default may cause ECM faults. 426 |
  • Renamed ConnectionState J1939Starting to CANStarting to reflect CAN Starting for either J1939 or OBD2. 427 |
  • Removed property SetIgnoreDataBuses because the properties IgnoreJ1939/J1708/OBD2 are required for the Adapter to connect to the correct ECUs. 428 |
  • Removed method UpdateSecurity because security parameters must be set prior to connecting to the Adapter. 429 |
  • Added property SetDisconnectedReboot that will instruct the Adapter to reboot at a set interval when not connected to the App (Firmware 3.12+). Note with Firmware 3.12+ the interval is fixed at one hour. With Firmware 3.15+ the interval is set with the property. 430 |
  • Added properties IsKeyOn and IsKeyOff that will check for key on/off and if off will set RPM, Speed, PctLoad, PctTorque, DrvPctTorque to 0. 431 |
  • Added property SendAllPackets that will instruct the Adapter to send all J1939 VIN, Make, Model, etc data packets at one time instead of in a conversational manner. This also applies to the ELD VIN. 432 |
  • The Adapter will wait to initiate a CAN connection until all Adapter data has been retrieved by the API. The previous API version initiated the CAN connection when the Adapter is authenticated. 433 |
  • The ConnectionState AdapterMessage will always return a complete message. 434 |
  • Compatible with Firmware 3.7. 435 |
  • The Demo App will show the API Beta version. 436 |
  • Updated the Demo App to reflect the above changes. 437 |
438 | 439 | Version 24.1:
    440 |
  • Added Property Force500kBus that will instruct the Adapter to connect only to a 500K CAN bus. 441 |
  • Fixed SleepMode not being initialized by the App or Service. 442 |
  • Internal fixes to potential issues. 443 |
  • Fixed Starting Service in the Demo App. 444 |
445 | 446 | Version 24.2:
    447 |
  • Properties IgnoreJ1708 and IgnoreOBD2 default to true (ignore both J1708 and OBD2) and IgnoreJ1939 defaults to false (retrieve J1939 data); If you want to retrieve J1708 or OBD2 data you must explicitly set them to false. 448 |
  • The API will not allow retrieving J1939/J1708 data along with OBD2 data (see below). 449 |
  • Setting IgnoreJ1939 or IgnoreOBD2 to false (retrieve J1939/J1708 data) will set IgnoreOBD2 true (ignore OBD2). 450 |
  • Setting IgnoreOBD2 to false (retrieve OBD2 data) will set IgnoreJ1939 and IgnoreJ1708 true (ignore them both). 451 |
  • Setting both UseBLE and UseBT21 on will cause the API to auto discover the Adapter (same as if they are both set off). 452 |
  • If UseBLE/UseBT21 are changed, or both set, or both not set, the API will reset the Previous Adapter Id, Discovery Timeout, and Advertisement Timeout. 453 |
  • Updated the Demo App to reflect the above changes. 454 |
455 | 456 | Version 24.3:
    457 |
  • Renamed the MonitorPGN method to StartMonitoringPGN. 458 |
  • Added multi-packet (BAM/RTS) PGN retrieval to the StartMonitoringPGN, StopMonitoringPGN, and RequestPGN methods. 459 |
  • SendPGN method parameters have changed to (Source, PGN, Priority, PGNData). 460 |
  • Updated the Demo App to reflect the above changes. 461 |
  • Updated the Demo App to show how to monitor multiple PGNs. 462 |
463 | 464 | Version 24.4:
    465 |
  • Fixed retrieving data from a 6-pin adapter. 466 |
467 | 468 | Version 24.5:
    469 |
  • Fixed backward compatibility with 3.7 Firmware on a 2.1 (Bluetooth Classic) adapter. 470 |
471 | 472 | Version 25.0:
    473 |
  • Changed event handling to be faster, more accurate and more reliable. 474 |
      475 |
    • Uses queues to ensure messages are received. 476 |
    • Events are received as soon as they are raised by the API. 477 |
    • Connection State and any Messages (if appropriate) are returned with the event. 478 |
    • Duplicate events no longer occur. 479 |
    • Imperative that events are allowed to be handled without interruption. 480 |
    • API will attempt to deliver an event for 2 seconds before ignoring it. 481 |
    • See the Demo App for correct implementation. 482 |
    483 |
  • Added ConnectionState AdapterReboot. 484 |
  • Removed ConnectionState AdapterConnected. 485 |
  • Renamed ConnectionState Authenticated to Connected. 486 |
  • Renamed ConnectionState CommTimeout to DataTimeout. 487 |
  • Renamed ConnectionState ConnectTimeout to BluetoothTimeout. 488 |
  • Added property ConnectAttempt. 489 |
  • Added property ReconnectAttempt. 490 |
  • Renamed property Message to ConnectionMessage. 491 |
  • Removed property ReconnectReason. 492 |
  • Removed property NotificationMessage. 493 |
  • Removed property NotificationLocation. 494 |
  • Removed method ClearMessages. 495 |
  • Removed method ClearNotificationMessage. 496 |
  • Fixed setting Notifications On/Off. 497 |
  • Fixed setting UserName without a Password. 498 |
  • Fixed updating security to the Adapter. 499 |
  • Fixed AdapterTimeout when connecting to a factory reset Adapter. 500 |
  • Added ignoring the Retarder and Exhaust ECMs due to failing to adhere to the J1939 standard and causing undue network traffic. 501 |
  • Updated the Demo App to reflect the above changes. 502 |
503 | 504 | Version 25.1:
    505 |
  • Added method SetKeepJ1708PortOpen that will cause the Adapter to keep the J1708 Com port open. This can be used for trucks that do not adhere to the J1708 spec and raise a fault when the Adapter is connected. 506 |
507 | 508 | Version 25.2:
    509 |
  • Added Interval to Send/Monitoring. 510 |
  • Added Send button to Send/Monitoring. Note, sending PGN data will continue based on the interval until either stopped, navigated to Truck/ELD data, or disconnected. 511 |
512 | 513 | Version 25.3:
    514 |
  • Fixed retrieving J1708 (6-pin) Distance and Odometer when they are different. Note, this can occur when the engine or cab/body/instrument ECMs are changed and the distance values are not synced between the two. 515 |
516 | 517 | Version 25.4:
    518 |
  • API is compiled with Target SDK 26 and Min SDK 22. Previous version was Target SDK 25 and Min SDK 23. 519 |
  • The Demo App checks for Min SDK 23 and Location Permission granted by the user. 520 |
521 | 522 | Version 25.5:
    523 |
  • API is compiled with Target SDK 28 and Min SDK 22. 524 |
525 | 526 | Version 26.0:
    527 |
  • Removed Eclipse support. 528 |
  • Renamed "Truck" to "Vehicle". 529 |
  • The ResetAdapter method now resets the adapter correctly. See the documentation for the changes. 530 |
  • Open sourced Bluetooth communication code Comm, CommBLE, CommBT2. Open source Comm modules use Pascal notation. 531 |
  • Added regions to Main, Comm, CommBLE, and CommBT2. 532 |
  • Updated documentation to v26.0 (pdf). 533 |
  • API is compiled with Target SDK 29 and Min SDK 23 (Android 5-10). 534 |
535 | 536 | Version 27.0:
    537 |
  • Android version 6 is not longer suppported. 538 |
  • API is compiled with Min SDK 24 and Target SDK 31 (Android 7-12). 539 |
-------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.1.2' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | google() 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## For more details on how to configure your build environment visit 2 | # http://www.gradle.org/docs/current/userguide/build_environment.html 3 | # 4 | # Specifies the JVM arguments used for the daemon process. 5 | # The setting is particularly useful for tweaking memory settings. 6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 7 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 8 | # 9 | # When configured, Gradle will run in incubating parallel mode. 10 | # This option should only be used with decoupled projects. More details, visit 11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 12 | # org.gradle.parallel=true 13 | #Sat Feb 26 15:34:17 PST 2022 14 | org.gradle.jvmargs=-Xmx1536m 15 | android.useAndroidX=true 16 | android.enableJetifier=true 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueFire-LLC/BlueFire-API-for-Android-Studio/62d5696102f6bcefa3ec0cd9b0b1f965f81ab739/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Feb 26 14:56:39 PST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Wed Dec 11 14:33:54 PST 2019 8 | sdk.dir=C\:\\Program Files (x86)\\Android\\android-sdk 9 | -------------------------------------------------------------------------------- /output.json: -------------------------------------------------------------------------------- 1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"25.5","enabled":true,"outputFile":"APIDemo-debug.apk","fullName":"debug","baseName":"debug"},"path":"APIDemo-debug.apk","properties":{}}] -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':APIDemo' 2 | --------------------------------------------------------------------------------