diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index fe72da5070..2cb270bcda 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -10,6 +10,7 @@
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 1f66ee889a..beeeadee79 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -4,6 +4,7 @@
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 66b3675c8d..74f1b69f37 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -57,46 +57,62 @@ android {
}
}
productFlavors {
+ flavorDimensions "standard", "wear"
full {
+ dimension "standard"
buildConfigField "boolean", "APS", "true"
buildConfigField "boolean", "PUMPDRIVERS", "true"
buildConfigField "boolean", "CLOSEDLOOP", "true"
buildConfigField "int", "MAXBOLUS", "17"
}
fullteenage {
+ dimension "standard"
buildConfigField "boolean", "APS", "true"
buildConfigField "boolean", "PUMPDRIVERS", "true"
buildConfigField "boolean", "CLOSEDLOOP", "true"
buildConfigField "int", "MAXBOLUS", "10"
}
fullchild {
+ dimension "standard"
buildConfigField "boolean", "APS", "true"
buildConfigField "boolean", "PUMPDRIVERS", "true"
buildConfigField "boolean", "CLOSEDLOOP", "true"
buildConfigField "int", "MAXBOLUS", "5"
}
danarcontrol {
+ dimension "standard"
buildConfigField "boolean", "APS", "false"
buildConfigField "boolean", "PUMPDRIVERS", "true"
buildConfigField "boolean", "CLOSEDLOOP", "false"
buildConfigField "int", "MAXBOLUS", "17"
}
careportal {
+ dimension "standard"
buildConfigField "boolean", "APS", "false"
buildConfigField "boolean", "PUMPDRIVERS", "false"
buildConfigField "boolean", "CLOSEDLOOP", "false"
buildConfigField "int", "MAXBOLUS", "17"
}
openloop {
+ dimension "standard"
buildConfigField "boolean", "APS", "true"
buildConfigField "boolean", "PUMPDRIVERS", "true"
buildConfigField "boolean", "CLOSEDLOOP", "false"
buildConfigField "int", "MAXBOLUS", "17"
}
+ wear {
+ dimension "wear"
+ buildConfigField "boolean", "WEAR", "true"
+ }
+ nowear {
+ dimension "wear"
+ buildConfigField "boolean", "WEAR", "false"
+ }
}
}
dependencies {
+ wearWearApp project(path: ':wear', configuration: 'fullRelease')
compile fileTree(dir: 'libs', include: ['*.jar'])
compile('com.crashlytics.sdk.android:crashlytics:2.5.7@aar') {
transitive = true;
@@ -116,4 +132,5 @@ dependencies {
compile 'com.eclipsesource.j2v8:j2v8:3.1.6@aar'
compile 'com.joanzapata.iconify:android-iconify-fontawesome:2.1.1'
testCompile 'junit:junit:4.12'
+ compile 'com.google.android.gms:play-services-wearable:7.5.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f7d91f83c1..b186abbbed 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,6 +14,7 @@
+
@@ -80,6 +81,10 @@
android:enabled="true"
android:exported="false" />
+
+
+
+
diff --git a/app/src/main/java/info/nightscout/androidaps/Config.java b/app/src/main/java/info/nightscout/androidaps/Config.java
index eeffc2a729..851ecdb39c 100644
--- a/app/src/main/java/info/nightscout/androidaps/Config.java
+++ b/app/src/main/java/info/nightscout/androidaps/Config.java
@@ -9,6 +9,7 @@ public class Config {
// PLUGINS
public static final boolean OPENAPSMAENABLED = APS;
public static final boolean LOOPENABLED = APS;
+ public static final boolean WEAR = BuildConfig.WEAR;
public static final boolean CAREPORTALENABLED = true;
public static final boolean SMSCOMMUNICATORENABLED = true;
diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java
index 6eb3e03e98..b2f46cddb8 100644
--- a/app/src/main/java/info/nightscout/androidaps/MainApp.java
+++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java
@@ -36,6 +36,7 @@ import info.nightscout.androidaps.plugins.SourceXdrip.SourceXdripFragment;
import info.nightscout.androidaps.plugins.TempBasals.TempBasalsFragment;
import info.nightscout.androidaps.plugins.Treatments.TreatmentsFragment;
import info.nightscout.androidaps.plugins.VirtualPump.VirtualPumpFragment;
+import info.nightscout.androidaps.plugins.Wear.WearFragment;
import io.fabric.sdk.android.Fabric;
@@ -85,6 +86,9 @@ public class MainApp extends Application {
pluginsList.add(SourceNSClientFragment.getPlugin());
if (Config.SMSCOMMUNICATORENABLED)
pluginsList.add(SmsCommunicatorFragment.getPlugin());
+
+ if (Config.WEAR) pluginsList.add(WearFragment.getPlugin(this));
+
pluginsList.add(sConfigBuilder = ConfigBuilderFragment.getPlugin());
MainApp.getConfigBuilder().initialize();
diff --git a/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java b/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java
index e1d7af8662..835fec8a59 100644
--- a/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java
+++ b/app/src/main/java/info/nightscout/androidaps/db/TempBasal.java
@@ -204,4 +204,25 @@ public class TempBasal {
}
}
+ public String toStringShort() {
+ String extended = isExtended ? "E" : "";
+
+ if (isAbsolute) {
+ return extended + DecimalFormatter.to2Decimal(absolute) + "U/h ";
+ } else { // percent
+ return percent + "% ";
+ }
+ }
+
+ public String toStringMedium() {
+ String extended = isExtended ? "E" : "";
+
+ if (isAbsolute) {
+ return extended + DecimalFormatter.to2Decimal(absolute) + "U/h ("
+ + getRealDuration() + "/" + duration + ") ";
+ } else { // percent
+ return percent + "% (" + getRealDuration() + "/" + duration + ") ";
+ }
+ }
+
}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java
index 7cff64a3da..1d9100f216 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java
@@ -3,12 +3,14 @@ package info.nightscout.androidaps.plugins.Overview;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.DialogInterface;
+import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AlertDialog;
@@ -72,6 +74,7 @@ import info.nightscout.utils.BolusWizard;
import info.nightscout.utils.DateUtil;
import info.nightscout.utils.DecimalFormatter;
import info.nightscout.utils.Round;
+import info.nightscout.utils.SafeParse;
import info.nightscout.utils.ToastUtils;
@@ -79,6 +82,7 @@ public class OverviewFragment extends Fragment {
private static Logger log = LoggerFactory.getLogger(OverviewFragment.class);
private static OverviewPlugin overviewPlugin = new OverviewPlugin();
+ private SharedPreferences prefs;
public static OverviewPlugin getPlugin() {
return overviewPlugin;
@@ -121,6 +125,8 @@ public class OverviewFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
+ prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+
View view = inflater.inflate(R.layout.overview_fragment, container, false);
bgView = (TextView) view.findViewById(R.id.overview_bg);
timeAgoView = (TextView) view.findViewById(R.id.overview_timeago);
@@ -578,8 +584,16 @@ public class OverviewFragment extends Fragment {
long toTime = calendar.getTimeInMillis() + 100000; // little bit more to avoid wrong rounding
long fromTime = toTime - hoursToFetch * 60 * 60 * 1000L;
- Double lowLine = NSProfile.fromMgdlToUnits(OverviewPlugin.bgTargetLow, units);
- Double highLine = NSProfile.fromMgdlToUnits(OverviewPlugin.bgTargetHigh, units);
+ Double lowLine = SafeParse.stringToDouble(prefs.getString("low_mark", "0"));
+ Double highLine = SafeParse.stringToDouble(prefs.getString("high_mark", "0"));
+
+ if (lowLine < 1){
+ lowLine = NSProfile.fromMgdlToUnits(OverviewPlugin.bgTargetLow, units);
+ }
+
+ if(highLine < 1){
+ highLine = NSProfile.fromMgdlToUnits(OverviewPlugin.bgTargetHigh, units);
+ }
BarGraphSeries basalsSeries = null;
LineGraphSeries seriesLow = null;
@@ -662,6 +676,7 @@ public class OverviewFragment extends Fragment {
}
maxBgValue = NSProfile.fromMgdlToUnits(maxBgValue, units);
maxBgValue = units.equals(Constants.MGDL) ? Round.roundTo(maxBgValue, 40d) + 80 : Round.roundTo(maxBgValue, 2d) + 4;
+ if(highLine > maxBgValue) maxBgValue = highLine;
Integer numOfHorizLines = units.equals(Constants.MGDL) ? (int) (maxBgValue / 40 + 1) : (int) (maxBgValue / 2 + 1);
BgReading[] inRange = new BgReading[inRangeArray.size()];
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearFragment.java
new file mode 100644
index 0000000000..8f4e7f9be2
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearFragment.java
@@ -0,0 +1,52 @@
+package info.nightscout.androidaps.plugins.Wear;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import info.nightscout.androidaps.R;
+import info.nightscout.androidaps.interfaces.FragmentBase;
+
+/**
+ * Created by adrian on 17/11/16.
+ */
+
+public class WearFragment extends Fragment implements FragmentBase {
+
+ private static WearPlugin wearPlugin;
+
+ public static WearPlugin getPlugin(Context ctx) {
+
+ if (wearPlugin == null){
+ wearPlugin = new WearPlugin(ctx);
+ }
+
+ return wearPlugin;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.wear_fragment, container, false);
+
+ view.findViewById(R.id.wear_resend).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ getPlugin(getContext()).resendDataToWatch();
+ }
+ });
+
+ view.findViewById(R.id.wear_opensettings).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ getPlugin(getContext()).openSettings();
+ }
+ });
+
+ return view;
+ }
+
+}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java
new file mode 100644
index 0000000000..4685d3b634
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java
@@ -0,0 +1,144 @@
+package info.nightscout.androidaps.plugins.Wear;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.squareup.otto.Subscribe;
+
+import info.nightscout.androidaps.MainApp;
+import info.nightscout.androidaps.R;
+import info.nightscout.androidaps.events.EventNewBG;
+import info.nightscout.androidaps.events.EventNewBasalProfile;
+import info.nightscout.androidaps.events.EventPreferenceChange;
+import info.nightscout.androidaps.events.EventRefreshGui;
+import info.nightscout.androidaps.events.EventTempBasalChange;
+import info.nightscout.androidaps.events.EventTreatmentChange;
+import info.nightscout.androidaps.interfaces.PluginBase;
+import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification;
+import info.nightscout.androidaps.plugins.Wear.wearintegration.WatchUpdaterService;
+
+/**
+ * Created by adrian on 17/11/16.
+ */
+
+public class WearPlugin implements PluginBase {
+
+ static boolean fragmentEnabled = true;
+ static boolean fragmentVisible = true;
+ private static WatchUpdaterService watchUS;
+ private final Context ctx;
+
+ WearPlugin(Context ctx){
+ this.ctx = ctx;
+ MainApp.bus().register(this);
+ }
+
+ @Override
+ public int getType() {
+ return PluginBase.GENERAL;
+ }
+
+ @Override
+ public String getFragmentClass() {
+ return WearFragment.class.getName();
+ }
+
+ @Override
+ public String getName() {
+ return ctx.getString(R.string.wear);
+ }
+
+ @Override
+ public boolean isEnabled(int type) {
+ return fragmentEnabled;
+ }
+
+ @Override
+ public boolean isVisibleInTabs(int type) {
+ return fragmentVisible;
+ }
+
+ @Override
+ public boolean canBeHidden(int type) {
+ return true;
+ }
+
+ @Override
+ public void setFragmentEnabled(int type, boolean fragmentEnabled) {
+ WearPlugin.fragmentEnabled = fragmentEnabled;
+ if(watchUS!=null){
+ watchUS.setSettings();
+ }
+ }
+
+ @Override
+ public void setFragmentVisible(int type, boolean fragmentVisible) {
+ WearPlugin.fragmentVisible = fragmentVisible;
+ }
+
+ private void sendDataToWatch(boolean status, boolean basals, boolean bgValue){
+ if (isEnabled(getType())) { //only start service when this plugin is enabled
+
+ if(bgValue){
+ ctx.startService(new Intent(ctx, WatchUpdaterService.class));
+ }
+
+ if(basals){
+ ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_BASALS));
+ }
+
+ if(status){
+ ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_SEND_STATUS));
+ }
+ }
+ }
+
+ void resendDataToWatch(){
+ ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_RESEND));
+ }
+
+ void openSettings(){
+ ctx.startService(new Intent(ctx, WatchUpdaterService.class).setAction(WatchUpdaterService.ACTION_OPEN_SETTINGS));
+ }
+
+
+ @Subscribe
+ public void onStatusEvent(final EventPreferenceChange ev) {
+ //possibly new high or low mark
+ resendDataToWatch();
+ }
+
+ @Subscribe
+ public void onStatusEvent(final EventTreatmentChange ev) {
+ sendDataToWatch(true, true, false);
+ }
+
+ @Subscribe
+ public void onStatusEvent(final EventTempBasalChange ev) {
+ sendDataToWatch(true, true, false);
+ }
+
+ @Subscribe
+ public void onStatusEvent(final EventNewBG ev) {
+ sendDataToWatch(true, true, true);
+ }
+
+ @Subscribe
+ public void onStatusEvent(final EventNewBasalProfile ev) {
+ sendDataToWatch(false, true, false);
+ }
+
+ public static boolean isEnabled() {
+ return fragmentEnabled;
+ }
+
+ public static void registerWatchUpdaterService(WatchUpdaterService wus){
+ watchUS = wus;
+ }
+
+ public static void unRegisterWatchUpdaterService(){
+ watchUS = null;
+ }
+
+
+}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/SendToDataLayerThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/SendToDataLayerThread.java
new file mode 100644
index 0000000000..02c74defca
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/SendToDataLayerThread.java
@@ -0,0 +1,52 @@
+package info.nightscout.androidaps.plugins.Wear.wearintegration;
+
+import android.os.AsyncTask;
+import android.util.Log;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.NodeApi;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.PutDataRequest;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by emmablack on 12/26/14.
+ */
+class SendToDataLayerThread extends AsyncTask {
+ private GoogleApiClient googleApiClient;
+ private static final String TAG = "SendDataThread";
+ String path;
+
+ SendToDataLayerThread(String path, GoogleApiClient pGoogleApiClient) {
+ this.path = path;
+ googleApiClient = pGoogleApiClient;
+ }
+
+ @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");
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Got exception sending data to wear: " + e.toString());
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java
new file mode 100644
index 0000000000..b9d42a72f1
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java
@@ -0,0 +1,474 @@
+package info.nightscout.androidaps.plugins.Wear.wearintegration;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+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.DataMap;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.PutDataRequest;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import info.nightscout.androidaps.Constants;
+import info.nightscout.androidaps.MainApp;
+import info.nightscout.androidaps.R;
+import info.nightscout.androidaps.db.BgReading;
+import info.nightscout.androidaps.db.DatabaseHelper;
+import info.nightscout.androidaps.db.TempBasal;
+import info.nightscout.androidaps.interfaces.PumpInterface;
+import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal;
+import info.nightscout.androidaps.plugins.Overview.OverviewPlugin;
+import info.nightscout.androidaps.plugins.Wear.WearPlugin;
+import info.nightscout.client.data.NSProfile;
+import info.nightscout.utils.DecimalFormatter;
+import info.nightscout.utils.SafeParse;
+
+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");
+ public static final String ACTION_SEND_BASALS = WatchUpdaterService.class.getName().concat(".SendBasals");
+
+
+ private GoogleApiClient googleApiClient;
+ public static final String WEARABLE_DATA_PATH = "/nightscout_watch_data";
+ public static final String WEARABLE_RESEND_PATH = "/nightscout_watch_data_resend";
+ private static final String OPEN_SETTINGS_PATH = "/openwearsettings";
+ private static final String NEW_STATUS_PATH = "/sendstatustowear";
+ public static final String BASAL_DATA_PATH = "/nightscout_watch_basal";
+
+
+ boolean wear_integration = false;
+ SharedPreferences mPrefs;
+
+ @Override
+ public void onCreate() {
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ listenForChangeInSettings();
+ setSettings();
+ if (wear_integration) {
+ googleApiConnect();
+ }
+ }
+
+ public void listenForChangeInSettings() {
+ WearPlugin.registerWatchUpdaterService(this);
+ }
+
+ public void setSettings() {
+ wear_integration = WearPlugin.isEnabled();
+ if (wear_integration) {
+ googleApiConnect();
+ }
+ }
+
+ public void googleApiConnect() {
+ if(googleApiClient != null && (googleApiClient.isConnected() || googleApiClient.isConnecting())) { googleApiClient.disconnect(); }
+ 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");
+ } else {
+ googleApiClient.connect();
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ double timestamp = 0;
+ if (intent != null) {
+ timestamp = intent.getDoubleExtra("timestamp", 0);
+ }
+
+ String action = null;
+ if (intent != null) {
+ action = intent.getAction();
+ }
+
+ if (wear_integration) {
+ if (googleApiClient.isConnected()) {
+ if (ACTION_RESEND.equals(action)) {
+ resendData();
+ } else if (ACTION_OPEN_SETTINGS.equals(action)) {
+ sendNotification();
+ } else if (ACTION_SEND_STATUS.equals(action)) {
+ sendStatus();
+ } else if (ACTION_SEND_BASALS.equals(action)) {
+ sendBasals();
+ } else {
+ sendData();
+ }
+ } else {
+ googleApiClient.connect();
+ }
+ }
+
+ return START_STICKY;
+ }
+
+
+ @Override
+ public void onConnected(Bundle connectionHint) {
+ sendData();
+ }
+
+ @Override
+ public void onMessageReceived(MessageEvent event) {
+ if (wear_integration) {
+ if (event != null && event.getPath().equals(WEARABLE_RESEND_PATH))
+ resendData();
+ }
+ }
+
+ public void sendData() {
+
+ BgReading lastBG = MainApp.getDbHelper().lastBg();
+ if (lastBG != null) {
+ DatabaseHelper.GlucoseStatus glucoseStatus = MainApp.getDbHelper().getGlucoseStatusData();
+
+ if(googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) { googleApiConnect(); }
+ if (wear_integration) {
+ new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient).execute(dataMapSingleBG(lastBG, glucoseStatus));
+ }
+ }
+ }
+
+ private DataMap dataMapSingleBG(BgReading lastBG, DatabaseHelper.GlucoseStatus glucoseStatus) {
+ NSProfile profile = MainApp.getConfigBuilder().getActiveProfile().getProfile();
+
+
+ Double lowLine = SafeParse.stringToDouble(mPrefs.getString("low_mark", "0"));
+ Double highLine = SafeParse.stringToDouble(mPrefs.getString("high_mark", "0"));
+
+ //convert to mg/dl
+ if (! profile.getUnits().equals(Constants.MGDL)){
+ lowLine *= Constants.MMOLL_TO_MGDL;
+ highLine *= Constants.MMOLL_TO_MGDL;
+
+ }
+
+ if (lowLine < 1){
+ lowLine = OverviewPlugin.bgTargetLow;
+ }
+
+ if(highLine < 1){
+ highLine = OverviewPlugin.bgTargetHigh;
+ }
+
+ long sgvLevel = 0l;
+ if (lastBG.value > highLine) {
+ sgvLevel = 1;
+ } else if (lastBG.value < lowLine) {
+ sgvLevel = -1;
+ }
+ DataMap dataMap = new DataMap();
+
+ int battery = getBatteryLevel(getApplicationContext());
+ dataMap.putString("sgvString", lastBG.valueToUnitsToString(profile.getUnits()));
+ dataMap.putDouble("timestamp", lastBG.getTimeIndex());
+ if(glucoseStatus == null) {
+ dataMap.putString("slopeArrow", "" );
+ dataMap.putString("delta", "");
+ dataMap.putString("avgDelta", "");
+ } else {
+ dataMap.putString("slopeArrow", slopeArrow(glucoseStatus.delta));
+ dataMap.putString("delta", deltastring(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, profile.getUnits()));
+ dataMap.putString("avgDelta", deltastring(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, profile.getUnits()));
+ }
+ dataMap.putString("battery", "" + battery);
+ dataMap.putLong("sgvLevel", sgvLevel);
+ dataMap.putInt("batteryLevel", (battery>=30)?1:0);
+ dataMap.putDouble("sgvDouble", lastBG.value);
+ dataMap.putDouble("high", highLine);
+ dataMap.putDouble("low", lowLine);
+ return dataMap;
+ }
+
+ private String deltastring(double deltaMGDL, double deltaMMOL, String units) {
+ String deltastring = "";
+ if (deltaMGDL >=0){
+ deltastring += "+";
+ } else{
+ deltastring += "-";
+
+ }
+ if (units.equals(Constants.MGDL)){
+ deltastring += DecimalFormatter.to1Decimal(Math.abs(deltaMGDL));
+ }
+ else {
+ deltastring += DecimalFormatter.to1Decimal(Math.abs(deltaMMOL));
+ }
+ return deltastring;
+ }
+
+ private String slopeArrow(double delta) {
+ if (delta <= (-3.5*5)) {
+ return "\u21ca";
+ } else if (delta <= (-2*5)) {
+ return "\u2193";
+ } else if (delta <= (-1*5)) {
+ return "\u2198";
+ } else if (delta <= (1*5)) {
+ return "\u2192";
+ } else if (delta <= (2*5)) {
+ return "\u2197";
+ } else if (delta <= (3.5*5)) {
+ return "\u2191";
+ } else {
+ return "\u21c8";
+ }
+ }
+
+
+ private void resendData() {
+ if(googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) { googleApiConnect(); }
+ long startTime = System.currentTimeMillis() - (long)(60000 * 60 * 5.5);
+ BgReading last_bg = MainApp.getDbHelper().lastBg();
+
+ if (last_bg == null) return;
+
+ List graph_bgs = MainApp.getDbHelper().getDataFromTime(startTime);
+ DatabaseHelper.GlucoseStatus glucoseStatus = MainApp.getDbHelper().getGlucoseStatusData();
+
+ if (!graph_bgs.isEmpty()) {
+ DataMap entries = dataMapSingleBG(last_bg, glucoseStatus);
+ final ArrayList dataMaps = new ArrayList<>(graph_bgs.size());
+ for (BgReading bg : graph_bgs) {
+ dataMaps.add(dataMapSingleBG(bg, glucoseStatus));
+ }
+ entries.putDataMapArrayList("entries", dataMaps);
+ new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient).execute(entries);
+ }
+ sendBasals();
+ sendStatus();
+ }
+
+ private void sendBasals() {
+ if(googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) { googleApiConnect(); }
+
+ long now = System.currentTimeMillis();
+ long startTimeWindow = now - (long)(60000 * 60 * 5.5);
+
+
+
+ ArrayList basals = new ArrayList<>();
+ ArrayList temps = new ArrayList<>();
+
+
+ NSProfile profile = MainApp.getConfigBuilder().getActiveProfile().getProfile();
+
+ if( profile == null) return;
+
+ long beginBasalSegmentTime = startTimeWindow;
+ long runningTime = startTimeWindow;
+
+ double beginBasalValue = profile.getBasal(NSProfile.secondsFromMidnight(new Date(beginBasalSegmentTime)));
+ double endBasalValue = beginBasalValue;
+
+ TempBasal tb1 = MainApp.getConfigBuilder().getTempBasal(new Date(runningTime));
+ TempBasal tb2 = MainApp.getConfigBuilder().getTempBasal(new Date(runningTime));
+ double tb_before = beginBasalValue;
+ double tb_amount = beginBasalValue;
+ long tb_start = runningTime;
+
+ if(tb1 != null){
+ tb_before = beginBasalValue;
+ tb_amount = tb1.tempBasalConvertedToAbsolute(new Date(runningTime));
+ tb_start = runningTime;
+ }
+
+
+ for(;runningTime push it
+ temps.add(tempDatamap(tb_start, tb_before, runningTime, endBasalValue, tb_amount));
+ tb1 = null;
+
+ } else if (tb1 == null && tb2 != null) {
+ //temp begins
+ tb1 = tb2;
+ tb_start = runningTime;
+ tb_before = endBasalValue;
+ tb_amount = tb1.tempBasalConvertedToAbsolute(new Date(runningTime));
+
+ } else if (tb1 != null && tb2 != null) {
+ double currentAmount = tb2.tempBasalConvertedToAbsolute(new Date(runningTime));
+ if(currentAmount != tb_amount){
+ temps.add(tempDatamap(tb_start, tb_before, runningTime, currentAmount, tb_amount));
+ tb_start = runningTime;
+ tb_before = tb_amount;
+ tb_amount = currentAmount;
+ tb1 = tb2;
+ }
+ }
+ }
+ if(beginBasalSegmentTime != runningTime){
+ //push the remaining segment
+ basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue));
+ }
+ if(tb1 != null){
+ tb2 = MainApp.getConfigBuilder().getTempBasal(new Date(now)); //use "now" to express current situation
+ if(tb2 == null) {
+ //express the cancelled temp by painting it down one minute early
+ temps.add(tempDatamap(tb_start, tb_before, now - 1 * 60 * 1000, endBasalValue, tb_amount));
+ } else {
+ //express currently running temp by painting it a bit into the future
+ double currentAmount = tb2.tempBasalConvertedToAbsolute(new Date(now));
+ if(currentAmount != tb_amount){
+ temps.add(tempDatamap(tb_start, tb_before, now, tb_amount, tb_amount));
+ temps.add(tempDatamap(now, tb_amount, runningTime + 5 * 60 * 1000, currentAmount, currentAmount));
+ } else {
+ temps.add(tempDatamap(tb_start, tb_before, runningTime + 5 * 60 * 1000, tb_amount, tb_amount));
+ }
+ }
+ } else {
+ tb2 = MainApp.getConfigBuilder().getTempBasal(new Date(now)); //use "now" to express current situation
+ if(tb2 != null) {
+ //onset at the end
+ double currentAmount = tb2.tempBasalConvertedToAbsolute(new Date(runningTime));
+ temps.add(tempDatamap(now - 1 * 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, currentAmount));
+ }
+ }
+
+ DataMap dm = new DataMap();
+ dm.putDataMapArrayList("basals", basals);
+ dm.putDataMapArrayList("temps", temps);
+
+ new SendToDataLayerThread(BASAL_DATA_PATH, googleApiClient).execute(dm);
+ }
+
+ private DataMap tempDatamap(long startTime, double startBasal, long to, double toBasal, double amount) {
+ DataMap dm = new DataMap();
+ dm.putLong("starttime", startTime);
+ dm.putDouble("startBasal", startBasal);
+ dm.putLong("endtime", to);
+ dm.putDouble("endbasal", toBasal);
+ dm.putDouble("amount", amount);
+ return dm;
+ }
+
+ private DataMap basalMap(long startTime, long endTime, double amount) {
+ DataMap dm = new DataMap();
+ dm.putLong("starttime", startTime);
+ dm.putLong("endtime", endTime);
+ dm.putDouble("amount", amount);
+ return dm;
+ }
+
+
+ private void sendNotification() {
+ if (googleApiClient.isConnected()) {
+ PutDataMapRequest dataMapRequest = PutDataMapRequest.create(OPEN_SETTINGS_PATH);
+ //unique content
+ dataMapRequest.getDataMap().putDouble("timestamp", System.currentTimeMillis());
+ dataMapRequest.getDataMap().putString("openSettings", "openSettings");
+ PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
+ Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
+ } else {
+ Log.e("OpenSettings", "No connection to wearable available!");
+ }
+ }
+
+ private void sendStatus() {
+ if (googleApiClient.isConnected()) {
+
+ String status = "";
+ boolean shortString = true;
+
+ //Temp basal
+ PumpInterface pump = MainApp.getConfigBuilder();
+
+ if (pump.isTempBasalInProgress()) {
+ TempBasal activeTemp = pump.getTempBasal();
+ if (shortString) {
+ status += activeTemp.toStringShort();
+ } else {
+ status += activeTemp.toStringMedium();
+ }
+ }
+
+ //IOB
+ MainApp.getConfigBuilder().getActiveTreatments().updateTotalIOB();
+ IobTotal bolusIob = MainApp.getConfigBuilder().getActiveTreatments().getLastCalculation().round();
+ if (bolusIob == null) bolusIob = new IobTotal();
+ MainApp.getConfigBuilder().getActiveTempBasals().updateTotalIOB();
+ IobTotal basalIob = MainApp.getConfigBuilder().getActiveTempBasals().getLastCalculation().round();
+ if (basalIob == null) basalIob = new IobTotal();
+ status += (shortString?"":(getString(R.string.treatments_iob_label_string) + " ")) + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "("
+ + DecimalFormatter.to2Decimal(bolusIob.iob) + "|"
+ + DecimalFormatter.to2Decimal(basalIob.basaliob) + ")";
+
+ PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_STATUS_PATH);
+ //unique content
+ dataMapRequest.getDataMap().putDouble("timestamp", System.currentTimeMillis());
+ dataMapRequest.getDataMap().putString("externalStatusString", status);
+ PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
+ Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
+ } else {
+ Log.e("SendStatus", "No connection to wearable available!");
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ if (googleApiClient != null && googleApiClient.isConnected()) {
+ googleApiClient.disconnect();
+ }
+ WearPlugin.unRegisterWatchUpdaterService();
+ }
+
+ @Override
+ public void onConnectionSuspended(int cause) {
+ }
+
+ @Override
+ public void onConnectionFailed(ConnectionResult connectionResult) {
+ }
+
+ public static int getBatteryLevel(Context context) {
+ Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+ int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+ if(level == -1 || scale == -1) {
+ return 50;
+ }
+ return (int)(((float)level / (float)scale) * 100.0f);
+ }
+}
diff --git a/app/src/main/res/layout/wear_fragment.xml b/app/src/main/res/layout/wear_fragment.xml
new file mode 100644
index 0000000000..e07b818bd2
--- /dev/null
+++ b/app/src/main/res/layout/wear_fragment.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7ff2308d0e..695dd8acad 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -364,4 +364,11 @@
CircadianPercentageProfile
Basal Temp End
Basal Temp Start
+ Range for Visualization
+ High and low mark for the charts in Overview and Smartwatch
+ LOW mark
+ HIGH mark
+ Wear
+ Resend All Data
+ Open Settings on Wear
diff --git a/app/src/main/res/xml/pref_others.xml b/app/src/main/res/xml/pref_others.xml
index a4b3b0703d..b3e7b8c9bc 100644
--- a/app/src/main/res/xml/pref_others.xml
+++ b/app/src/main/res/xml/pref_others.xml
@@ -24,5 +24,21 @@
android:inputType="numberDecimal">
+
+
+
+
+
+
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index e7b4def49c..9ccfb61915 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app'
+include ':app', ':wear'
diff --git a/wear/.gitignore b/wear/.gitignore
new file mode 100644
index 0000000000..796b96d1c4
--- /dev/null
+++ b/wear/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/wear/build.gradle b/wear/build.gradle
new file mode 100644
index 0000000000..3906ab934a
--- /dev/null
+++ b/wear/build.gradle
@@ -0,0 +1,56 @@
+apply plugin: 'com.android.application'
+
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.3"
+
+ defaultConfig {
+ applicationId "info.nightscout.androidaps"
+ minSdkVersion 20
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0.2"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ publishNonDefault true
+
+ productFlavors {
+ full {
+ applicationId = "info.nightscout.androidaps"
+ resValue "string", "label_xdrip", "AAPS"
+ resValue "string", "label_xdrip_large", "AAPS(Large)"
+ resValue "string", "label_xdrip_big_chart", "AAPS(BigChart)"
+ resValue "string", "label_xdrip_circle", "AAPS(Circle)"
+ resValue "string", "label_xdrip_activity", "AAPS Prefs."
+ resValue "string", "app_settings", "AAPS Settings"
+ }
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ flatDir {
+ dirs 'libs'
+ }
+ }
+}
+
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ //compile 'com.ustwo.android:clockwise-wearable:1.0.2'
+ compile 'com.google.android.support:wearable:1.4.0'
+ compile 'com.google.android.gms:play-services-wearable:7.3.0'
+ compile files('libs/hellocharts-library-1.5.5.jar')
+ compile(name:'ustwo-clockwise-debug', ext:'aar')
+ compile 'com.android.support:support-v4:23.0.1'
+ compile 'me.denley.wearpreferenceactivity:wearpreferenceactivity:0.5.0'
+}
diff --git a/wear/libs/hellocharts-library-1.5.5.jar b/wear/libs/hellocharts-library-1.5.5.jar
new file mode 100644
index 0000000000..142316706e
Binary files /dev/null and b/wear/libs/hellocharts-library-1.5.5.jar differ
diff --git a/wear/libs/ustwo-clockwise-debug.aar b/wear/libs/ustwo-clockwise-debug.aar
new file mode 100644
index 0000000000..8257a991be
Binary files /dev/null and b/wear/libs/ustwo-clockwise-debug.aar differ
diff --git a/wear/proguard-rules.pro b/wear/proguard-rules.pro
new file mode 100644
index 0000000000..3278718200
--- /dev/null
+++ b/wear/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/stephenblack/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..e534380a56
--- /dev/null
+++ b/wear/src/main/AndroidManifest.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wear/src/main/java/info/nightscout/androidaps/BIGChart.java b/wear/src/main/java/info/nightscout/androidaps/BIGChart.java
new file mode 100644
index 0000000000..637908600e
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/BIGChart.java
@@ -0,0 +1,632 @@
+package info.nightscout.androidaps;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.preference.PreferenceManager;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.wearable.view.WatchViewStub;
+import android.support.wearable.watchface.WatchFaceStyle;
+import android.text.format.DateFormat;
+import android.util.Log;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.google.android.gms.wearable.DataMap;
+import com.ustwo.clockwise.wearable.WatchFace;
+import com.ustwo.clockwise.common.WatchFaceTime;
+import com.ustwo.clockwise.common.WatchMode;
+import com.ustwo.clockwise.common.WatchShape;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+import lecho.lib.hellocharts.view.LineChartView;
+
+/**
+ * Created by adrianLxM.
+ */
+public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPreferenceChangeListener {
+ public final static IntentFilter INTENT_FILTER;
+ public TextView mTime, mSgv, mTimestamp, mDelta, mAvgDelta;
+ public RelativeLayout mRelativeLayout;
+ public long sgvLevel = 0;
+ public int batteryLevel = 1;
+ public int ageLevel = 1;
+ public int highColor = Color.YELLOW;
+ public int lowColor = Color.RED;
+ public int midColor = Color.WHITE;
+ public int gridColour = Color.WHITE;
+ public int basalBackgroundColor = Color.BLUE;
+ public int basalCenterColor = Color.BLUE;
+ public int pointSize = 2;
+ public boolean lowResMode = false;
+ public boolean layoutSet = false;
+ public BgGraphBuilder bgGraphBuilder;
+ public LineChartView chart;
+ public double datetime;
+ public ArrayList bgDataList = new ArrayList<>();
+ public ArrayList tempWatchDataList = new ArrayList<>();
+ public ArrayList basalWatchDataList = new ArrayList<>();
+ public PowerManager.WakeLock wakeLock;
+ public View layoutView;
+ private final Point displaySize = new Point();
+ private int specW, specH;
+ private int animationAngle = 0;
+ private boolean isAnimated = false;
+
+ private LocalBroadcastManager localBroadcastManager;
+ private MessageReceiver messageReceiver;
+
+ protected SharedPreferences sharedPrefs;
+ private String rawString = "000 | 000 | 000";
+ private String batteryString = "--";
+ private String sgvString = "--";
+ private String externalStatusString = "no status";
+ private TextView statusView;
+ private long chartTapTime = 0l;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay();
+ display.getSize(displaySize);
+ wakeLock = ((PowerManager) getSystemService(Context.POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Clock");
+
+ specW = View.MeasureSpec.makeMeasureSpec(displaySize.x,
+ View.MeasureSpec.EXACTLY);
+ specH = View.MeasureSpec.makeMeasureSpec(displaySize.y,
+ View.MeasureSpec.EXACTLY);
+ sharedPrefs = PreferenceManager
+ .getDefaultSharedPreferences(this);
+ sharedPrefs.registerOnSharedPreferenceChangeListener(this);
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ layoutView = inflater.inflate(R.layout.activity_bigchart, null);
+ performViewSetup();
+ }
+
+ @Override
+ protected void onLayout(WatchShape shape, Rect screenBounds, WindowInsets screenInsets) {
+ super.onLayout(shape, screenBounds, screenInsets);
+ layoutView.onApplyWindowInsets(screenInsets);
+ }
+
+ public void performViewSetup() {
+ final WatchViewStub stub = (WatchViewStub) layoutView.findViewById(R.id.watch_view_stub);
+ IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND);
+
+ messageReceiver = new MessageReceiver();
+ localBroadcastManager = LocalBroadcastManager.getInstance(this);
+ localBroadcastManager.registerReceiver(messageReceiver, messageFilter);
+
+ stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
+ @Override
+ public void onLayoutInflated(WatchViewStub stub) {
+ mTime = (TextView) stub.findViewById(R.id.watch_time);
+ mSgv = (TextView) stub.findViewById(R.id.sgv);
+ mTimestamp = (TextView) stub.findViewById(R.id.timestamp);
+ mDelta = (TextView) stub.findViewById(R.id.delta);
+ mAvgDelta = (TextView) stub.findViewById(R.id.avgdelta);
+ mRelativeLayout = (RelativeLayout) stub.findViewById(R.id.main_layout);
+ chart = (LineChartView) stub.findViewById(R.id.chart);
+ statusView = (TextView) stub.findViewById(R.id.aps_status);
+ layoutSet = true;
+ showAgeAndStatus();
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ }
+ });
+ ListenerService.requestData(this);
+ wakeLock.acquire(50);
+ }
+
+ @Override
+ protected void onTapCommand(int tapType, int x, int y, long eventTime) {
+
+ if (tapType == TAP_TYPE_TAP&&
+ x >=chart.getLeft() &&
+ x <= chart.getRight()&&
+ y >= chart.getTop() &&
+ y <= chart.getBottom()){
+ if (eventTime - chartTapTime < 800){
+ changeChartTimeframe();
+ }
+ chartTapTime = eventTime;
+ }
+ }
+
+ private void changeChartTimeframe() {
+ int timeframe = Integer.parseInt(sharedPrefs.getString("chart_timeframe", "3"));
+ timeframe = (timeframe%5) + 1;
+ sharedPrefs.edit().putString("chart_timeframe", "" + timeframe).commit();
+ }
+
+ protected void onWatchModeChanged(WatchMode watchMode) {
+
+ if(lowResMode ^ isLowRes(watchMode)){ //if there was a change in lowResMode
+ lowResMode = isLowRes(watchMode);
+ setColor();
+ } else if (! sharedPrefs.getBoolean("dark", true)){
+ //in bright mode: different colours if active:
+ setColor();
+ }
+ }
+
+ private boolean isLowRes(WatchMode watchMode) {
+ return (watchMode == WatchMode.LOW_BIT) || (watchMode == WatchMode.LOW_BIT_BURN_IN) || (watchMode == WatchMode.LOW_BIT_BURN_IN);
+ }
+
+
+ @Override
+ protected WatchFaceStyle getWatchFaceStyle(){
+ return new WatchFaceStyle.Builder(this).setAcceptsTapEvents(true).build();
+ }
+
+
+
+ public int ageLevel() {
+ if(timeSince() <= (1000 * 60 * 12)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ public double timeSince() {
+ return System.currentTimeMillis() - datetime;
+ }
+
+ public String readingAge(boolean shortString) {
+ if (datetime == 0) { return shortString?"--'":"-- Minute ago"; }
+ int minutesAgo = (int) Math.floor(timeSince()/(1000*60));
+ if (minutesAgo == 1) {
+ return minutesAgo + (shortString?"'":" Minute ago");
+ }
+ return minutesAgo + (shortString?"'":" Minutes ago");
+ }
+
+ @Override
+ public void onDestroy() {
+ if(localBroadcastManager != null && messageReceiver != null){
+ localBroadcastManager.unregisterReceiver(messageReceiver);}
+ if (sharedPrefs != null){
+ sharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
+ }
+ super.onDestroy();
+ }
+
+ static {
+ INTENT_FILTER = new IntentFilter();
+ INTENT_FILTER.addAction(Intent.ACTION_TIME_TICK);
+ INTENT_FILTER.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ INTENT_FILTER.addAction(Intent.ACTION_TIME_CHANGED);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if(layoutSet) {
+ this.mRelativeLayout.draw(canvas);
+ Log.d("onDraw", "draw");
+ }
+ }
+
+ @Override
+ protected void onTimeChanged(WatchFaceTime oldTime, WatchFaceTime newTime) {
+ if (layoutSet && (newTime.hasHourChanged(oldTime) || newTime.hasMinuteChanged(oldTime))) {
+ wakeLock.acquire(50);
+ final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(BIGChart.this);
+ mTime.setText(timeFormat.format(System.currentTimeMillis()));
+ showAgeAndStatus();
+
+ if(ageLevel()<=0) {
+ mSgv.setPaintFlags(mSgv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
+ } else {
+ mSgv.setPaintFlags(mSgv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
+ }
+
+ missedReadingAlert();
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ }
+ }
+
+ public class MessageReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Bundle bundle = intent.getBundleExtra("data");
+ if (layoutSet && bundle !=null) {
+ DataMap dataMap = DataMap.fromBundle(bundle);
+ wakeLock.acquire(50);
+ sgvLevel = dataMap.getLong("sgvLevel");
+ batteryLevel = dataMap.getInt("batteryLevel");
+ datetime = dataMap.getDouble("timestamp");
+ rawString = dataMap.getString("rawString");
+ sgvString = dataMap.getString("sgvString");
+ batteryString = dataMap.getString("battery");
+ mSgv.setText(dataMap.getString("sgvString"));
+
+ if(ageLevel()<=0) {
+ mSgv.setPaintFlags(mSgv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
+ } else {
+ mSgv.setPaintFlags(mSgv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
+ }
+
+ final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(BIGChart.this);
+ mTime.setText(timeFormat.format(System.currentTimeMillis()));
+
+ showAgeAndStatus();
+
+ String delta = dataMap.getString("delta");
+
+ if (delta.endsWith(" mg/dl")) {
+ mDelta.setText(delta.substring(0, delta.length() - 6));
+ } else if (delta.endsWith(" mmol/l")||delta.endsWith(" mmol")) {
+ mDelta.setText(delta.substring(0, delta.length() - 5));
+ } else {
+ mDelta.setText(delta);
+ }
+
+
+ String avgDelta = dataMap.getString("avgDelta");
+
+ if (delta.endsWith(" mg/dl")) {
+ mAvgDelta.setText(avgDelta.substring(0, avgDelta.length() - 6));
+ } else if (avgDelta.endsWith(" mmol/l")||avgDelta.endsWith(" mmol")) {
+ mAvgDelta.setText(avgDelta.substring(0, avgDelta.length() - 5));
+ } else {
+ mAvgDelta.setText(avgDelta);
+ }
+
+ if (chart != null) {
+ addToWatchSet(dataMap);
+ setupCharts();
+ }
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ invalidate();
+ setColor();
+
+ //start animation?
+ // dataMap.getDataMapArrayList("entries") == null -> not on "resend data".
+ if (!lowResMode && (sharedPrefs.getBoolean("animation", false) && dataMap.getDataMapArrayList("entries") == null && (sgvString.equals("100") || sgvString.equals("5.5") || sgvString.equals("5,5")))) {
+ startAnimation();
+ }
+ }
+ //status
+ bundle = intent.getBundleExtra("status");
+ if (layoutSet && bundle != null) {
+ DataMap dataMap = DataMap.fromBundle(bundle);
+ wakeLock.acquire(50);
+ externalStatusString = dataMap.getString("externalStatusString");
+
+ showAgeAndStatus();
+
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ invalidate();
+ setColor();
+ }
+ //basals and temps
+ bundle = intent.getBundleExtra("basals");
+ if (layoutSet && bundle != null) {
+ DataMap dataMap = DataMap.fromBundle(bundle);
+ wakeLock.acquire(500);
+
+ loadBasalsAndTemps(dataMap);
+
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ invalidate();
+ setColor();
+ }
+ }
+ }
+
+ private void loadBasalsAndTemps(DataMap dataMap) {
+ ArrayList temps = dataMap.getDataMapArrayList("temps");
+ if (temps != null) {
+ tempWatchDataList = new ArrayList<>();
+ for (DataMap temp : temps) {
+ TempWatchData twd = new TempWatchData();
+ twd.startTime = temp.getLong("starttime");
+ twd.startBasal = temp.getDouble("startBasal");
+ twd.endTime = temp.getLong("endtime");
+ twd.endBasal = temp.getDouble("endbasal");
+ twd.amount = temp.getDouble("amount");
+ tempWatchDataList.add(twd);
+ }
+ }
+ ArrayList basals = dataMap.getDataMapArrayList("basals");
+ if (basals != null) {
+ basalWatchDataList = new ArrayList<>();
+ for (DataMap basal : basals) {
+ BasalWatchData bwd = new BasalWatchData();
+ bwd.startTime = basal.getLong("starttime");
+ bwd.endTime = basal.getLong("endtime");
+ bwd.amount = basal.getDouble("amount");
+ basalWatchDataList.add(bwd);
+ }
+ }
+ }
+
+ private void showAgeAndStatus() {
+
+ if( mTimestamp != null){
+ mTimestamp.setText(readingAge(true));
+ }
+
+ boolean showStatus = sharedPrefs.getBoolean("showExternalStatus", true);
+ boolean showAvgDelta = sharedPrefs.getBoolean("showAvgDelta", true);
+
+ if(showAvgDelta){
+ mAvgDelta.setVisibility(View.VISIBLE);
+ } else {
+ mAvgDelta.setVisibility(View.GONE);
+ }
+
+ if(showStatus){
+ statusView.setText(externalStatusString);
+ statusView.setVisibility(View.VISIBLE);
+ } else {
+ statusView.setVisibility(View.GONE);
+ }
+ }
+
+ public void setColor() {
+ if(lowResMode){
+ setColorLowRes();
+ } else if (sharedPrefs.getBoolean("dark", true)) {
+ setColorDark();
+ } else {
+ setColorBright();
+ }
+
+ }
+
+
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key){
+ setColor();
+ if(layoutSet){
+ showAgeAndStatus();
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ }
+ invalidate();
+ }
+
+ protected void updateRainbow() {
+ animationAngle = (animationAngle + 1) % 360;
+ //Animation matrix:
+ int[] rainbow = {Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE
+ , Color.CYAN};
+ Shader shader = new LinearGradient(0, 0, 0, 20, rainbow,
+ null, Shader.TileMode.MIRROR);
+ Matrix matrix = new Matrix();
+ matrix.setRotate(animationAngle);
+ shader.setLocalMatrix(matrix);
+ mSgv.getPaint().setShader(shader);
+ invalidate();
+ }
+
+ private synchronized boolean isAnimated() {
+ return isAnimated;
+ }
+
+ private synchronized void setIsAnimated(boolean isAnimated) {
+ this.isAnimated = isAnimated;
+ }
+
+ void startAnimation() {
+ Log.d("CircleWatchface", "start startAnimation");
+
+ Thread animator = new Thread() {
+
+
+ public void run() {
+ setIsAnimated(true);
+ for (int i = 0; i <= 8 * 1000 / 40; i++) {
+ updateRainbow();
+ try {
+ Thread.sleep(40);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ mSgv.getPaint().setShader(null);
+ setIsAnimated(false);
+ invalidate();
+ setColor();
+
+ System.gc();
+ }
+ };
+
+ animator.start();
+ }
+
+ protected void setColorLowRes() {
+ mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime));
+ statusView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_statusView));
+ mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background));
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_Timestamp));
+ if (chart != null) {
+ highColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor);
+ lowColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor);
+ midColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor);
+ gridColour = ContextCompat.getColor(getApplicationContext(), R.color.dark_gridColor);
+ basalBackgroundColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_dark_lowres);
+ basalCenterColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_light_lowres);
+ pointSize = 2;
+ setupCharts();
+ }
+
+ }
+
+ protected void setColorDark() {
+ mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime));
+ statusView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_statusView));
+ mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background));
+ if (sgvLevel == 1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor));
+ mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor));
+ } else if (sgvLevel == 0) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ } else if (sgvLevel == -1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor));
+ mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor));
+ }
+
+ if (ageLevel == 1) {
+ mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_Timestamp));
+ } else {
+ mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld));
+ }
+
+ if (chart != null) {
+ highColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor);
+ lowColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor);
+ midColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor);
+ gridColour = ContextCompat.getColor(getApplicationContext(), R.color.dark_gridColor);
+ basalBackgroundColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_dark);
+ basalCenterColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_light);
+ pointSize = 2;
+ setupCharts();
+ }
+
+ }
+
+
+ protected void setColorBright() {
+
+ if (getCurrentWatchMode() == WatchMode.INTERACTIVE) {
+ mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_bigchart_time));
+ statusView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_bigchart_status));
+ mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_background));
+ if (sgvLevel == 1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor));
+ mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor));
+ } else if (sgvLevel == 0) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor));
+ mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor));
+ } else if (sgvLevel == -1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor));
+ mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor));
+ }
+
+ if (ageLevel == 1) {
+ mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_mTimestamp1));
+ } else {
+ mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_mTimestamp));
+ }
+
+ if (chart != null) {
+ highColor = ContextCompat.getColor(getApplicationContext(), R.color.light_highColor);
+ lowColor = ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor);
+ midColor = ContextCompat.getColor(getApplicationContext(), R.color.light_midColor);
+ gridColour = ContextCompat.getColor(getApplicationContext(), R.color.light_gridColor);
+ basalBackgroundColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_light);
+ basalCenterColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_dark);
+ pointSize = 2;
+ setupCharts();
+ }
+ } else {
+ setColorDark();
+ }
+ }
+
+ public void missedReadingAlert() {
+ int minutes_since = (int) Math.floor(timeSince()/(1000*60));
+ if(minutes_since >= 16 && ((minutes_since - 16) % 5) == 0) {
+ ListenerService.requestData(this); // attempt endTime recover missing data
+ }
+ }
+
+ public void addToWatchSet(DataMap dataMap) {
+
+ ArrayList entries = dataMap.getDataMapArrayList("entries");
+ if (entries != null) {
+ bgDataList = new ArrayList();
+ for (DataMap entry : entries) {
+ double sgv = entry.getDouble("sgvDouble");
+ double high = entry.getDouble("high");
+ double low = entry.getDouble("low");
+ double timestamp = entry.getDouble("timestamp");
+ bgDataList.add(new BgWatchData(sgv, high, low, timestamp));
+ }
+ } else {
+ double sgv = dataMap.getDouble("sgvDouble");
+ double high = dataMap.getDouble("high");
+ double low = dataMap.getDouble("low");
+ double timestamp = dataMap.getDouble("timestamp");
+
+ final int size = bgDataList.size();
+ if (size > 0) {
+ if (bgDataList.get(size - 1).timestamp == timestamp)
+ return; // Ignore duplicates.
+ }
+
+ bgDataList.add(new BgWatchData(sgv, high, low, timestamp));
+ }
+
+ for (int i = 0; i < bgDataList.size(); i++) {
+ if (bgDataList.get(i).timestamp < (new Date().getTime() - (1000 * 60 * 60 * 5))) {
+ bgDataList.remove(i); //Get rid of anything more than 5 hours old
+ break;
+ }
+ }
+ }
+
+ public void setupCharts() {
+ if(bgDataList.size() > 0) { //Dont crash things just because we dont have values, people dont like crashy things
+ int timeframe = Integer.parseInt(sharedPrefs.getString("chart_timeframe", "3"));
+ if (lowResMode) {
+ bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), bgDataList, tempWatchDataList, basalWatchDataList, pointSize, midColor, gridColour, basalBackgroundColor, basalCenterColor, timeframe);
+ } else {
+ bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), bgDataList, tempWatchDataList, basalWatchDataList, pointSize, highColor, lowColor, midColor, gridColour, basalBackgroundColor, basalCenterColor, timeframe);
+ }
+
+ chart.setLineChartData(bgGraphBuilder.lineData());
+ chart.setViewportCalculationEnabled(true);
+ chart.setMaximumViewport(chart.getMaximumViewport());
+ } else {
+ ListenerService.requestData(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/wear/src/main/java/info/nightscout/androidaps/BasalWatchData.java b/wear/src/main/java/info/nightscout/androidaps/BasalWatchData.java
new file mode 100644
index 0000000000..46b913aa7e
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/BasalWatchData.java
@@ -0,0 +1,11 @@
+package info.nightscout.androidaps;
+
+/**
+ * Created by adrian on 18/11/16.
+ */
+
+public class BasalWatchData {
+ public long startTime;
+ public long endTime;
+ public double amount;
+}
diff --git a/wear/src/main/java/info/nightscout/androidaps/BaseWatchFace.java b/wear/src/main/java/info/nightscout/androidaps/BaseWatchFace.java
new file mode 100644
index 0000000000..ce594f1d2f
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/BaseWatchFace.java
@@ -0,0 +1,441 @@
+package info.nightscout.androidaps;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.preference.PreferenceManager;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.wearable.view.WatchViewStub;
+import android.text.format.DateFormat;
+import android.util.Log;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.google.android.gms.wearable.DataMap;
+import com.ustwo.clockwise.common.WatchMode;
+import com.ustwo.clockwise.wearable.WatchFace;
+import com.ustwo.clockwise.common.WatchFaceTime;
+import com.ustwo.clockwise.common.WatchShape;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+import lecho.lib.hellocharts.view.LineChartView;
+
+/**
+ * Created by emmablack on 12/29/14.
+ */
+public abstract class BaseWatchFace extends WatchFace implements SharedPreferences.OnSharedPreferenceChangeListener {
+ public final static IntentFilter INTENT_FILTER;
+ public static final long[] vibratePattern = {0,400,300,400,300,400};
+ public TextView mTime, mSgv, mDirection, mTimestamp, mUploaderBattery, mDelta, mStatus;
+ public RelativeLayout mRelativeLayout;
+ public LinearLayout mLinearLayout;
+ public long sgvLevel = 0;
+ public int batteryLevel = 1;
+ public int ageLevel = 1;
+ public int highColor = Color.YELLOW;
+ public int lowColor = Color.RED;
+ public int midColor = Color.WHITE;
+ public int gridColor = Color.WHITE;
+ public int basalBackgroundColor = Color.BLUE;
+ public int basalCenterColor = Color.BLUE;
+ public boolean lowResMode = false;
+ public int pointSize = 2;
+ public boolean layoutSet = false;
+ public int missed_readings_alert_id = 818;
+ public BgGraphBuilder bgGraphBuilder;
+ public LineChartView chart;
+ public double datetime;
+ public ArrayList bgDataList = new ArrayList<>();
+ public ArrayList tempWatchDataList = new ArrayList<>();
+ public ArrayList basalWatchDataList = new ArrayList<>();
+ public PowerManager.WakeLock wakeLock;
+ // related endTime manual layout
+ public View layoutView;
+ private final Point displaySize = new Point();
+ private int specW, specH;
+
+ private LocalBroadcastManager localBroadcastManager;
+ private MessageReceiver messageReceiver;
+
+ protected SharedPreferences sharedPrefs;
+ private String batteryString = "--";
+ private String sgvString = "--";
+ private String externalStatusString = "no status";
+ private String avgDelta = "";
+ private String delta = "";
+
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay();
+ display.getSize(displaySize);
+ wakeLock = ((PowerManager) getSystemService(Context.POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Clock");
+
+ specW = View.MeasureSpec.makeMeasureSpec(displaySize.x,
+ View.MeasureSpec.EXACTLY);
+ specH = View.MeasureSpec.makeMeasureSpec(displaySize.y,
+ View.MeasureSpec.EXACTLY);
+ sharedPrefs = PreferenceManager
+ .getDefaultSharedPreferences(this);
+ sharedPrefs.registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ protected void onLayout(WatchShape shape, Rect screenBounds, WindowInsets screenInsets) {
+ super.onLayout(shape, screenBounds, screenInsets);
+ layoutView.onApplyWindowInsets(screenInsets);
+ }
+
+ public void performViewSetup() {
+ final WatchViewStub stub = (WatchViewStub) layoutView.findViewById(R.id.watch_view_stub);
+ IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND);
+
+ messageReceiver = new MessageReceiver();
+ localBroadcastManager = LocalBroadcastManager.getInstance(this);
+ localBroadcastManager.registerReceiver(messageReceiver, messageFilter);
+
+ stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
+ @Override
+ public void onLayoutInflated(WatchViewStub stub) {
+ mTime = (TextView) stub.findViewById(R.id.watch_time);
+ mSgv = (TextView) stub.findViewById(R.id.sgv);
+ mDirection = (TextView) stub.findViewById(R.id.direction);
+ mTimestamp = (TextView) stub.findViewById(R.id.timestamp);
+ mStatus = (TextView) stub.findViewById(R.id.externaltstatus);
+ mUploaderBattery = (TextView) stub.findViewById(R.id.uploader_battery);
+ mDelta = (TextView) stub.findViewById(R.id.delta);
+ mRelativeLayout = (RelativeLayout) stub.findViewById(R.id.main_layout);
+ mLinearLayout = (LinearLayout) stub.findViewById(R.id.secondary_layout);
+ chart = (LineChartView) stub.findViewById(R.id.chart);
+ layoutSet = true;
+ showAgoRawBattStatus();
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ }
+ });
+ ListenerService.requestData(this);
+ wakeLock.acquire(50);
+ }
+
+ public int ageLevel() {
+ if(timeSince() <= (1000 * 60 * 12)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ public double timeSince() {
+ return System.currentTimeMillis() - datetime;
+ }
+
+ public String readingAge(boolean shortString) {
+ if (datetime == 0) { return shortString?"--'":"-- Minute ago"; }
+ int minutesAgo = (int) Math.floor(timeSince()/(1000*60));
+ if (minutesAgo == 1) {
+ return minutesAgo + (shortString?"'":" Minute ago");
+ }
+ return minutesAgo + (shortString?"'":" Minutes ago");
+ }
+
+ @Override
+ public void onDestroy() {
+ if(localBroadcastManager != null && messageReceiver != null){
+ localBroadcastManager.unregisterReceiver(messageReceiver);}
+ if (sharedPrefs != null){
+ sharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
+ }
+ super.onDestroy();
+ }
+
+ static {
+ INTENT_FILTER = new IntentFilter();
+ INTENT_FILTER.addAction(Intent.ACTION_TIME_TICK);
+ INTENT_FILTER.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ INTENT_FILTER.addAction(Intent.ACTION_TIME_CHANGED);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if(layoutSet) {
+ this.mRelativeLayout.draw(canvas);
+ Log.d("onDraw", "draw");
+ }
+ }
+
+ @Override
+ protected void onTimeChanged(WatchFaceTime oldTime, WatchFaceTime newTime) {
+ if (layoutSet && (newTime.hasHourChanged(oldTime) || newTime.hasMinuteChanged(oldTime))) {
+ wakeLock.acquire(50);
+ final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(BaseWatchFace.this);
+ mTime.setText(timeFormat.format(System.currentTimeMillis()));
+ showAgoRawBattStatus();
+
+ if(ageLevel()<=0) {
+ mSgv.setPaintFlags(mSgv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
+ } else {
+ mSgv.setPaintFlags(mSgv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
+ }
+
+ missedReadingAlert();
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ }
+ }
+
+ public class MessageReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ //data
+ Bundle bundle = intent.getBundleExtra("data");
+ if (layoutSet && bundle != null) {
+ DataMap dataMap = DataMap.fromBundle(bundle);
+ wakeLock.acquire(50);
+ sgvLevel = dataMap.getLong("sgvLevel");
+ batteryLevel = dataMap.getInt("batteryLevel");
+ datetime = dataMap.getDouble("timestamp");
+ sgvString = dataMap.getString("sgvString");
+ batteryString = dataMap.getString("battery");
+ mSgv.setText(dataMap.getString("sgvString"));
+
+ if(ageLevel()<=0) {
+ mSgv.setPaintFlags(mSgv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
+ } else {
+ mSgv.setPaintFlags(mSgv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
+ }
+
+ final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(BaseWatchFace.this);
+ mTime.setText(timeFormat.format(System.currentTimeMillis()));
+
+ mDirection.setText(dataMap.getString("slopeArrow"));
+ avgDelta = dataMap.getString("avgDelta");
+ delta = dataMap.getString("delta");
+
+
+ showAgoRawBattStatus();
+
+
+ if (chart != null) {
+ addToWatchSet(dataMap);
+ setupCharts();
+ }
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ invalidate();
+ setColor();
+ }
+ //status
+ bundle = intent.getBundleExtra("status");
+ if (layoutSet && bundle != null) {
+ DataMap dataMap = DataMap.fromBundle(bundle);
+ wakeLock.acquire(50);
+ externalStatusString = dataMap.getString("externalStatusString");
+
+ showAgoRawBattStatus();
+
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ invalidate();
+ setColor();
+ }
+ //basals and temps
+ bundle = intent.getBundleExtra("basals");
+ if (layoutSet && bundle != null) {
+ DataMap dataMap = DataMap.fromBundle(bundle);
+ wakeLock.acquire(500);
+
+ loadBasalsAndTemps(dataMap);
+
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ invalidate();
+ setColor();
+ }
+ }
+ }
+
+ private void showAgoRawBattStatus() {
+
+
+ boolean showAvgDelta = sharedPrefs.getBoolean("showAvgDelta", true);
+ mDelta.setText(delta);
+ if(showAvgDelta){
+ mDelta.append(" " + avgDelta);
+ }
+
+
+ if( mTimestamp == null || mUploaderBattery == null|| mStatus == null){
+ return;
+ }
+
+ boolean showStatus = sharedPrefs.getBoolean("showExternalStatus", true);
+
+ if(showStatus){
+ //use short forms
+ mTimestamp.setText(readingAge(true));
+ mUploaderBattery.setText("U: " + batteryString + "%");
+ } else {
+ mTimestamp.setText(readingAge(false));
+ mUploaderBattery.setText("Uploader: " + batteryString + "%");
+ }
+
+
+ if (showStatus) {
+ mStatus.setVisibility(View.VISIBLE);
+ mStatus.setText("S: " + externalStatusString);
+ } else {
+ mStatus.setVisibility(View.GONE);
+ }
+ }
+
+ public void setColor() {
+ if(lowResMode){
+ setColorLowRes();
+ } else if (sharedPrefs.getBoolean("dark", true)) {
+ setColorDark();
+ } else {
+ setColorBright();
+ }
+ }
+
+ protected void onWatchModeChanged(WatchMode watchMode) {
+
+ if(lowResMode ^ isLowRes(watchMode)){ //if there was a change in lowResMode
+ lowResMode = isLowRes(watchMode);
+ setColor();
+ } else if (! sharedPrefs.getBoolean("dark", true)){
+ //in bright mode: different colours if active:
+ setColor();
+ }
+ }
+
+ private boolean isLowRes(WatchMode watchMode) {
+ return (watchMode == WatchMode.LOW_BIT) || (watchMode == WatchMode.LOW_BIT_BURN_IN) || (watchMode == WatchMode.LOW_BIT_BURN_IN);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key){
+ setColor();
+ if(layoutSet){
+ showAgoRawBattStatus();
+ mRelativeLayout.measure(specW, specH);
+ mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(),
+ mRelativeLayout.getMeasuredHeight());
+ }
+ invalidate();
+ }
+protected abstract void setColorDark();
+ protected abstract void setColorBright();
+ protected abstract void setColorLowRes();
+
+
+ public void missedReadingAlert() {
+ int minutes_since = (int) Math.floor(timeSince()/(1000*60));
+ if(minutes_since >= 16 && ((minutes_since - 16) % 5) == 0) {
+ ListenerService.requestData(this); // attempt endTime recover missing data
+ }
+ }
+
+ public void addToWatchSet(DataMap dataMap) {
+
+ ArrayList entries = dataMap.getDataMapArrayList("entries");
+ if (entries != null) {
+ bgDataList = new ArrayList();
+ for (DataMap entry : entries) {
+ double sgv = entry.getDouble("sgvDouble");
+ double high = entry.getDouble("high");
+ double low = entry.getDouble("low");
+ double timestamp = entry.getDouble("timestamp");
+ bgDataList.add(new BgWatchData(sgv, high, low, timestamp));
+ }
+ } else {
+ double sgv = dataMap.getDouble("sgvDouble");
+ double high = dataMap.getDouble("high");
+ double low = dataMap.getDouble("low");
+ double timestamp = dataMap.getDouble("timestamp");
+
+ final int size = bgDataList.size();
+ if (size > 0) {
+ if (bgDataList.get(size - 1).timestamp == timestamp)
+ return; // Ignore duplicates.
+ }
+
+ bgDataList.add(new BgWatchData(sgv, high, low, timestamp));
+ }
+
+ for (int i = 0; i < bgDataList.size(); i++) {
+ if (bgDataList.get(i).timestamp < (new Date().getTime() - (1000 * 60 * 60 * 5))) {
+ bgDataList.remove(i); //Get rid of anything more than 5 hours old
+ break;
+ }
+ }
+ }
+
+ public void setupCharts() {
+ if(bgDataList.size() > 0) { //Dont crash things just because we dont have values, people dont like crashy things
+ int timeframe = Integer.parseInt(sharedPrefs.getString("chart_timeframe", "3"));
+ if (lowResMode) {
+ bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), bgDataList, tempWatchDataList, basalWatchDataList, pointSize, midColor, gridColor, basalBackgroundColor, basalCenterColor, timeframe);
+ } else {
+ bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), bgDataList, tempWatchDataList, basalWatchDataList, pointSize, highColor, lowColor, midColor, gridColor, basalBackgroundColor, basalCenterColor, timeframe);
+ }
+
+ chart.setLineChartData(bgGraphBuilder.lineData());
+ chart.setViewportCalculationEnabled(true);
+ chart.setMaximumViewport(chart.getMaximumViewport());
+ } else {
+ ListenerService.requestData(this);
+ }
+ }
+
+ private void loadBasalsAndTemps(DataMap dataMap) {
+ ArrayList temps = dataMap.getDataMapArrayList("temps");
+ if (temps != null) {
+ tempWatchDataList = new ArrayList<>();
+ for (DataMap temp : temps) {
+ TempWatchData twd = new TempWatchData();
+ twd.startTime = temp.getLong("starttime");
+ twd.startBasal = temp.getDouble("startBasal");
+ twd.endTime = temp.getLong("endtime");
+ twd.endBasal = temp.getDouble("endbasal");
+ twd.amount = temp.getDouble("amount");
+ tempWatchDataList.add(twd);
+ }
+ }
+ ArrayList basals = dataMap.getDataMapArrayList("basals");
+ if (basals != null) {
+ basalWatchDataList = new ArrayList<>();
+ for (DataMap basal : basals) {
+ BasalWatchData bwd = new BasalWatchData();
+ bwd.startTime = basal.getLong("starttime");
+ bwd.endTime = basal.getLong("endtime");
+ bwd.amount = basal.getDouble("amount");
+ basalWatchDataList.add(bwd);
+ }
+ }
+ }
+}
diff --git a/wear/src/main/java/info/nightscout/androidaps/BgGraphBuilder.java b/wear/src/main/java/info/nightscout/androidaps/BgGraphBuilder.java
new file mode 100644
index 0000000000..825c256678
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/BgGraphBuilder.java
@@ -0,0 +1,354 @@
+package info.nightscout.androidaps;
+
+import android.content.Context;
+import android.graphics.DashPathEffect;
+import android.preference.PreferenceManager;
+import android.support.v4.content.ContextCompat;
+import android.text.format.DateFormat;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.TimeZone;
+
+import lecho.lib.hellocharts.model.Axis;
+import lecho.lib.hellocharts.model.AxisValue;
+import lecho.lib.hellocharts.model.Line;
+import lecho.lib.hellocharts.model.LineChartData;
+import lecho.lib.hellocharts.model.PointValue;
+import lecho.lib.hellocharts.model.Viewport;
+
+/**
+ * Created by emmablack on 11/15/14.
+ */
+public class BgGraphBuilder {
+ private ArrayList basalWatchDataList;
+ public List tempWatchDataList;
+ private int timespan;
+ public double end_time;
+ public double start_time;
+ public double fuzzyTimeDenom = (1000 * 60 * 1);
+ public Context context;
+ public double highMark;
+ public double lowMark;
+ public List bgDataList = new ArrayList();
+
+ public int pointSize;
+ public int highColor;
+ public int lowColor;
+ public int midColor;
+ public int gridColour;
+ public int basalCenterColor;
+ public int basalBackgroundColor;
+ public boolean singleLine = false;
+
+ private double endHour;
+ private List inRangeValues = new ArrayList();
+ private List highValues = new ArrayList();
+ private List lowValues = new ArrayList();
+ public Viewport viewport;
+
+
+ //used for low resolution screen.
+ public BgGraphBuilder(Context context, List aBgList, List tempWatchDataList, ArrayList basalWatchDataList, int aPointSize, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int timespan) {
+ end_time = new Date().getTime() + (1000 * 60 * 6 * timespan); //Now plus 30 minutes padding (for 5 hours. Less if less.)
+ start_time = new Date().getTime() - (1000 * 60 * 60 * timespan); //timespan hours ago
+ this.bgDataList = aBgList;
+ this.context = context;
+ this.highMark = aBgList.get(aBgList.size() - 1).high;
+ this.lowMark = aBgList.get(aBgList.size() - 1).low;
+ this.pointSize = aPointSize;
+ this.singleLine = false;
+ this.midColor = aMidColor;
+ this.lowColor = aMidColor;
+ this.highColor = aMidColor;
+ this.timespan = timespan;
+ this.tempWatchDataList = tempWatchDataList;
+ this.basalWatchDataList = basalWatchDataList;
+ this.gridColour = gridColour;
+ this.basalCenterColor = basalCenterColor;
+ this.basalBackgroundColor = basalBackgroundColor;
+ }
+
+ public BgGraphBuilder(Context context, List aBgList, List tempWatchDataList, ArrayList basalWatchDataList, int aPointSize, int aHighColor, int aLowColor, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int timespan) {
+ end_time = new Date().getTime() + (1000 * 60 * 6 * timespan); //Now plus 30 minutes padding (for 5 hours. Less if less.)
+ start_time = new Date().getTime() - (1000 * 60 * 60 * timespan); //timespan hours ago
+ this.bgDataList = aBgList;
+ this.context = context;
+ this.highMark = aBgList.get(aBgList.size() - 1).high;
+ this.lowMark = aBgList.get(aBgList.size() - 1).low;
+ this.pointSize = aPointSize;
+ this.highColor = aHighColor;
+ this.lowColor = aLowColor;
+ this.midColor = aMidColor;
+ this.timespan = timespan;
+ this.tempWatchDataList = tempWatchDataList;
+ this.basalWatchDataList = basalWatchDataList;
+ this.gridColour = gridColour;
+ this.basalCenterColor = basalCenterColor;
+ this.basalBackgroundColor = basalBackgroundColor;
+ }
+
+ public LineChartData lineData() {
+ LineChartData lineData = new LineChartData(defaultLines());
+ lineData.setAxisYLeft(yAxis());
+ lineData.setAxisXBottom(xAxis());
+ return lineData;
+ }
+
+ public List defaultLines() {
+ addBgReadingValues();
+ List lines = new ArrayList();
+ lines.add(highLine());
+ lines.add(lowLine());
+ lines.add(inRangeValuesLine());
+ lines.add(lowValuesLine());
+ lines.add(highValuesLine());
+
+ double minChart = lowMark;
+ double maxChart = highMark;
+
+ for ( BgWatchData bgd:bgDataList) {
+ if(bgd.sgv > maxChart){
+ maxChart = bgd.sgv;
+ }
+ if(bgd.sgv < minChart){
+ minChart = bgd.sgv;
+ }
+ }
+
+ double maxBasal = 0.1;
+ for (BasalWatchData bwd: basalWatchDataList) {
+ if(bwd.amount > maxBasal){
+ maxBasal = bwd.amount;
+ }
+ }
+
+ double maxTemp = maxBasal;
+ for (TempWatchData twd: tempWatchDataList) {
+ if(twd.amount > maxTemp){
+ maxTemp = twd.amount;
+ }
+ }
+
+ double factor = (maxChart-minChart)/maxTemp;
+ // in case basal is the highest, don't paint it totally at the top.
+ factor = Math.min(factor, ((maxChart-minChart)/maxBasal)*(2/3d));
+
+ boolean highlight = PreferenceManager
+ .getDefaultSharedPreferences(context)
+ .getBoolean("highlight_basals", false);
+
+ for (TempWatchData twd: tempWatchDataList) {
+ if(twd.endTime > start_time) {
+ lines.add(tempValuesLine(twd, (float) minChart, factor, false, highlight?3:2));
+ if(highlight){
+ lines.add(tempValuesLine(twd, (float) minChart, factor, true, 1));
+ }
+ }
+ }
+
+ lines.add(basalLine((float) minChart, factor, highlight));
+
+ return lines;
+ }
+
+ private Line basalLine(float offset, double factor, boolean highlight) {
+
+ List pointValues = new ArrayList();
+
+ for (BasalWatchData bwd: basalWatchDataList) {
+ if(bwd.endTime > start_time) {
+ long begin = (long) Math.max(start_time, bwd.startTime);
+ pointValues.add(new PointValue(fuzz(begin), offset + (float) (factor * bwd.amount)));
+ pointValues.add(new PointValue(fuzz(bwd.endTime), offset + (float) (factor * bwd.amount)));
+ }
+ }
+
+ Line basalLine = new Line(pointValues);
+ basalLine.setHasPoints(false);
+ basalLine.setColor(basalCenterColor);
+ basalLine.setPathEffect(new DashPathEffect(new float[]{4f, 3f}, 4f));
+ basalLine.setStrokeWidth(highlight?2:1);
+ return basalLine;
+
+
+ }
+
+ public Line highValuesLine() {
+ Line highValuesLine = new Line(highValues);
+ highValuesLine.setColor(highColor);
+ highValuesLine.setHasLines(false);
+ highValuesLine.setPointRadius(pointSize);
+ highValuesLine.setHasPoints(true);
+ return highValuesLine;
+ }
+
+ public Line lowValuesLine() {
+ Line lowValuesLine = new Line(lowValues);
+ lowValuesLine.setColor(lowColor);
+ lowValuesLine.setHasLines(false);
+ lowValuesLine.setPointRadius(pointSize);
+ lowValuesLine.setHasPoints(true);
+ return lowValuesLine;
+ }
+
+ public Line inRangeValuesLine() {
+ Line inRangeValuesLine = new Line(inRangeValues);
+ inRangeValuesLine.setColor(midColor);
+ if(singleLine) {
+ inRangeValuesLine.setHasLines(true);
+ inRangeValuesLine.setHasPoints(false);
+ inRangeValuesLine.setStrokeWidth(pointSize);
+ } else {
+ inRangeValuesLine.setPointRadius(pointSize);
+ inRangeValuesLine.setHasPoints(true);
+ inRangeValuesLine.setHasLines(false);
+ }
+ return inRangeValuesLine;
+ }
+
+
+ public Line tempValuesLine(TempWatchData twd, float offset, double factor, boolean isHighlightLine, int strokeWidth) {
+ List lineValues = new ArrayList();
+ long begin = (long) Math.max(start_time, twd.startTime);
+ lineValues.add(new PointValue(fuzz(begin), offset + (float) (factor * twd.startBasal)));
+ lineValues.add(new PointValue(fuzz(begin), offset + (float) (factor * twd.amount)));
+ lineValues.add(new PointValue(fuzz(twd.endTime), offset + (float) (factor * twd.amount)));
+ lineValues.add(new PointValue(fuzz(twd.endTime), offset + (float) (factor * twd.endBasal)));
+ Line valueLine = new Line(lineValues);
+ valueLine.setHasPoints(false);
+ if (isHighlightLine){
+ valueLine.setColor(basalCenterColor);
+ valueLine.setStrokeWidth(1);
+ }else {
+ valueLine.setColor(basalBackgroundColor);
+ valueLine.setStrokeWidth(strokeWidth);
+ }
+ return valueLine;
+ }
+
+
+
+
+ private void addBgReadingValues() {
+ if(singleLine) {
+ for (BgWatchData bgReading : bgDataList) {
+ if(bgReading.timestamp > start_time) {
+ if (bgReading.sgv >= 400) {
+ inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 400));
+ } else if (bgReading.sgv >= highMark) {
+ inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv));
+ } else if (bgReading.sgv >= lowMark) {
+ inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv));
+ } else if (bgReading.sgv >= 40) {
+ inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv));
+ } else if (bgReading.sgv >= 11) {
+ inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 40));
+ }
+ }
+ }
+ } else {
+ for (BgWatchData bgReading : bgDataList) {
+ if (bgReading.timestamp > start_time) {
+ if (bgReading.sgv >= 400) {
+ highValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 400));
+ } else if (bgReading.sgv >= highMark) {
+ highValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv));
+ } else if (bgReading.sgv >= lowMark) {
+ inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv));
+ } else if (bgReading.sgv >= 40) {
+ lowValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv));
+ } else if (bgReading.sgv >= 11) {
+ lowValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 40));
+ }
+ }
+ }
+ }
+ }
+
+ public Line highLine() {
+ List highLineValues = new ArrayList();
+ highLineValues.add(new PointValue(fuzz(start_time), (float) highMark));
+ highLineValues.add(new PointValue(fuzz(end_time), (float) highMark));
+ Line highLine = new Line(highLineValues);
+ highLine.setHasPoints(false);
+ highLine.setStrokeWidth(1);
+ highLine.setColor(highColor);
+ return highLine;
+ }
+
+ public Line lowLine() {
+ List lowLineValues = new ArrayList();
+ lowLineValues.add(new PointValue(fuzz(start_time), (float) lowMark));
+ lowLineValues.add(new PointValue(fuzz(end_time), (float) lowMark));
+ Line lowLine = new Line(lowLineValues);
+ lowLine.setHasPoints(false);
+ lowLine.setColor(lowColor);
+ lowLine.setStrokeWidth(1);
+ return lowLine;
+ }
+
+ /////////AXIS RELATED//////////////
+
+
+ public Axis yAxis() {
+ Axis yAxis = new Axis();
+ yAxis.setAutoGenerated(true);
+ List axisValues = new ArrayList();
+ yAxis.setValues(axisValues);
+ yAxis.setHasLines(false);
+ yAxis.setLineColor(gridColour);
+ return yAxis;
+ }
+
+ public Axis xAxis() {
+ final boolean is24 = DateFormat.is24HourFormat(context);
+ Axis xAxis = new Axis();
+ xAxis.setAutoGenerated(false);
+ List xAxisValues = new ArrayList();
+ GregorianCalendar now = new GregorianCalendar();
+ GregorianCalendar today = new GregorianCalendar(now.get(Calendar.YEAR), now.get(Calendar.MONTH), now.get(Calendar.DAY_OF_MONTH));
+ SimpleDateFormat timeFormat = new SimpleDateFormat(is24? "HH" : "h a");
+ timeFormat.setTimeZone(TimeZone.getDefault());
+ double start_hour = today.getTime().getTime();
+ double timeNow = new Date().getTime();
+ for (int l = 0; l <= 24; l++) {
+ if ((start_hour + (60000 * 60 * (l))) < timeNow) {
+ if ((start_hour + (60000 * 60 * (l + 1))) >= timeNow) {
+ endHour = start_hour + (60000 * 60 * (l));
+ l = 25;
+ }
+ }
+ }
+ //Display current time on the graph
+ SimpleDateFormat longTimeFormat = new SimpleDateFormat(is24? "HH:mm" : "h:mm a");
+ xAxisValues.add(new AxisValue(fuzz(timeNow), (longTimeFormat.format(timeNow)).toCharArray()));
+
+ //Add whole hours endTime the axis (as long as they are more than 15 mins away from the current time)
+ for (int l = 0; l <= 24; l++) {
+ double timestamp = endHour - (60000 * 60 * l);
+ if((timestamp - timeNow < 0) && (timestamp > start_time)) {
+ if(Math.abs(timestamp - timeNow) > (1000 * 60 * 8 * timespan)){
+ xAxisValues.add(new AxisValue(fuzz(timestamp), (timeFormat.format(timestamp)).toCharArray()));
+ }else {
+ xAxisValues.add(new AxisValue(fuzz(timestamp), "".toCharArray()));
+ }
+ }
+ }
+ xAxis.setValues(xAxisValues);
+ xAxis.setTextSize(10);
+ xAxis.setHasLines(true);
+ xAxis.setLineColor(gridColour);
+ xAxis.setTextColor(gridColour);
+
+ return xAxis;
+ }
+
+ public float fuzz(double value) {
+ return (float) Math.round(value / fuzzyTimeDenom);
+ }
+}
diff --git a/wear/src/main/java/info/nightscout/androidaps/BgWatchData.java b/wear/src/main/java/info/nightscout/androidaps/BgWatchData.java
new file mode 100644
index 0000000000..5e6df3c3da
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/BgWatchData.java
@@ -0,0 +1,39 @@
+package info.nightscout.androidaps;
+
+/**
+ * Created by emmablack on 1/7/15.
+ */
+public class BgWatchData implements Comparable{
+ public double sgv;
+ public double high;
+ public double low;
+ public double timestamp;
+
+ public BgWatchData(double aSgv, double aHigh, double aLow, double aTimestamp) {
+ this.sgv = aSgv;
+ this.high = aHigh;
+ this.low = aLow;
+ this.timestamp = aTimestamp;
+ }
+
+ @Override
+ public boolean equals(Object that){
+ if(! (that instanceof BgWatchData)){
+ return false;
+ }
+ return this.timestamp == ((BgWatchData) that).timestamp;
+ }
+
+ @Override
+ public int hashCode(){
+ return (int) (timestamp%Integer.MAX_VALUE);
+ }
+
+ @Override
+ public int compareTo(BgWatchData that) {
+ // reverse order endTime get latest first
+ if(this.timestamp < that.timestamp) return 1;
+ if(this.timestamp > that.timestamp) return -1;
+ return 0;
+ }
+}
diff --git a/wear/src/main/java/info/nightscout/androidaps/CircleWatchface.java b/wear/src/main/java/info/nightscout/androidaps/CircleWatchface.java
new file mode 100644
index 0000000000..c81b6de6f1
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/CircleWatchface.java
@@ -0,0 +1,703 @@
+package info.nightscout.androidaps;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.preference.PreferenceManager;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.google.android.gms.wearable.DataMap;
+import com.ustwo.clockwise.wearable.WatchFace;
+import com.ustwo.clockwise.common.WatchFaceTime;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.TreeSet;
+
+
+public class CircleWatchface extends WatchFace implements SharedPreferences.OnSharedPreferenceChangeListener {
+ public final float PADDING = 20f;
+ public final float CIRCLE_WIDTH = 10f;
+ public final int BIG_HAND_WIDTH = 16;
+ public final int SMALL_HAND_WIDTH = 8;
+ public final int NEAR = 2; //how near do the hands have endTime be endTime activate overlapping mode
+ public final boolean ALWAYS_HIGHLIGT_SMALL = false;
+
+ //variables for time
+ private float angleBig = 0f;
+ private float angleSMALL = 0f;
+ private int hour, minute;
+ private int color;
+ private Paint circlePaint = new Paint();
+ private Paint removePaint = new Paint();
+ private RectF rect, rectDelete;
+ private boolean overlapping;
+
+ private int animationAngle = 0;
+ private boolean isAnimated = false;
+
+
+ public Point displaySize = new Point();
+ private MessageReceiver messageReceiver = new MessageReceiver();
+
+ private int sgvLevel = 0;
+ private String sgvString = "999";
+ private String statusString = "no status";
+
+
+ private int batteryLevel = 0;
+ private double datetime = 0;
+ private String direction = "";
+ private String delta = "";
+ private String avgDelta = "";
+ public TreeSet bgDataList = new TreeSet();
+
+ private View layoutView;
+ private int specW;
+ private int specH;
+ private View myLayout;
+
+ protected SharedPreferences sharedPrefs;
+
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
+ PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "CreateWakelock");
+ wakeLock.acquire(30000);
+
+ Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay();
+ display.getSize(displaySize);
+
+ specW = View.MeasureSpec.makeMeasureSpec(displaySize.x,
+ View.MeasureSpec.EXACTLY);
+ specH = View.MeasureSpec.makeMeasureSpec(displaySize.y,
+ View.MeasureSpec.EXACTLY);
+
+ sharedPrefs = PreferenceManager
+ .getDefaultSharedPreferences(this);
+ sharedPrefs.registerOnSharedPreferenceChangeListener(this);
+
+ //register Message Receiver
+ LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, new IntentFilter(Intent.ACTION_SEND));
+
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ myLayout = inflater.inflate(R.layout.modern_layout, null);
+ prepareLayout();
+ prepareDrawTime();
+
+ //ListenerService.requestData(this); //usually connection is not set up yet
+
+ wakeLock.release();
+ }
+
+
+ @Override
+ public void onDestroy() {
+ if (messageReceiver != null) {
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(messageReceiver);
+ }
+ if (sharedPrefs != null) {
+ sharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
+ }
+ super.onDestroy();
+ }
+
+ @Override
+ protected synchronized void onDraw(Canvas canvas) {
+ Log.d("CircleWatchface", "start onDraw");
+ canvas.drawColor(getBackgroundColor());
+ drawTime(canvas);
+ drawOtherStuff(canvas);
+ myLayout.draw(canvas);
+
+ }
+
+ private synchronized void prepareLayout() {
+
+ Log.d("CircleWatchface", "start startPrepareLayout");
+
+ // prepare fields
+
+ TextView textView = null;
+
+ textView = (TextView) myLayout.findViewById(R.id.sgvString);
+ if (sharedPrefs.getBoolean("showBG", true)) {
+ textView.setVisibility(View.VISIBLE);
+ textView.setText(getSgvString());
+ textView.setTextColor(getTextColor());
+
+ } else {
+ //Also possible: View.INVISIBLE instead of View.GONE (no layout change)
+ textView.setVisibility(View.INVISIBLE);
+ }
+
+ textView = (TextView) myLayout.findViewById(R.id.statusString);
+ if (sharedPrefs.getBoolean("showExternalStatus", true)) {
+ textView.setVisibility(View.VISIBLE);
+ textView.setText(getStatusString());
+ textView.setTextColor(getTextColor());
+
+ } else {
+ //Also possible: View.INVISIBLE instead of View.GONE (no layout change)
+ textView.setVisibility(View.GONE);
+ }
+
+ textView = (TextView) myLayout.findViewById(R.id.agoString);
+ if (sharedPrefs.getBoolean("showAgo", true)) {
+ textView.setVisibility(View.VISIBLE);
+
+ if (sharedPrefs.getBoolean("showBigNumbers", false)) {
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 26);
+ } else {
+ ((TextView) myLayout.findViewById(R.id.agoString)).setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
+ }
+ textView.setText(getMinutes());
+ textView.setTextColor(getTextColor());
+ } else {
+ //Also possible: View.INVISIBLE instead of View.GONE (no layout change)
+ textView.setVisibility(View.INVISIBLE);
+ }
+
+ textView = (TextView) myLayout.findViewById(R.id.deltaString);
+ if (sharedPrefs.getBoolean("showDelta", true)) {
+ textView.setVisibility(View.VISIBLE);
+ textView.setText(getDelta());
+ textView.setTextColor(getTextColor());
+ if (sharedPrefs.getBoolean("showBigNumbers", false)) {
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 25);
+ } else {
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
+ }
+ if(sharedPrefs.getBoolean("showAvgDelta", true)){
+ textView.append(" " + getAvgDelta());
+ }
+
+ } else {
+ //Also possible: View.INVISIBLE instead of View.GONE (no layout change)
+ textView.setVisibility(View.INVISIBLE);
+ }
+
+ myLayout.measure(specW, specH);
+ myLayout.layout(0, 0, myLayout.getMeasuredWidth(),
+ myLayout.getMeasuredHeight());
+ }
+
+ public String getMinutes() {
+ String minutes = "--\'";
+ if (getDatetime() != 0) {
+ minutes = ((int) Math.floor((System.currentTimeMillis() - getDatetime()) / 60000)) + "\'";
+ }
+ return minutes;
+ }
+
+ private void drawTime(Canvas canvas) {
+
+ //draw circle
+ circlePaint.setColor(color);
+ circlePaint.setStrokeWidth(CIRCLE_WIDTH);
+ canvas.drawArc(rect, 0, 360, false, circlePaint);
+ //"remove" hands from circle
+ removePaint.setStrokeWidth(CIRCLE_WIDTH * 3);
+
+ canvas.drawArc(rectDelete, angleBig, (float) BIG_HAND_WIDTH, false, removePaint);
+ canvas.drawArc(rectDelete, angleSMALL, (float) SMALL_HAND_WIDTH, false, removePaint);
+
+
+ if (overlapping) {
+ //add small hand with extra
+ circlePaint.setStrokeWidth(CIRCLE_WIDTH * 2);
+ circlePaint.setColor(color);
+ canvas.drawArc(rect, angleSMALL, (float) SMALL_HAND_WIDTH, false, circlePaint);
+
+ //remove inner part of hands
+ removePaint.setStrokeWidth(CIRCLE_WIDTH);
+ canvas.drawArc(rect, angleBig, (float) BIG_HAND_WIDTH, false, removePaint);
+ canvas.drawArc(rect, angleSMALL, (float) SMALL_HAND_WIDTH, false, removePaint);
+ }
+
+ }
+
+ private synchronized void prepareDrawTime() {
+ Log.d("CircleWatchface", "start prepareDrawTime");
+
+ hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) % 12;
+ minute = Calendar.getInstance().get(Calendar.MINUTE);
+ angleBig = (((hour + minute / 60f) / 12f * 360) - 90 - BIG_HAND_WIDTH / 2f + 360) % 360;
+ angleSMALL = ((minute / 60f * 360) - 90 - SMALL_HAND_WIDTH / 2f + 360) % 360;
+
+
+ color = 0;
+ switch (getSgvLevel()) {
+ case -1:
+ color = getLowColor();
+ break;
+ case 0:
+ color = getInRangeColor();
+ break;
+ case 1:
+ color = getHighColor();
+ break;
+ }
+
+
+ if (isAnimated()) {
+ //Animation matrix:
+ int[] rainbow = {Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE
+ , Color.CYAN};
+ Shader shader = new LinearGradient(0, 0, 0, 20, rainbow,
+ null, Shader.TileMode.MIRROR);
+ Matrix matrix = new Matrix();
+ matrix.setRotate(animationAngle);
+ shader.setLocalMatrix(matrix);
+ circlePaint.setShader(shader);
+ } else {
+ circlePaint.setShader(null);
+ }
+
+
+ circlePaint.setStyle(Paint.Style.STROKE);
+ circlePaint.setStrokeWidth(CIRCLE_WIDTH);
+ circlePaint.setAntiAlias(true);
+ circlePaint.setColor(color);
+
+ removePaint.setStyle(Paint.Style.STROKE);
+ removePaint.setStrokeWidth(CIRCLE_WIDTH);
+ removePaint.setAntiAlias(true);
+ removePaint.setColor(getBackgroundColor());
+
+ ;
+
+ rect = new RectF(PADDING, PADDING, (float) (displaySize.x - PADDING), (float) (displaySize.y - PADDING));
+ rectDelete = new RectF(PADDING - CIRCLE_WIDTH / 2, PADDING - CIRCLE_WIDTH / 2, (float) (displaySize.x - PADDING + CIRCLE_WIDTH / 2), (float) (displaySize.y - PADDING + CIRCLE_WIDTH / 2));
+ overlapping = ALWAYS_HIGHLIGT_SMALL || areOverlapping(angleSMALL, angleSMALL + SMALL_HAND_WIDTH + NEAR, angleBig, angleBig + BIG_HAND_WIDTH + NEAR);
+ Log.d("CircleWatchface", "end prepareDrawTime");
+
+ }
+
+ synchronized void animationStep() {
+ animationAngle = (animationAngle + 1) % 360;
+ prepareDrawTime();
+ invalidate();
+ }
+
+
+ private boolean areOverlapping(float aBegin, float aEnd, float bBegin, float bEnd) {
+ return
+ aBegin <= bBegin && aEnd >= bBegin ||
+ aBegin <= bBegin && (bEnd > 360) && bEnd % 360 > aBegin ||
+ bBegin <= aBegin && bEnd >= aBegin ||
+ bBegin <= aBegin && aEnd > 360 && aEnd % 360 > bBegin;
+ }
+
+ @Override
+ protected void onTimeChanged(WatchFaceTime oldTime, WatchFaceTime newTime) {
+ if (oldTime.hasMinuteChanged(newTime)) {
+ PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
+ PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TimeChangedWakelock");
+ wakeLock.acquire(30000);
+ /*Preparing the layout just on every minute tick:
+ * - hopefully better battery life
+ * - drawback: might update the minutes since last reading up endTime one minute late*/
+ prepareLayout();
+ prepareDrawTime();
+ invalidate(); //redraw the time
+ wakeLock.release();
+
+ }
+ }
+
+
+ // defining color for dark and bright
+ public int getLowColor() {
+ if (sharedPrefs.getBoolean("dark", true)) {
+ return Color.argb(255, 255, 120, 120);
+ } else {
+ return Color.argb(255, 255, 80, 80);
+ }
+ }
+
+ public int getInRangeColor() {
+ if (sharedPrefs.getBoolean("dark", false)) {
+ return Color.argb(255, 120, 255, 120);
+ } else {
+ return Color.argb(255, 0, 240, 0);
+
+ }
+ }
+
+ public int getHighColor() {
+ if (sharedPrefs.getBoolean("dark", false)) {
+ return Color.argb(255, 255, 255, 120);
+ } else {
+ return Color.argb(255, 255, 200, 0);
+ }
+
+ }
+
+ public int getBackgroundColor() {
+ if (sharedPrefs.getBoolean("dark", false)) {
+ return Color.BLACK;
+ } else {
+ return Color.WHITE;
+
+ }
+ }
+
+ public int getTextColor() {
+ if (sharedPrefs.getBoolean("dark", false)) {
+ return Color.WHITE;
+ } else {
+ return Color.BLACK;
+
+ }
+ }
+
+ public void drawOtherStuff(Canvas canvas) {
+ Log.d("CircleWatchface", "start onDrawOtherStuff. bgDataList.size(): " + bgDataList.size());
+
+ if (isAnimated()) return; // too many repaints when animated
+ if (sharedPrefs.getBoolean("showRingHistory", false)) {
+ //Perfect low and High indicators
+ if (bgDataList.size() > 0) {
+ addIndicator(canvas, 100, Color.LTGRAY);
+ addIndicator(canvas, (float) bgDataList.iterator().next().low, getLowColor());
+ addIndicator(canvas, (float) bgDataList.iterator().next().high, getHighColor());
+
+
+ if (sharedPrefs.getBoolean("softRingHistory", true)) {
+ for (BgWatchData data : bgDataList) {
+ addReadingSoft(canvas, data);
+ }
+ } else {
+ for (BgWatchData data : bgDataList) {
+ addReading(canvas, data);
+ }
+ }
+ }
+ }
+ }
+
+ public int holdInMemory() {
+ return 6;
+ }
+
+ //getters & setters
+
+ private synchronized int getSgvLevel() {
+ return sgvLevel;
+ }
+
+ private synchronized void setSgvLevel(int sgvLevel) {
+ this.sgvLevel = sgvLevel;
+ }
+
+ private synchronized int getBatteryLevel() {
+ return batteryLevel;
+ }
+
+ private synchronized void setBatteryLevel(int batteryLevel) {
+ this.batteryLevel = batteryLevel;
+ }
+
+
+ private synchronized double getDatetime() {
+ return datetime;
+ }
+
+ private synchronized void setDatetime(double datetime) {
+ this.datetime = datetime;
+ }
+
+ private synchronized String getDirection() {
+ return direction;
+ }
+
+ private void setDirection(String direction) {
+ this.direction = direction;
+ }
+
+ String getSgvString() {
+ return sgvString;
+ }
+
+ void setSgvString(String sgvString) {
+ this.sgvString = sgvString;
+ }
+
+ String getStatusString() {
+ return statusString;
+ }
+
+ void setStatusString(String statusString) {
+ this.statusString = statusString;
+ }
+
+ public String getDelta() {
+ return delta;
+ }
+
+ private void setDelta(String delta) {
+ this.delta = delta;
+ }
+
+ private String getAvgDelta() {
+ return avgDelta;
+ }
+ private void setAvgDelta(String avgDelta) {
+ this.avgDelta = avgDelta;
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ prepareDrawTime();
+ prepareLayout();
+ invalidate();
+ }
+
+ private synchronized boolean isAnimated() {
+ return isAnimated;
+ }
+
+ private synchronized void setIsAnimated(boolean isAnimated) {
+ this.isAnimated = isAnimated;
+ }
+
+ void startAnimation() {
+ Log.d("CircleWatchface", "start startAnimation");
+
+ Thread animator = new Thread() {
+
+
+ public void run() {
+ setIsAnimated(true);
+ for (int i = 0; i <= 8 * 1000 / 40; i++) {
+ animationStep();
+ try {
+ Thread.sleep(40);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ setIsAnimated(false);
+ prepareDrawTime();
+ invalidate();
+ System.gc();
+ }
+ };
+
+ animator.start();
+ }
+
+
+ public class MessageReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
+ PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "MyWakelockTag");
+ wakeLock.acquire(30000);
+ Bundle bundle = intent.getBundleExtra("data");
+ if (bundle!= null) {
+ DataMap dataMap = DataMap.fromBundle(bundle);
+ setSgvLevel((int) dataMap.getLong("sgvLevel"));
+ Log.d("CircleWatchface", "sgv level : " + getSgvLevel());
+ setSgvString(dataMap.getString("sgvString"));
+ Log.d("CircleWatchface", "sgv string : " + getSgvString());
+ setDelta(dataMap.getString("delta"));
+ setAvgDelta(dataMap.getString("avgDelta"));
+ setDatetime(dataMap.getDouble("timestamp"));
+ addToWatchSet(dataMap);
+
+
+ //start animation?
+ // dataMap.getDataMapArrayList("entries") == null -> not on "resend data".
+ if (sharedPrefs.getBoolean("animation", false) && dataMap.getDataMapArrayList("entries") == null && (getSgvString().equals("100") || getSgvString().equals("5.5") || getSgvString().equals("5,5"))) {
+ startAnimation();
+ }
+
+ prepareLayout();
+ prepareDrawTime();
+ invalidate();
+ }
+ //status
+ bundle = intent.getBundleExtra("status");
+ if (bundle != null) {
+ DataMap dataMap = DataMap.fromBundle(bundle);
+ wakeLock.acquire(50);
+ setStatusString(dataMap.getString("externalStatusString"));
+
+ prepareLayout();
+ prepareDrawTime();
+ invalidate();
+ }
+ wakeLock.release();
+ }
+ }
+
+ public synchronized void addToWatchSet(DataMap dataMap) {
+
+ if(!sharedPrefs.getBoolean("showRingHistory", false)){
+ bgDataList.clear();
+ return;
+ }
+
+ Log.d("CircleWatchface", "start addToWatchSet");
+ ArrayList entries = dataMap.getDataMapArrayList("entries");
+ if (entries == null) {
+ double sgv = dataMap.getDouble("sgvDouble");
+ double high = dataMap.getDouble("high");
+ double low = dataMap.getDouble("low");
+ double timestamp = dataMap.getDouble("timestamp");
+ bgDataList.add(new BgWatchData(sgv, high, low, timestamp));
+ } else if (!sharedPrefs.getBoolean("animation", false)) {
+ // don't load history at once if animations are set (less resource consumption)
+ Log.d("addToWatchSet", "entries.size(): " + entries.size());
+
+ for (DataMap entry : entries) {
+ double sgv = entry.getDouble("sgvDouble");
+ double high = entry.getDouble("high");
+ double low = entry.getDouble("low");
+ double timestamp = entry.getDouble("timestamp");
+ bgDataList.add(new BgWatchData(sgv, high, low, timestamp));
+ }
+ } else
+
+ Log.d("addToWatchSet", "start removing bgDataList.size(): " + bgDataList.size());
+ HashSet removeSet = new HashSet();
+ double threshold = (new Date().getTime() - (1000 * 60 * 5 * holdInMemory()));
+ for (BgWatchData data : bgDataList) {
+ if (data.timestamp < threshold) {
+ removeSet.add(data);
+
+ }
+ }
+ bgDataList.removeAll(removeSet);
+ Log.d("addToWatchSet", "after bgDataList.size(): " + bgDataList.size());
+ removeSet = null;
+ System.gc();
+ }
+
+ public int darken(int color, double fraction) {
+ int red = Color.red(color);
+ int green = Color.green(color);
+ int blue = Color.blue(color);
+ red = darkenColor(red, fraction);
+ green = darkenColor(green, fraction);
+ blue = darkenColor(blue, fraction);
+ int alpha = Color.alpha(color);
+
+ return Color.argb(alpha, red, green, blue);
+ }
+
+ private int darkenColor(int color, double fraction) {
+
+ //if (sharedPrefs.getBoolean("dark", false)) {
+ return (int) Math.max(color - (color * fraction), 0);
+ //}
+ // return (int)Math.min(color + (color * fraction), 255);
+ }
+
+
+ public void addArch(Canvas canvas, float offset, int color, float size) {
+ Paint paint = new Paint();
+ paint.setColor(color);
+ RectF rectTemp = new RectF(PADDING + offset - CIRCLE_WIDTH / 2, PADDING + offset - CIRCLE_WIDTH / 2, (displaySize.x - PADDING - offset + CIRCLE_WIDTH / 2), (displaySize.y - PADDING - offset + CIRCLE_WIDTH / 2));
+ canvas.drawArc(rectTemp, 270, size, true, paint);
+ }
+
+ public void addArch(Canvas canvas, float start, float offset, int color, float size) {
+ Paint paint = new Paint();
+ paint.setColor(color);
+ RectF rectTemp = new RectF(PADDING + offset - CIRCLE_WIDTH / 2, PADDING + offset - CIRCLE_WIDTH / 2, (displaySize.x - PADDING - offset + CIRCLE_WIDTH / 2), (displaySize.y - PADDING - offset + CIRCLE_WIDTH / 2));
+ canvas.drawArc(rectTemp, start + 270, size, true, paint);
+ }
+
+ public void addIndicator(Canvas canvas, float bg, int color) {
+ float convertedBg;
+ convertedBg = bgToAngle(bg);
+ convertedBg += 270;
+ Paint paint = new Paint();
+ paint.setColor(color);
+ float offset = 9;
+ RectF rectTemp = new RectF(PADDING + offset - CIRCLE_WIDTH / 2, PADDING + offset - CIRCLE_WIDTH / 2, (displaySize.x - PADDING - offset + CIRCLE_WIDTH / 2), (displaySize.y - PADDING - offset + CIRCLE_WIDTH / 2));
+ canvas.drawArc(rectTemp, convertedBg, 2, true, paint);
+ }
+
+ private float bgToAngle(float bg) {
+ if (bg > 100) {
+ return (((bg - 100f) / 300f) * 225f) + 135;
+ } else {
+ return ((bg / 100) * 135);
+ }
+ }
+
+
+ public void addReadingSoft(Canvas canvas, BgWatchData entry) {
+
+ Log.d("CircleWatchface", "addReadingSoft");
+ double size;
+ int color = Color.LTGRAY;
+ if (sharedPrefs.getBoolean("dark", false)) {
+ color = Color.DKGRAY;
+ }
+
+ float offsetMultiplier = (((displaySize.x / 2f) - PADDING) / 12f);
+ float offset = (float) Math.max(1, Math.ceil((new Date().getTime() - entry.timestamp) / (1000 * 60 * 5)));
+ size = bgToAngle((float) entry.sgv);
+ addArch(canvas, offset * offsetMultiplier + 10, color, (float) size);
+ addArch(canvas, (float) size, offset * offsetMultiplier + 10, getBackgroundColor(), (float) (360 - size));
+ addArch(canvas, (offset + .8f) * offsetMultiplier + 10, getBackgroundColor(), 360);
+ }
+
+ public void addReading(Canvas canvas, BgWatchData entry) {
+ Log.d("CircleWatchface", "addReading");
+
+ double size;
+ int color = Color.LTGRAY;
+ int indicatorColor = Color.DKGRAY;
+ if (sharedPrefs.getBoolean("dark", false)) {
+ color = Color.DKGRAY;
+ indicatorColor = Color.LTGRAY;
+ }
+ int barColor = Color.GRAY;
+ if (entry.sgv >= entry.high) {
+ indicatorColor = getHighColor();
+ barColor = darken(getHighColor(), .5);
+ } else if (entry.sgv <= entry.low) {
+ indicatorColor = getLowColor();
+ barColor = darken(getLowColor(), .5);
+ }
+ float offsetMultiplier = (((displaySize.x / 2f) - PADDING) / 12f);
+ float offset = (float) Math.max(1, Math.ceil((new Date().getTime() - entry.timestamp) / (1000 * 60 * 5)));
+ size = bgToAngle((float) entry.sgv);
+ addArch(canvas, offset * offsetMultiplier + 11, barColor, (float) size - 2); // Dark Color Bar
+ addArch(canvas, (float) size - 2, offset * offsetMultiplier + 11, indicatorColor, 2f); // Indicator at end of bar
+ addArch(canvas, (float) size, offset * offsetMultiplier + 11, color, (float) (360f - size)); // Dark fill
+ addArch(canvas, (offset + .8f) * offsetMultiplier + 11, getBackgroundColor(), 360);
+ }
+}
\ No newline at end of file
diff --git a/wear/src/main/java/info/nightscout/androidaps/Home.java b/wear/src/main/java/info/nightscout/androidaps/Home.java
new file mode 100644
index 0000000000..e84098a341
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/Home.java
@@ -0,0 +1,160 @@
+package info.nightscout.androidaps;
+
+import android.graphics.Color;
+import android.support.v4.content.ContextCompat;
+import android.support.wearable.watchface.WatchFaceStyle;
+import android.view.LayoutInflater;
+
+import com.ustwo.clockwise.common.WatchMode;
+
+public class Home extends BaseWatchFace {
+
+ private long chartTapTime = 0;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
+ layoutView = inflater.inflate(R.layout.activity_home, null);
+ performViewSetup();
+ }
+
+ @Override
+ protected void onTapCommand(int tapType, int x, int y, long eventTime) {
+
+ if (tapType == TAP_TYPE_TAP&&
+ x >=chart.getLeft() &&
+ x <= chart.getRight()&&
+ y >= chart.getTop() &&
+ y <= chart.getBottom()){
+ if (eventTime - chartTapTime < 800){
+ changeChartTimeframe();
+ }
+ chartTapTime = eventTime;
+ }
+ }
+
+ private void changeChartTimeframe() {
+ int timeframe = Integer.parseInt(sharedPrefs.getString("chart_timeframe", "3"));
+ timeframe = (timeframe%5) + 1;
+ sharedPrefs.edit().putString("chart_timeframe", "" + timeframe).commit();
+ }
+
+ @Override
+ protected WatchFaceStyle getWatchFaceStyle(){
+ return new WatchFaceStyle.Builder(this).setAcceptsTapEvents(true).build();
+ }
+
+
+ protected void setColorDark() {
+ mLinearLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_statusView));
+ mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime));
+ mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background));
+ if (sgvLevel == 1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor));
+ } else if (sgvLevel == 0) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ } else if (sgvLevel == -1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor));
+ }
+
+ if (ageLevel == 1) {
+ mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTimestamp1_home));
+ } else {
+ mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld));
+ }
+
+ if (batteryLevel == 1) {
+ mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBattery));
+ } else {
+ mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBatteryEmpty));
+ }
+
+ mStatus.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mStatus_home));
+
+ if (chart != null) {
+ highColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor);
+ lowColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor);
+ midColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor);
+ gridColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_gridColor);
+ basalBackgroundColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_dark);
+ basalCenterColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_light);
+ pointSize = 2;
+ setupCharts();
+ }
+ }
+
+ protected void setColorLowRes() {
+ mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime));
+ mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background));
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_Timestamp));
+ if (chart != null) {
+ highColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor);
+ lowColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor);
+ midColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor);
+ gridColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_gridColor);
+ basalBackgroundColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_dark_lowres);
+ basalCenterColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_light_lowres);
+ pointSize = 2;
+ setupCharts();
+ }
+
+ }
+
+
+ protected void setColorBright() {
+
+ if (getCurrentWatchMode() == WatchMode.INTERACTIVE) {
+ mLinearLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_stripe_background));
+ mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_background));
+ if (sgvLevel == 1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor));
+ } else if (sgvLevel == 0) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor));
+ } else if (sgvLevel == -1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor));
+ }
+
+ if (ageLevel == 1) {
+ mTimestamp.setTextColor(Color.WHITE);
+ } else {
+ mTimestamp.setTextColor(Color.RED);
+ }
+
+ if (batteryLevel == 1) {
+ mUploaderBattery.setTextColor(Color.WHITE);
+ } else {
+ mUploaderBattery.setTextColor(Color.RED);
+ }
+ mStatus.setTextColor(Color.WHITE);
+
+ mTime.setTextColor(Color.BLACK);
+ if (chart != null) {
+ highColor = ContextCompat.getColor(getApplicationContext(), R.color.light_highColor);
+ lowColor = ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor);
+ midColor = ContextCompat.getColor(getApplicationContext(), R.color.light_midColor);
+ gridColor = ContextCompat.getColor(getApplicationContext(), R.color.light_gridColor);
+ basalBackgroundColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_light);
+ basalCenterColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_dark);
+ pointSize = 2;
+ setupCharts();
+ }
+ } else {
+ setColorDark();
+ }
+ }
+}
diff --git a/wear/src/main/java/info/nightscout/androidaps/LargeHome.java b/wear/src/main/java/info/nightscout/androidaps/LargeHome.java
new file mode 100644
index 0000000000..9f412702d4
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/LargeHome.java
@@ -0,0 +1,121 @@
+package info.nightscout.androidaps;
+
+import android.graphics.Color;
+import android.support.v4.content.ContextCompat;
+import android.view.LayoutInflater;
+
+import com.ustwo.clockwise.common.WatchMode;
+
+public class LargeHome extends BaseWatchFace {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
+ layoutView = inflater.inflate(R.layout.activity_home_large, null);
+ performViewSetup();
+ }
+
+ @Override
+ protected void setColorDark(){
+ mLinearLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mLinearLayout));
+ mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime));
+ mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background));
+ if (sgvLevel == 1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor));
+ } else if (sgvLevel == 0) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ } else if (sgvLevel == -1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor));
+ }
+
+ if (ageLevel == 1) {
+ mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTimestamp1_home));
+ } else {
+ mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld));
+ }
+
+ if (batteryLevel == 1) {
+ mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBattery));
+ } else {
+ mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBatteryEmpty));
+ }
+
+ mStatus.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mStatus_home));
+ }
+
+ @Override
+ protected void setColorBright() {
+ if (getCurrentWatchMode() == WatchMode.INTERACTIVE) {
+ mLinearLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_stripe_background));
+ mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_background));
+ if (sgvLevel == 1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor));
+ } else if (sgvLevel == 0) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor));
+ } else if (sgvLevel == -1) {
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor));
+ }
+
+ if (ageLevel == 1) {
+ mTimestamp.setTextColor(Color.WHITE);
+ } else {
+ mTimestamp.setTextColor(Color.RED);
+ }
+
+ if (batteryLevel == 1) {
+ mUploaderBattery.setTextColor(Color.WHITE);
+ } else {
+ mUploaderBattery.setTextColor(Color.RED);
+ }
+ mStatus.setTextColor(Color.WHITE);
+ mTime.setTextColor(Color.BLACK);
+ } else {
+ mRelativeLayout.setBackgroundColor(Color.BLACK);
+ mLinearLayout.setBackgroundColor(Color.LTGRAY);
+ if (sgvLevel == 1) {
+ mSgv.setTextColor(Color.YELLOW);
+ mDirection.setTextColor(Color.YELLOW);
+ mDelta.setTextColor(Color.YELLOW);
+ } else if (sgvLevel == 0) {
+ mSgv.setTextColor(Color.WHITE);
+ mDirection.setTextColor(Color.WHITE);
+ mDelta.setTextColor(Color.WHITE);
+ } else if (sgvLevel == -1) {
+ mSgv.setTextColor(Color.RED);
+ mDirection.setTextColor(Color.RED);
+ mDelta.setTextColor(Color.RED);
+ }
+
+ mUploaderBattery.setTextColor(Color.BLACK);
+ mTimestamp.setTextColor(Color.BLACK);
+ mStatus.setTextColor(Color.BLACK);
+ mTime.setTextColor(Color.WHITE);
+ }
+ }
+
+ @Override
+ protected void setColorLowRes() {
+ mLinearLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mLinearLayout));
+ mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime));
+ mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background));
+ mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor));
+ mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTimestamp1_home));
+ mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBattery));
+ mStatus.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mStatus_home));
+ }
+}
diff --git a/wear/src/main/java/info/nightscout/androidaps/ListenerService.java b/wear/src/main/java/info/nightscout/androidaps/ListenerService.java
new file mode 100644
index 0000000000..d969706e11
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/ListenerService.java
@@ -0,0 +1,155 @@
+package info.nightscout.androidaps;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.content.LocalBroadcastManager;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.NodeApi;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+/**
+ * Created by emmablack on 12/26/14.
+ */
+public class ListenerService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks,
+ GoogleApiClient.OnConnectionFailedListener {
+ private static final String WEARABLE_DATA_PATH = "/nightscout_watch_data";
+ private static final String WEARABLE_RESEND_PATH = "/nightscout_watch_data_resend";
+ private static final String OPEN_SETTINGS = "/openwearsettings";
+ private static final String NEW_STATUS_PATH = "/sendstatustowear";
+ public static final String BASAL_DATA_PATH = "/nightscout_watch_basal";
+
+
+ private static final String ACTION_RESEND = "com.dexdrip.stephenblack.nightwatch.RESEND_DATA";
+ private static final String ACTION_RESEND_BULK = "com.dexdrip.stephenblack.nightwatch.RESEND_BULK_DATA";
+ GoogleApiClient googleApiClient;
+ private long lastRequest = 0;
+
+ public class DataRequester extends AsyncTask {
+ Context mContext;
+
+ DataRequester(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (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);
+ }
+ }
+ } else
+ googleApiClient.connect();
+ return null;
+ }
+ }
+
+ public void requestData() {
+ new DataRequester(this).execute();
+ }
+
+ public void googleApiConnect() {
+ googleApiClient = new GoogleApiClient.Builder(this)
+ .addConnectionCallbacks(this)
+ .addOnConnectionFailedListener(this)
+ .addApi(Wearable.API)
+ .build();
+ Wearable.MessageApi.addListener(googleApiClient, this);
+ }
+
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent != null && ACTION_RESEND.equals(intent.getAction())) {
+ googleApiConnect();
+ requestData();
+ }
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDataChanged(DataEventBuffer dataEvents) {
+
+ DataMap dataMap;
+
+ for (DataEvent event : dataEvents) {
+
+ if (event.getType() == DataEvent.TYPE_CHANGED) {
+
+
+ String path = event.getDataItem().getUri().getPath();
+ if (path.equals(OPEN_SETTINGS)) {
+ Intent intent = new Intent(this, NWPreferences.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+
+ } else if (path.equals(NEW_STATUS_PATH)) {
+ dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap();
+ Intent messageIntent = new Intent();
+ messageIntent.setAction(Intent.ACTION_SEND);
+ messageIntent.putExtra("status", dataMap.toBundle());
+ LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
+ } else if (path.equals(BASAL_DATA_PATH)){
+ dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap();
+ Intent messageIntent = new Intent();
+ messageIntent.setAction(Intent.ACTION_SEND);
+ messageIntent.putExtra("basals", dataMap.toBundle());
+ LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
+ } else {
+ dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap();
+ Intent messageIntent = new Intent();
+ messageIntent.setAction(Intent.ACTION_SEND);
+ messageIntent.putExtra("data", dataMap.toBundle());
+ LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent);
+ }
+ }
+ }
+ }
+
+ public static void requestData(Context context) {
+ Intent intent = new Intent(context, ListenerService.class);
+ intent.setAction(ACTION_RESEND);
+ context.startService(intent);
+ }
+
+ @Override
+ public void onConnected(Bundle bundle) {
+ requestData();
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+
+ }
+
+ @Override
+ public void onConnectionFailed(ConnectionResult connectionResult) {
+
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (googleApiClient != null && googleApiClient.isConnected()) {
+ googleApiClient.disconnect();
+ }
+ if (googleApiClient != null) {
+ Wearable.MessageApi.removeListener(googleApiClient, this);
+ }
+ }
+}
diff --git a/wear/src/main/java/info/nightscout/androidaps/NWPreferences.java b/wear/src/main/java/info/nightscout/androidaps/NWPreferences.java
new file mode 100644
index 0000000000..a6e23c23cd
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/NWPreferences.java
@@ -0,0 +1,18 @@
+package info.nightscout.androidaps;
+
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+import info.nightscout.androidaps.R;
+import preference.WearPreferenceActivity;
+
+public class NWPreferences extends WearPreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences);
+ }
+
+}
\ No newline at end of file
diff --git a/wear/src/main/java/info/nightscout/androidaps/TempWatchData.java b/wear/src/main/java/info/nightscout/androidaps/TempWatchData.java
new file mode 100644
index 0000000000..4b0854c3fc
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/TempWatchData.java
@@ -0,0 +1,13 @@
+package info.nightscout.androidaps;
+
+/**
+ * Created by adrian on 17/11/16.
+ */
+
+public class TempWatchData {
+ public long startTime;
+ public double startBasal;
+ public long endTime;
+ public double endBasal;
+ public double amount;
+}
diff --git a/wear/src/main/res/drawable-hdpi/settings_off.png b/wear/src/main/res/drawable-hdpi/settings_off.png
new file mode 100644
index 0000000000..ab7e4249a7
Binary files /dev/null and b/wear/src/main/res/drawable-hdpi/settings_off.png differ
diff --git a/wear/src/main/res/drawable-hdpi/settings_on.png b/wear/src/main/res/drawable-hdpi/settings_on.png
new file mode 100644
index 0000000000..d22797b946
Binary files /dev/null and b/wear/src/main/res/drawable-hdpi/settings_on.png differ
diff --git a/wear/src/main/res/drawable-mdpi/settings_off.png b/wear/src/main/res/drawable-mdpi/settings_off.png
new file mode 100644
index 0000000000..8ead2a2c50
Binary files /dev/null and b/wear/src/main/res/drawable-mdpi/settings_off.png differ
diff --git a/wear/src/main/res/drawable-mdpi/settings_on.png b/wear/src/main/res/drawable-mdpi/settings_on.png
new file mode 100644
index 0000000000..b113766ec4
Binary files /dev/null and b/wear/src/main/res/drawable-mdpi/settings_on.png differ
diff --git a/wear/src/main/res/drawable-nodpi/ic_icon.png b/wear/src/main/res/drawable-nodpi/ic_icon.png
new file mode 100755
index 0000000000..700e274f5b
Binary files /dev/null and b/wear/src/main/res/drawable-nodpi/ic_icon.png differ
diff --git a/wear/src/main/res/drawable-xhdpi/settings_off.png b/wear/src/main/res/drawable-xhdpi/settings_off.png
new file mode 100644
index 0000000000..271ac298d7
Binary files /dev/null and b/wear/src/main/res/drawable-xhdpi/settings_off.png differ
diff --git a/wear/src/main/res/drawable-xhdpi/settings_on.png b/wear/src/main/res/drawable-xhdpi/settings_on.png
new file mode 100644
index 0000000000..db90aa602c
Binary files /dev/null and b/wear/src/main/res/drawable-xhdpi/settings_on.png differ
diff --git a/wear/src/main/res/drawable-xxhdpi/settings_off.png b/wear/src/main/res/drawable-xxhdpi/settings_off.png
new file mode 100644
index 0000000000..7776112fef
Binary files /dev/null and b/wear/src/main/res/drawable-xxhdpi/settings_off.png differ
diff --git a/wear/src/main/res/drawable-xxhdpi/settings_on.png b/wear/src/main/res/drawable-xxhdpi/settings_on.png
new file mode 100644
index 0000000000..6088091eae
Binary files /dev/null and b/wear/src/main/res/drawable-xxhdpi/settings_on.png differ
diff --git a/wear/src/main/res/drawable/ic_icon.png b/wear/src/main/res/drawable/ic_icon.png
new file mode 100644
index 0000000000..d65498c609
Binary files /dev/null and b/wear/src/main/res/drawable/ic_icon.png differ
diff --git a/wear/src/main/res/drawable/watchface_bigchart.png b/wear/src/main/res/drawable/watchface_bigchart.png
new file mode 100644
index 0000000000..34d267e45e
Binary files /dev/null and b/wear/src/main/res/drawable/watchface_bigchart.png differ
diff --git a/wear/src/main/res/drawable/watchface_circle.png b/wear/src/main/res/drawable/watchface_circle.png
new file mode 100644
index 0000000000..d33ae99fd4
Binary files /dev/null and b/wear/src/main/res/drawable/watchface_circle.png differ
diff --git a/wear/src/main/res/drawable/watchface_dark.png b/wear/src/main/res/drawable/watchface_dark.png
new file mode 100644
index 0000000000..9ad14f52a1
Binary files /dev/null and b/wear/src/main/res/drawable/watchface_dark.png differ
diff --git a/wear/src/main/res/drawable/watchface_graph.png b/wear/src/main/res/drawable/watchface_graph.png
new file mode 100644
index 0000000000..af9e88a227
Binary files /dev/null and b/wear/src/main/res/drawable/watchface_graph.png differ
diff --git a/wear/src/main/res/layout/activity_bigchart.xml b/wear/src/main/res/layout/activity_bigchart.xml
new file mode 100644
index 0000000000..1fa3cdad8d
--- /dev/null
+++ b/wear/src/main/res/layout/activity_bigchart.xml
@@ -0,0 +1,12 @@
+
+
diff --git a/wear/src/main/res/layout/activity_home.xml b/wear/src/main/res/layout/activity_home.xml
new file mode 100644
index 0000000000..152f7869a6
--- /dev/null
+++ b/wear/src/main/res/layout/activity_home.xml
@@ -0,0 +1,12 @@
+
+
diff --git a/wear/src/main/res/layout/activity_home_large.xml b/wear/src/main/res/layout/activity_home_large.xml
new file mode 100644
index 0000000000..3af188265c
--- /dev/null
+++ b/wear/src/main/res/layout/activity_home_large.xml
@@ -0,0 +1,12 @@
+
+
diff --git a/wear/src/main/res/layout/modern_layout.xml b/wear/src/main/res/layout/modern_layout.xml
new file mode 100644
index 0000000000..247de09964
--- /dev/null
+++ b/wear/src/main/res/layout/modern_layout.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/wear/src/main/res/layout/rect_activity_bigchart.xml b/wear/src/main/res/layout/rect_activity_bigchart.xml
new file mode 100644
index 0000000000..9fa101227c
--- /dev/null
+++ b/wear/src/main/res/layout/rect_activity_bigchart.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wear/src/main/res/layout/rect_activity_home.xml b/wear/src/main/res/layout/rect_activity_home.xml
new file mode 100644
index 0000000000..344321fea6
--- /dev/null
+++ b/wear/src/main/res/layout/rect_activity_home.xml
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wear/src/main/res/layout/rect_activity_home_large.xml b/wear/src/main/res/layout/rect_activity_home_large.xml
new file mode 100644
index 0000000000..20cc6ee40f
--- /dev/null
+++ b/wear/src/main/res/layout/rect_activity_home_large.xml
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wear/src/main/res/layout/round_activity_bigchart.xml b/wear/src/main/res/layout/round_activity_bigchart.xml
new file mode 100644
index 0000000000..9fa101227c
--- /dev/null
+++ b/wear/src/main/res/layout/round_activity_bigchart.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wear/src/main/res/layout/round_activity_home.xml b/wear/src/main/res/layout/round_activity_home.xml
new file mode 100644
index 0000000000..231a0910b8
--- /dev/null
+++ b/wear/src/main/res/layout/round_activity_home.xml
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wear/src/main/res/layout/round_activity_home_large.xml b/wear/src/main/res/layout/round_activity_home_large.xml
new file mode 100644
index 0000000000..6217af3a5c
--- /dev/null
+++ b/wear/src/main/res/layout/round_activity_home_large.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wear/src/main/res/values/colors.xml b/wear/src/main/res/values/colors.xml
new file mode 100644
index 0000000000..eae224ef41
--- /dev/null
+++ b/wear/src/main/res/values/colors.xml
@@ -0,0 +1,70 @@
+
+
+
+ @color/black
+ @color/black
+ @color/white
+ @color/black
+ @color/black
+ @color/red_600
+ @color/yellow_700
+ @color/red_600
+ @color/black
+ @color/black
+
+ @color/white
+ @color/white
+ @color/black
+ @color/grey_50
+ @color/white
+ @color/black
+ @color/red_600
+ @color/black
+ @color/red_600
+ @color/black
+
+ @color/yellow_A200
+ @color/RED
+ @color/grey_50
+ @color/grey_50
+
+
+ @color/blue_300
+ @color/BLUE
+ @color/grey_300
+ @color/grey_500
+
+
+
+
+ #E53935
+ #FF0000
+
+
+
+ #64B5F6
+
+
+
+ #FBC02D
+ #FFFF00
+
+
+
+ #FAFAFA
+ #E0E0E0
+ #9E9E9E
+
+
+
+ #0000FF
+
+
+
+ #000000
+
+
+
+ #FFFFFF
+
+
\ No newline at end of file
diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..2462ff00a4
--- /dev/null
+++ b/wear/src/main/res/values/strings.xml
@@ -0,0 +1,22 @@
+
+
+
+ AAPS Prefs.
+
+
+ - 1 hour
+ - 2 hours
+ - 3 hours
+ - 4 hours
+ - 5 hours
+
+
+
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+
+
+
diff --git a/wear/src/main/res/xml/preferences.xml b/wear/src/main/res/xml/preferences.xml
new file mode 100644
index 0000000000..97f8bb4027
--- /dev/null
+++ b/wear/src/main/res/xml/preferences.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wear/src/main/res/xml/watch_face.xml b/wear/src/main/res/xml/watch_face.xml
new file mode 100644
index 0000000000..699f0562ca
--- /dev/null
+++ b/wear/src/main/res/xml/watch_face.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/wear/wear.iml b/wear/wear.iml
new file mode 100644
index 0000000000..20c1f89da7
--- /dev/null
+++ b/wear/wear.iml
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateFullDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file