diff --git a/README.md b/README.md index d7be6776..a7b8dd01 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9816dc3f..c135f163 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,10 +12,10 @@ android:label="@string/app_name" android:theme="@style/AppTheme"> @@ -24,13 +24,15 @@ + + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/BluetoothCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/BluetoothCommunicationService.java new file mode 100644 index 00000000..605557ac --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/BluetoothCommunicationService.java @@ -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 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) { + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ControlCenter.java index b8a0923e..eb543160 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/ControlCenter.java @@ -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 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 { - @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(); - } - - } - } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/NotificationListener.java index bad25cfa..6b1b274d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/NotificationListener.java @@ -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 diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/PebbleProtocol.java index aa4f3f99..b0806344 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/PebbleProtocol.java @@ -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; } diff --git a/app/src/main/res/layout/activity_controlcenter.xml b/app/src/main/res/layout/activity_controlcenter.xml index ff147381..616f7321 100644 --- a/app/src/main/res/layout/activity_controlcenter.xml +++ b/app/src/main/res/layout/activity_controlcenter.xml @@ -51,4 +51,13 @@ android:text="Create test Notification" android:layout_alignParentBottom="true" android:layout_alignParentStart="true" /> + +