iDevicesInc / SweetBlue

BLE on Android, the easy way. THIS IS NOW DEPRECATED. Please visit website for info on new versions.
https://sweetblue.io
GNU General Public License v3.0
356 stars 57 forks source link
android-library ble bluetooth java

With the release of SweetBlue v3 (https://sweetblue.io), this project is no longer being actively developed. Consider this project as abandoned.



| Why? | Features | Getting Started | Licensing | Wiki | Toolbox


Why?

Android's BLE stack has some...issues...

SweetBlue is a blanket abstraction that shoves all that troublesome behavior behind a clean interface and gracefully degrades when the underlying stack becomes too unstable for even it to handle.

It’s built on the hard-earned experience of several commercial BLE projects and provides so many transparent workarounds to issues both annoying and fatal that it’s frankly impossible to imagine writing an app without it. It also supports many higher-level constructs, things like atomic transactions for coordinating authentication handshakes and firmware updates, flexible scanning configurations, read polling, transparent retries for transient failure conditions, and, well, the list goes on. The API is dead simple, with usage dependence on a few plain old Java objects and link dependence on standard Android classes. It offers conveniences for debugging and analytics and error handling that will save you months of work - last mile stuff you didn't even know you had to worry about.

Features

Getting Started

  1. If using Android Studio or Gradle...

    1. Download the latest release to a subfolder of your project such as MyApp/src/main/lib/. This ZIP contains several samples, precompiled JARS, and API docs and is preferable to downloading from GitHub, which only contains the raw source.
    2. Open the app module's build.gradle file.
    3. If building with source, your gradle file should look something like this:
    
    android {
        compileSdkVersion 25
        buildToolsVersion '25.0.3'
    
        defaultConfig {
            minSdkVersion 18
            targetSdkVersion 25
            ...
        }
    
        sourceSets {
            main.java.srcDirs += 'src/main/lib/sweetblue/src'
            main.res.srcDirs += 'src/main/lib/sweetblue/res'
            ...
        }
        ...
    }
    
    1. If you're building with source from github, the sourceSet path is a bit different:
    
      android {
          compileSdkVersion 25
          buildToolsVersion '25.0.3'
    
          defaultConfig {
              minSdkVersion 18
              targetSdkVersion 25
              ...
          }
    
          sourceSets {
              main.java.srcDirs += 'src/main/lib/sweetblue/library/src/main/java'              
              main.res.srcDirs += 'src/main/lib/sweetblue/library/src/main/res'
              ...
          }
          ...
      }
    
    1. Else if building with JAR, it should look something like this:
    
    android {
        compileSdkVersion 25
        buildToolsVersion '25.0.3'
    
        defaultConfig {
            minSdkVersion 18
            targetSdkVersion 25
            ...
        }
    
        dependencies {
            compile fileTree(dir: 'libs', include: '*.jar')
            ...
        }
        ...
    }
    
  2. Now add these to the root of MyApp/AndroidManifest.xml:

    <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="25" />
    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    
    <!-- NOTE: Location is a new requirement for scanning in Android M.  -->
    <!--       You may use ACCESS_FINE_LOCATION also or instead.         -->
  3. From your Activity or Service or Application instance, this is all it takes to discover a device, connect to it, and read a characteristic:

    // A ScanFilter decides whether a BleDevice instance will be created from a
    // BLE advertisement and passed to the DiscoveryListener implementation below.
    final ScanFilter scanFilter = new ScanFilter()
    {
        @Override public Please onEvent(ScanEvent e)
        {
            return Please.acknowledgeIf(e.name_normalized().contains("my_device"))
                         .thenStopScan();
        }
    };
    
    // New BleDevice instances are provided through this listener.
    // Nested listeners then listen for connection and read results.
    // Obviously you will want to structure your actual code a little better.
    // The deep nesting simply demonstrates the async-callback-based nature of the API.
    final DiscoveryListener discoveryListener = new DiscoveryListener()
    {
        @Override public void onEvent(DiscoveryEvent e)
        {
            if( e.was(LifeCycle.DISCOVERED) )
            {
                e.device().connect(new StateListener()
                {
                    @Override public void onEvent(StateEvent e)
                    {
                        if( e.didEnter(BleDeviceState.INITIALIZED) )
                        {
                            e.device().read(Uuids.BATTERY_LEVEL, new ReadWriteListener()
                            {
                                @Override public void onEvent(ReadWriteEvent e)
                                {
                                    if( e.wasSuccess() )
                                    {
                                        Log.i("", "Battery level is " + e.data_byte() + "%");
                                    }
                                }
                            });
                        }
                    }
                });
            }
        }
    };
    
    // This class helps you navigate the treacherous waters of Android M Location requirements for scanning.
    // First it enables bluetooth itself, then location permissions, then location services. The latter two
    // are only needed in Android M. This must be called from an Activity instance.
    BluetoothEnabler.start(this, new DefaultBluetoothEnablerFilter()
    {
        @Override public Please onEvent(BluetoothEnablerEvent e)
        {
            if( e.isDone() )
            {
                e.bleManager().startScan(scanFilter, discoveryListener);
            }
    
            return super.onEvent(e);
        }
    });

Licensing

SweetBlue is released here under the GPLv3. Please visit https://sweetblue.io for SweetBlue v3, as v2 is no longer licensable for commercial applications. In a nutshell, if you're developing a for-profit commercial app you may use this library for free for evaluation purposes, but most likely your use case will require purchasing a proprietary license before you can release your app to the public. See the FAQ for more details and https://tldrlegal.com/license/gnu-general-public-license-v3-%28gpl-3%29 for a general overview of the GPL.