Skip to content

Commit

Permalink
Add second layer of communication with HTTP server
Browse files Browse the repository at this point in the history
  • Loading branch information
Artaud committed Jan 7, 2022
1 parent decbac6 commit cbfb8e7
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 46 deletions.
1 change: 1 addition & 0 deletions SleepGarmin-android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ dependencies {
})
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.urbandroid:common:1.25@aar'
implementation 'org.nanohttpd:nanohttpd:2.2.0'
testImplementation 'junit:junit:4.12'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
1 change: 1 addition & 0 deletions SleepGarmin-android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
</provider>

<service android:name=".SleepAsAndroidProviderService" />
<service android:name=".HttpServerService" />

<receiver
android:name=".SleepAsGarminReceiver"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import android.content.IntentFilter;
import android.widget.Toast;

import androidx.core.content.ContextCompat;

import com.garmin.android.connectiq.ConnectIQ;
import com.garmin.android.connectiq.ConnectIQAdbStrategy;
import com.garmin.android.connectiq.IQApp;
Expand Down Expand Up @@ -114,6 +116,8 @@ public void onSdkReady() {
registerDeviceStatusReceiver();
isWatchAppAvailable();

startHttpServer(context);

MessageHandler.getInstance().handleMessageFromSleep(initialIntent, context);
}

Expand All @@ -126,6 +130,11 @@ public void onSdkShutDown() {
}
}

private void startHttpServer(Context context) {
Logger.logInfo(TAG + " ConnectIQ intent received, starting HTTP server service...");
ContextCompat.startForegroundService(context,new Intent(context, HttpServerService.class));
}

public void onOpenAppOnWatch(ConnectIQ.IQOpenApplicationListener listener) throws InvalidStateException, ServiceUnavailableException {
if (getDevice() == null) { return; }

Expand Down Expand Up @@ -251,7 +260,7 @@ public void shutdown(Context applicationContext) {
}
} catch (InvalidStateException e) {
// This is usually because the SDK was already shut down so no worries.
Logger.logSevere(e);
Logger.logSevere("This is usually because the SDK was already shut down so no worries.", e);
} catch (IllegalArgumentException e) {
Logger.logSevere(e);
} catch (RuntimeException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.urbandroid.sleep.garmin;

import android.content.Context;

import com.urbandroid.common.logging.Logger;

import org.json.JSONException;

import java.util.Map;

import fi.iki.elonen.NanoHTTPD;

public class HttpServer extends NanoHTTPD {

private static final String TAG = "HttpServer#";
Context context;

public HttpServer(int port, Context context) {
super(port);
this.context = context;
}

private final QueueToWatch queueToWatch = QueueToWatch.getInstance();

@Override
public Response serve(IHTTPSession session) {
Map<String, String> params = session.getParms();

for (Map.Entry<String,String> msg : params.entrySet()) {
// Get all messages from watch and send them to sleep
MessageHandler.getInstance().handleMessageFromWatchUsingHTTP(msg.getKey(), msg.getValue(), context);
}

// Serve all messages enqueued to watch
return newFixedLengthResponse(serveQueue());
}

private String serveQueue() {
String jsonQueue = null;
try {
jsonQueue = queueToWatch.getQueueAsJsonArray();
queueToWatch.emptyQueue();
} catch (JSONException e) {
Logger.logSevere(TAG + "serveQueue", e);
}
Logger.logDebug(TAG + "serveQueue: " + jsonQueue);
return jsonQueue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.urbandroid.sleep.garmin;

import static com.urbandroid.sleep.garmin.Constants.ACTION_STOP_SELF;
import static com.urbandroid.sleep.garmin.Notifications.NOTIFICATION_CHANNEL_ID_TRACKING;

import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

import com.urbandroid.common.logging.Logger;

import java.io.IOException;

public class HttpServerService extends Service {
private static final String TAG = "HttpServerService";
private static final int PORT_DEFAULT = 1765;

private boolean running = false;
private HttpServer server;

@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public void onCreate() {
super.onCreate();

if (!running) {
running = true;
server = new HttpServer(PORT_DEFAULT, this);
try {
server.start();
} catch (IOException e) {
e.printStackTrace();
Logger.logSevere(TAG + ": IOException when starting HttpServer", e);
}
Handler h = new Handler();
}

}

@Override
public void onDestroy() {
super.onDestroy();
server.stop();
}

@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
Logger.logDebug(TAG + "onStartCommand, intent " + ((intent != null && intent.getAction() != null) ? intent.getAction() : "null"));

startForeground();
running = true;

// ciqManager.init(this, intent);

return START_STICKY;
}

private void startForeground() {
final Intent stopIntent = new Intent(this, SleepAsAndroidProviderService.class);
stopIntent.setAction(ACTION_STOP_SELF);

PendingIntent pendingIntent = PendingIntent.getService(this, 151, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT);

if (Build.VERSION.SDK_INT >= 26) {
pendingIntent = PendingIntent.getForegroundService(this, 151, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}

final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID_TRACKING)
.setContentIntent(pendingIntent)
.setColor(getResources().getColor(R.color.tint_dark))
.addAction(R.drawable.ic_action_stop, getResources().getString(R.string.stop), pendingIntent)
.setContentText(getString(R.string.running_server));

if (Build.VERSION.SDK_INT < 24) {
notificationBuilder.setContentTitle(getResources().getString(R.string.app_name_long));
}

notificationBuilder.setSmallIcon(R.drawable.ic_action_track);

startForeground(1350, notificationBuilder.build());
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
import com.garmin.android.connectiq.exception.ServiceUnavailableException;
import com.urbandroid.common.logging.Logger;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
Expand Down Expand Up @@ -48,12 +52,14 @@ public void enqueue(final MessageToWatch message) {
this.logQueue();
}

public String getQueueAsJson() {
Map<String, String> map = new HashMap<>();
public String getQueueAsJsonArray() throws JSONException {
JSONArray ar = new JSONArray();
for (MessageToWatch msg: messageQueue) {
map.put(msg.command, msg.param.toString());
JSONObject jsonMsg = new JSONObject();
ar.put(jsonMsg.put("c", msg.command));
ar.put(jsonMsg.put("d", msg.param));
}
return new JSONObject(map).toString();
return ar.toString();
}

public void logQueue() {
Expand Down Expand Up @@ -85,7 +91,7 @@ public int size() {
return messageQueue.size();
}

public Boolean contains(String message) {
public Boolean contains(MessageToWatch message) {
return messageQueue.contains(message);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
package com.urbandroid.sleep.garmin;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.widget.Toast;

import androidx.core.content.ContextCompat;

import com.garmin.android.connectiq.ConnectIQ;
import com.garmin.android.connectiq.IQDevice;
import com.urbandroid.common.error.ErrorReporter;
import com.urbandroid.common.logging.Logger;

import static com.urbandroid.sleep.garmin.Constants.CHECK_CONNECTED;
import static com.urbandroid.sleep.garmin.Constants.DO_HR_MONITORING;
import static com.urbandroid.sleep.garmin.Constants.HINT;
Expand All @@ -30,6 +16,20 @@
import static com.urbandroid.sleep.garmin.Constants.UPDATE_ALARM;
import static com.urbandroid.sleep.garmin.GlobalInitializer.debug;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.widget.Toast;

import androidx.core.content.ContextCompat;

import com.garmin.android.connectiq.ConnectIQ;
import com.garmin.android.connectiq.IQDevice;
import com.urbandroid.common.error.ErrorReporter;
import com.urbandroid.common.logging.Logger;

/**
* Created by artaud on 29.12.16.
*/
Expand All @@ -44,38 +44,20 @@ public void onReceive(Context context, Intent intent) {
GlobalInitializer.initializeIfRequired(context);
Logger.logInfo(TAG + " onReceive: " + intent.getAction());


checkSleepInstalled(context);

try {
context.getPackageManager().getApplicationInfo(PACKAGE_SLEEP, 0);
} catch (PackageManager.NameNotFoundException e) {
Logger.logInfo(TAG + "Sleep not installed");
sleepInstalled = false;
}

if (!sleepInstalled) {
Toast.makeText(context, R.string.install_saa, Toast.LENGTH_LONG).show();
try {
Intent goToMarket = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + PACKAGE_SLEEP));
context.startActivity(goToMarket);
} catch (Exception e) {
Logger.logInfo(TAG, e);
}
}

try {

if (ConnectIQ.INCOMING_MESSAGE.equals(intent.getAction()) && !SleepAsAndroidProviderService.RUNNING) {

if (debug) {
IQDevice device = intent.getParcelableExtra(ConnectIQ.EXTRA_REMOTE_DEVICE);
if (intent.hasExtra(ConnectIQ.EXTRA_REMOTE_DEVICE) && device.getFriendlyName().equals("Simulator")) {
startProviderServiceBecauseWatchSaidSo(context);
startCommServicesBecauseWatchSaidSo(context);
}

} else if (intent.hasExtra(ConnectIQ.EXTRA_APPLICATION_ID) && IQ_APP_ID.equals(intent.getStringExtra(ConnectIQ.EXTRA_APPLICATION_ID)) &&
!SleepAsAndroidProviderService.RUNNING) {
startProviderServiceBecauseWatchSaidSo(context);
startCommServicesBecauseWatchSaidSo(context);
}
}
} catch (IllegalArgumentException e) {
Expand Down Expand Up @@ -173,11 +155,36 @@ public void onReceive(Context context, Intent intent) {
}
}

private void startProviderServiceBecauseWatchSaidSo(Context context) {
Logger.logInfo(TAG + " ConnectIQ intent received, starting service...");
ContextCompat.startForegroundService(context,new Intent(context, SleepAsAndroidProviderService.class));
private void checkSleepInstalled(Context context) {
try {
context.getPackageManager().getApplicationInfo(PACKAGE_SLEEP, 0);
} catch (PackageManager.NameNotFoundException e) {
Logger.logInfo(TAG + "Sleep not installed");
sleepInstalled = false;
}

if (!sleepInstalled) {
Toast.makeText(context, R.string.install_saa, Toast.LENGTH_LONG).show();
try {
Intent goToMarket = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + PACKAGE_SLEEP));
context.startActivity(goToMarket);
} catch (Exception e) {
Logger.logInfo(TAG, e);
}
}
}

private void startCommServicesBecauseWatchSaidSo(Context context) {
startProviderServiceBecauseWatchSaidSo(context);

Intent startIntent = new Intent(STARTED_ON_WATCH_NAME);
startIntent.setPackage(PACKAGE_SLEEP);
context.sendBroadcast(startIntent);
}


private void startProviderServiceBecauseWatchSaidSo(Context context) {
Logger.logInfo(TAG + " ConnectIQ intent received, starting provider service...");
ContextCompat.startForegroundService(context,new Intent(context, SleepAsAndroidProviderService.class));
}
}
1 change: 1 addition & 0 deletions SleepGarmin-android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<string name="install_sleep_watch_starter">Please install Watch/Phaser Starter for Sleep as Android to start tracking from the watch.</string>
<string name="log">LOG appears below</string>
<string name="running">Sleep tracking with Garmin. Tap to stop.</string>
<string name="running_server">Sleep tracking with Garmin using server. Tap to stop.</string>
<string name="cannot_start_from_phone">Please start tracking from the watch - Garmin Vívoactive 3 has a firmware bug that breaks starting from phone. Tap to see more information.</string>
<string name="generating_report">Generating wearable report...</string>
</resources>
2 changes: 1 addition & 1 deletion SleepGarmin-android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:7.0.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

// NOTE: Do not place your application dependencies here; they belong
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip

0 comments on commit cbfb8e7

Please sign in to comment.