Compare commits

...

1 Commits

Author SHA1 Message Date
Roberto Viola
dfcce41bc7 useless? 2024-03-12 11:35:52 +01:00
4 changed files with 239 additions and 62 deletions

View File

@@ -8,6 +8,7 @@
import UIKit
import CoreMotion
import HealthKit
var pedometer = CMPedometer()
@@ -27,9 +28,7 @@ var pedometer = CMPedometer()
} else {
// Fallback on earlier versions
}
if UIDevice.current.userInterfaceIdiom == .phone {
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateHeartRate), userInfo: nil, repeats: true)
}
Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateHeartRate), userInfo: nil, repeats: true)
Server.server?.start()
LocalNotificationHelper.requestPermission()
@@ -124,6 +123,13 @@ var pedometer = CMPedometer()
var sender: String
if UIDevice.current.userInterfaceIdiom == .pad {
sender = "PAD"
if #available(iOS 17.0, *) {
WorkoutTracking.shared.fetchLatestHeartRateSample { sample in
WatchKitConnection.currentHeartRate = Int(sample?.quantity.doubleValue(for: HKUnit(from: "count/min")) ?? 0)
}
} else {
// Fallback on earlier versions
}
} else {
sender = "PHONE"
}

View File

@@ -17,14 +17,17 @@ class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
WorkoutTracking.shared.authorizeHealthKit()
WorkoutTracking.shared.observerHeartRateSamples()
if #available(iOS 17.0, *) {
WorkoutTracking.authorizeHealthKit()
} else {
// Fallback on earlier versions
}
WatchKitConnection.shared.delegate = self
}
}
extension ViewController: WatchKitConnectionDelegate {
func didFinishedActiveSession() {
WatchKitConnection.shared.sendMessage(message: ["username" : "nhathm" as AnyObject])
}
}

View File

@@ -1,29 +1,85 @@
//
// WorkoutTracking.swift
// ElecDemo
// WatchWorkoutTracking.swift
// ElecDemo WatchKit Extension
//
// Created by NhatHM on 8/12/19.
// Copyright © 2019 GST.PID. All rights reserved.
//
import Foundation
import HealthKit
protocol WorkoutTrackingProtocol {
func authorizeHealthKit()
func observerHeartRateSamples()
protocol WorkoutTrackingDelegate: class {
func didReceiveHealthKitHeartRate(_ heartRate: Double)
func didReceiveHealthKitStepCounts(_ stepCounts: Double)
func didReceiveHealthKitStepCadence(_ stepCadence: Double)
func didReceiveHealthKitDistanceCycling(_ distanceCycling: Double)
func didReceiveHealthKitActiveEnergyBurned(_ activeEnergyBurned: Double)
}
class WorkoutTracking {
protocol WorkoutTrackingProtocol {
static func authorizeHealthKit()
func startWorkOut(deviceType: UInt16)
func stopWorkOut()
}
@available(iOS 17.0, *)
@objc class WorkoutTracking: NSObject {
static let shared = WorkoutTracking()
public static var lastDateMetric = Date()
public static var distance = Double()
public static var kcal = Double()
var sport: Int = 0
let healthStore = HKHealthStore()
var observerQuery: HKObserverQuery!
let configuration = HKWorkoutConfiguration()
var workoutBuilder: HKWorkoutBuilder!
var workoutInProgress: Bool = false
var queryRunning = true
init() {
weak var delegate: WorkoutTrackingDelegate?
override init() {
super.init()
}
}
@available(iOS 17.0, *)
extension WorkoutTracking {
func setSport(_ sport: Int) {
self.sport = sport
}
private func configWorkout() {
var activityType = HKWorkoutActivityType.cycling
if self.sport == 1 {
activityType = HKWorkoutActivityType.running
} else if self.sport == 2 {
activityType = HKWorkoutActivityType.cycling
} else if self.sport == 3 {
activityType = HKWorkoutActivityType.rowing
} else if self.sport == 4 {
activityType = HKWorkoutActivityType.elliptical
}
configuration.activityType = activityType
configuration.locationType = .indoor
do {
workoutBuilder = try HKWorkoutBuilder(healthStore: healthStore, configuration: configuration, device: .local())
} catch {
return
}
}
}
@available(iOS 17.0, *)
extension WorkoutTracking: WorkoutTrackingProtocol {
func authorizeHealthKit() {
@objc static func requestAuth() {
authorizeHealthKit()
}
@objc public static func authorizeHealthKit() {
if HKHealthStore.isHealthDataAvailable() {
let infoToRead = Set([
HKSampleType.quantityType(forIdentifier: .stepCount)!,
@@ -33,84 +89,190 @@ extension WorkoutTracking: WorkoutTrackingProtocol {
HKSampleType.workoutType()
])
let infoToShare = Set([
HKSampleType.quantityType(forIdentifier: .stepCount)!,
HKSampleType.quantityType(forIdentifier: .heartRate)!,
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKSampleType.workoutType()
])
var infoToShare: Set<HKSampleType> = []
healthStore.requestAuthorization(toShare: infoToShare, read: infoToRead) { (success, error) in
if success {
print("Authorization healthkit success")
} else if let error = error {
print(error)
if #available(watchOSApplicationExtension 10.0, *) {
infoToShare = Set([
HKSampleType.quantityType(forIdentifier: .stepCount)!,
HKSampleType.quantityType(forIdentifier: .heartRate)!,
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
HKSampleType.quantityType(forIdentifier: .distanceWalkingRunning)!,
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKSampleType.quantityType(forIdentifier: .cyclingPower)!,
HKSampleType.quantityType(forIdentifier: .cyclingSpeed)!,
HKSampleType.quantityType(forIdentifier: .cyclingCadence)!,
HKSampleType.quantityType(forIdentifier: .runningPower)!,
HKSampleType.quantityType(forIdentifier: .runningSpeed)!,
HKSampleType.quantityType(forIdentifier: .runningStrideLength)!,
HKSampleType.quantityType(forIdentifier: .runningVerticalOscillation)!,
HKSampleType.quantityType(forIdentifier: .walkingSpeed)!,
HKSampleType.quantityType(forIdentifier: .walkingStepLength)!,
HKSampleType.workoutType()
])
} else {
// Fallback on earlier versions
infoToShare = Set([
HKSampleType.quantityType(forIdentifier: .stepCount)!,
HKSampleType.quantityType(forIdentifier: .heartRate)!,
HKSampleType.quantityType(forIdentifier: .distanceCycling)!,
HKSampleType.quantityType(forIdentifier: .distanceWalkingRunning)!,
HKSampleType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKSampleType.workoutType()
])
}
DispatchQueue.main.async {
HKHealthStore().requestAuthorization(toShare: infoToShare, read: infoToRead) { (success, error) in
if success {
WorkoutTracking.shared.queryRunning = false
print("Authorization healthkit success")
} else if let error = error {
print(error)
}
}
}
} else {
print("HealthKit not available")
print("HealthKit not avaiable")
}
}
func observerHeartRateSamples() {
guard let heartRateSampleType = HKObjectType.quantityType(forIdentifier: .heartRate) else {
return
@objc func startWorkOut(deviceType: UInt16) {
if(workoutInProgress) {
return;
}
if let observerQuery = observerQuery {
healthStore.stop(observerQuery)
}
observerQuery = HKObserverQuery(sampleType: heartRateSampleType, predicate: nil) { [unowned self] (_, _, error) in
if let error = error {
print("Error: \(error.localizedDescription)")
return
}
self.fetchLatestHeartRateSample { (sample) in
guard let sample = sample else {
return
}
DispatchQueue.main.async {
let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute())
let heartRate = sample.quantity.doubleValue(for: heartRateUnit)
print("Heart Rate Sample: \(heartRate)")
//LocalNotificationHelper.fireHeartRate(heartRate)
}
}
}
healthStore.execute(observerQuery)
healthStore.enableBackgroundDelivery(for: heartRateSampleType, frequency: .immediate) { (success, error) in
workoutInProgress = true;
WorkoutTracking.lastDateMetric = Date()
print("Start workout")
setSport(Int(deviceType))
configWorkout()
workoutBuilder.beginCollection(withStart: Date()) { (success, error) in
print(success)
if let error = error {
print(error)
}
}
}
@objc func stopWorkOut() {
print("Stop workout")
guard let quantityType = HKQuantityType.quantityType(
forIdentifier: .activeEnergyBurned) else {
return
}
let unit = HKUnit.kilocalorie()
let totalEnergyBurned = WorkoutTracking.kcal
let quantity = HKQuantity(unit: unit,
doubleValue: totalEnergyBurned)
let sample = HKCumulativeQuantitySeriesSample(type: quantityType,
quantity: quantity,
start: workoutBuilder.startDate!,
end: Date())
workoutBuilder.add([sample]) {(success, error) in}
let unitDistance = HKUnit.mile()
let miles = WorkoutTracking.distance
let quantityMiles = HKQuantity(unit: unitDistance,
doubleValue: miles)
if(sport == 0) {
guard let quantityTypeDistance = HKQuantityType.quantityType(
forIdentifier: .distanceCycling) else {
return
}
let sampleDistance = HKCumulativeQuantitySeriesSample(type: quantityTypeDistance,
quantity: quantityMiles,
start: workoutBuilder.startDate!,
end: Date())
workoutBuilder.add([sampleDistance]) {(success, error) in
if let error = error {
print(error)
}
}
self.workoutBuilder.endCollection(withEnd: Date()) { (success, error) in
if let error = error {
print(error)
}
self.workoutBuilder.finishWorkout{ (workout, error) in
if let error = error {
print(error)
}
workout?.setValue(quantityMiles, forKey: "totalDistance")
}
}
} else {
guard let quantityTypeDistance = HKQuantityType.quantityType(
forIdentifier: .distanceWalkingRunning) else {
return
}
let sampleDistance = HKCumulativeQuantitySeriesSample(type: quantityTypeDistance,
quantity: quantityMiles,
start: workoutBuilder.startDate!,
end: Date())
workoutBuilder.add([sampleDistance]) {(success, error) in
if let error = error {
print(error)
}
self.workoutBuilder.endCollection(withEnd: Date()) { (success, error) in
if let error = error {
print(error)
}
self.workoutBuilder.finishWorkout{ (workout, error) in
if let error = error {
print(error)
}
workout?.setValue(quantityMiles, forKey: "totalDistance")
}
}
}
}
workoutInProgress = false;
}
}
@available(iOS 17.0, *)
extension WorkoutTracking {
private func fetchLatestHeartRateSample(completionHandler: @escaping (_ sample: HKQuantitySample?) -> Void) {
public func fetchLatestHeartRateSample(completionHandler: @escaping (_ sample: HKQuantitySample?) -> Void) {
if(self.queryRunning) {
return
}
self.queryRunning = true
guard let sampleType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else {
completionHandler(nil)
return
}
let predicate = HKQuery.predicateForSamples(withStart: Date.distantPast, end: Date(), options: .strictEndDate)
let tenMinutesAgo = Calendar.current.date(byAdding: .minute, value: -90, to: Date())!
let predicate = HKQuery.predicateForSamples(withStart: tenMinutesAgo, end: Date(), options: .strictEndDate)
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
let query = HKSampleQuery(sampleType: sampleType,
predicate: predicate,
limit: Int(HKObjectQueryNoLimit),
sortDescriptors: [sortDescriptor]) { (_, results, error) in
self.queryRunning = false
if let error = error {
print("Error: \(error.localizedDescription)")
return
}
completionHandler(results?[0] as? HKQuantitySample)
if(results?.count ?? 0 > 0) {
completionHandler(results?[0] as? HKQuantitySample)
} else {
completionHandler(nil)
}
}
healthStore.execute(query)

View File

@@ -23,6 +23,8 @@ static virtualbike_zwift* _virtualbike_zwift = nil;
static virtualrower* _virtualrower = nil;
static virtualtreadmill_zwift* _virtualtreadmill_zwift = nil;
static WorkoutTracking* workoutTracking = nil;
static GarminConnect* Garmin = 0;
static AdbClient *_adb = 0;
@@ -47,6 +49,10 @@ void lockscreen::request()
if (@available(iOS 17, *)) {
_adb = [[AdbClient alloc] initWithVerbose:YES];
}
if (@available(iOS 17, *)) {
workoutTracking = [[WorkoutTracking alloc] init];
[WorkoutTracking requestAuth];
}
}
long lockscreen::heartRate()