├── .github └── FUNDING.yml ├── .gitignore ├── BluetoothClassicLibrary ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── github │ └── douglasjunior │ └── bluetoothclassiclibrary │ ├── BluetoothClassicService.java │ ├── BluetoothConfiguration.java │ ├── BluetoothDeviceDecorator.java │ ├── BluetoothDeviceDecoratorNull.java │ ├── BluetoothService.java │ ├── BluetoothStatus.java │ └── BluetoothWriter.java ├── BluetoothLowEnergyLibrary ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── github │ └── douglasjunior │ └── bluetoothlowenergylibrary │ └── BluetoothLeService.java ├── LICENSE ├── README.md ├── Sample ├── build.gradle ├── libs │ ├── print_lib_0.13.jar │ └── zxing.jar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── github │ │ └── douglasjunior │ │ └── bluetoothsample │ │ ├── BitmapActivity.java │ │ ├── BitmapHelper.java │ │ ├── DeviceActivity.java │ │ ├── DeviceItemAdapter.java │ │ ├── EscPosHelper.java │ │ ├── MainActivity.java │ │ └── SampleApplication.java │ └── res │ ├── drawable │ ├── bmw.jpg │ ├── jac.JPG │ ├── loading.jpg │ └── palio.jpg │ ├── layout │ ├── activity_bitmap.xml │ ├── activity_device.xml │ ├── activity_main.xml │ └── device_item.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-v21 │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── SampleKotlin ├── .gitignore ├── build.gradle ├── libs │ ├── print_lib_0.13.jar │ └── zxing.jar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── github │ │ └── douglasjunior │ │ └── bluetoothsamplekotlin │ │ ├── BitmapActivity.java │ │ ├── BitmapHelper.java │ │ ├── DeviceActivity.java │ │ ├── DeviceItemAdapter.java │ │ ├── EscPosHelper.java │ │ ├── MainActivity.kt │ │ └── SampleApplication.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── bmw.jpg │ ├── ic_launcher_background.xml │ ├── jac.JPG │ ├── loading.jpg │ └── palio.jpg │ ├── layout │ ├── activity_bitmap.xml │ ├── activity_device.xml │ ├── activity_main.xml │ └── device_item.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: douglasjunior 4 | patreon: douglasjunior # Replace with a single Patreon username 5 | custom: paypal.me/douglasnassif 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | **/build 7 | /captures 8 | -------------------------------------------------------------------------------- /BluetoothClassicLibrary/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '25.0.0' 6 | defaultConfig { 7 | minSdkVersion 7 8 | targetSdkVersion 7 9 | versionCode 11 10 | versionName "0.3.5" 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | compileOptions { 19 | sourceCompatibility JavaVersion.VERSION_1_7 20 | targetCompatibility JavaVersion.VERSION_1_7 21 | } 22 | productFlavors { 23 | } 24 | } 25 | 26 | dependencies { 27 | compile 'com.android.support:support-annotations:25.0.0' 28 | } 29 | -------------------------------------------------------------------------------- /BluetoothClassicLibrary/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:/ferramentas/adt-bundle-windows-x86_64-20140702/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 | -------------------------------------------------------------------------------- /BluetoothClassicLibrary/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BluetoothClassicLibrary/src/main/java/com/github/douglasjunior/bluetoothclassiclibrary/BluetoothClassicService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | /* 26 | * Copyright (C) 2009 The Android Open Source Project 27 | * 28 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 29 | * the License. You may obtain a copy of the License at 30 | * 31 | * http://www.apache.org/licenses/LICENSE-2.0 32 | * 33 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 34 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 35 | * specific language governing permissions and limitations under the License. 36 | */ 37 | 38 | package com.github.douglasjunior.bluetoothclassiclibrary; 39 | 40 | import android.Manifest; 41 | import android.bluetooth.BluetoothAdapter; 42 | import android.bluetooth.BluetoothDevice; 43 | import android.bluetooth.BluetoothSocket; 44 | import android.content.BroadcastReceiver; 45 | import android.content.Context; 46 | import android.content.Intent; 47 | import android.content.IntentFilter; 48 | import android.media.AudioManager; 49 | import android.support.annotation.RequiresPermission; 50 | import android.util.Log; 51 | 52 | import java.io.InputStream; 53 | import java.io.OutputStream; 54 | 55 | /** 56 | * Code adapted from Android Open Source Project 57 | */ 58 | public class BluetoothClassicService extends BluetoothService { 59 | 60 | private static final String TAG = BluetoothClassicService.class.getSimpleName(); 61 | 62 | // Unique UUID for this application 63 | //private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); 64 | 65 | // Member fields 66 | private final BluetoothAdapter mAdapter; 67 | private ConnectThread mConnectThread; 68 | private ConnectedThread mConnectedThread; 69 | 70 | protected BluetoothClassicService(BluetoothConfiguration config) { 71 | super(config); 72 | mAdapter = BluetoothAdapter.getDefaultAdapter(); 73 | mStatus = BluetoothStatus.NONE; 74 | } 75 | 76 | 77 | /** 78 | * Start the ConnectThread to initiate a connection to a remote device. 79 | * 80 | * @param device The BluetoothDevice to connect 81 | */ 82 | @RequiresPermission(Manifest.permission.BLUETOOTH) 83 | public synchronized void connect(BluetoothDevice device) { 84 | if (D) 85 | Log.d(TAG, "connect to: " + device); 86 | 87 | disconnect(); 88 | 89 | // Start the thread to connect with the given device 90 | mConnectThread = new ConnectThread(device); 91 | mConnectThread.start(); 92 | updateState(BluetoothStatus.CONNECTING); 93 | } 94 | 95 | @Override 96 | public void disconnect() { 97 | if (D) 98 | Log.d(TAG, "disconnect"); 99 | 100 | // Cancel any thread attempting to make a connection 101 | if (mConnectThread != null) { 102 | mConnectThread.cancel(); 103 | mConnectThread = null; 104 | } 105 | 106 | // Cancel any thread currently running a connection 107 | if (mConnectedThread != null) { 108 | mConnectedThread.cancel(); 109 | mConnectedThread = null; 110 | } 111 | } 112 | 113 | /** 114 | * Start the ConnectedThread to begin managing a Bluetooth connection 115 | * 116 | * @param socket The BluetoothSocket on which the connection was made 117 | * @param device The BluetoothDevice that has been connected 118 | */ 119 | @RequiresPermission(Manifest.permission.BLUETOOTH) 120 | private synchronized void connected(BluetoothSocket socket, final BluetoothDevice device) { 121 | if (D) 122 | Log.d(TAG, "connected"); 123 | 124 | // Start the thread to manage the connection and perform transmissions 125 | mConnectedThread = new ConnectedThread(socket); 126 | mConnectedThread.start(); 127 | 128 | // Send the name of the connected device back to the UI Activity 129 | if (onEventCallback != null) 130 | runOnMainThread(new Runnable() { 131 | 132 | @RequiresPermission(Manifest.permission.BLUETOOTH) 133 | @Override 134 | public void run() { 135 | onEventCallback.onDeviceName(device.getName()); 136 | } 137 | 138 | }); 139 | 140 | updateState(BluetoothStatus.CONNECTED); 141 | } 142 | 143 | /** 144 | * Stop all threads 145 | */ 146 | public synchronized void stopService() { 147 | if (D) 148 | Log.d(TAG, "stop"); 149 | 150 | disconnect(); 151 | 152 | if (BluetoothService.mDefaultServiceInstance == this) 153 | BluetoothService.mDefaultServiceInstance = null; 154 | } 155 | 156 | /** 157 | * Required by BluetoothLeService.class and not supported in BluetoothClassicService.class. 158 | */ 159 | @Override 160 | public void requestConnectionPriority(int connectionPriority) { 161 | throw new UnsupportedOperationException("requestConnectionPriority is a feature in Bluetooth Low Energy " + 162 | "and not supported in BluetoothClassic"); 163 | } 164 | 165 | /** 166 | * Write to the ConnectedThread in an unsynchronized manner 167 | * 168 | * @param out The bytes to write 169 | * @see 170 | */ 171 | public synchronized void write(byte[] out) { 172 | // Create temporary object 173 | ConnectedThread r; 174 | // Synchronize a copy of the ConnectedThread 175 | 176 | if (mStatus != BluetoothStatus.CONNECTED) 177 | return; 178 | 179 | r = mConnectedThread; 180 | 181 | // Perform the write unsynchronized 182 | r.write(out); 183 | } 184 | 185 | /** 186 | * Indicate that the connection attempt failed and notify the UI Activity. 187 | */ 188 | private void connectionFailed() { 189 | updateState(BluetoothStatus.NONE); 190 | 191 | // Send a failure message back to the Activity 192 | if (onEventCallback != null) 193 | runOnMainThread(new Runnable() { 194 | @Override 195 | public void run() { 196 | onEventCallback.onToast("Could not connect to device"); 197 | } 198 | }); 199 | } 200 | 201 | /** 202 | * Indicate that the connection was lost and notify the UI Activity. 203 | */ 204 | private void connectionLost() { 205 | updateState(BluetoothStatus.NONE); 206 | 207 | // Send a failure message back to the Activity 208 | 209 | if (onEventCallback != null) 210 | runOnMainThread(new Runnable() { 211 | @Override 212 | public void run() { 213 | onEventCallback.onToast("Connection lost"); 214 | } 215 | }); 216 | } 217 | 218 | /** 219 | * This thread runs while attempting to make an outgoing connection with a device. It runs straight through; the 220 | * connection either succeeds or fails. 221 | */ 222 | private class ConnectThread extends Thread { 223 | private final BluetoothSocket mmSocket; 224 | private final BluetoothDevice mmDevice; 225 | 226 | @RequiresPermission(Manifest.permission.BLUETOOTH) 227 | public ConnectThread(BluetoothDevice device) { 228 | mmDevice = device; 229 | BluetoothSocket tmp = null; 230 | 231 | // Get a BluetoothSocket for a connection with the given BluetoothDevice 232 | try { 233 | tmp = device.createRfcommSocketToServiceRecord(mConfig.uuid); 234 | } catch (Exception e) { 235 | Log.e(TAG, "create() failed", e); 236 | } 237 | try { 238 | AudioManager mAudioManager = (AudioManager) mConfig.context.getSystemService(Context.AUDIO_SERVICE); 239 | //For phone speaker(loadspeaker) 240 | mAudioManager.setMode(AudioManager.MODE_NORMAL); 241 | mAudioManager.setBluetoothScoOn(false); 242 | mAudioManager.setSpeakerphoneOn(true); 243 | } catch (Exception ex) { 244 | ex.printStackTrace(); 245 | } 246 | mmSocket = tmp; 247 | } 248 | 249 | @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN}) 250 | public void run() { 251 | Log.i(TAG, "BEGIN mConnectThread"); 252 | setName("ConnectThread"); 253 | 254 | // Always cancel discovery because it will slow down a connection 255 | if (mAdapter.isDiscovering()) 256 | mAdapter.cancelDiscovery(); 257 | 258 | // Make a connection to the BluetoothSocket 259 | try { 260 | // This is a blocking call and will only return on a 261 | // successful connection or an exception 262 | mmSocket.connect(); 263 | } catch (Exception e) { 264 | e.printStackTrace(); 265 | connectionFailed(); 266 | // Close the socket 267 | try { 268 | mmSocket.close(); 269 | } catch (Exception e2) { 270 | Log.e(TAG, "unable to close() socket during connection failure", e2); 271 | } 272 | return; 273 | } 274 | 275 | // Reset the ConnectThread because we're done 276 | synchronized (BluetoothClassicService.this) { 277 | mConnectThread = null; 278 | } 279 | 280 | // Start the connected thread 281 | connected(mmSocket, mmDevice); 282 | } 283 | 284 | public void cancel() { 285 | try { 286 | mmSocket.close(); 287 | } catch (Exception e) { 288 | Log.e(TAG, "close() of connect socket failed", e); 289 | } 290 | try { 291 | interrupt(); 292 | } catch (Exception e) { 293 | Log.e(TAG, "interrupt() of Thread failed", e); 294 | } 295 | } 296 | } 297 | 298 | /** 299 | * This thread runs during a connection with a remote device. It handles all incoming and outgoing transmissions. 300 | */ 301 | private class ConnectedThread extends Thread { 302 | private final BluetoothSocket mmSocket; 303 | private final InputStream mmInStream; 304 | private final OutputStream mmOutStream; 305 | private boolean canceled = false; 306 | 307 | public ConnectedThread(BluetoothSocket socket) { 308 | Log.d(TAG, "create ConnectedThread"); 309 | mmSocket = socket; 310 | InputStream tmpIn = null; 311 | OutputStream tmpOut = null; 312 | 313 | // Get the BluetoothSocket input and output streams 314 | try { 315 | tmpIn = socket.getInputStream(); 316 | tmpOut = socket.getOutputStream(); 317 | } catch (Exception e) { 318 | Log.e(TAG, "temp sockets not created", e); 319 | } 320 | 321 | mmInStream = tmpIn; 322 | mmOutStream = tmpOut; 323 | } 324 | 325 | public void run() { 326 | Log.i(TAG, "BEGIN mConnectedThread"); 327 | byte temp; 328 | final byte[] buffer = new byte[mConfig.bufferSize]; 329 | int i = 0; 330 | byte byteDelimiter = (byte) mConfig.characterDelimiter; 331 | // Keep listening to the InputStream while connected 332 | while (!canceled) { 333 | try { 334 | // Read from the InputStream 335 | int read = mmInStream.read(); 336 | temp = (byte) read; 337 | 338 | if (temp == byteDelimiter) { 339 | if (i > 0) { 340 | dispatchBuffer(buffer, i); 341 | i = 0; 342 | } 343 | continue; 344 | } 345 | if (i == buffer.length - 1) { 346 | dispatchBuffer(buffer, i); 347 | i = 0; 348 | } 349 | buffer[i] = temp; 350 | i++; 351 | //System.out.println("read: " + new String(buffer, 0 , i)); 352 | } catch (Exception e) { 353 | Log.e(TAG, "disconnected", e); 354 | connectionLost(); 355 | break; 356 | } 357 | } 358 | 359 | } 360 | 361 | private void dispatchBuffer(byte[] buffer, int i) { 362 | final byte[] data = new byte[i]; 363 | System.arraycopy(buffer, 0, data, 0, i); 364 | if (onEventCallback != null) { 365 | runOnMainThread(new Runnable() { 366 | @Override 367 | public void run() { 368 | onEventCallback.onDataRead(data, data.length); 369 | } 370 | }); 371 | } 372 | } 373 | 374 | /** 375 | * Write to the connected OutStream. 376 | * 377 | * @param buffer The bytes to write 378 | */ 379 | public void write(final byte[] buffer) { 380 | try { 381 | mmOutStream.write(buffer); 382 | mmOutStream.flush(); 383 | 384 | if (onEventCallback != null) 385 | runOnMainThread(new Runnable() { 386 | @Override 387 | public void run() { 388 | onEventCallback.onDataWrite(buffer); 389 | } 390 | }); 391 | } catch (Exception e) { 392 | Log.e(TAG, "Exception during write", e); 393 | } 394 | } 395 | 396 | public void cancel() { 397 | canceled = true; 398 | try { 399 | mmSocket.close(); 400 | } catch (Exception e) { 401 | Log.e(TAG, "close() of connect socket failed", e); 402 | } 403 | try { 404 | mmInStream.close(); 405 | } catch (Exception e) { 406 | Log.e(TAG, "close() of connect socket failed", e); 407 | } 408 | try { 409 | interrupt(); 410 | } catch (Exception e) { 411 | Log.e(TAG, "interrupt() of Thread failed", e); 412 | } 413 | } 414 | } 415 | 416 | // The BroadcastReceiver that listens for discovered devices and 417 | // changes the title when discovery is finished 418 | private final BroadcastReceiver mScanReceiver = new BroadcastReceiver() { 419 | @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH}) 420 | @Override 421 | public void onReceive(Context context, Intent intent) { 422 | String action = intent.getAction(); 423 | 424 | // When discovery finds a device 425 | if (BluetoothDevice.ACTION_FOUND.equals(action)) { 426 | // Get the BluetoothDevice object from the Intent 427 | final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 428 | // If it's already paired, skip it, because it's been listed 429 | // already 430 | final int RSSI = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); 431 | 432 | if (onScanCallback != null) 433 | runOnMainThread(new Runnable() { 434 | @Override 435 | public void run() { 436 | onScanCallback.onDeviceDiscovered(device, RSSI); 437 | } 438 | }); 439 | } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { 440 | stopScan(); 441 | } 442 | } 443 | }; 444 | 445 | @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH}) 446 | @Override 447 | public void startScan() { 448 | if (onScanCallback != null) 449 | runOnMainThread(new Runnable() { 450 | @Override 451 | public void run() { 452 | onScanCallback.onStartScan(); 453 | } 454 | }); 455 | 456 | // Register for broadcasts when a device is discovered 457 | IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 458 | mConfig.context.registerReceiver(mScanReceiver, filter); 459 | 460 | // Register for broadcasts when discovery has finished 461 | filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 462 | mConfig.context.registerReceiver(mScanReceiver, filter); 463 | 464 | if (mAdapter.isDiscovering()) { 465 | mAdapter.cancelDiscovery(); 466 | } 467 | 468 | mAdapter.startDiscovery(); 469 | } 470 | 471 | @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH}) 472 | @Override 473 | public void stopScan() { 474 | try { 475 | mConfig.context.unregisterReceiver(mScanReceiver); 476 | } catch (IllegalArgumentException ex) { 477 | ex.printStackTrace(); 478 | } 479 | 480 | if (mAdapter.isDiscovering()) { 481 | mAdapter.cancelDiscovery(); 482 | } 483 | 484 | if (onScanCallback != null) 485 | runOnMainThread(new Runnable() { 486 | @Override 487 | public void run() { 488 | onScanCallback.onStopScan(); 489 | } 490 | }); 491 | } 492 | 493 | } 494 | -------------------------------------------------------------------------------- /BluetoothClassicLibrary/src/main/java/com/github/douglasjunior/bluetoothclassiclibrary/BluetoothConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothclassiclibrary; 26 | 27 | import android.bluetooth.BluetoothDevice; 28 | import android.content.Context; 29 | import android.os.Build; 30 | import android.util.Log; 31 | import android.bluetooth.BluetoothGatt; 32 | 33 | import java.util.UUID; 34 | 35 | /** 36 | * Created by dougl on 10/04/2017. 37 | */ 38 | public class BluetoothConfiguration { 39 | 40 | private static final String TAG = BluetoothConfiguration.class.getSimpleName(); 41 | 42 | /** 43 | * Class reference for the {@link BluetoothService} implementation. 44 | * 45 | * @see BluetoothClassicService 46 | * @see BluetoothLeService 47 | */ 48 | public Class bluetoothServiceClass; 49 | 50 | /** 51 | * {@link android.app.Application} context reference. 52 | */ 53 | public Context context; 54 | 55 | /** 56 | * Name of your application or device. 57 | */ 58 | public String deviceName; 59 | 60 | /** 61 | * Maximum of bytes to keep in the buffer before call the 62 | * {@link com.github.douglasjunior.bluetoothclassiclibrary.BluetoothService.OnBluetoothEventCallback#onDataRead(byte[], int)} 63 | */ 64 | public int bufferSize; 65 | 66 | /** 67 | * Character delimiter to know if a data is received completly and call the 68 | * {@link com.github.douglasjunior.bluetoothclassiclibrary.BluetoothService.OnBluetoothEventCallback#onDataRead(byte[], int)} 69 | */ 70 | public char characterDelimiter; 71 | 72 | /** 73 | * Required in {@link BluetoothClassicService}, is the UUID of the device that will connect in serial mode.
74 | * Optional in {@link BluetoothLeService}, is the UUID of the device that will be filtered in scan. 75 | * Set {@link null} if you want to scan all devices. 76 | */ 77 | public UUID uuid; 78 | 79 | /** 80 | * Required for {@link BluetoothLeService}
81 | * UUID of bluetooth service. 82 | */ 83 | public UUID uuidService; 84 | 85 | /** 86 | * Required for {@link BluetoothLeService}
87 | * UUID of bluetooth characteristic. 88 | */ 89 | public UUID uuidCharacteristic; 90 | 91 | /** 92 | * Preferred transport for GATT connections to remote dual-mode devices 93 | * {@link BluetoothDevice#TRANSPORT_AUTO} or 94 | * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} 95 | */ 96 | public int transport; 97 | 98 | /** 99 | * Whether to call the listener only in Main Thread (true) 100 | * or call in the Thread where the event occurs (false). 101 | */ 102 | public boolean callListenersInMainThread = true; 103 | 104 | /** 105 | * Required for {@link BluetoothLeService}
106 | * Request a specific connection priority. Must be one of 107 | * {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link BluetoothGatt#CONNECTION_PRIORITY_HIGH} 108 | * or {@link BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}. 109 | * 110 | * An application should only request high priority connection parameters to transfer 111 | * large amounts of data over LE quickly. Once the transfer is complete, the application 112 | * should request {@link BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters 113 | * to reduce energy use. 114 | */ 115 | public int connectionPriority; 116 | 117 | public BluetoothConfiguration() { 118 | setDefaultTransport(); 119 | } 120 | 121 | /** 122 | * Set the default value for {@link BluetoothConfiguration#transport}. 123 | */ 124 | private void setDefaultTransport() { 125 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 126 | transport = BluetoothDevice.TRANSPORT_LE; 127 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 128 | // From Android LOLLIPOP (21) the transport types exists, but them are hide for use, 129 | // so is needed to use relfection to get the value 130 | try { 131 | transport = BluetoothDevice.class.getDeclaredField("TRANSPORT_LE").getInt(null); 132 | } catch (Exception ex) { 133 | Log.d(TAG, "Error on get BluetoothDevice.TRANSPORT_LE with reflection.", ex); 134 | } 135 | } else { 136 | transport = -1; 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /BluetoothClassicLibrary/src/main/java/com/github/douglasjunior/bluetoothclassiclibrary/BluetoothDeviceDecorator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothclassiclibrary; 26 | 27 | import android.Manifest; 28 | import android.bluetooth.BluetoothDevice; 29 | import android.support.annotation.RequiresPermission; 30 | 31 | /** 32 | * Created by Douglas on 16/09/2014. 33 | */ 34 | public class BluetoothDeviceDecorator { 35 | 36 | private BluetoothDevice mDevice; 37 | private int mRSSI; 38 | 39 | public BluetoothDeviceDecorator(BluetoothDevice device) { 40 | mDevice = device; 41 | } 42 | 43 | public BluetoothDeviceDecorator(BluetoothDevice device, int RSSI) { 44 | this(device); 45 | mRSSI = RSSI; 46 | } 47 | 48 | @RequiresPermission(Manifest.permission.BLUETOOTH) 49 | public String getName() { 50 | return mDevice.getName() != null && mDevice.getName().length() != 0 ? mDevice.getName() : "Desconhecido..."; 51 | } 52 | 53 | public String getAddress() { 54 | return mDevice.getAddress(); 55 | } 56 | 57 | public int getRSSI() { 58 | return mRSSI; 59 | } 60 | 61 | public void setRSSI(int RSSI) { 62 | mRSSI = RSSI; 63 | } 64 | 65 | @Override 66 | public boolean equals(Object o) { 67 | if (this == o) return true; 68 | if (o == null || getClass() != o.getClass()) return false; 69 | 70 | BluetoothDeviceDecorator that = (BluetoothDeviceDecorator) o; 71 | 72 | return mDevice.equals(that.mDevice); 73 | } 74 | 75 | @Override 76 | public int hashCode() { 77 | return mDevice.hashCode(); 78 | } 79 | 80 | public void setDevice(BluetoothDevice device) { 81 | this.mDevice = device; 82 | } 83 | 84 | public BluetoothDevice getDevice() { 85 | return mDevice; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /BluetoothClassicLibrary/src/main/java/com/github/douglasjunior/bluetoothclassiclibrary/BluetoothDeviceDecoratorNull.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothclassiclibrary; 26 | 27 | import android.bluetooth.BluetoothDevice; 28 | 29 | /** 30 | * Created by Douglas on 16/09/2014. 31 | */ 32 | public class BluetoothDeviceDecoratorNull extends BluetoothDeviceDecorator { 33 | 34 | private BluetoothDeviceDecoratorNull(BluetoothDevice device) { 35 | super(device); 36 | } 37 | 38 | public static BluetoothDeviceDecoratorNull getInstance() { 39 | return new BluetoothDeviceDecoratorNull(null); 40 | } 41 | 42 | @Override 43 | public String getName() { 44 | return "No device found"; 45 | } 46 | 47 | @Override 48 | public String getAddress() { 49 | return ""; 50 | } 51 | 52 | @Override 53 | public int getRSSI() { 54 | return 9999; 55 | } 56 | 57 | @Override 58 | public void setRSSI(int RSSI) { 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /BluetoothClassicLibrary/src/main/java/com/github/douglasjunior/bluetoothclassiclibrary/BluetoothService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothclassiclibrary; 26 | 27 | import android.bluetooth.BluetoothDevice; 28 | import android.os.Handler; 29 | import android.util.Log; 30 | 31 | import java.lang.reflect.Constructor; 32 | import java.lang.reflect.InvocationTargetException; 33 | 34 | /** 35 | * Created by douglas on 23/03/15. 36 | */ 37 | public abstract class BluetoothService { 38 | // Debugging 39 | private static final String TAG = BluetoothService.class.getSimpleName(); 40 | protected static final boolean D = true; 41 | 42 | protected static BluetoothService mDefaultServiceInstance; 43 | protected BluetoothConfiguration mConfig; 44 | protected BluetoothStatus mStatus; 45 | 46 | private final Handler handler; 47 | 48 | protected OnBluetoothEventCallback onEventCallback; 49 | 50 | protected OnBluetoothScanCallback onScanCallback; 51 | 52 | private static BluetoothConfiguration mDefaultConfiguration; 53 | 54 | protected BluetoothService(BluetoothConfiguration config) { 55 | this.mConfig = config; 56 | this.mStatus = BluetoothStatus.NONE; 57 | this.handler = new Handler(); 58 | } 59 | 60 | public void setOnEventCallback(OnBluetoothEventCallback onEventCallback) { 61 | this.onEventCallback = onEventCallback; 62 | } 63 | 64 | public void setOnScanCallback(OnBluetoothScanCallback onScanCallback) { 65 | this.onScanCallback = onScanCallback; 66 | } 67 | 68 | public BluetoothConfiguration getConfiguration() { 69 | return mConfig; 70 | } 71 | 72 | protected synchronized void updateState(final BluetoothStatus status) { 73 | Log.v(TAG, "updateStatus() " + mStatus + " -> " + status); 74 | mStatus = status; 75 | 76 | // Give the new state to the Handler so the UI Activity can update 77 | if (onEventCallback != null) 78 | runOnMainThread(new Runnable() { 79 | @Override 80 | public void run() { 81 | onEventCallback.onStatusChange(status); 82 | } 83 | }); 84 | } 85 | 86 | protected void runOnMainThread(final Runnable runnable, final long delayMillis) { 87 | if (mConfig.callListenersInMainThread) { 88 | if (delayMillis > 0) { 89 | handler.postDelayed(runnable, delayMillis); 90 | } else { 91 | handler.post(runnable); 92 | } 93 | } else { 94 | if (delayMillis > 0) { 95 | new Thread() { 96 | @Override 97 | public void run() { 98 | try { 99 | Thread.sleep(delayMillis); 100 | runnable.run(); 101 | } catch (InterruptedException e) { 102 | e.printStackTrace(); 103 | } 104 | } 105 | }.start(); 106 | } else { 107 | runnable.run(); 108 | } 109 | } 110 | } 111 | 112 | protected void runOnMainThread(Runnable runnable) { 113 | runOnMainThread(runnable, 0); 114 | } 115 | 116 | protected void removeRunnableFromHandler(Runnable runnable) { 117 | if (handler != null) { 118 | handler.removeCallbacks(runnable); 119 | } 120 | } 121 | 122 | /** 123 | * Current BluetoothService status. 124 | * 125 | * @return 126 | */ 127 | public synchronized BluetoothStatus getStatus() { 128 | return mStatus; 129 | } 130 | 131 | /** 132 | * Start scan process and call the {@link OnBluetoothScanCallback} 133 | */ 134 | public abstract void startScan(); 135 | 136 | /** 137 | * Stop scan process and call the {@link OnBluetoothScanCallback} 138 | */ 139 | public abstract void stopScan(); 140 | 141 | /** 142 | * Try to connect to the device and call the {@link OnBluetoothEventCallback} 143 | */ 144 | public abstract void connect(BluetoothDevice device); 145 | 146 | /** 147 | * Try to disconnect to the device and call the {@link OnBluetoothEventCallback} 148 | */ 149 | public abstract void disconnect(); 150 | 151 | /** 152 | * Write a array of bytes to the connected device. 153 | */ 154 | public abstract void write(byte[] bytes); 155 | 156 | /** 157 | * Stops the BluetoothService and turn it unusable. 158 | */ 159 | public abstract void stopService(); 160 | 161 | /** 162 | * Request the connection priority. 163 | */ 164 | public abstract void requestConnectionPriority(int connectionPriority); 165 | 166 | /* ==================================== 167 | STATICS METHODS 168 | ====================================== */ 169 | 170 | /** 171 | * Use {@link BluetoothService#init(BluetoothConfiguration)} instead. 172 | * 173 | * @param config 174 | */ 175 | @Deprecated 176 | public static void setDefaultConfiguration(BluetoothConfiguration config) { 177 | init(config); 178 | } 179 | 180 | /** 181 | * Configures and initialize the BluetoothService singleton instance. 182 | * 183 | * @param config 184 | */ 185 | public static void init(BluetoothConfiguration config) { 186 | mDefaultConfiguration = config; 187 | if (mDefaultServiceInstance != null) { 188 | mDefaultServiceInstance.stopService(); 189 | mDefaultServiceInstance = null; 190 | } 191 | try { 192 | Constructor constructor = 193 | (Constructor) mDefaultConfiguration.bluetoothServiceClass.getDeclaredConstructors()[0]; 194 | constructor.setAccessible(true); 195 | BluetoothService bluetoothService = constructor.newInstance(mDefaultConfiguration); 196 | mDefaultServiceInstance = bluetoothService; 197 | } catch (InvocationTargetException e) { 198 | throw new RuntimeException(e); 199 | } catch (InstantiationException e) { 200 | throw new RuntimeException(e); 201 | } catch (IllegalAccessException e) { 202 | throw new RuntimeException(e); 203 | } 204 | } 205 | 206 | /** 207 | * Get the BluetoothService singleton instance. 208 | * 209 | * @return 210 | */ 211 | public synchronized static BluetoothService getDefaultInstance() { 212 | if (mDefaultServiceInstance == null) { 213 | throw new IllegalStateException("BluetoothService is not initialized. Call BluetoothService.init(config)."); 214 | } 215 | return mDefaultServiceInstance; 216 | } 217 | 218 | /* ==================================== 219 | CALLBACKS 220 | ====================================== */ 221 | 222 | public interface OnBluetoothEventCallback { 223 | void onDataRead(byte[] buffer, int length); 224 | 225 | void onStatusChange(BluetoothStatus status); 226 | 227 | void onDeviceName(String deviceName); 228 | 229 | void onToast(String message); 230 | 231 | void onDataWrite(byte[] buffer); 232 | } 233 | 234 | public interface OnBluetoothScanCallback { 235 | void onDeviceDiscovered(BluetoothDevice device, int rssi); 236 | 237 | void onStartScan(); 238 | 239 | void onStopScan(); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /BluetoothClassicLibrary/src/main/java/com/github/douglasjunior/bluetoothclassiclibrary/BluetoothStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothclassiclibrary; 26 | 27 | /** 28 | * Created by douglas on 25/11/14. 29 | */ 30 | public enum BluetoothStatus { 31 | CONNECTED, 32 | CONNECTING, 33 | NONE 34 | } 35 | -------------------------------------------------------------------------------- /BluetoothClassicLibrary/src/main/java/com/github/douglasjunior/bluetoothclassiclibrary/BluetoothWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothclassiclibrary; 26 | 27 | public class BluetoothWriter { 28 | private final BluetoothService service; 29 | 30 | public BluetoothWriter(BluetoothService service) { 31 | this.service = service; 32 | } 33 | 34 | public void write(String msg) { 35 | if (service != null) 36 | service.write(msg.getBytes()); 37 | } 38 | 39 | public void write(Integer number) { 40 | write(number.toString()); 41 | } 42 | 43 | public void writeln(String msg) { 44 | write(msg + service.getConfiguration().characterDelimiter); 45 | } 46 | 47 | public void write(Character c){ 48 | write(c.toString()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /BluetoothLowEnergyLibrary/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '25.0.0' 6 | defaultConfig { 7 | minSdkVersion 18 8 | targetSdkVersion 18 9 | versionCode 11 10 | versionName "0.3.5" 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled false 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 16 | } 17 | } 18 | compileOptions { 19 | sourceCompatibility JavaVersion.VERSION_1_7 20 | targetCompatibility JavaVersion.VERSION_1_7 21 | } 22 | productFlavors { 23 | } 24 | } 25 | 26 | dependencies { 27 | compile project(':BluetoothClassicLibrary') 28 | compile 'com.android.support:support-annotations:25.0.0' 29 | } 30 | -------------------------------------------------------------------------------- /BluetoothLowEnergyLibrary/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:/ferramentas/adt-bundle-windows-x86_64-20140702/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 | -------------------------------------------------------------------------------- /BluetoothLowEnergyLibrary/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BluetoothLowEnergyLibrary/src/main/java/com/github/douglasjunior/bluetoothlowenergylibrary/BluetoothLeService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothlowenergylibrary; 26 | 27 | import android.Manifest; 28 | import android.bluetooth.BluetoothAdapter; 29 | import android.bluetooth.BluetoothDevice; 30 | import android.bluetooth.BluetoothGatt; 31 | import android.bluetooth.BluetoothGattCallback; 32 | import android.bluetooth.BluetoothGattCharacteristic; 33 | import android.bluetooth.BluetoothGattDescriptor; 34 | import android.bluetooth.BluetoothGattService; 35 | import android.bluetooth.BluetoothManager; 36 | import android.bluetooth.BluetoothProfile; 37 | import android.content.Context; 38 | import android.os.Build; 39 | import android.support.annotation.RequiresApi; 40 | import android.support.annotation.RequiresPermission; 41 | import android.util.Log; 42 | 43 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothConfiguration; 44 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothService; 45 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothStatus; 46 | 47 | import java.lang.reflect.Method; 48 | import java.nio.ByteBuffer; 49 | import java.nio.ByteOrder; 50 | import java.util.ArrayList; 51 | import java.util.Arrays; 52 | import java.util.List; 53 | import java.util.UUID; 54 | 55 | 56 | /** 57 | * Created by douglas on 16/03/15. 58 | */ 59 | public class BluetoothLeService extends BluetoothService { 60 | 61 | private static final String TAG = BluetoothLeService.class.getSimpleName(); 62 | 63 | private static final long SCAN_PERIOD = 10000; 64 | 65 | private final BluetoothAdapter btAdapter; 66 | private BluetoothGatt bluetoothGatt; 67 | private BluetoothGattCharacteristic characteristicRxTx; 68 | 69 | private final byte[] readBuffer; 70 | private int readBufferIndex = 0; 71 | private byte[][] writeBuffer; 72 | private int writeBufferIndex = 0; 73 | 74 | private int maxTransferBytes = 20; 75 | 76 | protected BluetoothLeService(BluetoothConfiguration config) { 77 | super(config); 78 | BluetoothManager btManager = (BluetoothManager) config.context.getSystemService(Context.BLUETOOTH_SERVICE); 79 | btAdapter = btManager.getAdapter(); 80 | readBuffer = new byte[config.bufferSize]; 81 | } 82 | 83 | private final BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() { 84 | 85 | @Override 86 | public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { 87 | super.onMtuChanged(gatt, mtu, status); 88 | Log.v(TAG, "onMtuChanged: " + mtu + " status: " + status); 89 | // Receive the requested MTU size. 90 | // See also https://stackoverflow.com/questions/24135682/android-sending-data-20-bytes-by-ble 91 | if (status == BluetoothGatt.GATT_SUCCESS) { 92 | // It discounts 3 bytes of metadata. 93 | maxTransferBytes = mtu - 3; 94 | } 95 | } 96 | 97 | @Override 98 | public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { 99 | final byte[] data = characteristic.getValue(); 100 | Log.v(TAG, "onCharacteristicChanged: " + new String(characteristic.getValue())); 101 | readData(data); 102 | super.onCharacteristicChanged(gatt, characteristic); 103 | } 104 | 105 | @Override 106 | public void onCharacteristicRead(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, int status) { 107 | super.onCharacteristicRead(gatt, characteristic, status); 108 | final byte[] data = characteristic.getValue(); 109 | Log.v(TAG, "onCharacteristicRead: " + new String(data)); 110 | if (BluetoothGatt.GATT_SUCCESS == status) { 111 | readData(data); 112 | } else { 113 | System.err.println("onCharacteristicRead error " + status); 114 | } 115 | } 116 | 117 | @Override 118 | public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { 119 | super.onCharacteristicWrite(gatt, characteristic, status); 120 | final byte[] data = characteristic.getValue(); 121 | //Log.v(TAG, "onCharacteristicWrite status: " + status + " data: " + new String(data)); 122 | Log.v(TAG, "onCharacteristicWrite status: " + status + " data: " + data.length); 123 | if (BluetoothGatt.GATT_SUCCESS == status || status == 11) { 124 | if (onEventCallback != null) 125 | runOnMainThread(new Runnable() { 126 | @Override 127 | public void run() { 128 | onEventCallback.onDataWrite(data); 129 | } 130 | }); 131 | writeCharacteristic(); 132 | } else { 133 | System.err.println("onCharacteristicWrite error " + status); 134 | } 135 | 136 | } 137 | 138 | @RequiresPermission(Manifest.permission.BLUETOOTH) 139 | @Override 140 | public void onConnectionStateChange(final BluetoothGatt gatt, int status, final int newState) { 141 | super.onConnectionStateChange(gatt, status, newState); 142 | Log.v(TAG, "onConnectionStateChange: status: " + status + " newState: " + newState); 143 | if (status != BluetoothGatt.GATT_SUCCESS || newState == BluetoothProfile.STATE_DISCONNECTED) { 144 | gatt.close(); 145 | if (mStatus == BluetoothStatus.NONE || mStatus == BluetoothStatus.CONNECTING) 146 | makeToast("Unable to connect to device"); 147 | else if (mStatus == BluetoothStatus.CONNECTED) 148 | makeToast("Connection lost"); 149 | updateState(BluetoothStatus.NONE); 150 | } else { 151 | if (newState == BluetoothProfile.STATE_CONNECTED) { 152 | //createBound(gatt.getDevice()); 153 | gatt.discoverServices(); 154 | } else if (newState == BluetoothProfile.STATE_CONNECTING) { 155 | updateState(BluetoothStatus.CONNECTING); 156 | } 157 | } 158 | } 159 | 160 | @Override 161 | public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { 162 | Log.v(TAG, "onDescriptorRead"); 163 | super.onDescriptorRead(gatt, descriptor, status); 164 | } 165 | 166 | @Override 167 | public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { 168 | Log.v(TAG, "onDescriptorWrite"); 169 | super.onDescriptorWrite(gatt, descriptor, status); 170 | } 171 | 172 | @Override 173 | public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { 174 | Log.v(TAG, "onReadRemoteRssi"); 175 | super.onReadRemoteRssi(gatt, rssi, status); 176 | } 177 | 178 | @Override 179 | public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { 180 | Log.v(TAG, "onReliableWriteCompleted"); 181 | super.onReliableWriteCompleted(gatt, status); 182 | } 183 | 184 | @RequiresPermission(Manifest.permission.BLUETOOTH) 185 | @Override 186 | public void onServicesDiscovered(BluetoothGatt gatt, int status) { 187 | super.onServicesDiscovered(gatt, status); 188 | Log.v(TAG, "onServicesDiscovered: " + status); 189 | 190 | if (BluetoothGatt.GATT_SUCCESS == status) { 191 | for (BluetoothGattService service : gatt.getServices()) { 192 | Log.v(TAG, "Service: " + service.getUuid()); 193 | if (mConfig.uuidService == null || service.getUuid().equals(mConfig.uuidService)) { 194 | for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) { 195 | final int props = characteristic.getProperties(); 196 | Log.v(TAG, "Characteristic: " + characteristic.getUuid() + 197 | " PROPERTY_WRITE: " + (props & BluetoothGattCharacteristic.PROPERTY_WRITE) + 198 | " PROPERTY_WRITE_NO_RESPONSE: " + (props & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)); 199 | if (characteristic.getUuid().equals(mConfig.uuidCharacteristic)) { 200 | characteristicRxTx = characteristic; 201 | gatt.setCharacteristicNotification(characteristic, true); 202 | 203 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 204 | // Request the MTU size to device. 205 | // See also https://stackoverflow.com/questions/24135682/android-sending-data-20-bytes-by-ble 206 | boolean requestMtu = bluetoothGatt.requestMtu(512); 207 | Log.v(TAG, "requestMtu: " + requestMtu); 208 | 209 | // Request a specific connection priority. 210 | // CONNECTION_PRIORITY_BALANCED is the default value if no connection parameter update is requested 211 | if (mConfig.connectionPriority != BluetoothGatt.CONNECTION_PRIORITY_BALANCED) 212 | requestConnectionPriority(mConfig.connectionPriority); 213 | } 214 | 215 | updateDeviceName(gatt.getDevice()); 216 | updateState(BluetoothStatus.CONNECTED); 217 | return; 218 | } 219 | } 220 | } 221 | } 222 | Log.e(TAG, "Could not find uuidService:" + mConfig.uuidService + " and uuidCharacteristic:" + mConfig.uuidCharacteristic); 223 | } else { 224 | Log.e(TAG, "onServicesDiscovered error " + status); 225 | } 226 | 227 | // If arrived here, no service or characteristic has been found. 228 | gatt.disconnect(); 229 | } 230 | }; 231 | 232 | @RequiresPermission(Manifest.permission.BLUETOOTH) 233 | private void updateDeviceName(final BluetoothDevice device) { 234 | if (onEventCallback != null) 235 | runOnMainThread(new Runnable() { 236 | @RequiresPermission(Manifest.permission.BLUETOOTH) 237 | @Override 238 | public void run() { 239 | BluetoothDevice dev = device; 240 | if (dev.getName() == null) 241 | dev = btAdapter.getRemoteDevice(dev.getAddress()); 242 | onEventCallback.onDeviceName(dev.getName()); 243 | } 244 | }); 245 | } 246 | 247 | // private void createBound(BluetoothDevice device) { 248 | // try { 249 | // if (device.getBondState() != BluetoothDevice.BOND_BONDED) { 250 | // Method method = device.getClass().getMethod("createBond", (Class[]) null); 251 | // method.invoke(device, (Object[]) null); 252 | // } 253 | // } catch (Exception e) { 254 | // e.printStackTrace(); 255 | // } 256 | // } 257 | 258 | private void readData(byte[] data) { 259 | final byte byteDelimiter = (byte) mConfig.characterDelimiter; 260 | for (byte temp : data) { 261 | 262 | if (temp == byteDelimiter) { 263 | if (readBufferIndex > 0) { 264 | dispatchBuffer(readBuffer, readBufferIndex); 265 | readBufferIndex = 0; 266 | } 267 | continue; 268 | } 269 | if (readBufferIndex == readBuffer.length - 1) { 270 | dispatchBuffer(readBuffer, readBufferIndex); 271 | readBufferIndex = 0; 272 | } 273 | readBuffer[readBufferIndex] = temp; 274 | readBufferIndex++; 275 | 276 | } 277 | } 278 | 279 | private void dispatchBuffer(byte[] buffer, int i) { 280 | final byte[] data = new byte[i]; 281 | System.arraycopy(buffer, 0, data, 0, i); 282 | if (onEventCallback != null) { 283 | runOnMainThread(new Runnable() { 284 | @Override 285 | public void run() { 286 | onEventCallback.onDataRead(data, data.length); 287 | } 288 | }); 289 | } 290 | } 291 | 292 | private void makeToast(final String message) { 293 | if (onEventCallback != null) 294 | runOnMainThread(new Runnable() { 295 | @Override 296 | public void run() { 297 | onEventCallback.onToast(message); 298 | } 299 | }); 300 | } 301 | 302 | @RequiresPermission(Manifest.permission.BLUETOOTH) 303 | public void connect(BluetoothDevice bluetoothDevice) { 304 | if (btAdapter != null && btAdapter.isEnabled()) { 305 | if (bluetoothGatt != null) { 306 | bluetoothGatt.disconnect(); 307 | } 308 | 309 | updateState(BluetoothStatus.CONNECTING); 310 | 311 | /* 312 | About this issue: 313 | https://code.google.com/p/android/issues/detail?id=92949 314 | http://stackoverflow.com/q/27633680/2826279 315 | */ 316 | 317 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 318 | // If android verion is greather or equal to Android M (23), then call the connectGatt with TRANSPORT_LE. 319 | bluetoothGatt = bluetoothDevice.connectGatt(mConfig.context, false, btleGattCallback, mConfig.transport); 320 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 321 | // From Android LOLLIPOP (21) the transport types exists, but them are hide for use, 322 | // so is needed to use relfection to get the value 323 | try { 324 | Method connectGattMethod = bluetoothDevice.getClass().getDeclaredMethod("connectGatt", Context.class, boolean.class, BluetoothGattCallback.class, int.class); 325 | connectGattMethod.setAccessible(true); 326 | bluetoothGatt = (BluetoothGatt) connectGattMethod.invoke(bluetoothDevice, mConfig.context, false, btleGattCallback, mConfig.transport); 327 | } catch (Exception ex) { 328 | Log.d(TAG, "Error on call BluetoothDevice.connectGatt with reflection.", ex); 329 | } 330 | } 331 | 332 | // If any try is fail, then call the connectGatt without transport 333 | if (bluetoothGatt == null) { 334 | bluetoothGatt = bluetoothDevice.connectGatt(mConfig.context, false, btleGattCallback); 335 | } 336 | } 337 | } 338 | 339 | @Override 340 | public void disconnect() { 341 | if (bluetoothGatt != null) { 342 | bluetoothGatt.disconnect(); 343 | } 344 | } 345 | 346 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) 347 | final Runnable mStopScanRunnable = new Runnable() { 348 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) 349 | @Override 350 | public void run() { 351 | stopScan(); 352 | } 353 | }; 354 | 355 | 356 | final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { 357 | 358 | @RequiresPermission(Manifest.permission.BLUETOOTH) 359 | @Override 360 | public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) { 361 | List uuids = parseUUIDs(scanRecord); 362 | Log.v(TAG, "onLeScan " + device.getName() + " " + new String(scanRecord) + " -> uuids: " + uuids); 363 | if (onScanCallback != null && (mConfig.uuid == null || uuids.contains(mConfig.uuid))) { 364 | runOnMainThread(new Runnable() { 365 | @Override 366 | public void run() { 367 | onScanCallback.onDeviceDiscovered(device, rssi); 368 | } 369 | }); 370 | } 371 | } 372 | 373 | }; 374 | 375 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) 376 | @Override 377 | public void startScan() { 378 | if (onScanCallback != null) 379 | runOnMainThread(new Runnable() { 380 | @Override 381 | public void run() { 382 | onScanCallback.onStartScan(); 383 | } 384 | }); 385 | 386 | btAdapter.stopLeScan(mLeScanCallback); 387 | 388 | btAdapter.startLeScan(mLeScanCallback); 389 | 390 | runOnMainThread(mStopScanRunnable, SCAN_PERIOD); 391 | } 392 | 393 | private List parseUUIDs(final byte[] advertisedData) { 394 | List uuids = new ArrayList<>(); 395 | 396 | int offset = 0; 397 | while (offset < (advertisedData.length - 2)) { 398 | int len = advertisedData[offset++]; 399 | if (len == 0) 400 | break; 401 | 402 | int type = advertisedData[offset++]; 403 | switch (type) { 404 | case 0x02: // Partial list of 16-bit UUIDs 405 | case 0x03: // Complete list of 16-bit UUIDs 406 | while (len > 1) { 407 | int uuid16 = advertisedData[offset++]; 408 | uuid16 += (advertisedData[offset++] << 8); 409 | len -= 2; 410 | uuids.add(UUID.fromString(String.format("%08x-0000-1000-8000-00805f9b34fb", uuid16))); 411 | } 412 | break; 413 | case 0x06:// Partial list of 128-bit UUIDs 414 | case 0x07:// Complete list of 128-bit UUIDs 415 | // Loop through the advertised 128-bit UUID's. 416 | while (len >= 16) { 417 | try { 418 | // Wrap the advertised bits and order them. 419 | ByteBuffer buffer = ByteBuffer.wrap(advertisedData, 420 | offset++, 16).order(ByteOrder.LITTLE_ENDIAN); 421 | long mostSignificantBit = buffer.getLong(); 422 | long leastSignificantBit = buffer.getLong(); 423 | uuids.add(new UUID(leastSignificantBit, 424 | mostSignificantBit)); 425 | } catch (IndexOutOfBoundsException e) { 426 | // Defensive programming. 427 | Log.e(TAG, e.toString()); 428 | continue; 429 | } finally { 430 | // Move the offset to read the next uuid. 431 | offset += 15; 432 | len -= 16; 433 | } 434 | } 435 | break; 436 | default: 437 | offset += (len - 1); 438 | break; 439 | } 440 | } 441 | 442 | return uuids; 443 | } 444 | 445 | @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) 446 | @Override 447 | public void stopScan() { 448 | removeRunnableFromHandler(mStopScanRunnable); 449 | btAdapter.stopLeScan(mLeScanCallback); 450 | 451 | if (onScanCallback != null) 452 | runOnMainThread(new Runnable() { 453 | @Override 454 | public void run() { 455 | onScanCallback.onStopScan(); 456 | } 457 | }); 458 | } 459 | 460 | public void stopService() { 461 | if (bluetoothGatt != null) { 462 | bluetoothGatt.disconnect(); 463 | bluetoothGatt.close(); 464 | } 465 | bluetoothGatt = null; 466 | } 467 | 468 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) 469 | @Override 470 | public void requestConnectionPriority(int connectionPriority) { 471 | if (bluetoothGatt != null) { 472 | if (connectionPriority >= BluetoothGatt.CONNECTION_PRIORITY_BALANCED 473 | && connectionPriority <= BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER) { 474 | boolean requestConnectionPriority = bluetoothGatt.requestConnectionPriority(connectionPriority); 475 | Log.v(TAG, "requestConnectionPriority("+connectionPriority+"): " + requestConnectionPriority); 476 | } else 477 | Log.e(TAG, "requestConnectionPriority("+connectionPriority+"): ERROR - connectionPriority not within valid range"); 478 | } 479 | } 480 | 481 | /** 482 | * Splits the bytes into packets according to the MTU size of the device, and writes the packets sequentially. 483 | * 484 | * See also https://stackoverflow.com/questions/24135682/android-sending-data-20-bytes-by-ble 485 | * 486 | * @param data 487 | */ 488 | public void write(byte[] data) { 489 | Log.v(TAG, "write: " + data.length); 490 | if (bluetoothGatt != null && characteristicRxTx != null && mStatus == BluetoothStatus.CONNECTED) { 491 | if (data.length <= maxTransferBytes) { 492 | writeBufferIndex = 0; 493 | writeBuffer = new byte[1][data.length]; 494 | writeBuffer[0] = data; 495 | } else { 496 | writeBufferIndex = 0; 497 | int bufferSize = (data.length / maxTransferBytes) + 1; 498 | writeBuffer = new byte[bufferSize][maxTransferBytes]; 499 | 500 | for (int i = 0; i < writeBuffer.length; i++) { 501 | int start = i * maxTransferBytes; 502 | int end = start + maxTransferBytes; 503 | if (start >= data.length) 504 | break; 505 | if (end > data.length) 506 | end = data.length; 507 | writeBuffer[i] = Arrays.copyOfRange(data, start, end); 508 | } 509 | } 510 | writeCharacteristic(); 511 | } 512 | } 513 | 514 | /** 515 | * Writes next packet to the Characteristic. 516 | * 517 | */ 518 | private void writeCharacteristic() { 519 | Log.v(TAG, "writeCharacteristic " + writeBufferIndex); 520 | if (writeBufferIndex >= writeBuffer.length) 521 | return; 522 | 523 | byte[] bytes = writeBuffer[writeBufferIndex]; 524 | 525 | boolean setValue = characteristicRxTx.setValue(bytes); 526 | Log.v(TAG, "setValue: " + setValue); 527 | 528 | boolean writeCharacteristic = bluetoothGatt.writeCharacteristic(characteristicRxTx); 529 | Log.v(TAG, "writeCharacteristic: " + writeCharacteristic); 530 | 531 | writeBufferIndex++; 532 | } 533 | 534 | } 535 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015 Douglas Nassif Roma Junior 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidBluetoothLibrary 2 | 3 | [![Licence MIT](https://img.shields.io/badge/licence-MIT-blue.svg)](https://github.com/douglasjunior/AndroidBluetoothLibrary/blob/master/LICENSE) 4 | [![Release](https://jitpack.io/v/douglasjunior/AndroidBluetoothLibrary.svg)](https://jitpack.io/#douglasjunior/AndroidBluetoothLibrary) 5 | [![Downloads](https://jitpack.io/v/douglasjunior/AndroidBluetoothLibrary/month.svg)](#download) 6 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Android%20Bluetooth%20Library-yellow.svg?style=flat)](http://android-arsenal.com/details/1/5821) 7 | 8 | A Library for easy implementation of Serial Bluetooth Classic and Low Energy on Android. 💙 9 | 10 | - Bluetooth Classic working from Android 2.1 (API 7) 11 | - Bluetooth Low Energy working from Android 4.3 (API 18) 12 | 13 | ## Use 14 | 15 | ### Configuration 16 | 17 | #### Bluetooth Classic 18 | ```java 19 | BluetoothConfiguration config = new BluetoothConfiguration(); 20 | config.context = getApplicationContext(); 21 | config.bluetoothServiceClass = BluetoothClassicService.class; 22 | config.bufferSize = 1024; 23 | config.characterDelimiter = '\n'; 24 | config.deviceName = "Your App Name"; 25 | config.callListenersInMainThread = true; 26 | 27 | config.uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); // Required 28 | 29 | BluetoothService.init(config); 30 | ``` 31 | 32 | #### Bluetooth Low Energy 33 | ```java 34 | BluetoothConfiguration config = new BluetoothConfiguration(); 35 | config.context = getApplicationContext(); 36 | config.bluetoothServiceClass = BluetoothLeService.class; 37 | config.bufferSize = 1024; 38 | config.characterDelimiter = '\n'; 39 | config.deviceName = "Your App Name"; 40 | config.callListenersInMainThread = true; 41 | 42 | config.uuidService = UUID.fromString("e7810a71-73ae-499d-8c15-faa9aef0c3f2"); // Required 43 | config.uuidCharacteristic = UUID.fromString("bef8d6c9-9c21-4c9e-b632-bd58c1009f9f"); // Required 44 | config.transport = BluetoothDevice.TRANSPORT_LE; // Required for dual-mode devices 45 | config.uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); // Used to filter found devices. Set null to find all devices. 46 | 47 | BluetoothService.init(config); 48 | ``` 49 | 50 | ### Getting BluetoothService 51 | 52 | ```java 53 | BluetoothService service = BluetoothService.getDefaultInstance(); 54 | ``` 55 | 56 | ### Scanning 57 | 58 | ```java 59 | service.setOnScanCallback(new BluetoothService.OnBluetoothScanCallback() { 60 | @Override 61 | public void onDeviceDiscovered(BluetoothDevice device, int rssi) { 62 | } 63 | 64 | @Override 65 | public void onStartScan() { 66 | } 67 | 68 | @Override 69 | public void onStopScan() { 70 | } 71 | }); 72 | 73 | service.startScan(); // See also service.stopScan(); 74 | ``` 75 | 76 | ### Connecting 77 | 78 | ```java 79 | service.setOnEventCallback(new BluetoothService.OnBluetoothEventCallback() { 80 | @Override 81 | public void onDataRead(byte[] buffer, int length) { 82 | } 83 | 84 | @Override 85 | public void onStatusChange(BluetoothStatus status) { 86 | } 87 | 88 | @Override 89 | public void onDeviceName(String deviceName) { 90 | } 91 | 92 | @Override 93 | public void onToast(String message) { 94 | } 95 | 96 | @Override 97 | public void onDataWrite(byte[] buffer) { 98 | } 99 | }); 100 | 101 | service.connect(device); // See also service.disconnect(); 102 | ``` 103 | 104 | ### Writing 105 | 106 | ```java 107 | BluetoothWriter writer = new BluetoothWriter(service); 108 | 109 | writer.writeln("Your text here"); 110 | ``` 111 | 112 | ### Complete example 113 | 114 | See the [sample project](https://github.com/douglasjunior/AndroidBluetoothLibrary/tree/master/Sample/src/main/java/com/github/douglasjunior/bluetoothsample). 115 | 116 | ## Download 117 | 118 | 1. Add it in your root build.gradle at the end of repositories: 119 | ```javascript 120 | allprojects { 121 | repositories { 122 | ... 123 | maven { url "https://jitpack.io" } 124 | } 125 | } 126 | ``` 127 | 128 | 2. Add the dependency 129 | 130 | 2.1. Bluetooth Classic 131 | ```javascript 132 | dependencies { 133 | implementation 'com.github.douglasjunior.AndroidBluetoothLibrary:BluetoothClassicLibrary:0.3.5' 134 | } 135 | ``` 136 | 137 | 2.2. Bluetooth Low Energy 138 | ```javascript 139 | dependencies { 140 | implementation 'com.github.douglasjunior.AndroidBluetoothLibrary:BluetoothLowEnergyLibrary:0.3.5' 141 | } 142 | ``` 143 | 144 | 3. Add permission in `AndroidManifest.xml` 145 | 146 | ```xml 147 | 148 | 149 | 150 | 151 | 152 | ... 153 | 154 | ``` 155 | 156 | ## Known Issues / Troubleshooting 157 | 158 | 159 | 160 | - Scanning will not detect bluetooth devices if the user has denied Location Privacy Permission to your app. This library does not test for the permission and will not raise errors. (Android 6.0+) See: http://stackoverflow.com/a/33045489/2826279 161 | 162 | ## Contribute 163 | 164 | New features, bug fixes and improvements are welcome! For questions and suggestions use the [issues](https://github.com/douglasjunior/AndroidBluetoothLibrary/issues). 165 | 166 | Before submit your PR, run the gradle check. 167 | ```bash 168 | ./gradlew build connectedCheck 169 | ``` 170 | 171 | Become a Patron! 172 | [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=E32BUP77SVBA2) 173 | 174 | ## Licence 175 | 176 | ``` 177 | The MIT License (MIT) 178 | 179 | Copyright (c) 2015 Douglas Nassif Roma Junior 180 | ``` 181 | 182 | See the full [licence file](https://github.com/douglasjunior/AndroidBluetoothLibrary/blob/master/LICENSE). 183 | 184 | -------------------------------------------------------------------------------- /Sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion "26.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.github.douglasjunior.bluetoothsample" 9 | minSdkVersion 18 10 | targetSdkVersion 26 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | 22 | } 23 | 24 | dependencies { 25 | compile fileTree(include: ['*.jar'], dir: 'libs') 26 | compile "com.android.support:appcompat-v7:$androidSupportVersion" 27 | compile "com.android.support:cardview-v7:$androidSupportVersion" 28 | compile "com.android.support:design:$androidSupportVersion" 29 | compile project(':BluetoothLowEnergyLibrary') 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Sample/libs/print_lib_0.13.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglasjunior/AndroidBluetoothLibrary/65180766464f3ba702b4825fa9cfef13c4083308/Sample/libs/print_lib_0.13.jar -------------------------------------------------------------------------------- /Sample/libs/zxing.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglasjunior/AndroidBluetoothLibrary/65180766464f3ba702b4825fa9cfef13c4083308/Sample/libs/zxing.jar -------------------------------------------------------------------------------- /Sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/douglas/ferramentas/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 | -------------------------------------------------------------------------------- /Sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Sample/src/main/java/com/github/douglasjunior/bluetoothsample/BitmapActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothsample; 26 | 27 | import android.graphics.Bitmap; 28 | import android.graphics.BitmapFactory; 29 | import android.os.Bundle; 30 | import android.support.design.widget.FloatingActionButton; 31 | import android.support.v7.app.AppCompatActivity; 32 | import android.support.v7.widget.Toolbar; 33 | import android.view.View; 34 | import android.widget.ImageView; 35 | 36 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothService; 37 | import com.wpx.util.WPXUtils; 38 | 39 | /** 40 | * Created by douglas on 26/05/17. 41 | */ 42 | 43 | public class BitmapActivity extends AppCompatActivity implements View.OnClickListener { 44 | 45 | private FloatingActionButton mFab; 46 | private ImageView mImgOriginal; 47 | private ImageView mImgBlackWhite; 48 | 49 | private BluetoothService mService; 50 | 51 | private Bitmap imageBitmap; 52 | 53 | @Override 54 | protected void onCreate(Bundle savedInstanceState) { 55 | super.onCreate(savedInstanceState); 56 | 57 | setContentView(R.layout.activity_bitmap); 58 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 59 | setSupportActionBar(toolbar); 60 | 61 | mFab = (FloatingActionButton) findViewById(R.id.fab); 62 | mFab.setOnClickListener(this); 63 | 64 | mService = BluetoothService.getDefaultInstance(); 65 | 66 | mImgOriginal = (ImageView) findViewById(R.id.img_original); 67 | mImgBlackWhite = (ImageView) findViewById(R.id.img_blackwhite); 68 | 69 | new Thread() { 70 | @Override 71 | public void run() { 72 | final Bitmap original = BitmapFactory.decodeResource(getResources(), R.drawable.bmw); 73 | 74 | final Bitmap resized = Bitmap.createScaledBitmap(original, 255, 255, false); 75 | 76 | final Bitmap editedBrightness = BitmapHelper.changeBitmapContrastBrightness(resized, 1, 50); 77 | 78 | resized.recycle(); 79 | 80 | imageBitmap = editedBrightness; 81 | 82 | final Bitmap editedGray = BitmapHelper.changeBitmapBlackWhite(editedBrightness); 83 | 84 | runOnUiThread(new Runnable() { 85 | @Override 86 | public void run() { 87 | mImgOriginal.setImageBitmap(original); 88 | mImgBlackWhite.setImageBitmap(editedGray); 89 | } 90 | }); 91 | } 92 | }.start(); 93 | } 94 | 95 | @Override 96 | public void onClick(View v) { 97 | new Thread() { 98 | @Override 99 | public void run() { 100 | try { 101 | byte[] bytes = WPXUtils.decodeBitmap(imageBitmap); 102 | mService.write(bytes); 103 | } catch (Exception e) { 104 | e.printStackTrace(); 105 | } 106 | } 107 | }.start(); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /Sample/src/main/java/com/github/douglasjunior/bluetoothsample/BitmapHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothsample; 26 | 27 | import android.graphics.Bitmap; 28 | import android.graphics.Canvas; 29 | import android.graphics.ColorMatrix; 30 | import android.graphics.ColorMatrixColorFilter; 31 | import android.graphics.Paint; 32 | 33 | /** 34 | * Created by douglas on 30/05/17. 35 | */ 36 | public final class BitmapHelper { 37 | 38 | private BitmapHelper(){} 39 | 40 | /** 41 | * From https://stackoverflow.com/a/17887577/2826279 42 | * 43 | * @param bmp input bitmap 44 | * @param contrast 0..10 1 is default 45 | * @param brightness -255..255 0 is default 46 | * @return new bitmap 47 | */ 48 | public static Bitmap changeBitmapContrastBrightness(Bitmap bmp, float contrast, float brightness) { 49 | ColorMatrix cm = new ColorMatrix(new float[] 50 | { 51 | contrast, 0, 0, 0, brightness, 52 | 0, contrast, 0, 0, brightness, 53 | 0, 0, contrast, 0, brightness, 54 | 0, 0, 0, 1, 0 55 | }); 56 | Bitmap ret = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig()); 57 | 58 | Canvas canvas = new Canvas(ret); 59 | 60 | Paint paint = new Paint(); 61 | paint.setColorFilter(new ColorMatrixColorFilter(cm)); 62 | canvas.drawBitmap(bmp, 0, 0, paint); 63 | 64 | return ret; 65 | } 66 | 67 | public static Bitmap changeBitmapBlackWhite(Bitmap bmp) { 68 | ColorMatrix cm = new ColorMatrix(new float[] 69 | { 70 | 1.5f, 1.5f, 1.5f, 0, 0, 71 | 1.5f, 1.5f, 1.5f, 0, 0, 72 | 1.5f, 1.5f, 1.5f, 0, 0, 73 | 0, 0, 0, 1, 0 74 | }); 75 | cm.setSaturation(0); 76 | Bitmap ret = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig()); 77 | 78 | Canvas canvas = new Canvas(ret); 79 | 80 | Paint paint = new Paint(); 81 | paint.setColorFilter(new ColorMatrixColorFilter(cm)); 82 | canvas.drawBitmap(bmp, 0, 0, paint); 83 | 84 | return ret; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /Sample/src/main/java/com/github/douglasjunior/bluetoothsample/DeviceActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothsample; 26 | 27 | import android.os.Bundle; 28 | import android.support.design.widget.FloatingActionButton; 29 | import android.support.v7.app.AppCompatActivity; 30 | import android.support.v7.widget.Toolbar; 31 | import android.util.Log; 32 | import android.view.View; 33 | import android.widget.EditText; 34 | 35 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothService; 36 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothStatus; 37 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothWriter; 38 | 39 | /** 40 | * Created by douglas on 10/04/2017. 41 | */ 42 | 43 | public class DeviceActivity extends AppCompatActivity implements BluetoothService.OnBluetoothEventCallback, View.OnClickListener { 44 | 45 | private static final String TAG = "DeviceActivity"; 46 | 47 | private FloatingActionButton mFab; 48 | private EditText mEdRead; 49 | private EditText mEdWrite; 50 | 51 | private BluetoothService mService; 52 | private BluetoothWriter mWriter; 53 | 54 | @Override 55 | protected void onCreate(Bundle savedInstanceState) { 56 | super.onCreate(savedInstanceState); 57 | setContentView(R.layout.activity_device); 58 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 59 | setSupportActionBar(toolbar); 60 | 61 | mFab = (FloatingActionButton) findViewById(R.id.fab); 62 | mFab.setOnClickListener(this); 63 | 64 | mEdRead = (EditText) findViewById(R.id.ed_read); 65 | mEdWrite = (EditText) findViewById(R.id.ed_write); 66 | 67 | mService = BluetoothService.getDefaultInstance(); 68 | mWriter = new BluetoothWriter(mService); 69 | } 70 | 71 | @Override 72 | protected void onResume() { 73 | super.onResume(); 74 | mService.setOnEventCallback(this); 75 | } 76 | 77 | @Override 78 | public void onDataRead(byte[] buffer, int length) { 79 | Log.d(TAG, "onDataRead: " + new String(buffer, 0, length)); 80 | mEdRead.append("< " + new String(buffer, 0, length) + "\n"); 81 | } 82 | 83 | @Override 84 | protected void onDestroy() { 85 | super.onDestroy(); 86 | mService.disconnect(); 87 | } 88 | 89 | @Override 90 | public void onStatusChange(BluetoothStatus status) { 91 | Log.d(TAG, "onStatusChange: " + status); 92 | } 93 | 94 | @Override 95 | public void onDeviceName(String deviceName) { 96 | Log.d(TAG, "onDeviceName: " + deviceName); 97 | } 98 | 99 | @Override 100 | public void onToast(String message) { 101 | Log.d(TAG, "onToast"); 102 | } 103 | 104 | @Override 105 | public void onDataWrite(byte[] buffer) { 106 | Log.d(TAG, "onDataWrite"); 107 | mEdRead.append("> " + new String(buffer)); 108 | } 109 | 110 | @Override 111 | public void onClick(View v) { 112 | mWriter.writeln(mEdWrite.getText().toString()); 113 | mEdWrite.setText(""); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Sample/src/main/java/com/github/douglasjunior/bluetoothsample/DeviceItemAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothsample; 26 | 27 | import android.bluetooth.BluetoothDevice; 28 | import android.content.Context; 29 | import android.support.v7.widget.RecyclerView; 30 | import android.text.TextUtils; 31 | import android.view.LayoutInflater; 32 | import android.view.View; 33 | import android.view.ViewGroup; 34 | import android.widget.TextView; 35 | 36 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothDeviceDecorator; 37 | 38 | import java.util.ArrayList; 39 | import java.util.Collection; 40 | import java.util.List; 41 | import java.util.Set; 42 | 43 | /** 44 | * Created by douglas on 10/04/2017. 45 | */ 46 | 47 | public class DeviceItemAdapter extends RecyclerView.Adapter { 48 | 49 | private final Context mContext; 50 | private final List mDevices; 51 | private final LayoutInflater mInflater; 52 | private OnAdapterItemClickListener mOnItemClickListener; 53 | 54 | 55 | public DeviceItemAdapter(Context context, List devices) { 56 | super(); 57 | mContext = context; 58 | mDevices = decorateDevices(devices); 59 | mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 60 | } 61 | 62 | public static List decorateDevices(Collection btDevices) { 63 | List devices = new ArrayList<>(); 64 | for (BluetoothDevice dev : btDevices) { 65 | devices.add(new BluetoothDeviceDecorator(dev, 0)); 66 | } 67 | return devices; 68 | } 69 | 70 | public DeviceItemAdapter(Context context, Set devices) { 71 | this(context, new ArrayList<>(devices)); 72 | } 73 | 74 | @Override 75 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 76 | ViewHolder vh = new ViewHolder(mInflater.inflate(R.layout.device_item, parent, false)); 77 | return vh; 78 | } 79 | 80 | @Override 81 | public void onBindViewHolder(ViewHolder holder, final int position) { 82 | final BluetoothDeviceDecorator device = mDevices.get(position); 83 | 84 | holder.tvName.setText(TextUtils.isEmpty(device.getName()) ? "---" : device.getName()); 85 | holder.tvAddress.setText(device.getAddress()); 86 | holder.tvRSSI.setText(device.getRSSI() + ""); 87 | 88 | holder.itemView.setOnClickListener(new View.OnClickListener() { 89 | @Override 90 | public void onClick(View v) { 91 | mOnItemClickListener.onItemClick(device, position); 92 | } 93 | }); 94 | } 95 | 96 | @Override 97 | public int getItemCount() { 98 | return mDevices.size(); 99 | } 100 | 101 | public List getDevices() { 102 | return mDevices; 103 | } 104 | 105 | public void setOnAdapterItemClickListener(OnAdapterItemClickListener onItemClickListener) { 106 | mOnItemClickListener = onItemClickListener; 107 | } 108 | 109 | public interface OnAdapterItemClickListener { 110 | public void onItemClick(BluetoothDeviceDecorator device, int position); 111 | } 112 | 113 | public static class ViewHolder extends RecyclerView.ViewHolder { 114 | 115 | private final TextView tvName; 116 | private final TextView tvAddress; 117 | private final TextView tvRSSI; 118 | 119 | public ViewHolder(View itemView) { 120 | super(itemView); 121 | tvName = (TextView) itemView.findViewById(R.id.tv_name); 122 | tvAddress = (TextView) itemView.findViewById(R.id.tv_address); 123 | tvRSSI = (TextView) itemView.findViewById(R.id.tv_rssi); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Sample/src/main/java/com/github/douglasjunior/bluetoothsample/EscPosHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothsample; 26 | 27 | import android.graphics.Bitmap; 28 | 29 | import java.io.ByteArrayOutputStream; 30 | import java.io.IOException; 31 | 32 | /** 33 | * Code adapted from http://new-grumpy-mentat.blogspot.com.br/2014/06/java-escpos-image-printing.html 34 | */ 35 | public final class EscPosHelper { 36 | 37 | private EscPosHelper() { 38 | } 39 | 40 | private final static char ESC_CHAR = 0x1B; 41 | private final static byte[] FEED_LINE = {10}; 42 | private final static byte[] SELECT_BIT_IMAGE_MODE = {ESC_CHAR, 0x2A, 33}; 43 | private final static byte[] SET_LINE_SPACE_24 = new byte[]{ESC_CHAR, 0x33, 24}; 44 | private final static byte[] SET_LINE_SPACE_30 = new byte[]{ESC_CHAR, 0x33, 30}; 45 | 46 | /** 47 | * Send image to the printer to be printed. 48 | * 49 | * @param image 2D Array of RGB colors (Row major order) 50 | */ 51 | public static byte[] printImage(Bitmap image) throws IOException { 52 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 53 | 54 | baos.write(SET_LINE_SPACE_24); 55 | for (int y = 0; y < image.getHeight(); y += 24) { 56 | baos.write(SELECT_BIT_IMAGE_MODE);// bit mode 57 | baos.write(new byte[]{(byte) (0x00ff & image.getWidth()), (byte) ((0xff00 & image.getWidth()) >> 8)});// width, low & high 58 | for (int x = 0; x < image.getWidth(); x++) { 59 | // For each vertical line/slice must collect 3 bytes (24 bytes) 60 | baos.write(collectSlice(y, x, image)); 61 | } 62 | 63 | baos.write(FEED_LINE); 64 | } 65 | baos.write(SET_LINE_SPACE_30); 66 | 67 | return baos.toByteArray(); 68 | } 69 | 70 | /** 71 | * Defines if a color should be printed (burned). 72 | * 73 | * @param color RGB color. 74 | * @return true if should be printed/burned (black), false otherwise (white). 75 | */ 76 | private static boolean shouldPrintColor(int color) { 77 | final int threshold = 127; 78 | int a, r, g, b, luminance; 79 | a = (color >> 24) & 0xff; 80 | if (a != 0xff) { // ignore pixels with alpha channel 81 | return false; 82 | } 83 | r = (color >> 16) & 0xff; 84 | g = (color >> 8) & 0xff; 85 | b = color & 0xff; 86 | 87 | luminance = (int) (0.299 * r + 0.587 * g + 0.114 * b); 88 | 89 | return luminance < threshold; 90 | } 91 | 92 | /** 93 | * Collect a slice of 3 bytes with 24 dots for image printing. 94 | * 95 | * @param y row position of the pixel. 96 | * @param x column position of the pixel. 97 | * @param image 2D array of pixels of the image (RGB, row major order). 98 | * @return 3 byte array with 24 dots (field set). 99 | */ 100 | private static byte[] collectSlice(int y, int x, Bitmap image) { 101 | byte[] slices = new byte[]{0, 0, 0}; 102 | for (int yy = y, i = 0; yy < y + 24 && i < 3; yy += 8, i++) {// va a hacer 3 ciclos 103 | byte slice = 0; 104 | for (int b = 0; b < 8; b++) { 105 | int yyy = yy + b; 106 | if (yyy >= image.getHeight()) { 107 | continue; 108 | } 109 | int color = image.getPixel(x, yyy); 110 | boolean v = shouldPrintColor(color); 111 | slice |= (byte) ((v ? 1 : 0) << (7 - b)); 112 | } 113 | slices[i] = slice; 114 | } 115 | 116 | return slices; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Sample/src/main/java/com/github/douglasjunior/bluetoothsample/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothsample; 26 | 27 | import android.bluetooth.BluetoothAdapter; 28 | import android.bluetooth.BluetoothDevice; 29 | import android.content.DialogInterface; 30 | import android.content.Intent; 31 | import android.os.Bundle; 32 | import android.support.v7.app.AlertDialog; 33 | import android.support.v7.app.AppCompatActivity; 34 | import android.support.v7.widget.LinearLayoutManager; 35 | import android.support.v7.widget.RecyclerView; 36 | import android.support.v7.widget.Toolbar; 37 | import android.util.Log; 38 | import android.view.Menu; 39 | import android.view.MenuItem; 40 | import android.view.View; 41 | import android.widget.ProgressBar; 42 | import android.widget.Toast; 43 | 44 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothDeviceDecorator; 45 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothService; 46 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothStatus; 47 | 48 | import java.util.Arrays; 49 | 50 | public class MainActivity extends AppCompatActivity implements BluetoothService.OnBluetoothScanCallback, BluetoothService.OnBluetoothEventCallback, DeviceItemAdapter.OnAdapterItemClickListener { 51 | 52 | public static final String TAG = "BluetoothExample"; 53 | 54 | private ProgressBar pgBar; 55 | private Menu mMenu; 56 | private RecyclerView mRecyclerView; 57 | private DeviceItemAdapter mAdapter; 58 | 59 | private BluetoothAdapter mBluetoothAdapter; 60 | private BluetoothService mService; 61 | private boolean mScanning; 62 | 63 | @Override 64 | protected void onCreate(Bundle savedInstanceState) { 65 | super.onCreate(savedInstanceState); 66 | setContentView(R.layout.activity_main); 67 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 68 | setSupportActionBar(toolbar); 69 | 70 | pgBar = (ProgressBar) findViewById(R.id.pg_bar); 71 | pgBar.setVisibility(View.GONE); 72 | 73 | mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 74 | 75 | mRecyclerView = (RecyclerView) findViewById(R.id.rv); 76 | LinearLayoutManager lm = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); 77 | mRecyclerView.setLayoutManager(lm); 78 | 79 | mAdapter = new DeviceItemAdapter(this, mBluetoothAdapter.getBondedDevices()); 80 | mAdapter.setOnAdapterItemClickListener(this); 81 | mRecyclerView.setAdapter(mAdapter); 82 | 83 | mService = BluetoothService.getDefaultInstance(); 84 | 85 | mService.setOnScanCallback(this); 86 | mService.setOnEventCallback(this); 87 | } 88 | 89 | @Override 90 | protected void onResume() { 91 | super.onResume(); 92 | mService.setOnEventCallback(this); 93 | } 94 | 95 | @Override 96 | public boolean onCreateOptionsMenu(Menu menu) { 97 | getMenuInflater().inflate(R.menu.menu_main, menu); 98 | mMenu = menu; 99 | return true; 100 | } 101 | 102 | @Override 103 | public boolean onOptionsItemSelected(MenuItem item) { 104 | int id = item.getItemId(); 105 | 106 | if (id == R.id.action_scan) { 107 | startStopScan(); 108 | return true; 109 | } 110 | 111 | return super.onOptionsItemSelected(item); 112 | } 113 | 114 | private void startStopScan() { 115 | if (!mScanning) { 116 | mService.startScan(); 117 | } else { 118 | mService.stopScan(); 119 | } 120 | } 121 | 122 | @Override 123 | public void onDeviceDiscovered(BluetoothDevice device, int rssi) { 124 | Log.d(TAG, "onDeviceDiscovered: " + device.getName() + " - " + device.getAddress() + " - " + Arrays.toString(device.getUuids())); 125 | BluetoothDeviceDecorator dv = new BluetoothDeviceDecorator(device, rssi); 126 | int index = mAdapter.getDevices().indexOf(dv); 127 | if (index < 0) { 128 | mAdapter.getDevices().add(dv); 129 | mAdapter.notifyItemInserted(mAdapter.getDevices().size() - 1); 130 | } else { 131 | mAdapter.getDevices().get(index).setDevice(device); 132 | mAdapter.getDevices().get(index).setRSSI(rssi); 133 | mAdapter.notifyItemChanged(index); 134 | } 135 | } 136 | 137 | @Override 138 | public void onStartScan() { 139 | Log.d(TAG, "onStartScan"); 140 | mScanning = true; 141 | pgBar.setVisibility(View.VISIBLE); 142 | mMenu.findItem(R.id.action_scan).setTitle(R.string.action_stop); 143 | } 144 | 145 | @Override 146 | public void onStopScan() { 147 | Log.d(TAG, "onStopScan"); 148 | mScanning = false; 149 | pgBar.setVisibility(View.GONE); 150 | mMenu.findItem(R.id.action_scan).setTitle(R.string.action_scan); 151 | } 152 | 153 | @Override 154 | public void onDataRead(byte[] buffer, int length) { 155 | Log.d(TAG, "onDataRead"); 156 | } 157 | 158 | @Override 159 | public void onStatusChange(BluetoothStatus status) { 160 | Log.d(TAG, "onStatusChange: " + status); 161 | Toast.makeText(this, status.toString(), Toast.LENGTH_SHORT).show(); 162 | 163 | if (status == BluetoothStatus.CONNECTED) { 164 | CharSequence colors[] = new CharSequence[]{"Try text", "Try picture"}; 165 | 166 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 167 | builder.setTitle("Select"); 168 | builder.setItems(colors, new DialogInterface.OnClickListener() { 169 | @Override 170 | public void onClick(DialogInterface dialog, int which) { 171 | if (which == 0) { 172 | startActivity(new Intent(MainActivity.this, DeviceActivity.class)); 173 | } else { 174 | startActivity(new Intent(MainActivity.this, BitmapActivity.class)); 175 | } 176 | } 177 | }); 178 | builder.setCancelable(false); 179 | builder.show(); 180 | } 181 | 182 | } 183 | 184 | @Override 185 | public void onDeviceName(String deviceName) { 186 | Log.d(TAG, "onDeviceName: " + deviceName); 187 | } 188 | 189 | @Override 190 | public void onToast(String message) { 191 | Log.d(TAG, "onToast"); 192 | } 193 | 194 | @Override 195 | public void onDataWrite(byte[] buffer) { 196 | Log.d(TAG, "onDataWrite"); 197 | } 198 | 199 | @Override 200 | public void onItemClick(BluetoothDeviceDecorator device, int position) { 201 | mService.connect(device.getDevice()); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /Sample/src/main/java/com/github/douglasjunior/bluetoothsample/SampleApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2015 Douglas Nassif Roma Junior 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package com.github.douglasjunior.bluetoothsample; 26 | 27 | import android.app.Application; 28 | import android.bluetooth.BluetoothDevice; 29 | import android.bluetooth.BluetoothGatt; 30 | 31 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothClassicService; 32 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothConfiguration; 33 | import com.github.douglasjunior.bluetoothclassiclibrary.BluetoothService; 34 | 35 | import java.util.UUID; 36 | 37 | /** 38 | * Created by douglas on 29/05/17. 39 | */ 40 | public class SampleApplication extends Application { 41 | 42 | /* 43 | * Change for the UUID that you want. 44 | */ 45 | private static final UUID UUID_DEVICE = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); 46 | private static final UUID UUID_SERVICE = UUID.fromString("e7810a71-73ae-499d-8c15-faa9aef0c3f2"); 47 | private static final UUID UUID_CHARACTERISTIC = UUID.fromString("bef8d6c9-9c21-4c9e-b632-bd58c1009f9f"); 48 | 49 | @Override 50 | public void onCreate() { 51 | super.onCreate(); 52 | 53 | BluetoothConfiguration config = new BluetoothConfiguration(); 54 | 55 | config.bluetoothServiceClass = BluetoothClassicService.class; // BluetoothClassicService.class or BluetoothLeService.class 56 | 57 | config.context = getApplicationContext(); 58 | config.bufferSize = 1024; 59 | config.characterDelimiter = '\n'; 60 | config.deviceName = "Bluetooth Sample"; 61 | config.callListenersInMainThread = true; 62 | 63 | //config.uuid = null; // When using BluetoothLeService.class set null to show all devices on scan. 64 | config.uuid = UUID_DEVICE; // For Classic 65 | 66 | config.uuidService = UUID_SERVICE; // For BLE 67 | config.uuidCharacteristic = UUID_CHARACTERISTIC; // For BLE 68 | config.transport = BluetoothDevice.TRANSPORT_LE; // Only for dual-mode devices 69 | 70 | // For BLE 71 | config.connectionPriority = BluetoothGatt.CONNECTION_PRIORITY_HIGH; // Automatically request connection priority just after connection is through. 72 | //or request connection priority manually, mService.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH); 73 | 74 | BluetoothService.init(config); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Sample/src/main/res/drawable/bmw.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglasjunior/AndroidBluetoothLibrary/65180766464f3ba702b4825fa9cfef13c4083308/Sample/src/main/res/drawable/bmw.jpg -------------------------------------------------------------------------------- /Sample/src/main/res/drawable/jac.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglasjunior/AndroidBluetoothLibrary/65180766464f3ba702b4825fa9cfef13c4083308/Sample/src/main/res/drawable/jac.JPG -------------------------------------------------------------------------------- /Sample/src/main/res/drawable/loading.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglasjunior/AndroidBluetoothLibrary/65180766464f3ba702b4825fa9cfef13c4083308/Sample/src/main/res/drawable/loading.jpg -------------------------------------------------------------------------------- /Sample/src/main/res/drawable/palio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglasjunior/AndroidBluetoothLibrary/65180766464f3ba702b4825fa9cfef13c4083308/Sample/src/main/res/drawable/palio.jpg -------------------------------------------------------------------------------- /Sample/src/main/res/layout/activity_bitmap.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 37 | 38 | 44 | 45 | 51 | 52 | 53 | 54 | 61 | 62 | -------------------------------------------------------------------------------- /Sample/src/main/res/layout/activity_device.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 37 | 38 | 39 | 43 | 44 | 53 | 54 | 58 | 59 | 66 | 67 | 68 | 75 | 76 | -------------------------------------------------------------------------------- /Sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 23 | 24 | 31 | 32 | 33 | 34 | 35 | 36 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Sample/src/main/res/layout/device_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 22 | 23 | 27 | 28 | 35 | 36 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Sample/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglasjunior/AndroidBluetoothLibrary/65180766464f3ba702b4825fa9cfef13c4083308/Sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglasjunior/AndroidBluetoothLibrary/65180766464f3ba702b4825fa9cfef13c4083308/Sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglasjunior/AndroidBluetoothLibrary/65180766464f3ba702b4825fa9cfef13c4083308/Sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglasjunior/AndroidBluetoothLibrary/65180766464f3ba702b4825fa9cfef13c4083308/Sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douglasjunior/AndroidBluetoothLibrary/65180766464f3ba702b4825fa9cfef13c4083308/Sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Sample/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /Sample/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /Sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /Sample/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /Sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Bluetooth Sample 3 | SCAN 4 | STOP 5 | Device 6 | 7 | -------------------------------------------------------------------------------- /Sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 14 | 10 | 14 |