mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 17:11:56 +01:00
Mi Band 8: Send gps to watch (wip)
This commit is contained in:
parent
0c27772bb5
commit
d3eb69fcf7
@ -333,8 +333,10 @@ public abstract class XiaomiCoordinator extends AbstractBLEDeviceCoordinator {
|
||||
//
|
||||
// Calendar
|
||||
//
|
||||
if (supportsCalendarEvents()) {
|
||||
settings.add(R.xml.devicesettings_header_calendar);
|
||||
settings.add(R.xml.devicesettings_sync_calendar);
|
||||
}
|
||||
|
||||
//
|
||||
// Other
|
||||
|
@ -274,8 +274,7 @@ public abstract class XiaomiSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onSetGpsLocation(final Location location) {
|
||||
// TODO onSetGpsLocation
|
||||
super.onSetGpsLocation(location);
|
||||
healthService.onSetGpsLocation(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -17,6 +17,9 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.services;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
@ -27,11 +30,9 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
@ -42,9 +43,10 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePref
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.HuamiExtendedActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.User;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.opentracks.OpenTracksController;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
@ -77,6 +79,9 @@ public class XiaomiHealthService extends AbstractXiaomiService {
|
||||
private static final int CMD_CONFIG_STANDING_REMINDER_SET = 13;
|
||||
private static final int CMD_CONFIG_STRESS_GET = 14;
|
||||
private static final int CMD_CONFIG_STRESS_SET = 15;
|
||||
private static final int CMD_WORKOUT_WATCH_STATUS = 26;
|
||||
private static final int CMD_WORKOUT_WATCH_OPEN = 30;
|
||||
private static final int CMD_WORKOUT_LOCATION = 48;
|
||||
private static final int CMD_REALTIME_STATS_START = 45;
|
||||
private static final int CMD_REALTIME_STATS_STOP = 46;
|
||||
private static final int CMD_REALTIME_STATS_EVENT = 47;
|
||||
@ -84,10 +89,20 @@ public class XiaomiHealthService extends AbstractXiaomiService {
|
||||
private static final int GENDER_MALE = 1;
|
||||
private static final int GENDER_FEMALE = 2;
|
||||
|
||||
private static final int WORKOUT_STARTED = 0;
|
||||
private static final int WORKOUT_RESUMED = 1;
|
||||
private static final int WORKOUT_PAUSED = 2;
|
||||
private static final int WORKOUT_FINISHED = 3;
|
||||
|
||||
private boolean realtimeStarted = false;
|
||||
private boolean realtimeOneShot = false;
|
||||
private int previousSteps = -1;
|
||||
|
||||
private boolean gpsStarted = false;
|
||||
private boolean gpsFixAcquired = false;
|
||||
private boolean workoutStarted = false;
|
||||
private final Handler gpsTimeoutHandler = new Handler();
|
||||
|
||||
private final XiaomiActivityFileFetcher activityFetcher = new XiaomiActivityFileFetcher(this);
|
||||
|
||||
public XiaomiHealthService(final XiaomiSupport support) {
|
||||
@ -116,6 +131,12 @@ public class XiaomiHealthService extends AbstractXiaomiService {
|
||||
case CMD_CONFIG_STRESS_GET:
|
||||
handleStressConfig(cmd.getHealth().getStress());
|
||||
return;
|
||||
case CMD_WORKOUT_WATCH_STATUS:
|
||||
handleWorkoutStatus(cmd.getHealth().getWorkoutStatusWatch());
|
||||
return;
|
||||
case CMD_WORKOUT_WATCH_OPEN:
|
||||
handleWorkoutOpen(cmd.getHealth().getWorkoutOpenWatch());
|
||||
return;
|
||||
case CMD_REALTIME_STATS_EVENT:
|
||||
handleRealtimeStats(cmd.getHealth().getRealTimeStats());
|
||||
return;
|
||||
@ -406,6 +427,128 @@ public class XiaomiHealthService extends AbstractXiaomiService {
|
||||
);
|
||||
}
|
||||
|
||||
private void handleWorkoutOpen(final XiaomiProto.WorkoutOpenWatch workoutOpenWatch) {
|
||||
LOG.debug("Workout open on watch: {}", workoutOpenWatch.getSport());
|
||||
|
||||
workoutStarted = false;
|
||||
|
||||
final boolean sendGpsToBand = getDevicePrefs().getBoolean(DeviceSettingsPreferenceConst.PREF_WORKOUT_SEND_GPS_TO_BAND, false);
|
||||
if (!sendGpsToBand) {
|
||||
getSupport().sendCommand(
|
||||
"send location disabled",
|
||||
XiaomiProto.Command.newBuilder()
|
||||
.setType(COMMAND_TYPE)
|
||||
.setSubtype(CMD_WORKOUT_WATCH_OPEN)
|
||||
.setHealth(XiaomiProto.Health.newBuilder().setWorkoutOpenReply(
|
||||
XiaomiProto.WorkoutOpenReply.newBuilder()
|
||||
.setUnknown1(3)
|
||||
.setUnknown2(2)
|
||||
.setUnknown3(10)
|
||||
))
|
||||
.build()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gpsStarted) {
|
||||
gpsStarted = true;
|
||||
gpsFixAcquired = false;
|
||||
GBLocationManager.start(getSupport().getContext(), getSupport());
|
||||
}
|
||||
|
||||
gpsTimeoutHandler.removeCallbacksAndMessages(null);
|
||||
// Timeout if the watch stops sending workout open
|
||||
gpsTimeoutHandler.postDelayed(() -> GBLocationManager.stop(getSupport().getContext(), getSupport()), 5000);
|
||||
}
|
||||
|
||||
private void handleWorkoutStatus(final XiaomiProto.WorkoutStatusWatch workoutStatus) {
|
||||
LOG.debug("Got workout status: {}", workoutStatus.getStatus());
|
||||
|
||||
final boolean startOnPhone = getDevicePrefs().getBoolean(DeviceSettingsPreferenceConst.PREF_WORKOUT_START_ON_PHONE, false);
|
||||
|
||||
switch (workoutStatus.getStatus()) {
|
||||
case WORKOUT_STARTED:
|
||||
workoutStarted = true;
|
||||
gpsTimeoutHandler.removeCallbacksAndMessages(null);
|
||||
if (startOnPhone) {
|
||||
OpenTracksController.startRecording(getSupport().getContext(), sportToActivityKind(workoutStatus.getSport()));
|
||||
}
|
||||
break;
|
||||
case WORKOUT_RESUMED:
|
||||
case WORKOUT_PAUSED:
|
||||
break;
|
||||
case WORKOUT_FINISHED:
|
||||
GBLocationManager.stop(getSupport().getContext(), getSupport());
|
||||
if (startOnPhone) {
|
||||
OpenTracksController.stopRecording(getSupport().getContext());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void onSetGpsLocation(final Location location) {
|
||||
if (!gpsFixAcquired) {
|
||||
gpsFixAcquired = true;
|
||||
getSupport().sendCommand(
|
||||
"send gps fix",
|
||||
XiaomiProto.Command.newBuilder()
|
||||
.setType(COMMAND_TYPE)
|
||||
.setSubtype(CMD_WORKOUT_WATCH_OPEN)
|
||||
.setHealth(XiaomiProto.Health.newBuilder().setWorkoutOpenReply(
|
||||
XiaomiProto.WorkoutOpenReply.newBuilder()
|
||||
.setUnknown1(0)
|
||||
.setUnknown2(2)
|
||||
.setUnknown3(10)
|
||||
))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
if (workoutStarted) {
|
||||
final XiaomiProto.WorkoutLocation.Builder workoutLocation = XiaomiProto.WorkoutLocation.newBuilder()
|
||||
.setNumSatellites(10)
|
||||
.setTimestamp((int) (location.getTime() / 1000L))
|
||||
.setLongitude(location.getLongitude())
|
||||
.setLatitude(location.getLatitude())
|
||||
.setAltitude(location.getAltitude())
|
||||
.setSpeed(location.getSpeed())
|
||||
.setBearing(location.getBearing())
|
||||
.setHorizontalAccuracy(location.getAccuracy());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
workoutLocation.setVerticalAccuracy(location.getVerticalAccuracyMeters());
|
||||
}
|
||||
|
||||
getSupport().sendCommand(
|
||||
"send gps location",
|
||||
XiaomiProto.Command.newBuilder()
|
||||
.setType(COMMAND_TYPE)
|
||||
.setSubtype(CMD_WORKOUT_LOCATION)
|
||||
.setHealth(XiaomiProto.Health.newBuilder().setWorkoutLocation(workoutLocation))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private int sportToActivityKind(final int sport) {
|
||||
switch (sport) {
|
||||
case 1: // outdoor run
|
||||
case 5: // trail run
|
||||
return ActivityKind.TYPE_RUNNING;
|
||||
case 2:
|
||||
return ActivityKind.TYPE_WALKING;
|
||||
case 3: // hiking
|
||||
case 4: // trekking
|
||||
return ActivityKind.TYPE_HIKING;
|
||||
case 6:
|
||||
return ActivityKind.TYPE_CYCLING;
|
||||
}
|
||||
|
||||
LOG.warn("Unknown sport {}", sport);
|
||||
|
||||
return ActivityKind.TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
public XiaomiActivityFileFetcher getActivityFetcher() {
|
||||
return activityFetcher;
|
||||
}
|
||||
|
@ -392,7 +392,11 @@ message VitalityScore {
|
||||
|
||||
message WorkoutStatusWatch {
|
||||
optional uint32 timestamp = 1; // seconds
|
||||
optional uint32 unknown2 = 2;
|
||||
optional uint32 sport = 3;
|
||||
optional uint32 status = 4; // 0 started, 1 resumed, 2 paused, 3 finished
|
||||
optional bytes activityFileIds = 5;
|
||||
optional uint32 unknown6 = 6; // 2
|
||||
optional uint32 unknown10 = 10; // 0
|
||||
}
|
||||
|
||||
message WorkoutOpenWatch {
|
||||
@ -404,7 +408,7 @@ message WorkoutOpenWatch {
|
||||
|
||||
message WorkoutOpenReply {
|
||||
// 3 2 10 when no gps permissions at all
|
||||
// 5 2 10 when no background permissions?
|
||||
// 5 2 10 when no all time gps permission
|
||||
// ...
|
||||
// 0 * * when phone gps is working fine
|
||||
// 0 2 10
|
||||
@ -415,14 +419,15 @@ message WorkoutOpenReply {
|
||||
}
|
||||
|
||||
message WorkoutLocation {
|
||||
optional uint32 unknown1 = 1; // 10, sometimes 2
|
||||
optional uint32 numSatellites = 1; // 10, sometimes 2?
|
||||
optional uint32 timestamp = 2; // seconds
|
||||
optional double longitude = 3;
|
||||
optional double latitude = 4;
|
||||
optional float unknown6 = 6; // ?
|
||||
optional float unknown7 = 7; // altitude?
|
||||
optional float unknown8 = 8; // ?
|
||||
optional float unknown9 = 9; // ?
|
||||
optional double altitude = 5;
|
||||
optional float speed = 6;
|
||||
optional float bearing = 7;
|
||||
optional float horizontalAccuracy = 8;
|
||||
optional float verticalAccuracy = 9;
|
||||
}
|
||||
|
||||
message RealTimeStats {
|
||||
|
Loading…
Reference in New Issue
Block a user