diff --git a/app/build.gradle b/app/build.gradle
index 3dbe684a74..fe60a69960 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -68,6 +68,7 @@ android {
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ // if you change minSdkVersion to less than 11, you need to change executeTask for wear
ndk {
moduleName "BleCommandUtil"
@@ -194,7 +195,7 @@ dependencies {
implementation "org.slf4j:slf4j-api:1.7.12"
implementation "com.jjoe64:graphview:4.0.1"
implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.1.1"
- implementation "com.google.android.gms:play-services-wearable:7.5.0"
+ implementation 'com.google.android.gms:play-services-wearable:10.2.1'
implementation(name: "android-edittext-validator-v1.3.4-mod", ext: "aar")
implementation(name: "sightparser-release", ext: "aar")
implementation 'com.madgag.spongycastle:core:1.58.0.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2ec11e5a33..da3643e662 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -135,6 +135,7 @@
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
private GoogleApiClient googleApiClient;
- private static final String TAG = "SendDataThread";
- String path;
+ private static final String TAG = "SendToDataLayerThread";
+ private String path;
+ private String logPrefix = ""; // "WR: ";
+ private static int concurrency = 0;
+ private static int state = 0;
+ private static final ReentrantLock lock = new ReentrantLock();
+ private static long lastlock = 0;
+ private static final boolean testlockup = false; // always false in production
+
SendToDataLayerThread(String path, GoogleApiClient pGoogleApiClient) {
+ // Log.d(TAG, logPrefix + "SendToDataLayerThread: " + path);
this.path = path;
googleApiClient = pGoogleApiClient;
}
+
+ @Override
+ protected void onPreExecute() {
+ concurrency++;
+ if ((concurrency > 12) || ((concurrency > 3 && (lastlock != 0) && (tsl() - lastlock) > 300000))) {
+ // error if 9 concurrent threads or lock held for >5 minutes with concurrency of 4
+ final String err = "Wear Integration deadlock detected!! " + ((lastlock != 0) ? "locked" : "") + " state:"
+ + state + " @" + hourMinuteString(tsl());
+ // Home.toaststaticnext(err);
+ Log.e(TAG, logPrefix + err);
+ }
+ if (concurrency < 0)
+ Log.d(TAG, logPrefix + "Wear Integration impossible concurrency!!");
+
+ Log.d(TAG, logPrefix + "SendDataToLayerThread pre-execute concurrency: " + concurrency);
+ }
+
+
@Override
protected Void doInBackground(DataMap... params) {
- try {
- final NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await(15, TimeUnit.SECONDS);
- for (Node node : nodes.getNodes()) {
- for (DataMap dataMap : params) {
- PutDataMapRequest putDMR = PutDataMapRequest.create(path);
- putDMR.getDataMap().putAll(dataMap);
- PutDataRequest request = putDMR.asPutDataRequest();
- DataApi.DataItemResult result = Wearable.DataApi.putDataItem(googleApiClient, request).await(15, TimeUnit.SECONDS);
- if (result.getStatus().isSuccess()) {
- Log.d(TAG, "DataMap: " + dataMap + " sent to: " + node.getDisplayName());
- } else {
- Log.d(TAG, "ERROR: failed to send DataMap");
- }
- }
+ if (testlockup) {
+ try {
+ Log.e(TAG, logPrefix + "WARNING RUNNING TEST LOCK UP CODE - NEVER FOR PRODUCTION");
+ Thread.sleep(1000000); // DEEEBBUUGGGG
+ } catch (Exception e) {
}
- } catch (Exception e) {
- Log.e(TAG, "Got exception sending data to wear: " + e.toString());
}
+ sendToWear(params);
+ concurrency--;
+ Log.d(TAG, logPrefix + "SendDataToLayerThread post-execute concurrency: " + concurrency);
return null;
}
+
+
+ // Debug function to expose where it might be locking up
+ private synchronized void sendToWear(final DataMap... params) {
+ if (!lock.tryLock()) {
+ Log.d(TAG, logPrefix + "Concurrent access - waiting for thread unlock");
+ lock.lock(); // enforce single threading
+ Log.d(TAG, logPrefix + "Thread unlocked - proceeding");
+ }
+ lastlock = tsl();
+ try {
+ if (state != 0) {
+ Log.e(TAG, logPrefix + "WEAR STATE ERROR: state=" + state);
+ }
+ state = 1;
+ final NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await(15,
+ TimeUnit.SECONDS);
+
+ Log.d(TAG, logPrefix + "Nodes: " + nodes);
+
+ state = 2;
+ for (Node node : nodes.getNodes()) {
+ state = 3;
+ for (DataMap dataMap : params) {
+ state = 4;
+ PutDataMapRequest putDMR = PutDataMapRequest.create(path);
+ state = 5;
+ putDMR.getDataMap().putAll(dataMap);
+ putDMR.setUrgent();
+ state = 6;
+ PutDataRequest request = putDMR.asPutDataRequest();
+ state = 7;
+ DataApi.DataItemResult result = Wearable.DataApi.putDataItem(googleApiClient, request).await(15,
+ TimeUnit.SECONDS);
+ state = 8;
+ if (result.getStatus().isSuccess()) {
+ Log.d(TAG, logPrefix + "DataMap: " + dataMap + " sent to: " + node.getDisplayName());
+ } else {
+ Log.e(TAG, logPrefix + "ERROR: failed to send DataMap");
+ result = Wearable.DataApi.putDataItem(googleApiClient, request).await(30, TimeUnit.SECONDS);
+ if (result.getStatus().isSuccess()) {
+ Log.d(TAG, logPrefix + "DataMap retry: " + dataMap + " sent to: " + node.getDisplayName());
+ } else {
+ Log.e(TAG, logPrefix + "ERROR on retry: failed to send DataMap: "
+ + result.getStatus().toString());
+ }
+ }
+ state = 9;
+ }
+ }
+ state = 0;
+ } catch (Exception e) {
+ Log.e(TAG, logPrefix + "Got exception in sendToWear: " + e.toString());
+ } finally {
+ lastlock = 0;
+ lock.unlock();
+ }
+ }
+
+
+ private static long tsl() {
+ return System.currentTimeMillis();
+ }
+
+
+ private static String hourMinuteString(long timestamp) {
+ return android.text.format.DateFormat.format("kk:mm", timestamp).toString();
+ }
}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java
index 12dc116895..806ee9d31f 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java
@@ -1,9 +1,17 @@
package info.nightscout.androidaps.plugins.general.wear.wearintegration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.os.AsyncTask;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Handler;
@@ -14,8 +22,11 @@ import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.CapabilityApi;
+import com.google.android.gms.wearable.CapabilityInfo;
import com.google.android.gms.wearable.DataMap;
import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.PutDataMapRequest;
import com.google.android.gms.wearable.PutDataRequest;
import com.google.android.gms.wearable.Wearable;
@@ -42,21 +53,24 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorP
import info.nightscout.androidaps.plugins.treatments.Treatment;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.TreatmentsInterface;
-import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
+import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
+import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus;
import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler;
import info.nightscout.androidaps.plugins.general.wear.WearPlugin;
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
+import info.nightscout.androidaps.plugins.treatments.Treatment;
+import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.SafeParse;
import info.nightscout.androidaps.utils.ToastUtils;
-public class WatchUpdaterService extends WearableListenerService implements
- GoogleApiClient.ConnectionCallbacks,
- GoogleApiClient.OnConnectionFailedListener {
+public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+
public static final String ACTION_RESEND = WatchUpdaterService.class.getName().concat(".Resend");
public static final String ACTION_OPEN_SETTINGS = WatchUpdaterService.class.getName().concat(".OpenSettings");
public static final String ACTION_SEND_STATUS = WatchUpdaterService.class.getName().concat(".SendStatus");
@@ -91,6 +105,17 @@ public class WatchUpdaterService extends WearableListenerService implements
private Handler handler;
+ // Phone
+ private static final String CAPABILITY_PHONE_APP = "phone_app_sync_bgs";
+ private static final String MESSAGE_PATH_PHONE = "/phone_message_path";
+ // Wear
+ private static final String CAPABILITY_WEAR_APP = "wear_app_sync_bgs";
+ private static final String MESSAGE_PATH_WEAR = "/wear_message_path";
+ private String mWearNodeId = null;
+ private String localnode = null;
+ private String logPrefix = ""; // "WR: "
+
+
@Override
public void onCreate() {
mPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
@@ -112,24 +137,24 @@ public class WatchUpdaterService extends WearableListenerService implements
public void setSettings() {
wear_integration = WearPlugin.getPlugin().isEnabled(PluginType.GENERAL);
+ // Log.d(TAG, "WR: wear_integration=" + wear_integration);
if (wear_integration) {
googleApiConnect();
}
}
- public void googleApiConnect() {
+
+ private void googleApiConnect() {
if (googleApiClient != null && (googleApiClient.isConnected() || googleApiClient.isConnecting())) {
googleApiClient.disconnect();
}
- googleApiClient = new GoogleApiClient.Builder(this)
- .addConnectionCallbacks(this)
- .addOnConnectionFailedListener(this)
- .addApi(Wearable.API)
- .build();
+ googleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this)
+ .addOnConnectionFailedListener(this).addApi(Wearable.API).build();
Wearable.MessageApi.addListener(googleApiClient, this);
if (googleApiClient.isConnected()) {
- Log.d("WatchUpdater", "API client is connected");
+ Log.d(TAG, logPrefix + "API client is connected");
} else {
+ // Log.d("WatchUpdater", logPrefix + "API client is not connected and is trying to connect");
googleApiClient.connect();
}
}
@@ -138,6 +163,8 @@ public class WatchUpdaterService extends WearableListenerService implements
public int onStartCommand(Intent intent, int flags, int startId) {
String action = intent != null ? intent.getAction() : null;
+ // Log.d(TAG, logPrefix + "onStartCommand: " + action);
+
if (wear_integration) {
handler.post(() -> {
if (googleApiClient.isConnected()) {
@@ -177,13 +204,63 @@ public class WatchUpdaterService extends WearableListenerService implements
}
+ private void updateWearSyncBgsCapability(CapabilityInfo capabilityInfo) {
+ Log.d("WatchUpdaterService", logPrefix + "CabilityInfo: " + capabilityInfo);
+ Set connectedNodes = capabilityInfo.getNodes();
+ mWearNodeId = pickBestNodeId(connectedNodes);
+ }
+
+
+ private String pickBestNodeId(Set nodes) {
+ String bestNodeId = null;
+ // Find a nearby node or pick one arbitrarily
+ for (Node node : nodes) {
+ if (node.isNearby()) {
+ return node.getId();
+ }
+ bestNodeId = node.getId();
+ }
+ return bestNodeId;
+ }
+
+
@Override
public void onConnected(Bundle connectionHint) {
+ CapabilityApi.CapabilityListener capabilityListener = capabilityInfo -> {
+ updateWearSyncBgsCapability(capabilityInfo);
+ // Log.d(TAG, logPrefix + "onConnected onCapabilityChanged mWearNodeID:" + mWearNodeId);
+ // new CheckWearableConnected().execute();
+ };
+
+ Wearable.CapabilityApi.addCapabilityListener(googleApiClient, capabilityListener, CAPABILITY_WEAR_APP);
sendData();
}
+
+ @Override
+ public void onPeerConnected(com.google.android.gms.wearable.Node peer) {// KS
+ super.onPeerConnected(peer);
+ String id = peer.getId();
+ String name = peer.getDisplayName();
+ // Log.d(TAG, logPrefix + "onPeerConnected peer name & ID: " + name + "|" + id);
+ }
+
+
+ @Override
+ public void onPeerDisconnected(com.google.android.gms.wearable.Node peer) {// KS
+ super.onPeerDisconnected(peer);
+ String id = peer.getId();
+ String name = peer.getDisplayName();
+ // Log.d(TAG, logPrefix + "onPeerDisconnected peer name & ID: " + name + "|" + id);
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ }
+
+
@Override
public void onMessageReceived(MessageEvent event) {
+
+ // Log.d(TAG, logPrefix + "onMessageRecieved: " + event);
+
if (wear_integration) {
if (event != null && event.getPath().equals(WEARABLE_RESEND_PATH)) {
resendData();
@@ -214,6 +291,7 @@ public class WatchUpdaterService extends WearableListenerService implements
private void sendData() {
BgReading lastBG = DatabaseHelper.lastBg();
+ // Log.d(TAG, logPrefix + "LastBg=" + lastBG);
if (lastBG != null) {
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
@@ -228,18 +306,19 @@ public class WatchUpdaterService extends WearableListenerService implements
return;
}
- new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient).execute(dataMap);
+ executeTask(new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient), dataMap);
}
}
}
+
private DataMap dataMapSingleBG(BgReading lastBG, GlucoseStatus glucoseStatus) {
String units = ProfileFunctions.getInstance().getProfileUnits();
Double lowLine = SafeParse.stringToDouble(mPrefs.getString("low_mark", "0"));
Double highLine = SafeParse.stringToDouble(mPrefs.getString("high_mark", "0"));
- //convert to mg/dl
+ // convert to mg/dl
if (!units.equals(Constants.MGDL)) {
lowLine *= Constants.MMOLL_TO_MGDL;
highLine *= Constants.MMOLL_TO_MGDL;
@@ -287,7 +366,6 @@ public class WatchUpdaterService extends WearableListenerService implements
deltastring += "+";
} else {
deltastring += "-";
-
}
boolean detailed = SP.getBoolean("wear_detailed_delta", false);
@@ -352,7 +430,7 @@ public class WatchUpdaterService extends WearableListenerService implements
}
}
entries.putDataMapArrayList("entries", dataMaps);
- new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient).execute(entries);
+ executeTask(new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient), entries);
}
sendPreferences();
sendBasals();
@@ -501,7 +579,7 @@ public class WatchUpdaterService extends WearableListenerService implements
dm.putDataMapArrayList("boluses", boluses);
dm.putDataMapArrayList("predictions", predictions);
- new SendToDataLayerThread(BASAL_DATA_PATH, googleApiClient).execute(dm);
+ executeTask(new SendToDataLayerThread(BASAL_DATA_PATH, googleApiClient), dm);
}
private DataMap tempDatamap(long startTime, double startBasal, long to, double toBasal, double amount) {
@@ -548,6 +626,7 @@ public class WatchUpdaterService extends WearableListenerService implements
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putString("openSettings", "openSettings");
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
+ debugData("sendNotification", putDataRequest);
Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
} else {
Log.e("OpenSettings", "No connection to wearable available!");
@@ -563,6 +642,7 @@ public class WatchUpdaterService extends WearableListenerService implements
dataMapRequest.getDataMap().putString("progressstatus", status);
dataMapRequest.getDataMap().putInt("progresspercent", progresspercent);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
+ debugData("sendBolusProgress", putDataRequest);
Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
} else {
Log.e("BolusProgress", "No connection to wearable available!");
@@ -582,6 +662,7 @@ public class WatchUpdaterService extends WearableListenerService implements
log.debug("Requesting confirmation from wear: " + actionstring);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
+ debugData("sendActionConfirmationRequest", putDataRequest);
Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
} else {
Log.e("confirmationRequest", "No connection to wearable available!");
@@ -601,6 +682,7 @@ public class WatchUpdaterService extends WearableListenerService implements
log.debug("Requesting confirmation from wear: " + actionstring);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
+ debugData("sendChangeConfirmationRequest", putDataRequest);
Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
} else {
Log.e("changeConfirmRequest", "No connection to wearable available!");
@@ -618,6 +700,7 @@ public class WatchUpdaterService extends WearableListenerService implements
log.debug("Canceling notification on wear: " + actionstring);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
+ debugData("sendCancelNotificationRequest", putDataRequest);
Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
} else {
Log.e("cancelNotificationRequest", "No connection to wearable available!");
@@ -683,6 +766,7 @@ public class WatchUpdaterService extends WearableListenerService implements
dataMapRequest.getDataMap().putBoolean("showBgi", mPrefs.getBoolean("wear_showbgi", false));
dataMapRequest.getDataMap().putInt("batteryLevel", (phoneBattery >= 30) ? 1 : 0);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
+ debugData("sendStatus", putDataRequest);
Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
} else {
Log.e("SendStatus", "No connection to wearable available!");
@@ -699,12 +783,29 @@ public class WatchUpdaterService extends WearableListenerService implements
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putBoolean("wearcontrol", wearcontrol);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
+ debugData("sendPreferences", putDataRequest);
Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
} else {
Log.e("SendStatus", "No connection to wearable available!");
}
}
+
+ private void debugData(String source, Object data) {
+ // Log.d(TAG, "WR: " + source + " " + data);
+ }
+
+
+ private void executeTask(AsyncTask task, DataMap... parameters) {
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, parameters);
+ // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ // task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ // } else {
+ // task.execute();
+ // }
+ }
+
+
@NonNull
private String generateStatusString(Profile profile, String currentBasal, String iobSum, String iobDetail, String bgiString) {
diff --git a/app/src/main/res/values/wear.xml b/app/src/main/res/values/wear.xml
new file mode 100644
index 0000000000..41df8ef193
--- /dev/null
+++ b/app/src/main/res/values/wear.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ - phone_app_sync_bgs
+
+
\ No newline at end of file
diff --git a/wear/build.gradle b/wear/build.gradle
index 1dd07ff1a5..78a221837b 100644
--- a/wear/build.gradle
+++ b/wear/build.gradle
@@ -2,7 +2,7 @@ apply plugin: 'com.android.application'
ext {
wearableVersion = "2.0.1"
- playServicesWearable = "9.4.0"
+ playServicesWearable = "10.2.1"
}
def generateGitBuild = { ->
diff --git a/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java b/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java
index 326d154079..094b8aad76 100644
--- a/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java
+++ b/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java
@@ -1,5 +1,8 @@
package info.nightscout.androidaps.data;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -14,11 +17,16 @@ import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.wearable.CapabilityApi;
+import com.google.android.gms.wearable.CapabilityInfo;
import com.google.android.gms.wearable.ChannelApi;
import com.google.android.gms.wearable.DataEvent;
import com.google.android.gms.wearable.DataEventBuffer;
@@ -81,57 +89,156 @@ public class ListenerService extends WearableListenerService implements GoogleAp
private static final String TAG = "ListenerService";
private DataRequester mDataRequester = null;
+ private static final int GET_CAPABILITIES_TIMEOUT_MS = 5000;
+ // Phone
+ private static final String CAPABILITY_PHONE_APP = "phone_app_sync_bgs";
+ private static final String MESSAGE_PATH_PHONE = "/phone_message_path";
+ // Wear
+ private static final String CAPABILITY_WEAR_APP = "wear_app_sync_bgs";
+ private static final String MESSAGE_PATH_WEAR = "/wear_message_path";
+ private String mPhoneNodeId = null;
+ private String localnode = null;
+ private String logPrefix = ""; // "WR: "
public class DataRequester extends AsyncTask {
Context mContext;
String path;
byte[] payload;
-// DataRequester(Context context) {
-// mContext = context;
-// }
-
-
DataRequester(Context context, String thispath, byte[] thispayload) {
path = thispath;
payload = thispayload;
- Log.d(TAG, "DataRequester DataRequester: " + thispath + " lastRequest:" + lastRequest);
+ // Log.d(TAG, logPrefix + "DataRequester DataRequester: " + thispath + " lastRequest:" + lastRequest);
}
@Override
protected Void doInBackground(Void... params) {
- if (googleApiClient.isConnected()) {
- if (System.currentTimeMillis() - lastRequest > 20 * 1000) { // enforce 20-second debounce period
- lastRequest = System.currentTimeMillis();
+ // Log.d(TAG, logPrefix + "DataRequester: doInBack: " + params);
- NodeApi.GetConnectedNodesResult nodes =
- Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
- for (Node node : nodes.getNodes()) {
- Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), WEARABLE_RESEND_PATH, null);
- }
+ try {
+
+ forceGoogleApiConnect();
+ DataMap datamap;
+
+ if (isCancelled()) {
+ Log.d(TAG, "doInBackground CANCELLED programmatically");
+ return null;
}
- } else {
- googleApiClient.blockingConnect(15, TimeUnit.SECONDS);
- if (googleApiClient.isConnected()) {
- if (System.currentTimeMillis() - lastRequest > 20 * 1000) { // enforce 20-second debounce period
+
+ if (googleApiClient != null) {
+ if (!googleApiClient.isConnected())
+ googleApiClient.blockingConnect(15, TimeUnit.SECONDS);
+ }
+
+ // this code might not be needed in this way, but we need to see that later
+ if ((googleApiClient != null) && (googleApiClient.isConnected())) {
+ if ((System.currentTimeMillis() - lastRequest > 20 * 1000)) {
+
+ // enforce 20-second debounce period
lastRequest = System.currentTimeMillis();
- NodeApi.GetConnectedNodesResult nodes =
- Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
- for (Node node : nodes.getNodes()) {
- Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), WEARABLE_RESEND_PATH, null);
+ // NodeApi.GetConnectedNodesResult nodes =
+ // Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
+ if (localnode == null || (localnode != null && localnode.isEmpty()))
+ setLocalNodeName();
+
+ CapabilityInfo capabilityInfo = getCapabilities();
+
+ int count = 0;
+ Node phoneNode = null;
+
+ if (capabilityInfo != null) {
+ phoneNode = updatePhoneSyncBgsCapability(capabilityInfo);
+ count = capabilityInfo.getNodes().size();
}
+
+ Log.d(TAG, "doInBackground connected. CapabilityApi.GetCapabilityResult mPhoneNodeID="
+ + (phoneNode != null ? phoneNode.getId() : "") + " count=" + count + " localnode="
+ + localnode);// KS
+
+ if (count > 0) {
+
+ for (Node node : capabilityInfo.getNodes()) {
+
+ // Log.d(TAG, "doInBackground path: " + path);
+
+ switch (path) {
+ // simple send as is payloads
+
+ case WEARABLE_RESEND_PATH:
+ Wearable.MessageApi.sendMessage(googleApiClient, node.getId(),
+ WEARABLE_RESEND_PATH, null);
+ break;
+ case WEARABLE_DATA_PATH:
+ case WEARABLE_CANCELBOLUS_PATH:
+ case WEARABLE_CONFIRM_ACTIONSTRING_PATH:
+ case WEARABLE_INITIATE_ACTIONSTRING_PATH:
+ case OPEN_SETTINGS:
+ case NEW_STATUS_PATH:
+ case NEW_PREFERENCES_PATH:
+ case BASAL_DATA_PATH:
+ case BOLUS_PROGRESS_PATH:
+ case ACTION_CONFIRMATION_REQUEST_PATH:
+ case NEW_CHANGECONFIRMATIONREQUEST_PATH:
+ case ACTION_CANCELNOTIFICATION_REQUEST_PATH: {
+ Log.w(TAG, logPrefix + "Unhandled path");
+ // sendMessagePayload(node, path, path, payload);
+ }
+
+ default:// SYNC_ALL_DATA
+ // this fall through is messy and non-deterministic for new paths
+
+ }
+ }
+ } else {
+
+ Log.d(TAG, logPrefix + "doInBackground connected but getConnectedNodes returns 0.");
+
+ }
+ } else {
+ // no resend
+ Log.d(TAG, logPrefix + "Inside the timeout, will not be executed");
+
+ }
+ } else {
+ Log.d(TAG, logPrefix + "Not connected for sending: api "
+ + ((googleApiClient == null) ? "is NULL!" : "not null"));
+ if (googleApiClient != null) {
+ googleApiClient.connect();
+ } else {
+ googleApiConnect();
}
}
+
+ } catch (Exception ex) {
+ Log.e(TAG, logPrefix + "Error executing DataRequester in background. Exception: " + ex.getMessage());
}
+
return null;
}
}
+
+ public CapabilityInfo getCapabilities() {
+
+ CapabilityApi.GetCapabilityResult capabilityResult = Wearable.CapabilityApi.getCapability(googleApiClient,
+ CAPABILITY_PHONE_APP, CapabilityApi.FILTER_REACHABLE).await(GET_CAPABILITIES_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS);
+
+ if (!capabilityResult.getStatus().isSuccess()) {
+ Log.e(TAG, logPrefix + "doInBackground Failed to get capabilities, status: "
+ + capabilityResult.getStatus().getStatusMessage());
+ return null;
+ }
+
+ return capabilityResult.getCapability();
+
+ }
+
public class BolusCancelTask extends AsyncTask {
Context mContext;
@@ -141,6 +248,8 @@ public class ListenerService extends WearableListenerService implements GoogleAp
@Override
protected Void doInBackground(Void... params) {
+ // Log.d(TAG, logPrefix + "BolusCancelTask: doInBack: " + params);
+
if (googleApiClient.isConnected()) {
NodeApi.GetConnectedNodesResult nodes =
Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
@@ -217,29 +326,54 @@ public class ListenerService extends WearableListenerService implements GoogleAp
}
- private synchronized void sendData(String path, byte[] payload) {
- if (path == null) return;
- if (mDataRequester != null) {
- Log.d(TAG, "sendData DataRequester != null lastRequest:" + WearUtil.dateTimeText(lastRequest));
- if (mDataRequester.getStatus() != AsyncTask.Status.FINISHED) {
- Log.d(TAG, "sendData Should be canceled? Let run 'til finished.");
- //mDataRequester.cancel(true);
+ private Node updatePhoneSyncBgsCapability(CapabilityInfo capabilityInfo) {
+ // Log.d(TAG, "CapabilityInfo: " + capabilityInfo);
+
+ Set connectedNodes = capabilityInfo.getNodes();
+ return pickBestNode(connectedNodes);
+ // mPhoneNodeId = pickBestNodeId(connectedNodes);
+ }
+
+
+ private Node pickBestNode(Set nodes) {
+ Node bestNode = null;
+ // Find a nearby node or pick one arbitrarily
+ for (Node node : nodes) {
+ if (node.isNearby()) {
+ return node;
+ }
+ bestNode = node;
+ }
+ return bestNode;
+ }
+
+
+ private synchronized void sendData(String path, byte[] payload) {
+ // Log.d(TAG, "WR: sendData: path: " + path + ", payload=" + payload);
+
+ if (path == null)
+ return;
+ if (mDataRequester != null) {
+ // Log.d(TAG, logPrefix + "sendData DataRequester != null lastRequest:" +
+ // WearUtil.dateTimeText(lastRequest));
+ if (mDataRequester.getStatus() != AsyncTask.Status.FINISHED) {
+ // Log.d(TAG, logPrefix + "sendData Should be canceled? Let run 'til finished.");
+ // mDataRequester.cancel(true);
}
- //mDataRequester = null;
}
- Log.d(TAG, "sendData: execute lastRequest:" + WearUtil.dateTimeText(lastRequest));
- mDataRequester = (DataRequester) new DataRequester(this, path, payload).execute();
+ Log.d(TAG, logPrefix + "sendData: execute lastRequest:" + WearUtil.dateTimeText(lastRequest));
+ mDataRequester = (DataRequester)new DataRequester(this, path, payload).execute();
+ // executeTask(mDataRequester);
-
-// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
-// Log.d(TAG, "sendData SDK < M call execute lastRequest:" + WearUtil.dateTimeText(lastRequest));
-// mDataRequester = (DataRequester) new DataRequester(this, path, payload).execute();
-// } else {
-// Log.d(TAG, "sendData SDK >= M call executeOnExecutor lastRequest:" + WearUtil.dateTimeText(lastRequest));
-// // TODO xdrip executor
-// mDataRequester = (DataRequester) new DataRequester(this, path, payload).executeOnExecutor(xdrip.executor);
-// }
+ // if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ // Log.d(TAG, "sendData SDK < M call execute lastRequest:" + WearUtil.dateTimeText(lastRequest));
+ // mDataRequester = (DataRequester) new DataRequester(this, path, payload).execute();
+ // } else {
+ // Log.d(TAG, "sendData SDK >= M call executeOnExecutor lastRequest:" + WearUtil.dateTimeText(lastRequest));
+ // // TODO xdrip executor
+ // mDataRequester = (DataRequester) new DataRequester(this, path, payload).executeOnExecutor(xdrip.executor);
+ // }
}
@@ -283,6 +417,9 @@ public class ListenerService extends WearableListenerService implements GoogleAp
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
+
+ // Log.d(TAG, logPrefix + "onStartCommand: Intent: " + intent);
+
if (intent != null && ACTION_RESEND.equals(intent.getAction())) {
googleApiConnect();
requestData();
@@ -336,6 +473,7 @@ public class ListenerService extends WearableListenerService implements GoogleAp
public void onDataChanged(DataEventBuffer dataEvents) {
DataMap dataMap;
+ // Log.d(TAG, logPrefix + "onDataChanged: DataEvents=" + dataEvents);
for (DataEvent event : dataEvents) {
@@ -343,7 +481,7 @@ public class ListenerService extends WearableListenerService implements GoogleAp
String path = event.getDataItem().getUri().getPath();
- Log.d(TAG, "Path: {}" + path + ", Event=" + event);
+ //Log.d(TAG, "WR: onDataChanged: Path: " + path + ", EventDataItem=" + event.getDataItem());
if (path.equals(OPEN_SETTINGS)) {
Intent intent = new Intent(this, AAPSPreferences.class);
@@ -562,7 +700,19 @@ public class ListenerService extends WearableListenerService implements GoogleAp
@Override
public void onConnected(Bundle bundle) {
- Log.d(TAG, "onConnected call requestData");
+ // Log.d(TAG, logPrefix + "onConnected call requestData");
+
+ CapabilityApi.CapabilityListener capabilityListener = new CapabilityApi.CapabilityListener() {
+
+ @Override
+ public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
+ updatePhoneSyncBgsCapability(capabilityInfo);
+ Log.d(TAG, logPrefix + "onConnected onCapabilityChanged mPhoneNodeID:" + mPhoneNodeId
+ + ", Capability: " + capabilityInfo);
+ }
+ };
+
+ Wearable.CapabilityApi.addCapabilityListener(googleApiClient, capabilityListener, CAPABILITY_PHONE_APP);
Wearable.ChannelApi.addListener(googleApiClient, this);
requestData();
@@ -578,6 +728,28 @@ public class ListenerService extends WearableListenerService implements GoogleAp
}
+
+ private void setLocalNodeName() {
+ forceGoogleApiConnect();
+ PendingResult result = Wearable.NodeApi.getLocalNode(googleApiClient);
+ result.setResultCallback(new ResultCallback() {
+
+ @Override
+ public void onResult(NodeApi.GetLocalNodeResult getLocalNodeResult) {
+ if (!getLocalNodeResult.getStatus().isSuccess()) {
+ Log.e(TAG, "ERROR: failed to getLocalNode Status="
+ + getLocalNodeResult.getStatus().getStatusMessage());
+ } else {
+ Log.d(TAG, "getLocalNode Status=: " + getLocalNodeResult.getStatus().getStatusMessage());
+ Node getnode = getLocalNodeResult.getNode();
+ localnode = getnode != null ? getnode.getDisplayName() + "|" + getnode.getId() : "";
+ Log.d(TAG, "setLocalNodeName. localnode=" + localnode);
+ }
+ }
+ });
+ }
+
+
@Override
public void onDestroy() {
super.onDestroy();
diff --git a/wear/src/main/res/values/wear.xml b/wear/src/main/res/values/wear.xml
new file mode 100644
index 0000000000..41df8ef193
--- /dev/null
+++ b/wear/src/main/res/values/wear.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ - phone_app_sync_bgs
+
+
\ No newline at end of file