├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── joelwasserman │ │ └── androidbletutorial │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── joelwasserman │ │ │ └── androidbletutorial │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_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-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── joelwasserman │ └── androidbletutorial │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android-BLE-Scan-Example 2 | This is a simple example app that turns on ble and scans for devices, listing them out. It's for Android (M) API 23 3 | 4 | Built in Android Studio with gradle system. The app scans for nearby BLE devices and displays them in a TextView. 5 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.example.joelwasserman.androidbletutorial" 9 | minSdkVersion 23 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.3.0' 26 | } 27 | -------------------------------------------------------------------------------- /app/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/joelwasserman/Library/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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/joelwasserman/androidbletutorial/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.joelwasserman.androidbletutorial; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/joelwasserman/androidbletutorial/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.joelwasserman.androidbletutorial; 2 | 3 | import android.Manifest; 4 | import android.app.AlertDialog; 5 | import android.bluetooth.BluetoothAdapter; 6 | import android.bluetooth.BluetoothDevice; 7 | import android.bluetooth.BluetoothManager; 8 | import android.bluetooth.le.BluetoothLeScanner; 9 | import android.bluetooth.le.ScanCallback; 10 | import android.bluetooth.le.ScanResult; 11 | import android.content.Context; 12 | import android.content.DialogInterface; 13 | import android.content.Intent; 14 | import android.content.pm.PackageManager; 15 | import android.location.Location; 16 | import android.location.LocationManager; 17 | import android.os.AsyncTask; 18 | import android.support.v4.app.ActivityCompat; 19 | import android.support.v4.content.ContextCompat; 20 | import android.support.v7.app.AppCompatActivity; 21 | import android.os.Bundle; 22 | import android.text.method.ScrollingMovementMethod; 23 | import android.util.Log; 24 | import android.view.View; 25 | import android.widget.Button; 26 | import android.widget.TextView; 27 | 28 | public class MainActivity extends AppCompatActivity { 29 | 30 | BluetoothManager btManager; 31 | BluetoothAdapter btAdapter; 32 | BluetoothLeScanner btScanner; 33 | Button startScanningButton; 34 | Button stopScanningButton; 35 | TextView peripheralTextView; 36 | private final static int REQUEST_ENABLE_BT = 1; 37 | private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1; 38 | 39 | @Override 40 | protected void onCreate(Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | setContentView(R.layout.activity_main); 43 | 44 | peripheralTextView = (TextView) findViewById(R.id.PeripheralTextView); 45 | peripheralTextView.setMovementMethod(new ScrollingMovementMethod()); 46 | 47 | startScanningButton = (Button) findViewById(R.id.StartScanButton); 48 | startScanningButton.setOnClickListener(new View.OnClickListener() { 49 | public void onClick(View v) { 50 | startScanning(); 51 | } 52 | }); 53 | 54 | stopScanningButton = (Button) findViewById(R.id.StopScanButton); 55 | stopScanningButton.setOnClickListener(new View.OnClickListener() { 56 | public void onClick(View v) { 57 | stopScanning(); 58 | } 59 | }); 60 | stopScanningButton.setVisibility(View.INVISIBLE); 61 | 62 | btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); 63 | btAdapter = btManager.getAdapter(); 64 | btScanner = btAdapter.getBluetoothLeScanner(); 65 | 66 | 67 | if (btAdapter != null && !btAdapter.isEnabled()) { 68 | Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 69 | startActivityForResult(enableIntent,REQUEST_ENABLE_BT); 70 | } 71 | 72 | // Make sure we have access coarse location enabled, if not, prompt the user to enable it 73 | if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { 74 | final AlertDialog.Builder builder = new AlertDialog.Builder(this); 75 | builder.setTitle("This app needs location access"); 76 | builder.setMessage("Please grant location access so this app can detect peripherals."); 77 | builder.setPositiveButton(android.R.string.ok, null); 78 | builder.setOnDismissListener(new DialogInterface.OnDismissListener() { 79 | @Override 80 | public void onDismiss(DialogInterface dialog) { 81 | requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION); 82 | } 83 | }); 84 | builder.show(); 85 | } 86 | } 87 | 88 | // Device scan callback. 89 | private ScanCallback leScanCallback = new ScanCallback() { 90 | @Override 91 | public void onScanResult(int callbackType, ScanResult result) { 92 | peripheralTextView.append("Device Name: " + result.getDevice().getName() + " rssi: " + result.getRssi() + "\n"); 93 | 94 | // auto scroll for text view 95 | final int scrollAmount = peripheralTextView.getLayout().getLineTop(peripheralTextView.getLineCount()) - peripheralTextView.getHeight(); 96 | // if there is no need to scroll, scrollAmount will be <=0 97 | if (scrollAmount > 0) 98 | peripheralTextView.scrollTo(0, scrollAmount); 99 | } 100 | }; 101 | 102 | @Override 103 | public void onRequestPermissionsResult(int requestCode, 104 | String permissions[], int[] grantResults) { 105 | switch (requestCode) { 106 | case PERMISSION_REQUEST_COARSE_LOCATION: { 107 | if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { 108 | System.out.println("coarse location permission granted"); 109 | } else { 110 | final AlertDialog.Builder builder = new AlertDialog.Builder(this); 111 | builder.setTitle("Functionality limited"); 112 | builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background."); 113 | builder.setPositiveButton(android.R.string.ok, null); 114 | builder.setOnDismissListener(new DialogInterface.OnDismissListener() { 115 | 116 | @Override 117 | public void onDismiss(DialogInterface dialog) { 118 | } 119 | 120 | }); 121 | builder.show(); 122 | } 123 | return; 124 | } 125 | } 126 | } 127 | 128 | public void startScanning() { 129 | System.out.println("start scanning"); 130 | peripheralTextView.setText(""); 131 | startScanningButton.setVisibility(View.INVISIBLE); 132 | stopScanningButton.setVisibility(View.VISIBLE); 133 | AsyncTask.execute(new Runnable() { 134 | @Override 135 | public void run() { 136 | btScanner.startScan(leScanCallback); 137 | } 138 | }); 139 | } 140 | 141 | public void stopScanning() { 142 | System.out.println("stopping scanning"); 143 | peripheralTextView.append("Stopped Scanning"); 144 | startScanningButton.setVisibility(View.VISIBLE); 145 | stopScanningButton.setVisibility(View.INVISIBLE); 146 | AsyncTask.execute(new Runnable() { 147 | @Override 148 | public void run() { 149 | btScanner.stopScan(leScanCallback); 150 | } 151 | }); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 |