I am having some trouble with BLE communication working on a foreground service.
Although the logic on the service works fine, if the activity is open, every time a Bluetooth device is in range my service does what it has to do. But after I close or suspend the activity, my service only works one time and it seems that it ignores the scan callback and doesn't do anything.
The expected behavior should be that even when the main activity is closed, the service should find and do the associated logic.
public class DeviceControlService extends Service implements BluetoothAdapter.LeScanCallback{
private String TAG = "In Service --> ";
private BluetoothAdapter mBluetoothAdapter; //BluetoothAdapter represents the radio in the Smartphone
private boolean mScanning; //Keep track of whether there is a scan in progress
private static final long SCAN_PERIOD = 100000; //Length of time in milliseconds to scan for BLE devices
private String wantedDevice = "00:1E:C0:29:2A:BE";
private boolean found = false;
InServiceControl deviceControl;
private boolean switchStatus = true;
BluetoothManager bluetoothManager;
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
private States state = IDLE;
private PendingIntent pendingIntent;
private BluetoothAdapter.LeScanCallback mLeScanCallback;
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
DeviceControlService getService() {
// Return this instance of LocalService so clients can call public methods
return DeviceControlService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void stopThread(){
Log.d(TAG, "thread stoped");
stopScanning();
switchStatus = false;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId){
Log.d(TAG, "On start");
return START_STICKY;
}
#Override
public void onCreate(){
state = INIT;
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification.Builder(this)
.setContentTitle("MBO")
.setContentText("Bluetooth is running.")
.setSmallIcon(R.drawable.tooth)
.setContentIntent(pendingIntent)
.build();
startForeground(101, notification);
adapterInit();
scanLeDevice();
new Thread(new Runnable() {
#Override
public void run() {
while(switchStatus)
states();
}
}).start();
}
#Override
public void onDestroy() {
deviceControl = null;
bluetoothManager = null;
mBluetoothAdapter = null;
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
public void adapterInit(){
Log.d(TAG, "in adapter init");
bluetoothManager = (android.bluetooth.BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); //Get the BluetoothManager
mBluetoothAdapter = bluetoothManager.getAdapter();
deviceControl = new InServiceControl(mBluetoothAdapter, this);
}
public void states(){
switch(state){
case IDLE:
break;
case INIT:
switchStatus = true;
Log.d(TAG, "init");
if(mBluetoothAdapter == null){
adapterInit();
}
state = PROCESSING;
break;
case PROCESSING:
if(!mScanning){
scanLeDevice();
}else{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case DONE:
Log.d(TAG, "done");
state = PROCESSING;
break;
default:
break;
}
}
// ----------------------------------------------------------------------------------------------------------------
// Scan for BLE device for SCAN_PERIOD milliseconds.
// The mLeScanCallback method is called each time a device is found during the scan
private void scanLeDevice() {
Log.d(TAG, "Entered into scanning");
if(!mScanning){
Log.d(TAG, "started Scanning");
mBluetoothAdapter.startLeScan(this);
mScanning = true;
}
}
private void stopScanning(){
Log.d(TAG, "scanning stoped");
if(mScanning) {
mBluetoothAdapter.stopLeScan(this);
mScanning = false;
}
}
public void autoConnect(BluetoothDevice device){
//Stop the scan in progress
deviceControl.init(device.getName(), device.getAddress());
state = DONE;
}
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { //Android calls method with Bluetooth device advertising information
if(device.getAddress().equals(wantedDevice)){
stopScanning();
autoConnect(device);
Toast.makeText(getApplicationContext(), "found", Toast.LENGTH_SHORT).show();
}
Log.d(TAG, "Found BLE Device: " + device.getAddress() ); //Debug information to log the devices as they are found
}
Is it possible that android is ignoring the scan callback for some reason that i am missing?
Thanks in advance
Related
I'm trying to implement Bluetooth advertiser and scanner. My advertiser code is advertising with some service data in the advertisement packet. On the scanner side when I tried to get the data using the same UUID that is used during adding service data in the advertiser the getService data method is returning null object. Here is my advertiser code.
public class Main_Service extends Service{
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
private Intent mIntent;
private String RSSI,SSID,MAC;
#Override
public IBinder onBind(Intent intent) {
return null;
}
public int onStartCommand (Intent intent, int flags, int startId) {
RSSI = intent.getStringExtra("RSSI");
SSID = intent.getStringExtra("SSID");
MAC= intent.getStringExtra("MAC");
Toast.makeText(this, RSSI+";"+SSID+";"+MAC+":", Toast.LENGTH_LONG).show();
initialize();
startadvertising();
return 0;
}
public void onCreate() {
Toast.makeText(this, "Service Created", Toast.LENGTH_LONG).show();
}
private void startadvertising() {
AdvertiseSettings settings = buildAdvertiseSettings();
AdvertiseData data = buildAdvertiseData();
if (mBluetoothLeAdvertiser != null) {
mBluetoothLeAdvertiser.startAdvertising(settings, data, new AdvertiseCallback() {
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Toast.makeText(getBaseContext(), "Adertising", Toast.LENGTH_LONG).show();
}
#Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Toast.makeText(getBaseContext(), "Advertisement failed", Toast.LENGTH_LONG).show();
}
});
}
else{
Toast.makeText(getBaseContext(), "BLE is not supported", Toast.LENGTH_LONG).show();
}
}
private void initialize() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter!=null){
if(!mBluetoothAdapter.isEnabled()){
mBluetoothAdapter.enable();
}
}
else{
Toast.makeText(getBaseContext(), "BL is not supported", Toast.LENGTH_LONG).show();
}
mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
}
private AdvertiseSettings buildAdvertiseSettings() {
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
settingsBuilder.setTimeout(0);
return settingsBuilder.build();
}
private AdvertiseData buildAdvertiseData() {
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
//String data = RSSI+SSID.substring(0,10)+MAC.replaceAll(":","");
byte[] serviceData = new byte[15];
serviceData[0] = (byte)Integer.parseInt(RSSI);
String[] macAddressParts = MAC.split(":");
for(int i=0; i<6; i++){
Integer hex = Integer.parseInt(macAddressParts[i], 16);
serviceData[i+1] = hex.byteValue();
}
for(int i=0;i<8;i++) {
serviceData[i+7] = (byte)(SSID.charAt(i));
}
long mostsignificant=0,leastsignificant=5122;
UUID s_id = new UUID(mostsignificant,leastsignificant);
dataBuilder.addServiceData(new ParcelUuid(s_id),serviceData);
Toast.makeText(this, s_id.toString(), Toast.LENGTH_LONG).show();
return dataBuilder.build();
}
public void onStart(Intent intent, int startid) {
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
}
public void onDestroy() {
mBluetoothAdapter = null;
mBluetoothLeAdvertiser = null;
Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
}
}
In normal BLE flow, We need to call discoverService() after establishing a successful connection with the BLE device. We will get a callback in onServiceDiscoverd(). After that, we need to approach the getService() with corresponding Service & Characteristic UUID. Before discoverService() call, the getService() will always return null. I hope this will help you. If you required, I can share my complete service flow.
I'm trying to creat a bacckground service that will have a thread that adds one to a counter every minute.
I want other apps to be able to call a methed call getBot that will retunt the value.
Right now I have a test app that has a activty that bindss to the app so it can talk to it to get the counter. When it exits it unbounds to it.
It has a button called botGert that gets the current counter value from the service.
When it unbounds to it, the service shuts down. How can I get it so it does not shut down????
service class
public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
static final int MSG_BOT_GET = 2;
static final int MSG_BOT_SET = 3;
static final int MSG_BOT_STOP = 4;
static final int MSG_BOT_PAUSE = 5;
static final int MSG_GET_ORDERS = 6;
static final int MSG_GET_POINT = 7;
static final int MSG_BOT_GET_RES = 2;
static final int PRICE_STATIC =1;
static final int PRICE_PERCENTAGE =2;
static final int PRICE_ALL = 3;
static final int VOL_STATIC = 1;
static final int VOL_PERCENTAGE = 2;
static final int VOL_ALL = 3;
int counter;
boolean isStop=false; // put bot in pause ste on next 1 min update
boolean isUYpdateNextMinute=false;
/**
This is called by a client to exicute code in this service
*/
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_BOT_GET:
Message resp = Message.obtain(null, MSG_BOT_GET_RES);
Bundle bResp = new Bundle();
bResp.putString("respData", Integer.toString(counter ));
resp.setData(bResp);
try {
msg.replyTo.send(resp);
} catch (Exception e)
{
}
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
#Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
//////////////////////////////////////////////////////////////////////////////
// emplanent callbacks
MessengerService parent;
#Override
public void onCreate() {
Toast.makeText(this, "Service Created", Toast.LENGTH_LONG).show();
parent=this;
}
#Override
public void onStart(Intent intent, int startid) {
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
ThreadDemo T1 = new ThreadDemo( "Thread-1");
T1.start();
// sgart 1 minute update thread
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
}
///////////////////////////////////////////////////////////////
// thread code that runbs counter
class ThreadDemo extends Thread {
private String threadName;
ThreadDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
try {
do {
counter++;
Thread.sleep(1000 *60 );
counter++;
} while (true);
} catch(Exception e)
{
Toast.makeText(parent, "thread stopped", Toast.LENGTH_LONG).show();
}
} // end inner thread class
} // end class
}
/////////////////////////////////////////////////////////////////////
activity class
public class ActivityBoundMessenger extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bound_messenger);
}
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
/**
* Class for interacting with the main interface of the service.
*/
/////////////////////////////////////////////////////////////////////////////////////////
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false;
}
};
////////////////////////////////////////////////////////////////////////////////
public void sayHello(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void butSet(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_BOT_SET, 0, 0);
Bundle b = new Bundle();
b.putString("data", "json object");
msg.setData(b);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void butGet(View v) {
if (!mBound) return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message
.obtain(null, MessengerService.MSG_BOT_GET);
msg.replyTo = new Messenger(new ResponseHandler());
// We pass the value
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
////////////////////////////////////////////////////////////////////////////////////////
// these metheds get called whenb this acty starts and endsa
#Override
protected void onStart() {
super.onStart();
// Bind to the service
////////////////////////////////////////////
Intent ser = new Intent(this, MessengerService.class);
startService(ser);
bindService(ser, mConnection,
Context.BIND_AUTO_CREATE);
///////////////////////////////////////////
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
// This class handles the Service response
int ted=0;
String nextUpdate=null;
class ResponseHandler extends Handler {
#Override
public void handleMessage(Message msg) {
int respCode = msg.what;
switch (respCode) {
case MessengerService.MSG_BOT_GET_RES:
String nextUpdate= msg.getData().getString("respData");
ted++;
}
}
}
}
From Android Documentation:
When the last client unbinds from the service, the system destroys the service, unless the service was also started by startService().
So, all you have to do is call this in your onCreate() method in the Service:
startService(new Intent(this, MessengerService.class))
Of course make sure to call stopSelf() when you are done with the service.
I am trying to manage my temperature service and LED service of my BLE device at the same Android interface. However, whenever I try to turn the LED on, the app dies, and I don't know how to fix this (I am not good at either Java or BLE).
I have all of the UUIDs of the characteristics and services I need. I am trying to create two BluetoothGattCharacteristic
private BluetoothGattCharacteristic characteristic;
private BluetoothGattCharacteristic characteristicCTRL2;
It works fine when I call the temperature characteristic and get the temperature measurement. However, the app dies whenever I try to turn the LED on /call the LED characteristic.
10-30 08:48:33.026 1033-1033/com.example.android.bluetoothlegatt E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.bluetoothlegatt, PID: 1033
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.bluetooth.BluetoothGattCharacteristic.setValue(byte[])' on a null object reference
at com.example.android.bluetoothlegatt.AutoConnectActivity.turnLEDon(AutoConnectActivity.java:71)
at com.example.android.bluetoothlegatt.AutoConnectActivity$6.onClick(AutoConnectActivity.java:264)
at android.view.View.performClick(View.java:4756)
at android.view.View$PerformClick.run(View.java:19754)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5219)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
The following code is how I transfer all characteristics & services together.
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
charas.add(gattCharacteristic);
if ( gattCharacteristic.getUuid().equals(UUID.fromString(SampleGattAttributes.CTRL_2))){ //CTRL_2 is the UUID of my LED characteristic
Log.e(TAG, "GattCharacteristics= " + gattCharacteristic.getUuid());
Intent intent = new Intent(DeviceControlActivity.this, AutoConnectActivity.class);
intent.putExtra("character_id", "F000AA01-0451-4000-B000-000000000000"); //UUID of my temperature characteristic
intent.putExtra("service_id", "F000AA00-0451-4000-B000-000000000000");//UUID of my temperature service
intent.putExtra("address", mDeviceAddress);
intent.putExtra("ctrl2_character_id", gattCharacteristic.getUuid().toString()); //UUID of my LED characteristics
intent.putExtra("ctrl2_service_id", gattCharacteristic.getService().getUuid().toString());//UUID of my LED service
startActivity(intent);
}
The part I fail is the function that I try to turn LED on.
public boolean turnLEDon()
{
byte[] value = {(byte)1};
characteristicCTRL2.setValue(value);//This is the line I failed
boolean status = mBluetoothLeService.mBluetoothGatt.writeCharacteristic(characteristicCTRL2);
return status;
};
The following is my complete code of trying to manipulate two characteristics at the same interface.
package com.example.android.bluetoothlegatt;
public class AutoConnectActivity extends Activity {
private BluetoothGattCharacteristic characteristic;
private BluetoothGattCharacteristic characteristicCTRL2;
private String address;
private UUID serviceId;
private UUID charId;
private UUID ctrl2ServiceId;
private UUID ctrl2CharId;
private TextView debugText;
private Button startBtn;
private Button stopBtn;
private Button ctrlStartBtn;
private Button ctrlStopBtn;
private Timer timer = new Timer();
private boolean started = false;
private ArrayAdapter<String> adapter;
private String Entry;
private File file;
private boolean check= true;
private BluetoothLeService mBluetoothLeService;
public boolean turnLEDon()
{
byte[] value = {(byte)1};
characteristicCTRL2.setValue(value);//This is the line I failed
boolean status = mBluetoothLeService.mBluetoothGatt.writeCharacteristic(characteristicCTRL2);
return status;
};
public boolean turnLEDoff()
{
byte[] value = {(byte)0};
characteristicCTRL2.setValue(value);
boolean status = mBluetoothLeService.mBluetoothGatt.writeCharacteristic(characteristicCTRL2);
return !status;
};
private TimerTask timerTask = new TimerTask() {
#Override
public void run() {
mBluetoothLeService.connect(address);
}
};
// Code to manage Service lifecycle.
private final ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
if (!mBluetoothLeService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
mBluetoothLeService.connect(address);
List<BluetoothGattService> list = mBluetoothLeService.getSupportedGattServices();
for(BluetoothGattService s : list){
if(s.getUuid().compareTo(serviceId) == 0){
if (check==true) {
characteristic = s.getCharacteristic(charId);
mBluetoothLeService.disconnect();
}
return;
}
if(s.getUuid().compareTo(ctrl2ServiceId) == 0){
if(check==false) {
characteristicCTRL2 = s.getCharacteristic(ctrl2CharId);
}
return;
}
}
mBluetoothLeService.disconnect();
Log.e(TAG, "not find device");
debugText.setText("not find device");
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
mBluetoothLeService = null;
}
};
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
debugText.setText(action);
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
// if temperature measurement
if (check==true){
mBluetoothLeService.readCharacteristic(characteristic);}
// if control LED
if(check==false){
mBluetoothLeService.readCharacteristic(characteristicCTRL2);
turnLEDon();
}
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action))
{
if (check==false) {
String CTRL_Status = intent.getStringExtra(BluetoothLeService.EXTRA_DATA);
adapter.add("CTRL Status: " + CTRL_Status + " " + new Date(System.currentTimeMillis()));
turnLEDoff();
mBluetoothLeService.disconnect();
}
if(check==true) {
String temperature = intent.getStringExtra(BluetoothLeService.EXTRA_DATA);
adapter.add("Temperature: " + temperature + "°C " + new Date(System.currentTimeMillis()));
Entry = address + "," + new Date(System.currentTimeMillis()).toString() + "," + temperature.toString() + "\n";
try {
FileOutputStream out = new FileOutputStream(file, true);
out.write(Entry.getBytes());
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
mBluetoothLeService.disconnect();
}
}
}
};
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.autoconnect);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Intent intent = getIntent();
address = intent.getStringExtra("address");
serviceId = UUID.fromString(intent.getStringExtra("service_id"));
charId = UUID.fromString(intent.getStringExtra("character_id"));
ctrl2ServiceId = UUID.fromString(intent.getStringExtra("ctrl2_service_id"));
Log.e(TAG, " CTRL2 SERVICE: " + ctrl2ServiceId);
ctrl2CharId = UUID.fromString(intent.getStringExtra("ctrl2_character_id"));
Log.e(TAG, " CTRL2 CHARAC: " + ctrl2CharId);
((TextView)findViewById(R.id.address_txt)).setText(address);
((TextView)findViewById(R.id.service_txt)).setText(serviceId.toString());
((TextView)findViewById(R.id.char_txt)).setText(charId.toString());
debugText = (TextView)findViewById(R.id.debug_txt);
startBtn = (Button)findViewById(R.id.start_btn);
stopBtn = (Button)findViewById(R.id.stop_btn);
ctrlStartBtn = (Button)findViewById(R.id.ctrl_start_btn);
ctrlStopBtn = (Button)findViewById(R.id.ctrl_stop_btn);
ListView listView = (ListView)findViewById(R.id.result_list);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
listView.setAdapter(adapter);
startBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(started) return;
started = true;
check=true;
//External Storage
String state;
state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
File root = Environment.getExternalStorageDirectory();
File Dir = new File(root.getAbsolutePath() + "/MeasurementDataFile");
if (!Dir.exists()) {
Dir.mkdir();
}
file = new File(Dir, "Temperature.csv");
}
if (check==true){
timer.schedule(timerTask, 0, 1000 * 5); }
}
});
stopBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!started) return;
started = false;
check=true;
timer.cancel();
}
});
ctrlStartBtn.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
mBluetoothLeService.connect(address);
check=false;
if(started) return;
started = true;
Toast.makeText (getBaseContext(), "Turn CTRL 1 ON", Toast.LENGTH_SHORT).show();
Log.e(TAG, " On?: " + turnLEDon());
}
});
ctrlStopBtn.setOnClickListener(new View.OnClickListener() {
//mBluetoothLeService.connect(address);
#Override
public void onClick(View v) {
if(!started) return;
started = false;
check=false;
Log.e(TAG, " Off?: " + turnLEDoff());
Toast.makeText (getBaseContext(), "Turn CTRL 1 OFF", Toast.LENGTH_SHORT).show();
}
});
Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(mGattUpdateReceiver, DeviceControlActivity.makeGattUpdateIntentFilter());
if (mBluetoothLeService != null) {
final boolean result = mBluetoothLeService.connect(address);
Log.d(TAG, "Connect request result=" + result);
}
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(mGattUpdateReceiver);
}
}
All BLE calls are asynchronous. This is really pain-in-neck, but we have what we have. So you cannot just write:
{
boolean status = writeCharacteristic(characteristic);
return status;
}
and hope all will be ok. In this case, you obtain a result of the writeCharacteristic instruction only, but not the real write operation result! For the last, you need to register the part of your code as BluetoothGattCallback descendant and read OnCharacteristicWrite event, where you can really know the result of your write operation.
All BLE read-write operations should be done in a strict one-by-one sequence and from main app thread only.
Different devices have different capabilities and productivity, so you can obtain different results - IMHO, this is less strange, than Google's decision to realize BLE stack in this manner :)
Pretty BLE guide for Android can be found here: https://droidcon.de/sites/global.droidcon.cod.newthinking.net/files/media/documents/practical_bluetooth_le_on_android_0.pdf
Edit
The link above is broken but the other one can be found here:
https://speakerdeck.com/erikhellman/practical-bluetooth-low-energy-on-android
How can I receive external sensor data even when the app is closed or screen is off?
I am currently collecting data via bluetooth low energy using this function:
public void onDataRecieved(BleSensor<?> sensor, String text) {
if (sensor instanceof BleHeartRateSensor) {
final BleSensor hSensor = (BleSensor) sensor;
float[] values = hSensor.getData();
//Start service to write data to a file
viewText.setText(text);
}
Here is the class that is used to implement the BLE sensor listener. It is an activity. I am having trouble trying to convert it to a service.
public abstract class DemoSensorActivity extends Activity {
private final static String TAG = DemoSensorActivity.class.getSimpleName();
public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";
public static final String EXTRAS_SENSOR_UUID = "SERVICE_UUID";
private BleService bleService;
private String serviceUuid;
private String deviceAddress;
// Handles various events fired by the Service.
// ACTION_GATT_CONNECTED: connected to a GATT server.
// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
// ACTION_DATA_AVAILABLE: received data from the device. This can be a result of read
// or notification operations.
private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BleService.ACTION_GATT_DISCONNECTED.equals(action)) {
//TODO: show toast
finish();
} else if (BleService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
final BleSensor<?> sensor = BleSensors.getSensor(serviceUuid);
bleService.enableSensor(sensor, true);
} else if (BleService.ACTION_DATA_AVAILABLE.equals(action)) {
final BleSensor<?> sensor = BleSensors.getSensor(serviceUuid);
final String text = intent.getStringExtra(BleService.EXTRA_TEXT);
onDataRecieved(sensor, text);
}
}
};
// Code to manage Service lifecycle.
private final ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
bleService = ((BleService.LocalBinder) service).getService();
if (!bleService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
// Automatically connects to the device upon successful start-up initialization.
bleService.connect(deviceAddress);
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
bleService = null;
//TODO: show toast
finish();
}
};
public abstract void onDataRecieved(BleSensor<?> sensor, String text);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
deviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);
serviceUuid = intent.getStringExtra(EXTRAS_SENSOR_UUID);
getActionBar().setDisplayHomeAsUpEnabled(true);
final Intent gattServiceIntent = new Intent(this, BleService.class);
bindService(gattServiceIntent, serviceConnection, BIND_AUTO_CREATE);
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter());
if (bleService != null) {
final boolean result = bleService.connect(deviceAddress);
Log.d(TAG, "Connect request result=" + result);
}
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(gattUpdateReceiver);
}
#Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
bleService = null;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BleService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BleService.ACTION_GATT_DISCONNECTED);
intentFilter.addAction(BleService.ACTION_DATA_AVAILABLE);
return intentFilter;
}
}
Currently this is only running when the app is open and the screen is turned on. Is there a way to continue to run this data collection when the app is closed and the screen is off?
I have a service class and I fetch data from an Intent in the method "onHandleIntent" and pick up in a variable. I would to call the value of this variable in onCreate, but when I call this variable I get a NULL value. (for examole in onHandleIntent i have a variable a = True, but in onCreate this variable is not true). I would to take this variable from onHandleIntent to onCreate. This is my code:
public class LogService extends IntentService
{
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
SoundManager sm;
private boolean notifica = false;
private String id;
Boolean a = false;
private static final int REQUEST_ENABLE_BT = 1;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 2100000000;
Handler handler;
public LogService()
{
super("LogService");
}
#Override
protected void onHandleIntent(Intent intent) {
id=(String) intent.getStringExtra("idc");
System.out.println("id1 e'" + id);
a = true;
}
#Override
public void onCreate() {
// ActiveBluetooth();
handler = new Handler();
super.onCreate();
// super.onCreate(savedInstanceState);
mHandler = new Handler();
System.out.println("Vero o falso?'" + a);
System.out.println("id e'" + id);
// Boolean a = getIntent().getExtras().getBoolean("ok");
// Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
//startActivities(enableBtIntent, REQUEST_ENABLE_BT);
// Use this check to determine whether BLE is supported on the device. Then you can
// selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
super.onDestroy();
}
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
super.onDestroy();
return;
}
// Checks if Bluetooth is supported on the device.
//onResume();
// BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
/* if (!mBluetoothAdapter.isEnabled()) {
Toast.makeText(this, "Auto attivazione blueooth: Bluetooth attivato.", Toast.LENGTH_SHORT).show();
mBluetoothAdapter.enable();
scanLeDevice(true);
Intent refresh = new Intent(LogService.this, LogService.class);
startService(refresh);
// Intent refresh = new Intent(this, LogService.class);
//startService(refresh);
Toast.makeText(this, "Refresh", Toast.LENGTH_SHORT).show();
}*/
// if (!mBluetoothAdapter.isEnabled()) {
// Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
// ab.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
//}
scanLeDevice(true);
}
/*
protected void onResume() {
// Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
// fire an intent to display a dialog asking the user to grant permission to enable it.
if (!mBluetoothAdapter.isEnabled()) {
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
//startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
// Initializes list view adapter.
scanLeDevice(true);
}*/
private void scanLeDevice(final boolean enable) {
if (!mBluetoothAdapter.isEnabled() && notifica == false) {
Toast.makeText(this, "Auto attivazione blueooth: Bluetooth attivato.", Toast.LENGTH_SHORT).show();
mBluetoothAdapter.enable();
stopService(new Intent(this, LogService.class));
startService(new Intent(this, LogService.class));
}
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i("Trovato", "Evento n.");
System.out.println("TROVATO");
notifica = true;
notifica(device.getAddress());
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
//System.out.println(device.getAddress());
onDestroy();
// mBluetoothAdapter.stopLeScan(mLeScanCallback);
try {
Thread.sleep(10000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
};
#Override
public void onDestroy()
{
Log.i("PROVA SERVICE", "Distruzione Service");
System.out.println("Distruzione");
// mScanning = false;
// mBluetoothAdapter.stopLeScan(mLeScanCallback); //� stato invocato il metodo onDestroy(), posso fermare la scansione
super.onDestroy();
}
private void runOnUiThread(Runnable runnable) {
handler.post(runnable);
}
private void notifica(String a) {
Log.i("Trovato", "Evento n.");
System.out.println("VAAAAAAAAA");
System.out.println("Indirizzo dispositivo" + a);
NotificationCompat.Builder n = new NotificationCompat.Builder(this);
// n.setLargeIcon(Imag);
n.setContentInfo("Affrettati!");
n.setContentTitle("La corriera e' in arrivo!");
n.setContentText(a);
n.setSmallIcon(R.drawable.bus);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, n.build());
sm.init(LogService.this);
sm.play();
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
Toast.makeText(this, "Auto disattivazione blueooth: Bluetooth disattivato.", Toast.LENGTH_SHORT).show();
mBluetoothAdapter.disable();
super.onDestroy();
}
}
Can someone help me? Thanks
mHandler = new Handler();
mHandler.onHandleIntent(Intent Var);
yes , when load your class .on create is the first run that method . so ,not initialized 'onHandleIntent' method.so , you call that method , within on create method.I think this will solve your problem.
note : you pass 'Intent' object as a parameter.
example :
Intent i = new Intent(LogService.this);
mHandler = new Handler();
mHandler.onHandleIntent(i);