Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/wear-os-hex-editor-watchface.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2023-08-13 11:34:37 +0300
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2023-08-13 11:34:37 +0300
commiteb1d62a5b6da490fb7fea6414cfa78a8542b14cb (patch)
tree8a7788a4e3aa41761a5958d182f940ee86afab9e
parent8b2fa7a2ac97990930668c0a0a0bf8252c76ab35 (diff)
Daily step counter
-rw-r--r--app/build.gradle28
-rw-r--r--app/src/main/AndroidManifest.xml5
-rw-r--r--app/src/main/java/com/clusterrr/hexeditorwatchface/HexWatchFace.java151
-rw-r--r--app/src/main/res/xml/watch_face_info.xml5
-rw-r--r--gradle.properties4
5 files changed, 120 insertions, 73 deletions
diff --git a/app/build.gradle b/app/build.gradle
index c18b71c..76e5aac 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -3,14 +3,14 @@ plugins {
}
android {
- compileSdk 32
+ compileSdk 34
defaultConfig {
applicationId "com.clusterrr.hexeditorwatchface"
- minSdk 23
- targetSdk 32
- versionCode 18
- versionName "1.6"
+ minSdk 30
+ targetSdk 34
+ versionCode 19
+ versionName "1.7"
}
buildTypes {
@@ -30,13 +30,15 @@ android {
}
dependencies {
- implementation 'androidx.recyclerview:recyclerview:1.2.1'
- implementation 'com.google.android.support:wearable:2.8.1'
- implementation 'com.google.android.gms:play-services-base:17.6.0'
+ implementation 'androidx.recyclerview:recyclerview:1.3.1'
+ implementation 'androidx.health:health-services-client:1.1.0-alpha01'
+ implementation 'com.google.android.support:wearable:2.9.0'
+ implementation 'com.google.android.gms:play-services-base:18.2.0'
implementation 'androidx.palette:palette:1.0.0'
- implementation 'androidx.appcompat:appcompat:1.4.1'
- implementation 'androidx.preference:preference:1.1.1'
- implementation 'com.google.android.material:material:1.4.0'
- implementation 'androidx.wear:wear:1.2.0'
- compileOnly 'com.google.android.wearable:wearable:2.8.1'
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'androidx.preference:preference:1.2.1'
+ implementation 'com.google.android.material:material:1.9.0'
+ implementation 'androidx.wear:wear:1.3.0'
+ implementation 'com.google.guava:guava:29.0-android'
+ compileOnly 'com.google.android.wearable:wearable:2.9.0'
} \ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c003ccc..1792fa8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required to act as a custom watch face. -->
<uses-permission android:name="android.permission.BODY_SENSORS" />
+ <uses-permission android:name="android.permission.BODY_SENSORS_BACKGROUND" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Required for complications to receive complication data and open the provider chooser. -->
@@ -22,6 +23,10 @@
android:supportsRtl="true"
android:icon="@drawable/icon">
+ <property
+ android:name="com.google.wear.watchface.format.version"
+ android:value="1" />
+
<meta-data
android:name="com.google.android.wearable.standalone"
android:value="true" />
diff --git a/app/src/main/java/com/clusterrr/hexeditorwatchface/HexWatchFace.java b/app/src/main/java/com/clusterrr/hexeditorwatchface/HexWatchFace.java
index e5afd18..14c01cc 100644
--- a/app/src/main/java/com/clusterrr/hexeditorwatchface/HexWatchFace.java
+++ b/app/src/main/java/com/clusterrr/hexeditorwatchface/HexWatchFace.java
@@ -1,5 +1,7 @@
package com.clusterrr.hexeditorwatchface;
+import static kotlin.jvm.internal.Reflection.createKotlinClass;
+
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -17,10 +19,29 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.health.connect.HealthConnectException;
+import android.health.connect.ReadRecordsResponse;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Message;
+import androidx.annotation.NonNull;
+import androidx.health.services.client.HealthServices;
+import androidx.health.services.client.HealthServicesClient;
+import androidx.health.services.client.PassiveListenerCallback;
+import androidx.health.services.client.PassiveMonitoringClient;
+import androidx.health.services.client.data.DataPoint;
+import androidx.health.services.client.data.DataPointContainer;
+import androidx.health.services.client.data.DataType;
+import androidx.health.services.client.data.DeltaDataType;
+import androidx.health.services.client.data.ExerciseType;
+import androidx.health.services.client.data.IntervalDataPoint;
+import androidx.health.services.client.data.PassiveListenerConfig;
+import androidx.health.services.client.data.PassiveMonitoringCapabilities;
+import androidx.health.services.client.data.UserActivityInfo;
+import androidx.health.services.client.data.UserActivityState;
+
+import android.os.SystemClock;
import android.support.wearable.watchface.CanvasWatchFaceService;
import android.support.wearable.watchface.WatchFaceStyle;
import android.util.Log;
@@ -28,9 +49,16 @@ import android.view.SurfaceHolder;
import androidx.core.content.ContextCompat;
+import com.google.common.util.concurrent.ListenableFuture;
+
import java.lang.ref.WeakReference;
+import java.time.Instant;
import java.util.Arrays;
import java.util.Calendar;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@@ -76,7 +104,7 @@ public class HexWatchFace extends CanvasWatchFaceService {
}
}
- private class Engine extends CanvasWatchFaceService.Engine implements SensorEventListener {
+ private class Engine extends CanvasWatchFaceService.Engine implements SensorEventListener, PassiveListenerCallback {
/* Handler to update the time once a second in interactive mode. */
private final Handler mUpdateTimeHandler = new EngineHandler(this);
private Calendar mCalendar;
@@ -108,7 +136,7 @@ public class HexWatchFace extends CanvasWatchFaceService {
private int mTouchCount = 0;
private SensorManager mSensorManager = null;
private Sensor mHeartRateSensor = null;
- private Sensor mStepCountSensor = null;
+ private PassiveMonitoringClient mStepPassiveMonitoringClient = null;
private int mBackgroundMinX = 0;
private int mBackgroundMinY = 0;
private int mBackgroundMaxX = 0;
@@ -144,10 +172,6 @@ public class HexWatchFace extends CanvasWatchFaceService {
mLegendHeartRate = BitmapFactory.decodeResource(res, R.drawable.legend_heart_rate);
mNumbers = new HexNumbers(res);
mSensorManager = ((SensorManager)getSystemService(SENSOR_SERVICE));
-
- if (mCalendar.get(Calendar.DAY_OF_MONTH) == prefs.getInt(getString(R.string.pref_steps_day), 0)) {
- mStepCounter = prefs.getInt(getString(R.string.pref_today_step_last), 0);
- }
}
@Override
@@ -634,20 +658,29 @@ public class HexWatchFace extends CanvasWatchFaceService {
/* && !mAmbient && isVisible() */)
{
// Enable step sensor if need
- if (mStepCountSensor == null) {
- mStepCountSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
- mSensorManager.registerListener(this, mStepCountSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ if (mStepPassiveMonitoringClient == null) {
+ HealthServicesClient healthServicesClient = HealthServices.getClient(getApplicationContext());
+ mStepPassiveMonitoringClient = healthServicesClient.getPassiveMonitoringClient();
+
+ Set<DataType<?,?>> dataTypes = new HashSet<>();
+ dataTypes.add(DataType.STEPS_DAILY);
+ PassiveListenerConfig passiveListenerConfig = PassiveListenerConfig.builder()
+ //.setShouldUserActivityInfoBeRequested(true)
+ .setDataTypes(dataTypes)
+ .build();
+ mStepPassiveMonitoringClient.setPassiveListenerCallback(passiveListenerConfig, this);
Log.i(TAG, "Step sensor enabled");
}
- } else if (mStepCountSensor != null)
+ } else if (mStepPassiveMonitoringClient != null)
{
// Disable step sensor
- mSensorManager.unregisterListener(this, mStepCountSensor);
- mStepCountSensor = null;
+ mStepPassiveMonitoringClient.clearPassiveListenerCallbackAsync();
+ mStepPassiveMonitoringClient = null;
Log.i(TAG, "Step sensor Disabled");
}
}
+ // Heart rate receiver
@Override
public void onSensorChanged(SensorEvent event) {
//Log.d(TAG, "New sensor data: " + event.sensor.getType());
@@ -659,57 +692,59 @@ public class HexWatchFace extends CanvasWatchFaceService {
//Log.d(TAG, "Heart rate: " + mHeartRate);
}
break;
- case Sensor.TYPE_STEP_COUNTER:
- SharedPreferences prefs = getApplicationContext().getSharedPreferences(getString(R.string.app_name), MODE_PRIVATE);
- int steps;
- try {
- steps = (int) event.values[0];
- }
- catch (Exception ex) {
- steps = 0;
- }
- // It's a bit tricky because we can get steps since reboot only
- int todayStepStart = prefs.getInt(getString(R.string.pref_today_step_start), 0);
- if (steps >= 0 && (
- // Check if it's new day
- (mCalendar.get(Calendar.DAY_OF_MONTH) != prefs.getInt(getString(R.string.pref_steps_day), 0))
- || (steps < todayStepStart)) // or value reset
- ) {
- // Store new day values
- prefs.edit()
- .putInt(getString(R.string.pref_steps_day), mCalendar.get(Calendar.DAY_OF_MONTH))
- .putInt(getString(R.string.pref_today_step_start), steps)
- .apply();
- steps = 0;
- } else {
- // Calculate today steps
- steps = Math.max(steps - todayStepStart, 0);
- int last = prefs.getInt(getString(R.string.pref_today_step_last), 0);
- if (steps < last) {
- // Reboot?
- Log.d(TAG, "Reboot? Recalculate todayStepStart from " + todayStepStart + " to todayStepStart-"+last);
- todayStepStart -= last;
- prefs.edit()
- .putInt(getString(R.string.pref_steps_day), mCalendar.get(Calendar.DAY_OF_MONTH))
- .putInt(getString(R.string.pref_today_step_start), steps)
- .apply();
- steps = Math.max(steps - todayStepStart, 0);
- }
- }
- if (steps / STEPS_SAVE_INTERVAL != mStepCounter / STEPS_SAVE_INTERVAL) {
- // Save last value every 10 steps
- prefs.edit()
- .putInt(getString(R.string.pref_today_step_last), steps)
- .apply();
- }
- mStepCounter = steps;
- Log.d(TAG, "Steps: " + steps + ", today: " + mStepCounter);
- break;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
+ // unused
+ }
+
+ // Steps receiver
+ @Override
+ public void onNewDataPointsReceived(@NonNull DataPointContainer dataPoints) {
+ PassiveListenerCallback.super.onNewDataPointsReceived(dataPoints);
+
+ List<IntervalDataPoint<Long>> dps = dataPoints.getData(DataType.STEPS_DAILY);
+ Instant bootInstant = Instant.ofEpochMilli(System.currentTimeMillis() - SystemClock.elapsedRealtime());
+
+ long ts = 0;
+ long steps = 0;
+
+ if (!dps.isEmpty()) {
+ for (IntervalDataPoint<Long> dp : dps)
+ {
+ Instant endTime = dp.getEndInstant(bootInstant);
+ if (endTime.toEpochMilli() > ts)
+ {
+ ts = endTime.toEpochMilli();
+ steps = dp.getValue();
+ }
+ }
+ }
+
+ mStepCounter = (int)steps;
+ Log.d(TAG, "Today steps: " + mStepCounter);
+ }
+
+ @Override
+ public void onRegistered() {
+ PassiveListenerCallback.super.onRegistered();
+ Log.d(TAG, "Step counter sensor registered");
+ }
+
+ @Override
+ public void onPermissionLost() {
+ PassiveListenerCallback.super.onPermissionLost();
+ mStepPassiveMonitoringClient = null;
+ Log.e(TAG, "Step counter permission lost");
+ }
+
+ @Override
+ public void onRegistrationFailed(@NonNull Throwable throwable) {
+ PassiveListenerCallback.super.onRegistrationFailed(throwable);
+ mStepPassiveMonitoringClient = null;
+ Log.d(TAG, "Step counter sensor unregistered");
}
}
} \ No newline at end of file
diff --git a/app/src/main/res/xml/watch_face_info.xml b/app/src/main/res/xml/watch_face_info.xml
new file mode 100644
index 0000000..d3d363c
--- /dev/null
+++ b/app/src/main/res/xml/watch_face_info.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<WatchFaceInfo xmlns:android="http://schemas.android.com/apk/res/android">
+ <Preview android:value="@drawable/preview_notround" />
+ <Editable android:value="true" />
+</WatchFaceInfo> \ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index d1f95ee..37a4c64 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
@@ -21,4 +21,4 @@ android.useAndroidX=true
android.nonTransitiveRClass=true
android.defaults.buildfeatures.buildconfig=true
android.nonFinalResIds=false
-org.gradle.unsafe.configuration-cache=true \ No newline at end of file
+org.gradle.unsafe.configuration-cache=true