diff --git a/README.md b/README.md
index 0d8889ba..e261759e 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,16 @@ One more try to make it working with Inmotion V8
Added support for Samsung Gear Tizen based watches
-Pebble app code
+## Pebble app code
https://github.com/JumpMaster/WheelLogPebble
-Samsung Gear app code
+## Samsung Gear app code
https://github.com/juliomap/WheelLog-Tizen
+
+## Garmin Connect IQ app code
+
+https://github.com/marccardinal/WheelLog-Garmin-ConnectIQ
+
+The watch application is also available for download directly on the ConnectIQ store https://apps.garmin.com/en-US/apps/07a231a9-3f2f-4762-b0bb-b8a0b5594f40
diff --git a/app/build.gradle b/app/build.gradle
index 50cc0c72..9be99ee3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -7,7 +7,7 @@ repositories {
android {
compileSdkVersion 26
- buildToolsVersion '27.0.3'
+ buildToolsVersion '28.0.3'
lintOptions {
abortOnError false
@@ -17,8 +17,8 @@ android {
applicationId "com.cooper.wheellog"
minSdkVersion 18
targetSdkVersion 26
- versionCode 36
- versionName "2.0.19"
+ versionCode 43
+ versionName "2.0.23"
buildConfigField 'String', 'BUILD_TIME', 'new java.text.SimpleDateFormat("HH:mm dd.MM.yyyy", java.util.Locale.US).format(new java.util.Date(' + System.currentTimeMillis() +'L))'
}
buildTypes {
@@ -33,7 +33,7 @@ android {
dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:design:25.1.0'
implementation 'com.android.support:gridlayout-v7:25.1.0'
implementation 'com.google.android.gms:play-services-drive:10.0.1'
@@ -42,6 +42,8 @@ dependencies {
implementation 'com.jakewharton.timber:timber:4.3.1'
implementation 'com.pavelsikun:material-seekbar-preference:2.3.0+'
implementation 'com.github.PhilJay:MPAndroidChart:v3.0.0'
- implementation "com.github.hotchemi:permissionsdispatcher:2.2.0"
+ implementation 'com.github.hotchemi:permissionsdispatcher:2.2.0'
annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:2.2.0"
+ implementation files('libs/connectiq.jar')
+ implementation 'org.nanohttpd:nanohttpd:2.3.1'
}
diff --git a/app/libs/connectiq.jar b/app/libs/connectiq.jar
new file mode 100644
index 00000000..914b6392
Binary files /dev/null and b/app/libs/connectiq.jar differ
diff --git a/app/release/output.json b/app/release/output.json
index e0817289..07e9fe73 100644
--- a/app/release/output.json
+++ b/app/release/output.json
@@ -1 +1 @@
-[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":36,"versionName":"2.0.19","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
\ No newline at end of file
+[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":43,"versionName":"2.0.23","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 83d20957..7fe5c2ba 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -58,7 +58,9 @@
-
+
+
+
mDevices;
+ private IQDevice mDevice;
+ private IQApp mMyApp;
+
+ private GarminConnectIQWebServer mWebServer;
+
+ public static boolean isInstanceCreated() {
+ return instance != null;
+ }
+
+ public static GarminConnectIQ instance() {
+ return instance;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.i(TAG, "onBind");
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.d(TAG,"onStartCommand");
+ super.onStartCommand(intent, flags, startId);
+
+ instance = this;
+
+ // Setup Connect IQ
+ mMyApp = new IQApp(APP_ID);
+ mConnectIQ = ConnectIQ.getInstance(this, IQConnectType.WIRELESS);
+ mConnectIQ.initialize(this, true, this);
+
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d(TAG,"onDestroy");
+ super.onDestroy();
+
+ cancelRefreshTimer();
+
+ try {
+ mConnectIQ.unregisterAllForEvents();
+ mConnectIQ.shutdown(this);
+ } catch (InvalidStateException e) {
+ // This is usually because the SDK was already shut down
+ // so no worries.
+ }
+
+ stopWebServer();
+
+ instance = null;
+ }
+
+ // General METHODS
+ private void populateDeviceList() {
+ Log.d(TAG,"populateDeviceList");
+
+ try {
+ mDevices = mConnectIQ.getKnownDevices();
+
+ if (mDevices != null && !mDevices.isEmpty()) {
+ mDevice = mDevices.get(0);
+ registerWithDevice();
+ }
+
+ } catch (InvalidStateException e) {
+ // This generally means you forgot to call initialize(), but since
+ // we are in the callback for initialize(), this should never happen
+ } catch (ServiceUnavailableException e) {
+ // This will happen if for some reason your app was not able to connect
+ // to the ConnectIQ service running within Garmin Connect Mobile. This
+ // could be because Garmin Connect Mobile is not installed or needs to
+ // be upgraded.
+ Toast.makeText(this, R.string.garmin_connectiq_service_unavailable_message, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void registerWithDevice() {
+ Log.d(TAG,"registerWithDevice");
+
+ if (mDevice != null && mSdkReady) {
+ // Register for device status updates
+ try {
+ mConnectIQ.registerForDeviceEvents(mDevice, this);
+ } catch (InvalidStateException e) {
+ Log.wtf(TAG, "InvalidStateException: We should not be here!");
+ }
+
+ // Register for application status updates
+ try {
+ mConnectIQ.getApplicationInfo(APP_ID, mDevice, this);
+ } catch (InvalidStateException e1) {
+ Log.d(TAG, "e1: " + e1.getMessage());
+ } catch (ServiceUnavailableException e1) {
+ Log.d(TAG, "e2: " + e1.getMessage());
+ }
+
+ // Register to receive messages from the device
+ try {
+ mConnectIQ.registerForAppEvents(mDevice, mMyApp, this);
+ } catch (InvalidStateException e) {
+ Toast.makeText(this, "ConnectIQ is not in a valid state", Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
+ private void unregisterWithDevice() {
+ Log.d(TAG,"unregisterWithDevice");
+
+ if (mDevice != null && mSdkReady) {
+ // It is a good idea to unregister everything and shut things down to
+ // release resources and prevent unwanted callbacks.
+ try {
+ mConnectIQ.unregisterForDeviceEvents(mDevice);
+
+ if (mMyApp != null) {
+ mConnectIQ.unregisterForApplicationEvents(mDevice, mMyApp);
+ }
+ } catch (InvalidStateException e) {
+ }
+ }
+ }
+
+ private void cancelRefreshTimer() {
+ if (keepAliveTimer != null) {
+ keepAliveTimer.cancel();
+ keepAliveTimer = null;
+ }
+ }
+
+ private void startRefreshTimer() {
+ TimerTask timerTask = new TimerTask() {
+ @Override
+ public void run() {
+ handler.post(new Runnable() {
+ public void run() {
+ refreshData();
+ }
+ });
+ }
+ };
+
+ keepAliveTimer = new Timer();
+ keepAliveTimer.scheduleAtFixedRate(timerTask, 0, 1500); // 1.5cs
+ }
+
+ private void refreshData() {
+ if (WheelData.getInstance() == null)
+ return;
+
+ try {
+ HashMap