mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-27 12:26:48 +01:00
Put Bluetooth communication into a foreground service and keep socket open.
This commit is contained in:
parent
cc2b224a71
commit
85fcdb208a
@ -16,8 +16,6 @@ Known Visible Issues:
|
||||
* No special notifications, EVERYTHING will be send as a Chat/SMS message
|
||||
* Notifications are not properly queued, if two arrive at about the same time,
|
||||
one of them will get lost
|
||||
* Connection to Pebble will be reopened and closed for every message (dont know
|
||||
if this saves or consumes more energy)
|
||||
* Android 4.4+ only, we can only change this by implementing an
|
||||
AccessibiltyService. Don't know if it is worth the hassle.
|
||||
* This will open the dialog to allow capturing notifications every time the
|
||||
|
@ -12,10 +12,10 @@
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name="nodomain.freeyourgadget.gadgetbridge.SettingsActivity"
|
||||
android:name=".SettingsActivity"
|
||||
android:label="@string/app_name"></activity>
|
||||
<activity
|
||||
android:name="nodomain.freeyourgadget.gadgetbridge.ControlCenter"
|
||||
android:name=".ControlCenter"
|
||||
android:label="@string/title_activity_main">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@ -24,13 +24,15 @@
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name="nodomain.freeyourgadget.gadgetbridge.NotificationListener"
|
||||
android:name=".NotificationListener"
|
||||
android:label="@string/app_name"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.notification.NotificationListenerService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service android:name=".BluetoothCommunicationService" >
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
@ -0,0 +1,166 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelUuid;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Set;
|
||||
|
||||
public class BluetoothCommunicationService extends Service {
|
||||
private static final String TAG = "BluetoothCommunicationService";
|
||||
|
||||
// TODO: put in separate static class
|
||||
public static final String ACTION_STARTBLUETOOTHCOMMUNITCATIONSERVICE
|
||||
= "nodomain.freeyourgadget.gadgetbride.bluetoothcommunicationservice.action.start";
|
||||
public static final String ACTION_STOPBLUETOOTHCOMMUNITCATIONSERVICE
|
||||
= "nodomain.freeyourgadget.gadgetbride.bluetoothcommunicationservice.action.stop";
|
||||
public static final String ACTION_SENDBLUETOOTHMESSAGE
|
||||
= "nodomain.freeyourgadget.gadgetbride.bluetoothcommunicationservice.action.sendbluetoothmessage";
|
||||
|
||||
private BluetoothAdapter mBtAdapter = null;
|
||||
private String mBtDeviceAddress = null;
|
||||
private BluetoothSocket mBtSocket = null;
|
||||
private BtSocketIoThread mBtSocketIoThread = null;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent.getAction().equals(ACTION_STARTBLUETOOTHCOMMUNITCATIONSERVICE)) {
|
||||
Intent notificationIntent = new Intent(this, ControlCenter.class);
|
||||
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
|
||||
notificationIntent, 0);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(this)
|
||||
.setContentTitle("Gadgetbridge")
|
||||
.setTicker("Gadgetbridge Running")
|
||||
.setContentText("Gadgetbrige Running")
|
||||
.setSmallIcon(R.drawable.ic_launcher)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setOngoing(true).build();
|
||||
|
||||
|
||||
//Check the system status
|
||||
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (mBtAdapter == null) {
|
||||
Toast.makeText(this, "Bluetooth is not supported.", Toast.LENGTH_SHORT).show();
|
||||
} else if (!mBtAdapter.isEnabled()) {
|
||||
Toast.makeText(this, "Bluetooth is disabled.", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
|
||||
for (BluetoothDevice device : pairedDevices) {
|
||||
if (device.getName().indexOf("Pebble") == 0) {
|
||||
// Matching device found
|
||||
mBtDeviceAddress = device.getAddress();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (mBtSocket == null || !mBtSocket.isConnected()) {
|
||||
BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(mBtDeviceAddress);
|
||||
ParcelUuid uuids[] = btDevice.getUuids();
|
||||
mBtSocket = btDevice.createRfcommSocketToServiceRecord(uuids[0].getUuid());
|
||||
mBtSocket.connect();
|
||||
mBtSocketIoThread = new BtSocketIoThread(mBtSocket.getInputStream(), mBtSocket.getOutputStream());
|
||||
mBtSocketIoThread.start();
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
startForeground(1, notification); //FIXME: don't hardcode id
|
||||
}
|
||||
} else if (intent.getAction().equals(ACTION_STOPBLUETOOTHCOMMUNITCATIONSERVICE)) {
|
||||
try {
|
||||
mBtSocketIoThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
mBtSocket.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mBtSocket = null;
|
||||
mBtSocketIoThread = null;
|
||||
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
} else if (intent.getAction().equals(ACTION_SENDBLUETOOTHMESSAGE)) {
|
||||
String title = intent.getStringExtra("notification_title");
|
||||
String content = intent.getStringExtra("notification_content");
|
||||
if (mBtSocketIoThread != null) {
|
||||
byte[] msg;
|
||||
msg = PebbleProtocol.encodeSMS(title, content);
|
||||
mBtSocketIoThread.write(msg);
|
||||
}
|
||||
}
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private class BtSocketIoThread extends Thread {
|
||||
private final InputStream mmInStream;
|
||||
private final OutputStream mmOutStream;
|
||||
|
||||
public BtSocketIoThread(InputStream instream, OutputStream outstream) {
|
||||
mmInStream = instream;
|
||||
mmOutStream = outstream;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
byte[] buffer = new byte[1000]; // buffer store for the stream
|
||||
int bytes; // bytes returned from read()
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
byte[] ping = {0, 0, 0, 0};
|
||||
// Read from the InputStream
|
||||
//bytes = mmInStream.read(buffer,0,2);
|
||||
//ByteBuffer buf = ByteBuffer.wrap(buffer);
|
||||
//buf.order(ByteOrder.BIG_ENDIAN);
|
||||
//short length = buf.getShort();
|
||||
//Log.e(TAG,Integer.toString(length+4) + " as total length");
|
||||
bytes = mmInStream.read(buffer);
|
||||
Log.e(TAG, Integer.toString(bytes) + ": " + PebbleProtocol.decodeResponse(buffer));
|
||||
write(ping);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized public void write(byte[] bytes) {
|
||||
try {
|
||||
mmOutStream.write(bytes);
|
||||
mmOutStream.flush();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,9 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.view.Menu;
|
||||
@ -18,77 +11,48 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ControlCenter extends ActionBarActivity {
|
||||
// SPP Serial Device UUID
|
||||
private static final UUID SERIAL_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
BluetoothAdapter mBtAdapter;
|
||||
String mBtDeviceAddress = null;
|
||||
BluetoothSocket mBtSocket;
|
||||
Button sendButton;
|
||||
Button testNotificationButton;
|
||||
Button startServiceButton;
|
||||
EditText editTitle;
|
||||
EditText editContent;
|
||||
private NotificationReceiver nReceiver;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_controlcenter);
|
||||
|
||||
//Check the system status
|
||||
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (mBtAdapter == null) {
|
||||
Toast.makeText(this, "Bluetooth is not supported.", Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (!mBtAdapter.isEnabled()) {
|
||||
Toast.makeText(this, "Bluetooth is disabled.", Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
|
||||
for (BluetoothDevice device : pairedDevices) {
|
||||
if (device.getName().indexOf("Pebble") == 0) {
|
||||
// Matching device found
|
||||
mBtDeviceAddress = device.getAddress();
|
||||
}
|
||||
}
|
||||
|
||||
editTitle = (EditText) findViewById(R.id.editTitle);
|
||||
editContent = (EditText) findViewById(R.id.editContent);
|
||||
startServiceButton = (Button) findViewById(R.id.startServiceButton);
|
||||
startServiceButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent startIntent = new Intent(ControlCenter.this, BluetoothCommunicationService.class);
|
||||
startIntent.setAction(BluetoothCommunicationService.ACTION_STARTBLUETOOTHCOMMUNITCATIONSERVICE);
|
||||
startService(startIntent);
|
||||
}
|
||||
});
|
||||
sendButton = (Button) findViewById(R.id.sendButton);
|
||||
sendButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
if (!mBtAdapter.isEnabled() || mBtDeviceAddress == null)
|
||||
return;
|
||||
String title = editTitle.getText().toString();
|
||||
String content = editContent.getText().toString();
|
||||
try {
|
||||
if (mBtSocket == null || !mBtSocket.isConnected()) {
|
||||
BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(mBtDeviceAddress);
|
||||
mBtSocket = btDevice.createRfcommSocketToServiceRecord(SERIAL_UUID);
|
||||
mBtSocket.connect();
|
||||
}
|
||||
ConnectedTask task = new ConnectedTask();
|
||||
task.execute(mBtSocket, title, content);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Intent startIntent = new Intent(ControlCenter.this, BluetoothCommunicationService.class);
|
||||
startIntent.setAction(BluetoothCommunicationService.ACTION_SENDBLUETOOTHMESSAGE);
|
||||
startIntent.putExtra("notification_title", editTitle.getText().toString());
|
||||
startIntent.putExtra("notification_content", editTitle.getText().toString());
|
||||
startService(startIntent);
|
||||
}
|
||||
});
|
||||
|
||||
testNotificationButton = (Button) findViewById(R.id.testNotificationButton);
|
||||
testNotificationButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@ -100,11 +64,9 @@ public class ControlCenter extends ActionBarActivity {
|
||||
Intent enableIntent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
|
||||
|
||||
startActivity(enableIntent);
|
||||
nReceiver = new NotificationReceiver();
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction("nodomain.freeyourgadget.gadgetbridge.NOTIFICATION_LISTENER");
|
||||
registerReceiver(nReceiver, filter);
|
||||
}
|
||||
|
||||
private void testNotification() {
|
||||
@ -112,7 +74,7 @@ public class ControlCenter extends ActionBarActivity {
|
||||
NotificationCompat.Builder ncomp = new NotificationCompat.Builder(this);
|
||||
ncomp.setContentTitle("Test Notification");
|
||||
ncomp.setContentText("This is a Test Notification from Gadgetbridge");
|
||||
ncomp.setTicker("This is a Test Notificytion from Gadgetbridge");
|
||||
ncomp.setTicker("This is a Test Notification from Gadgetbridge");
|
||||
ncomp.setSmallIcon(R.drawable.ic_launcher);
|
||||
ncomp.setAutoCancel(true);
|
||||
nManager.notify((int) System.currentTimeMillis(), ncomp.build());
|
||||
@ -141,99 +103,4 @@ public class ControlCenter extends ActionBarActivity {
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
try {
|
||||
if (mBtSocket != null) {
|
||||
mBtSocket.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (nReceiver != null) {
|
||||
unregisterReceiver(nReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//AsyncTask to receive a single line of data and post
|
||||
private class ConnectedTask extends
|
||||
AsyncTask<Object, Void, String> {
|
||||
@Override
|
||||
protected String doInBackground(
|
||||
Object... params) {
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
BluetoothSocket socket = (BluetoothSocket) params[0];
|
||||
String title = (String) params[1];
|
||||
String content = (String) params[2];
|
||||
try {
|
||||
byte[] buffer = new byte[1024];
|
||||
String result;
|
||||
in = socket.getInputStream();
|
||||
|
||||
//in.read(buffer);
|
||||
//result = PebbleProtocol.decodeResponse(buffer);
|
||||
|
||||
out = socket.getOutputStream();
|
||||
|
||||
|
||||
byte[] msg;
|
||||
msg = PebbleProtocol.encodeSMS(title, content);
|
||||
|
||||
//msg = PebbleProtocol.encodeSetTime();
|
||||
//msg = PebbleProtocol.encodeIncomingCall("03012323", title);
|
||||
//msg = PebbleProtocol.encodeEmail(title, "subject", content);
|
||||
|
||||
out.write(msg);
|
||||
SystemClock.sleep(500);
|
||||
//in.read(buffer);
|
||||
//result = PebbleProtocol.decodeResponse(buffer);
|
||||
result = "ok";
|
||||
//Close the connection
|
||||
return result.trim();
|
||||
} catch (Exception exc) {
|
||||
return "error";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
Toast.makeText(ControlCenter.this, result,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
||||
try {
|
||||
mBtSocket.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class NotificationReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!mBtAdapter.isEnabled() || mBtDeviceAddress == null)
|
||||
return;
|
||||
|
||||
String title = intent.getStringExtra("notification_title");
|
||||
String content = intent.getStringExtra("notification_content");
|
||||
try {
|
||||
if (mBtSocket == null || !mBtSocket.isConnected()) {
|
||||
BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(mBtDeviceAddress);
|
||||
mBtSocket = btDevice.createRfcommSocketToServiceRecord(SERIAL_UUID);
|
||||
mBtSocket.connect();
|
||||
}
|
||||
ConnectedTask task = new ConnectedTask();
|
||||
task.execute(mBtSocket, title, content);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -22,14 +22,20 @@ public class NotificationListener extends NotificationListenerService {
|
||||
|
||||
@Override
|
||||
public void onNotificationPosted(StatusBarNotification sbn) {
|
||||
Intent i = new Intent("nodomain.freeyourgadget.gadgetbridge.NOTIFICATION_LISTENER");
|
||||
Notification notification = sbn.getNotification();
|
||||
Bundle extras = notification.extras;
|
||||
String title = extras.getCharSequence(Notification.EXTRA_TITLE).toString();
|
||||
String content = extras.getCharSequence(Notification.EXTRA_TEXT).toString();
|
||||
i.putExtra("notification_title", title);
|
||||
i.putExtra("notification_content", content);
|
||||
sendBroadcast(i);
|
||||
String title = extras.getString(Notification.EXTRA_TITLE);
|
||||
String content = "";
|
||||
if (extras.containsKey(Notification.EXTRA_TEXT))
|
||||
content = extras.getString(Notification.EXTRA_TEXT);
|
||||
|
||||
if (content != null) {
|
||||
Intent startIntent = new Intent(NotificationListener.this, BluetoothCommunicationService.class);
|
||||
startIntent.setAction(BluetoothCommunicationService.ACTION_SENDBLUETOOTHMESSAGE);
|
||||
startIntent.putExtra("notification_title", title);
|
||||
startIntent.putExtra("notification_content", content);
|
||||
startService(startIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -126,7 +126,7 @@ public class PebbleProtocol {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
String ret = Short.toString(length) + "/" + Short.toString(endpoint) + "/" + Byte.toString(extra);
|
||||
String ret = "length: " + Short.toString(length) + " Endpoint:" + Short.toString(endpoint) + "/" + Byte.toString(extra);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -51,4 +51,13 @@
|
||||
android:text="Create test Notification"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="start Service"
|
||||
android:id="@+id/startServiceButton"
|
||||
android:layout_above="@+id/sendButton"
|
||||
android:layout_alignEnd="@+id/sendButton"
|
||||
android:layout_alignParentStart="true" />
|
||||
</RelativeLayout>
|
||||
|
Loading…
Reference in New Issue
Block a user